Merge "[WebView Support Library] Add initial WebViewClientCompat" into pi-dev
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index c42446b..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,77 +0,0 @@
-# Copyright (C) 2014 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Disable the frameworks support for PDK builds instead
-# use prebuilts/sdk/current/
-ifneq ($(TARGET_BUILD_PDK),true)
-# Don't include in unbundled build.
-ifeq ($(TARGET_BUILD_APPS),)
-
-SUPPORT_CURRENT_SDK_VERSION := current
-
-###########################################################
-# Find all of the files in the given subdirs that match the
-# specified pattern but do not match another pattern. This
-# function uses $(1) instead of LOCAL_PATH as the base.
-# $(1): the base dir, relative to the root of the source tree.
-# $(2): the file name pattern to match.
-# $(3): the file name pattern to exclude.
-# $(4): a list of subdirs of the base dir.
-# Returns: a list of paths relative to the base dir.
-###########################################################
-
-define find-files-in-subdirs-exclude
-$(sort $(patsubst ./%,%, \
- $(shell cd $(1) ; \
- find -L $(4) -name $(2) -and -not -name $(3) -and -not -name ".*") \
- ))
-endef
-
-###########################################################
-## Find all of the files under the named directories where
-## the file name matches the specified pattern but does not
-## match another pattern. Meant to be used like:
-## SRC_FILES := $(call all-named-files-under,.*\.h,src tests)
-###########################################################
-
-define all-named-files-under-exclude
-$(call find-files-in-subdirs-exclude,$(LOCAL_PATH),"$(1)","$(2)",$(3))
-endef
-
-###########################################################
-## Find all of the files under the current directory where
-## the file name matches the specified pattern but does not
-## match another pattern.
-###########################################################
-
-define all-subdir-named-files-exclude
-$(call all-named-files-under-exclude,$(1),$(2),.)
-endef
-
-# Pre-process support library AIDLs
-aidl_files := $(addprefix $(LOCAL_PATH)/, $(call all-subdir-named-files-exclude,*.aidl,I*.aidl))
-support-aidl := $(TARGET_OUT_COMMON_INTERMEDIATES)/support.aidl
-$(support-aidl): $(aidl_files) | $(AIDL)
- $(AIDL) --preprocess $@ $(aidl_files)
-
-# Build all support libraries
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
-# Clear out variables
-SUPPORT_CURRENT_SDK_VERSION :=
-
-endif
-endif
diff --git a/CleanSpec.mk b/CleanSpec.mk
deleted file mode 100644
index 7dc4050..0000000
--- a/CleanSpec.mk
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (C) 2013 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.
-#
-
-# If you don't need to do a full clean build but would like to touch
-# a file or delete some intermediate files, add a clean step to the end
-# of the list. These steps will only be run once, if they haven't been
-# run before.
-#
-# E.g.:
-# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
-# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
-#
-# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
-# files that are missing or have been moved.
-#
-# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
-# Use $(OUT_DIR) to refer to the "out" directory.
-#
-# If you need to re-do something that's already mentioned, just copy
-# the command and add it to the bottom of the list. E.g., if a change
-# that you made last week required touching a file and a change you
-# made today requires touching the same file, just copy the old
-# touch step and add it to the end of the list.
-#
-# ************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
-# ************************************************
-
-# For example:
-#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
-#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
-#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
-#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
-
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android-support-v*)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/support.aidl)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android-support-customtabs_intermediates/src/)
-$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android-support-annotations_intermediates)
-
-# ************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
-# ************************************************
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/init.gradle b/app-toolkit/init.gradle
index 3c58368..6a91a4e 100644
--- a/app-toolkit/init.gradle
+++ b/app-toolkit/init.gradle
@@ -44,7 +44,8 @@
buildServerAnchorTask.dependsOn createDiffArchive
buildServerAnchorTask.dependsOn createArchive
rootProject.tasks.whenTaskAdded { task ->
- if (CheckExternalDependencyLicensesTask.ROOT_TASK_NAME.equals(task.name)) {
+ if (CheckExternalDependencyLicensesTask.ROOT_TASK_NAME.equals(task.name)
+ || "dejetifyArchive".equals(task.name)) {
buildServerAnchorTask.dependsOn task
}
}
diff --git a/app-toolkit/settings.gradle b/app-toolkit/settings.gradle
index 3cd11cc..4a9a91c 100644
--- a/app-toolkit/settings.gradle
+++ b/app-toolkit/settings.gradle
@@ -79,6 +79,7 @@
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"))
includeProject(":jetifier-gradle-plugin", new File(supportRoot, "jetifier/jetifier/gradle-plugin"))
includeProject(":jetifier-standalone", new File(supportRoot, "jetifier/jetifier/standalone"))
includeProject(":jetifier-preprocessor", new File(supportRoot, "jetifier/jetifier/preprocessor"))
diff --git a/build.gradle b/build.gradle
index c731c92..88257c7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -34,7 +34,6 @@
dependencies {
classpath build_libs.gradle
- classpath build_libs.lint
classpath build_libs.jacoco
classpath build_libs.kotlin.gradle_plugin
}
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 537f042..88a8aa8 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -17,7 +17,6 @@
dependencies {
classpath build_libs.kotlin.gradle_plugin
- classpath build_libs.lint
}
configurations.classpath.resolutionStrategy {
@@ -43,7 +42,6 @@
dependencies {
compile build_libs.gradle
- compile build_libs.lint
compile build_libs.jacoco
compile build_libs.error_prone_gradle
compile build_libs.jarjar_gradle
diff --git a/buildSrc/build_dependencies.gradle b/buildSrc/build_dependencies.gradle
index 4e23e62..0b292f3 100644
--- a/buildSrc/build_dependencies.gradle
+++ b/buildSrc/build_dependencies.gradle
@@ -29,15 +29,7 @@
build_libs.gradle = 'com.android.tools.build:gradle:' + androidPluginVersionOverride
} else {
// Keep gradle plugin version in sync with ub_supportlib-master manifest.
- build_libs.gradle = 'com.android.tools.build:gradle:3.0.0'
-}
-
-// lint
-def lintPluginVersionOverride = System.getenv("LINT_PLUGIN_VERSION")
-if (lintPluginVersionOverride != null) {
- build_libs.lint = 'com.android.tools.lint:lint:' + lintPluginVersionOverride
-} else {
- build_libs.lint = 'com.android.tools.lint:lint:26.0.0'
+ build_libs.gradle = 'com.android.tools.build:gradle:3.2.0-alpha09'
}
// jarjar plugin
diff --git a/buildSrc/init.gradle b/buildSrc/init.gradle
index df9fc85..046376f 100644
--- a/buildSrc/init.gradle
+++ b/buildSrc/init.gradle
@@ -161,6 +161,7 @@
rootProject.tasks.whenTaskAdded { task ->
if ("createArchive".equals(task.name)
|| "distDocs".equals(task.name)
+ || "dejetifyArchive".equals(task.name)
|| CheckExternalDependencyLicensesTask.ROOT_TASK_NAME.equals(task.name)) {
buildOnServerTask.dependsOn task
}
diff --git a/buildSrc/jetify.gradle b/buildSrc/jetify.gradle
index e81d325..c991385 100644
--- a/buildSrc/jetify.gradle
+++ b/buildSrc/jetify.gradle
@@ -25,7 +25,7 @@
dependsOn ':jetifier-standalone:installDist'
inputs.file project.tasks['createArchive'].archivePath
- outputs.file "${rootProject.ext.distDir}/top-of-tree-m2-repository-dejetified-${project.ext.buildNumber}.zip"
+ outputs.file "${rootProject.ext.distDir}/top-of-tree-m2repository-dejetified-${project.ext.buildNumber}.zip"
commandLine ("${jetifierBin}", "-s", "-r", "-outputfile", "${outputs.files.singleFile}", "-i", "${inputs.files.singleFile}", "-l", "error", "-rebuildTopOfTree")
}
diff --git a/buildSrc/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt b/buildSrc/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
index 10c2566..f9c82cf 100644
--- a/buildSrc/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/ErrorProneConfiguration.kt
@@ -27,6 +27,7 @@
val compilerArgs = this.options.compilerArgs
compilerArgs += listOf(
"-XDcompilePolicy=simple", // Workaround for b/36098770
+ "-XepExcludedPaths:.*/(build/generated|external)/.*",
// Enforce the following checks.
"-Xep:RestrictTo:OFF",
@@ -41,6 +42,7 @@
"-Xep:IntLongMath:ERROR",
"-Xep:MissingFail:ERROR",
"-Xep:JavaLangClash:ERROR",
+ "-Xep:PrivateConstructorForUtilityClass:ERROR",
// Nullaway
"-XepIgnoreUnknownCheckNames", // https://github.com/uber/NullAway/issues/25
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
index 4a791cc..9ba3c38 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
@@ -70,5 +70,5 @@
const val ARCH_CORE = "androidx.arch.core"
const val PAGING = "androidx.paging"
const val NAVIGATION = "androidx.navigation"
- const val JETIFIER = "com.android.support.jetifier"
+ const val JETIFIER = "com.android.tools.build.jetifier"
}
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index e2993e9..37869e4 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -23,17 +23,17 @@
/**
* Version code of the support library components.
*/
- val SUPPORT_LIBRARY = Version("1.0.0-SNAPSHOT")
+ val SUPPORT_LIBRARY = Version("1.0.0-alpha1")
/**
* Version code for Room
*/
- val ROOM = Version("2.0.0-SNAPSHOT")
+ val ROOM = Version("2.0.0-alpha1")
/**
* Version code for Lifecycle extensions (ProcessLifecycleOwner, Fragment support)
*/
- val LIFECYCLES_EXT = Version("2.0.0-SNAPSHOT")
+ val LIFECYCLES_EXT = Version("2.0.0-alpha1")
/**
* Version code for Lifecycle LiveData
@@ -48,9 +48,9 @@
/**
* Version code for Paging
*/
- val PAGING = Version("2.0.0-SNAPSHOT")
+ val PAGING = Version("2.0.0-alpha1")
- private val LIFECYCLES = Version("2.0.0-SNAPSHOT")
+ private val LIFECYCLES = Version("2.0.0-alpha1")
/**
* Version code for Lifecycle libs that are required by the support library
@@ -65,7 +65,7 @@
/**
* Version code for shared code of flatfoot
*/
- val ARCH_CORE = Version("2.0.0-SNAPSHOT")
+ val ARCH_CORE = Version("2.0.0-alpha1")
/**
* Version code for shared code of flatfoot runtime
@@ -80,5 +80,5 @@
/**
* Version code for Jetifier
*/
- val JETIFIER = Version("0.2.0")
+ val JETIFIER = Version("0.0.1")
}
diff --git a/buildSrc/src/main/kotlin/androidx/build/SupportAndroidLibraryPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/SupportAndroidLibraryPlugin.kt
index d65dd5e..dd2e6da 100644
--- a/buildSrc/src/main/kotlin/androidx/build/SupportAndroidLibraryPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/SupportAndroidLibraryPlugin.kt
@@ -160,11 +160,8 @@
lintOptions.isNoLines = false
lintOptions.isQuiet = true
- //lintOptions.fatal("NewApi")
+ lintOptions.fatal("NewApi")
lintOptions.fatal("ObsoleteSdkInt")
- lintOptions.disable("NewApi")
- lintOptions.disable("MissingPermission")
- lintOptions.disable("ResourceType")
if (verifyTranslations) {
lintOptions.fatal("MissingTranslation")
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index 9cd1a93..606ac30 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -55,7 +55,7 @@
const val TEST_RULES_TMP = "com.android.temp.support.test:rules:1.0.1"
// AndroidX libraries
-private const val SUPPORT_VERSION = "1.0.0-SNAPSHOT"
+private const val SUPPORT_VERSION = "1.0.0-alpha1"
const val SUPPORT_ANNOTATIONS = "androidx.annotation:annotation:$SUPPORT_VERSION"
const val SUPPORT_APPCOMPAT = "androidx.appcompat:appcompat:$SUPPORT_VERSION"
const val SUPPORT_CARDVIEW = "androidx.cardview:cardview:$SUPPORT_VERSION"
@@ -66,7 +66,8 @@
const val SUPPORT_V4 = "androidx.legacy:legacy-support-v4:$SUPPORT_VERSION"
// Arch libraries
-const val ARCH_LIFECYCLE_RUNTIME = "androidx.lifecycle:lifecycle-runtime:2.0.0-SNAPSHOT@aar"
-const val ARCH_LIFECYCLE_LIVEDATA_CORE = "androidx.lifecycle:lifecycle-livedata-core:2.0.0-SNAPSHOT@aar"
-const val ARCH_LIFECYCLE_VIEWMODEL = "androidx.lifecycle:lifecycle-viewmodel:2.0.0-SNAPSHOT@aar"
-const val ARCH_LIFECYCLE_EXTENSIONS = "androidx.lifecycle:lifecycle-extensions:2.0.0-SNAPSHOT@aar"
+const val ARCH_LIFECYCLE_RUNTIME = "androidx.lifecycle:lifecycle-runtime:2.0.0-alpha1@aar"
+const val ARCH_LIFECYCLE_LIVEDATA_CORE =
+ "androidx.lifecycle:lifecycle-livedata-core:2.0.0-alpha1@aar"
+const val ARCH_LIFECYCLE_VIEWMODEL = "androidx.lifecycle:lifecycle-viewmodel:2.0.0-alpha1@aar"
+const val ARCH_LIFECYCLE_EXTENSIONS = "androidx.lifecycle:lifecycle-extensions:2.0.0-alpha1@aar"
diff --git a/car/api/current.txt b/car/api/current.txt
new file mode 100644
index 0000000..0aee549
--- /dev/null
+++ b/car/api/current.txt
@@ -0,0 +1,462 @@
+package androidx.car.app {
+
+ public class CarAlertDialog extends android.app.Dialog {
+ }
+
+ public static final class CarAlertDialog.Builder {
+ ctor public CarAlertDialog.Builder(android.content.Context);
+ method public androidx.car.app.CarAlertDialog create();
+ method public androidx.car.app.CarAlertDialog.Builder setBody(int);
+ method public androidx.car.app.CarAlertDialog.Builder setBody(java.lang.CharSequence);
+ method public androidx.car.app.CarAlertDialog.Builder setCancelable(boolean);
+ method public androidx.car.app.CarAlertDialog.Builder setNegativeButton(int, android.content.DialogInterface.OnClickListener);
+ method public androidx.car.app.CarAlertDialog.Builder setNegativeButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+ method public androidx.car.app.CarAlertDialog.Builder setOnCancelListener(android.content.DialogInterface.OnCancelListener);
+ method public androidx.car.app.CarAlertDialog.Builder setOnDismissListener(android.content.DialogInterface.OnDismissListener);
+ method public androidx.car.app.CarAlertDialog.Builder setPositiveButton(int, android.content.DialogInterface.OnClickListener);
+ method public androidx.car.app.CarAlertDialog.Builder setPositiveButton(java.lang.CharSequence, android.content.DialogInterface.OnClickListener);
+ method public androidx.car.app.CarAlertDialog.Builder setTitle(int);
+ method public androidx.car.app.CarAlertDialog.Builder setTitle(java.lang.CharSequence);
+ }
+
+ public class CarListDialog extends android.app.Dialog {
+ }
+
+ public static final class CarListDialog.Builder {
+ ctor public CarListDialog.Builder(android.content.Context);
+ method public androidx.car.app.CarListDialog create();
+ method public androidx.car.app.CarListDialog.Builder setCancelable(boolean);
+ method public androidx.car.app.CarListDialog.Builder setInitialPosition(int);
+ method public androidx.car.app.CarListDialog.Builder setItems(java.lang.String[], android.content.DialogInterface.OnClickListener);
+ method public androidx.car.app.CarListDialog.Builder setOnCancelListener(android.content.DialogInterface.OnCancelListener);
+ method public androidx.car.app.CarListDialog.Builder setOnDismissListener(android.content.DialogInterface.OnDismissListener);
+ }
+
+}
+
+package androidx.car.drawer {
+
+ public class CarDrawerActivity extends androidx.appcompat.app.AppCompatActivity {
+ ctor public CarDrawerActivity();
+ method protected int getContentContainerId();
+ method protected androidx.car.drawer.CarDrawerController getDrawerController();
+ method protected deprecated androidx.car.drawer.CarDrawerAdapter getRootAdapter();
+ method public void setMainContent(android.view.View);
+ method public void setMainContent(int);
+ method public void setToolbarAlwaysShow();
+ method public void setToolbarClickThrough(boolean);
+ method public void setToolbarCollapsible();
+ method public void setToolbarElevation(float);
+ method public void setToolbarElevationWithAnimation(float);
+ }
+
+ public abstract class CarDrawerAdapter extends androidx.recyclerview.widget.RecyclerView.Adapter implements androidx.car.drawer.DrawerItemClickListener androidx.car.widget.PagedListView.ItemCap {
+ ctor protected CarDrawerAdapter(android.content.Context, boolean);
+ method public void cleanup();
+ method protected abstract int getActualItemCount();
+ method public final int getItemCount();
+ method public final int getItemViewType(int);
+ method public final void onBindViewHolder(androidx.car.drawer.DrawerItemViewHolder, int);
+ method public final androidx.car.drawer.DrawerItemViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+ method protected abstract void populateViewHolder(androidx.car.drawer.DrawerItemViewHolder, int);
+ method public final void setMaxItems(int);
+ method public final void setTitle(java.lang.CharSequence);
+ method public void start();
+ method public void stop();
+ method protected boolean usesSmallLayout(int);
+ }
+
+ public class CarDrawerController {
+ ctor public CarDrawerController(androidx.appcompat.widget.Toolbar, androidx.drawerlayout.widget.DrawerLayout, androidx.appcompat.app.ActionBarDrawerToggle);
+ method public void addDrawerListener(androidx.drawerlayout.widget.DrawerLayout.DrawerListener);
+ method public void closeDrawer();
+ method public void onConfigurationChanged(android.content.res.Configuration);
+ method public boolean onOptionsItemSelected(android.view.MenuItem);
+ method public void openDrawer();
+ method public final void pushAdapter(androidx.car.drawer.CarDrawerAdapter);
+ method public void removeDrawerListener(androidx.drawerlayout.widget.DrawerLayout.DrawerListener);
+ method public void scrollToPosition(int);
+ method public void setRootAdapter(androidx.car.drawer.CarDrawerAdapter);
+ method public void showLoadingProgressBar(boolean);
+ method public void syncState();
+ }
+
+ public abstract interface DrawerItemClickListener {
+ method public abstract void onItemClick(int);
+ }
+
+ public class DrawerItemViewHolder extends androidx.recyclerview.widget.RecyclerView.ViewHolder {
+ method public android.widget.ImageView getEndIcon();
+ method public android.widget.ImageView getIcon();
+ method public android.widget.TextView getText();
+ method public android.widget.TextView getTitle();
+ }
+
+}
+
+package androidx.car.moderator {
+
+ public class ContentRateLimiter {
+ ctor public ContentRateLimiter(float, float, long);
+ method public float getAvailablePermits();
+ method public float getMaxStoredPermits();
+ method public void setAvailablePermits(float);
+ method public void setUnlimitedMode(boolean);
+ method public boolean tryAcquire();
+ method public boolean tryAcquire(int);
+ }
+
+ public class SpeedBumpView extends android.widget.FrameLayout {
+ ctor public SpeedBumpView(android.content.Context);
+ ctor public SpeedBumpView(android.content.Context, android.util.AttributeSet);
+ ctor public SpeedBumpView(android.content.Context, android.util.AttributeSet, int);
+ ctor public SpeedBumpView(android.content.Context, android.util.AttributeSet, int, int);
+ method public void onAttachedToWindow();
+ method public void onDetachedFromWindow();
+ }
+
+}
+
+package androidx.car.utils {
+
+ public class ColumnCalculator {
+ method public int getColumnWidth();
+ method public int getGutterSize();
+ method public static androidx.car.utils.ColumnCalculator getInstance(android.content.Context);
+ method public int getNumOfColumns();
+ method public int getNumOfGutters();
+ method public int getSizeForColumnSpan(int);
+ }
+
+ public class ListItemBackgroundResolver {
+ method public static void setBackground(android.view.View, int, int);
+ }
+
+}
+
+package androidx.car.widget {
+
+ public class ActionBar extends android.widget.RelativeLayout {
+ ctor public ActionBar(android.content.Context);
+ ctor public ActionBar(android.content.Context, android.util.AttributeSet);
+ ctor public ActionBar(android.content.Context, android.util.AttributeSet, int);
+ ctor public ActionBar(android.content.Context, android.util.AttributeSet, int, int);
+ method public void setExpandCollapseView(android.view.View);
+ method public void setView(android.view.View, int);
+ method public void setViews(android.view.View[]);
+ field public static final int SLOT_EXPAND_COLLAPSE = 3; // 0x3
+ field public static final int SLOT_LEFT = 1; // 0x1
+ field public static final int SLOT_MAIN = 0; // 0x0
+ field public static final int SLOT_RIGHT = 2; // 0x2
+ }
+
+ public static abstract class ActionBar.SlotPosition implements java.lang.annotation.Annotation {
+ }
+
+ public class AlphaJumpBucketer {
+ ctor public AlphaJumpBucketer();
+ ctor public AlphaJumpBucketer(androidx.car.widget.AlphaJumpBucketer.Bucket[]);
+ method public java.util.Collection<androidx.car.widget.IAlphaJumpAdapter.Bucket> createBuckets(java.lang.String[]);
+ method public java.util.Collection<androidx.car.widget.IAlphaJumpAdapter.Bucket> createBuckets(java.lang.Iterable<java.lang.String>);
+ method public java.util.Collection<androidx.car.widget.IAlphaJumpAdapter.Bucket> createBuckets(java.util.Iterator<java.lang.String>);
+ }
+
+ public static class AlphaJumpBucketer.Bucket implements androidx.car.widget.IAlphaJumpAdapter.Bucket {
+ method public int getIndex();
+ method public java.lang.CharSequence getLabel();
+ method public boolean isEmpty();
+ }
+
+ public class AlphaJumpOverlayView extends androidx.gridlayout.widget.GridLayout {
+ ctor public AlphaJumpOverlayView(android.content.Context);
+ }
+
+ public class ClickThroughToolbar extends androidx.appcompat.widget.Toolbar {
+ ctor public ClickThroughToolbar(android.content.Context);
+ ctor public ClickThroughToolbar(android.content.Context, android.util.AttributeSet);
+ ctor public ClickThroughToolbar(android.content.Context, android.util.AttributeSet, int);
+ method public void setClickPassThrough(boolean);
+ }
+
+ public final class ColumnCardView extends androidx.cardview.widget.CardView {
+ ctor public ColumnCardView(android.content.Context);
+ ctor public ColumnCardView(android.content.Context, android.util.AttributeSet);
+ ctor public ColumnCardView(android.content.Context, android.util.AttributeSet, int);
+ method public int getColumnSpan();
+ method public void onMeasure(int, int);
+ method public void setColumnSpan(int);
+ }
+
+ public abstract class DayNightStyle implements java.lang.annotation.Annotation {
+ field public static final int AUTO = 0; // 0x0
+ field public static final int AUTO_INVERSE = 1; // 0x1
+ field public static final int FORCE_DAY = 3; // 0x3
+ field public static final int FORCE_NIGHT = 2; // 0x2
+ }
+
+ public abstract interface IAlphaJumpAdapter {
+ method public abstract java.util.Collection<androidx.car.widget.IAlphaJumpAdapter.Bucket> getAlphaJumpBuckets();
+ method public abstract void onAlphaJumpEnter();
+ method public abstract void onAlphaJumpLeave(androidx.car.widget.IAlphaJumpAdapter.Bucket);
+ }
+
+ public static abstract interface IAlphaJumpAdapter.Bucket {
+ method public abstract int getIndex();
+ method public abstract java.lang.CharSequence getLabel();
+ method public abstract boolean isEmpty();
+ }
+
+ public abstract class ListItem<VH extends androidx.car.widget.ListItem.ViewHolder> {
+ ctor public ListItem();
+ method public final void addViewBinder(androidx.car.widget.ListItem.ViewBinder<VH>);
+ method public final void addViewBinder(androidx.car.widget.ListItem.ViewBinder<VH>, androidx.car.widget.ListItem.ViewBinder<VH>);
+ method public abstract int getViewType();
+ method protected boolean isDirty();
+ method protected void markClean();
+ method protected void markDirty();
+ method protected abstract void onBind(VH);
+ method public boolean removeViewBinder(androidx.car.widget.ListItem.ViewBinder<VH>);
+ method protected abstract void resolveDirtyState();
+ method public void setHideDivider(boolean);
+ method public boolean shouldHideDivider();
+ }
+
+ public static abstract interface ListItem.ViewBinder<VH> {
+ method public abstract void bind(VH);
+ }
+
+ public static abstract class ListItem.ViewHolder extends androidx.recyclerview.widget.RecyclerView.ViewHolder {
+ ctor public ListItem.ViewHolder(android.view.View);
+ method public final void addCleanUp(androidx.car.widget.ListItem.ViewBinder<androidx.car.widget.ListItem.ViewHolder>);
+ method public final void cleanUp();
+ }
+
+ public class ListItemAdapter extends androidx.recyclerview.widget.RecyclerView.Adapter implements androidx.car.widget.PagedListView.DividerVisibilityManager androidx.car.widget.PagedListView.ItemCap {
+ ctor public ListItemAdapter(android.content.Context, androidx.car.widget.ListItemProvider);
+ ctor public ListItemAdapter(android.content.Context, androidx.car.widget.ListItemProvider, int);
+ method public int getItemCount();
+ method public void onBindViewHolder(androidx.car.widget.ListItem.ViewHolder, int);
+ method public androidx.car.widget.ListItem.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
+ method public void registerListItemViewType(int, int, java.util.function.Function<android.view.View, androidx.car.widget.ListItem.ViewHolder>);
+ method public void setMaxItems(int);
+ method public boolean shouldHideDivider(int);
+ method public void start();
+ method public void stop();
+ }
+
+ public static final class ListItemAdapter.BackgroundStyle {
+ field public static final int CARD = 2; // 0x2
+ field public static final int NONE = 1; // 0x1
+ field public static final int PANEL = 3; // 0x3
+ field public static final int SOLID = 0; // 0x0
+ }
+
+ public abstract class ListItemProvider {
+ ctor public ListItemProvider();
+ method public abstract androidx.car.widget.ListItem get(int);
+ method public abstract int size();
+ }
+
+ public static class ListItemProvider.ListProvider extends androidx.car.widget.ListItemProvider {
+ ctor public ListItemProvider.ListProvider(java.util.List<androidx.car.widget.ListItem>);
+ method public androidx.car.widget.ListItem get(int);
+ method public int size();
+ }
+
+ public class PagedListView extends android.widget.FrameLayout {
+ ctor public PagedListView(android.content.Context);
+ ctor public PagedListView(android.content.Context, android.util.AttributeSet);
+ ctor public PagedListView(android.content.Context, android.util.AttributeSet, int);
+ ctor public PagedListView(android.content.Context, android.util.AttributeSet, int, int);
+ method public void addItemDecoration(androidx.recyclerview.widget.RecyclerView.ItemDecoration);
+ method public void addOnItemTouchListener(androidx.recyclerview.widget.RecyclerView.OnItemTouchListener);
+ method public androidx.recyclerview.widget.RecyclerView.Adapter<? extends androidx.recyclerview.widget.RecyclerView.ViewHolder> getAdapter();
+ method public int getMaxPages();
+ method public int getPage(int);
+ method public androidx.recyclerview.widget.RecyclerView getRecyclerView();
+ method public int getRowsPerPage();
+ method public boolean isAtEnd();
+ method public boolean isAtStart();
+ method public void onLayout(boolean, int, int, int, int);
+ method public void onRestoreInstanceState(android.os.Parcelable);
+ method public android.os.Parcelable onSaveInstanceState();
+ method public int positionOf(android.view.View);
+ method public void removeItemDecoration(androidx.recyclerview.widget.RecyclerView.ItemDecoration);
+ method public void removeOnItemTouchListener(androidx.recyclerview.widget.RecyclerView.OnItemTouchListener);
+ method public void resetMaxPages();
+ method public void resetScrollbarColor();
+ method public void scrollToPosition(int);
+ method public void setAdapter(androidx.recyclerview.widget.RecyclerView.Adapter<? extends androidx.recyclerview.widget.RecyclerView.ViewHolder>);
+ method public void setDayNightStyle(int);
+ method public void setDefaultMaxPages(int);
+ method public void setDividerVisibilityManager(androidx.car.widget.PagedListView.DividerVisibilityManager);
+ method public void setDownButtonIcon(android.graphics.drawable.Drawable);
+ method public void setGutter(int);
+ method public void setGutterSize(int);
+ method public void setItemSpacing(int);
+ method public void setListContentTopOffset(int);
+ method public void setMaxPages(int);
+ method public void setOnScrollListener(androidx.car.widget.PagedListView.OnScrollListener);
+ method public void setScrollBarContainerWidth(int);
+ method public void setScrollBarTopMargin(int);
+ method public void setScrollbarColor(int);
+ method public void setUpButtonIcon(android.graphics.drawable.Drawable);
+ method public void snapToPosition(int);
+ field public static final int DEFAULT_MAX_CLICKS = 6; // 0x6
+ field public static final int UNLIMITED_PAGES = -1; // 0xffffffff
+ }
+
+ public static abstract interface PagedListView.DividerVisibilityManager {
+ method public abstract boolean shouldHideDivider(int);
+ }
+
+ public static abstract class PagedListView.Gutter implements java.lang.annotation.Annotation {
+ field public static final int BOTH = 3; // 0x3
+ field public static final int END = 2; // 0x2
+ field public static final int NONE = 0; // 0x0
+ field public static final int START = 1; // 0x1
+ }
+
+ public static abstract interface PagedListView.ItemCap {
+ method public abstract void setMaxItems(int);
+ field public static final int UNLIMITED = -1; // 0xffffffff
+ }
+
+ public static abstract interface PagedListView.ItemPositionOffset {
+ method public abstract void setPositionOffset(int);
+ }
+
+ public static abstract class PagedListView.OnScrollListener {
+ ctor public PagedListView.OnScrollListener();
+ method public void onAlphaJumpButtonClicked();
+ method public void onGestureDown();
+ method public void onGestureUp();
+ method public void onReachBottom();
+ method public void onScrollDownButtonClicked();
+ method public void onScrollStateChanged(androidx.recyclerview.widget.RecyclerView, int);
+ method public void onScrollUpButtonClicked();
+ method public void onScrolled(androidx.recyclerview.widget.RecyclerView, int, int);
+ }
+
+ public class PagedScrollBarView extends android.widget.FrameLayout {
+ ctor public PagedScrollBarView(android.content.Context, android.util.AttributeSet);
+ ctor public PagedScrollBarView(android.content.Context, android.util.AttributeSet, int);
+ ctor public PagedScrollBarView(android.content.Context, android.util.AttributeSet, int, int);
+ method public boolean isDownEnabled();
+ method public boolean isDownPressed();
+ method public boolean isUpPressed();
+ method public void resetThumbColor();
+ method public void setDayNightStyle(int);
+ method public void setDownButtonIcon(android.graphics.drawable.Drawable);
+ method public void setDownEnabled(boolean);
+ method public void setPaginationListener(androidx.car.widget.PagedScrollBarView.PaginationListener);
+ method public void setParameters(int, int, int, boolean);
+ method public void setThumbColor(int);
+ method public void setUpButtonIcon(android.graphics.drawable.Drawable);
+ method public void setUpEnabled(boolean);
+ }
+
+ public static abstract interface PagedScrollBarView.PaginationListener {
+ method public abstract void onAlphaJump();
+ method public abstract void onPaginate(int);
+ field public static final int PAGE_DOWN = 1; // 0x1
+ field public static final int PAGE_UP = 0; // 0x0
+ }
+
+ public final class PagedSmoothScroller extends androidx.recyclerview.widget.LinearSmoothScroller {
+ ctor public PagedSmoothScroller(android.content.Context);
+ }
+
+ public class PagedSnapHelper extends androidx.recyclerview.widget.LinearSnapHelper {
+ ctor public PagedSnapHelper(android.content.Context);
+ method public void attachToRecyclerView(androidx.recyclerview.widget.RecyclerView);
+ method public boolean isAtEnd(androidx.recyclerview.widget.RecyclerView.LayoutManager);
+ method public boolean isAtStart(androidx.recyclerview.widget.RecyclerView.LayoutManager);
+ }
+
+ public class SeekbarListItem extends androidx.car.widget.ListItem {
+ ctor public SeekbarListItem(android.content.Context, int, int, android.widget.SeekBar.OnSeekBarChangeListener, java.lang.String);
+ method public static androidx.car.widget.SeekbarListItem.ViewHolder createViewHolder(android.view.View);
+ method public int getViewType();
+ method protected void onBind(androidx.car.widget.SeekbarListItem.ViewHolder);
+ method protected void resolveDirtyState();
+ method public void setPrimaryActionEmptyIcon();
+ method public void setPrimaryActionIcon(int);
+ method public void setPrimaryActionIcon(android.graphics.drawable.Drawable);
+ method public void setSupplementalEmptyIcon(boolean);
+ method public void setSupplementalIcon(int, boolean);
+ method public void setSupplementalIcon(int, boolean, android.view.View.OnClickListener);
+ }
+
+ public static class SeekbarListItem.ViewHolder extends androidx.car.widget.ListItem.ViewHolder {
+ ctor public SeekbarListItem.ViewHolder(android.view.View);
+ method public android.widget.RelativeLayout getContainerLayout();
+ method public android.widget.ImageView getPrimaryIcon();
+ method public android.widget.SeekBar getSeekBar();
+ method public android.widget.LinearLayout getSeekBarContainer();
+ method public android.widget.ImageView getSupplementalIcon();
+ method public android.view.View getSupplementalIconDivider();
+ method public android.widget.TextView getText();
+ }
+
+ public class SubheaderListItem extends androidx.car.widget.ListItem {
+ ctor public SubheaderListItem(android.content.Context, java.lang.String);
+ method public static androidx.car.widget.SubheaderListItem.ViewHolder createViewHolder(android.view.View);
+ method public int getViewType();
+ method protected void onBind(androidx.car.widget.SubheaderListItem.ViewHolder);
+ method protected void resolveDirtyState();
+ method public void setText(java.lang.String);
+ method public void setTextStartMarginType(int);
+ field public static final int TEXT_START_MARGIN_TYPE_LARGE = 2; // 0x2
+ field public static final int TEXT_START_MARGIN_TYPE_NONE = 0; // 0x0
+ field public static final int TEXT_START_MARGIN_TYPE_SMALL = 1; // 0x1
+ }
+
+ public static abstract class SubheaderListItem.TextStartMarginType implements java.lang.annotation.Annotation {
+ }
+
+ public static class SubheaderListItem.ViewHolder extends androidx.car.widget.ListItem.ViewHolder {
+ ctor public SubheaderListItem.ViewHolder(android.view.View);
+ method public android.widget.TextView getText();
+ }
+
+ public class TextListItem extends androidx.car.widget.ListItem {
+ ctor public TextListItem(android.content.Context);
+ method public static androidx.car.widget.TextListItem.ViewHolder createViewHolder(android.view.View);
+ method public int getViewType();
+ method public void onBind(androidx.car.widget.TextListItem.ViewHolder);
+ method protected void resolveDirtyState();
+ method public void setAction(java.lang.String, boolean, android.view.View.OnClickListener);
+ method public void setActions(java.lang.String, boolean, android.view.View.OnClickListener, java.lang.String, boolean, android.view.View.OnClickListener);
+ method public void setBody(java.lang.String);
+ method public void setBody(java.lang.String, boolean);
+ method public void setOnClickListener(android.view.View.OnClickListener);
+ method public void setPrimaryActionEmptyIcon();
+ method public void setPrimaryActionIcon(int, boolean);
+ method public void setPrimaryActionIcon(android.graphics.drawable.Drawable, boolean);
+ method public void setPrimaryActionNoIcon();
+ method public void setSupplementalIcon(int, boolean);
+ method public void setSupplementalIcon(int, boolean, android.view.View.OnClickListener);
+ method public void setSwitch(boolean, boolean, android.widget.CompoundButton.OnCheckedChangeListener);
+ method public void setSwitchState(boolean);
+ method public void setTitle(java.lang.String);
+ }
+
+ public static class TextListItem.ViewHolder extends androidx.car.widget.ListItem.ViewHolder {
+ ctor public TextListItem.ViewHolder(android.view.View);
+ method public android.widget.Button getAction1();
+ method public android.view.View getAction1Divider();
+ method public android.widget.Button getAction2();
+ method public android.view.View getAction2Divider();
+ method public android.widget.TextView getBody();
+ method public android.widget.RelativeLayout getContainerLayout();
+ method public android.widget.ImageView getPrimaryIcon();
+ method public android.widget.ImageView getSupplementalIcon();
+ method public android.view.View getSupplementalIconDivider();
+ method public android.widget.Switch getSwitch();
+ method public android.view.View getSwitchDivider();
+ method public android.widget.TextView getTitle();
+ }
+
+}
+
diff --git a/car/build.gradle b/car/build.gradle
index d971e78..52d2a40 100644
--- a/car/build.gradle
+++ b/car/build.gradle
@@ -22,6 +22,7 @@
api project(':annotation')
api project(':legacy-support-v4')
api project(':recyclerview')
+ api project(':gridlayout')
androidTestImplementation(TEST_RUNNER_TMP, libs.exclude_for_espresso)
androidTestImplementation(ESPRESSO_CORE_TMP, libs.exclude_for_espresso)
@@ -46,7 +47,7 @@
supportLibrary {
name = "Android Car Support UI"
- publish = false
+ publish = true
mavenVersion = LibraryVersions.SUPPORT_LIBRARY
mavenGroup = LibraryGroups.CAR
inceptionYear = "2017"
diff --git a/car/car-stubs/android.car.jar b/car/car-stubs/android.car.jar
index b7ee974..01f5cd9 100644
--- a/car/car-stubs/android.car.jar
+++ b/car/car-stubs/android.car.jar
Binary files differ
diff --git a/car/res/drawable/car_borderless_button_background.xml b/car/res/drawable/car_borderless_button_background.xml
new file mode 100644
index 0000000..0ef4bea
--- /dev/null
+++ b/car/res/drawable/car_borderless_button_background.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+<ripple
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/listItemBackgroundColor">
+ <item android:id="@android:id/mask">
+ <color android:color="#42ffffff "/>
+ </item>
+</ripple>
diff --git a/car/res/layout/car_alpha_jump_button.xml b/car/res/layout/car_alpha_jump_button.xml
new file mode 100644
index 0000000..c5e90fd
--- /dev/null
+++ b/car/res/layout/car_alpha_jump_button.xml
@@ -0,0 +1,35 @@
+<?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.
+ -->
+
+<!-- This is sized in code, so we'll set it to 0dp for now. -->
+<android.support.v7.widget.CardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:grid="http://schemas.android.com/apk/res-auto"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ grid:layout_gravity="fill"
+ grid:layout_columnWeight="1"
+ grid:layout_rowWeight="1"
+ android:layout_margin="@dimen/car_padding_2">
+ <TextView
+ android:textAppearance="@style/TextAppearance.Car.Title"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/car_card_ripple_background"
+ android:id="@+id/button"
+ android:gravity="center"/>
+</android.support.v7.widget.CardView>
diff --git a/car/res/layout/car_list_item_seekbar_content.xml b/car/res/layout/car_list_item_seekbar_content.xml
index 2e3b165..eedbe73 100644
--- a/car/res/layout/car_list_item_seekbar_content.xml
+++ b/car/res/layout/car_list_item_seekbar_content.xml
@@ -14,15 +14,14 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
+<!-- This layout should only be used by class SeekbarListItem, as it requires layout params
+ being set programmatically depending on item data/view configuration. -->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="wrap_content"
android:layout_height="match_parent">
- <!-- This layout should only be used by class SeekbarListItem, as it requires layout params
- being set programmatically depending on item data/view configuration. -->
-
<!-- Primary Action. -->
<ImageView
android:id="@+id/primary_icon"
diff --git a/car/res/layout/car_list_item_subheader_content.xml b/car/res/layout/car_list_item_subheader_content.xml
new file mode 100644
index 0000000..7bd8580
--- /dev/null
+++ b/car/res/layout/car_list_item_subheader_content.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.
+ -->
+<!-- This layout should only be used by class SubheaderListItem, as it requires layout params
+ being set programmatically depending on item data/view configuration. -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:maxLines="1"
+ android:textAppearance="@style/TextAppearance.Car.Subheader"/>
+</FrameLayout>
diff --git a/car/res/layout/car_list_item_text_content.xml b/car/res/layout/car_list_item_text_content.xml
index a85a31e..0e5d4b9 100644
--- a/car/res/layout/car_list_item_text_content.xml
+++ b/car/res/layout/car_list_item_text_content.xml
@@ -88,8 +88,6 @@
android:ellipsize="end"
android:maxLength="@integer/car_borderless_button_text_length_limit"
android:maxLines="1"
- android:background="@color/car_card"
- android:foreground="@drawable/car_card_ripple_background"
style="?android:attr/borderlessButtonStyle"/>
<View
android:id="@+id/action1_divider"
@@ -107,7 +105,5 @@
android:ellipsize="end"
android:maxLength="@integer/car_borderless_button_text_length_limit"
android:maxLines="1"
- android:background="@color/car_card"
- android:foreground="@drawable/car_card_ripple_background"
style="?android:attr/borderlessButtonStyle"/>
</RelativeLayout>
diff --git a/car/res/layout/car_paged_scrollbar_buttons.xml b/car/res/layout/car_paged_scrollbar_buttons.xml
index e982b66..cd62329 100644
--- a/car/res/layout/car_paged_scrollbar_buttons.xml
+++ b/car/res/layout/car_paged_scrollbar_buttons.xml
@@ -26,10 +26,26 @@
android:id="@+id/page_up"
android:layout_width="@dimen/car_scroll_bar_button_size"
android:layout_height="@dimen/car_scroll_bar_button_size"
+ android:background="@drawable/car_button_ripple_background"
+ android:focusable="false"
+ android:hapticFeedbackEnabled="false"
+ android:src="@drawable/ic_up"
+ android:scaleType="centerInside" />
+
+ <!-- TODO(deanh): Replace this with a proper asset when we have done. -->
+ <TextView
+ android:textAppearance="@style/TextAppearance.Car.Title"
+ android:id="@+id/alpha_jump"
+ android:layout_width="@dimen/car_scroll_bar_button_size"
+ android:layout_height="@dimen/car_scroll_bar_button_size"
android:background="@drawable/car_card_ripple_background"
android:focusable="false"
android:hapticFeedbackEnabled="false"
- android:src="@drawable/ic_up" />
+ android:gravity="center"
+ android:layout_marginBottom="@dimen/car_padding_3"
+ android:layout_marginTop="@dimen/car_padding_3"
+ android:visibility="gone"
+ android:text="A" />
<FrameLayout
android:id="@+id/filler"
@@ -51,8 +67,9 @@
android:id="@+id/page_down"
android:layout_width="@dimen/car_scroll_bar_button_size"
android:layout_height="@dimen/car_scroll_bar_button_size"
- android:background="@drawable/car_card_ripple_background"
+ android:background="@drawable/car_button_ripple_background"
android:focusable="false"
android:hapticFeedbackEnabled="false"
- android:src="@drawable/ic_down" />
+ android:src="@drawable/ic_down"
+ android:scaleType="centerInside" />
</LinearLayout>
diff --git a/car/res/values-as/strings.xml b/car/res/values-as/strings.xml
new file mode 100644
index 0000000..e5a0015
--- /dev/null
+++ b/car/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<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-or/strings.xml b/car/res/values-or/strings.xml
new file mode 100644
index 0000000..3a003b8
--- /dev/null
+++ b/car/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<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/attrs.xml b/car/res/values/attrs.xml
index 0548f79..7108c05 100644
--- a/car/res/values/attrs.xml
+++ b/car/res/values/attrs.xml
@@ -111,6 +111,8 @@
<attr name="listItemTitleTextAppearance" format="reference"/>
<!-- The TextAppearance of the body text in the ListItem. -->
<attr name="listItemBodyTextAppearance" format="reference"/>
+ <!-- The TextAppearance of the text in the Subheader ListItem. -->
+ <attr name="listItemSubheaderTextAppearance" format="reference"/>
</declare-styleable>
<!-- The attributes for customizing the appearance of the hamburger and back arrow in the
diff --git a/car/res/values/integers.xml b/car/res/values/integers.xml
index 6c737af..a9f6284 100644
--- a/car/res/values/integers.xml
+++ b/car/res/values/integers.xml
@@ -58,4 +58,8 @@
<integer name="car_action_bar_expand_anim_duration">333</integer>
<integer name="car_action_bar_collapse_anim_duration">233</integer>
+ <!-- The number of columns of buttons to display in the alpha jump overview. -->
+ <!-- TODO(deanh): this could be calculated dynamically, or something else to better support
+ adaptive/responsive. -->
+ <integer name="alpha_jump_button_columns">6</integer>
</resources>
diff --git a/car/res/values/styles.xml b/car/res/values/styles.xml
index ccd661a..7e32c39 100644
--- a/car/res/values/styles.xml
+++ b/car/res/values/styles.xml
@@ -58,6 +58,21 @@
<item name="android:textColor">@color/car_title2_light</item>
</style>
+ <!-- The styling for subheader text. This text uses color accent. -->
+ <style name="TextAppearance.Car.Subheader" parent="TextAppearance.Car.Title2" >
+ <item name="android:textColor">@color/car_accent</item>
+ </style>
+
+ <!-- Subheader text that is permanently a dark color. -->
+ <style name="TextAppearance.Car.Subheader.Dark" parent="TextAppearance.Car.Subheader" >
+ <item name="android:textColor">@color/car_accent_dark</item>
+ </style>
+
+ <!-- Subheader text that is permanently a light color. -->
+ <style name="TextAppearance.Car.Subheader.Light" parent="TextAppearance.Car.Subheader" >
+ <item name="android:textColor">@color/car_accent_light</item>
+ </style>
+
<!-- The styling for the main headline text. The color of this text changes based on the
day/night mode. -->
<style name="TextAppearance.Car.Headline1">
@@ -205,6 +220,7 @@
<item name="android:paddingEnd">@dimen/car_borderless_button_horizontal_padding</item>
<item name="android:textSize">@dimen/car_action1_size</item>
<item name="android:textColor">@drawable/car_borderless_button_text_color</item>
+ <item name="android:background">@drawable/car_borderless_button_background</item>
</style>
<style name="Widget.Car.Button.Borderless.Colored.Light">
diff --git a/car/res/values/themes.xml b/car/res/values/themes.xml
index bc3880d..60949e5 100644
--- a/car/res/values/themes.xml
+++ b/car/res/values/themes.xml
@@ -36,6 +36,7 @@
<item name="listItemBackgroundColor">@color/car_card</item>
<item name="listItemTitleTextAppearance">@style/TextAppearance.Car.Body1</item>
<item name="listItemBodyTextAppearance">@style/TextAppearance.Car.Body2</item>
+ <item name="listItemSubheaderTextAppearance">@style/TextAppearance.Car.Subheader</item>
</style>
<!-- Theme for the Car that is a passthrough for the default theme. -->
@@ -51,6 +52,7 @@
<item name="listItemBackgroundColor">@color/car_card_dark</item>
<item name="listItemTitleTextAppearance">@style/TextAppearance.Car.Body1.Light</item>
<item name="listItemBodyTextAppearance">@style/TextAppearance.Car.Body2.Light</item>
+ <item name="listItemSubheaderTextAppearance">@style/TextAppearance.Car.Subheader.Light</item>
</style>
<!-- A Theme for activities that have a drawer affordance. This theme will automatically switch
diff --git a/car/src/androidTest/java/androidx/car/utils/CarUxRestrictionsTestUtils.java b/car/src/androidTest/java/androidx/car/utils/CarUxRestrictionsTestUtils.java
index 15ff3f1..7100488 100644
--- a/car/src/androidTest/java/androidx/car/utils/CarUxRestrictionsTestUtils.java
+++ b/car/src/androidTest/java/androidx/car/utils/CarUxRestrictionsTestUtils.java
@@ -30,7 +30,7 @@
return new CarUxRestrictions(true, CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED, 0);
}
- public static CarUxRestrictions getUnrestricted() {
- return new CarUxRestrictions(false, CarUxRestrictions.UX_RESTRICTIONS_UNRESTRICTED, 0);
+ public static CarUxRestrictions getBaseline() {
+ return new CarUxRestrictions(false, CarUxRestrictions.UX_RESTRICTIONS_BASELINE, 0);
}
}
diff --git a/car/src/androidTest/java/androidx/car/widget/SubheaderListItemTest.java b/car/src/androidTest/java/androidx/car/widget/SubheaderListItemTest.java
new file mode 100644
index 0000000..1c5bc39
--- /dev/null
+++ b/car/src/androidTest/java/androidx/car/widget/SubheaderListItemTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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.car.widget;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.contrib.RecyclerViewActions.scrollToPosition;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertThat;
+
+import android.content.pm.PackageManager;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import androidx.car.test.R;
+
+/**
+ * Tests the layout configuration in {@link SubheaderListItem}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SubheaderListItemTest {
+
+ @Rule
+ public ActivityTestRule<PagedListViewTestActivity> mActivityRule =
+ new ActivityTestRule<>(PagedListViewTestActivity.class);
+
+ private PagedListViewTestActivity mActivity;
+ private PagedListView mPagedListView;
+
+ private boolean isAutoDevice() {
+ PackageManager packageManager = mActivityRule.getActivity().getPackageManager();
+ return packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ }
+
+ @Before
+ public void setUp() {
+ Assume.assumeTrue(isAutoDevice());
+ mActivity = mActivityRule.getActivity();
+ mPagedListView = mActivity.findViewById(R.id.paged_list_view);
+ }
+
+ private void setupPagedListView(List<? extends ListItem> items) {
+ ListItemProvider provider = new ListItemProvider.ListProvider(
+ new ArrayList<>(items));
+ try {
+ mActivityRule.runOnUiThread(() -> {
+ mPagedListView.setAdapter(new ListItemAdapter(mActivity, provider));
+ });
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ throw new RuntimeException(throwable);
+ }
+ // Wait for paged list view to layout by using espresso to scroll to a position.
+ onView(withId(R.id.recycler_view)).perform(scrollToPosition(0));
+ }
+
+ private SubheaderListItem.ViewHolder getViewHolderAtPosition(int position) {
+ return (SubheaderListItem.ViewHolder) mPagedListView.getRecyclerView()
+ .findViewHolderForAdapterPosition(position);
+ }
+
+ private TextListItem.ViewHolder getTextViewHolderAtPosition(int position) {
+ return (TextListItem.ViewHolder) mPagedListView.getRecyclerView()
+ .findViewHolderForAdapterPosition(position);
+ }
+
+ @Test
+ public void testEmptyStartMargin() {
+ SubheaderListItem subheader = new SubheaderListItem(mActivity, "text");
+ subheader.setTextStartMarginType(SubheaderListItem.TEXT_START_MARGIN_TYPE_NONE);
+
+ TextListItem item = new TextListItem(mActivity);
+ item.setTitle("title");
+ item.setPrimaryActionNoIcon();
+
+ setupPagedListView(Arrays.asList(subheader, item));
+
+ assertThat(getViewHolderAtPosition(0).getText().getLeft(),
+ is(equalTo(getTextViewHolderAtPosition(1).getTitle().getLeft())));
+ }
+
+ @Test
+ public void testStartMarginMatchesSmallIcon() {
+ SubheaderListItem subheader = new SubheaderListItem(mActivity, "text");
+ subheader.setTextStartMarginType(SubheaderListItem.TEXT_START_MARGIN_TYPE_SMALL);
+
+ TextListItem item = new TextListItem(mActivity);
+ item.setTitle("title");
+ item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, /* useLargeIcon= */ false);
+
+ setupPagedListView(Arrays.asList(subheader, item));
+
+ assertThat(getViewHolderAtPosition(0).getText().getLeft(),
+ is(equalTo(getTextViewHolderAtPosition(1).getTitle().getLeft())));
+ }
+
+ @Test
+ public void testStartMarginMatchesLargeIcon() {
+ SubheaderListItem subheader = new SubheaderListItem(mActivity, "text");
+ subheader.setTextStartMarginType(SubheaderListItem.TEXT_START_MARGIN_TYPE_LARGE);
+
+ TextListItem item = new TextListItem(mActivity);
+ item.setTitle("title");
+ item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, /* useLargeIcon= */ true);
+
+ setupPagedListView(Arrays.asList(subheader, item));
+
+ assertThat(getViewHolderAtPosition(0).getText().getLeft(),
+ is(equalTo(getTextViewHolderAtPosition(1).getTitle().getLeft())));
+ }
+}
diff --git a/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java b/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java
index d413b5c..9a9e46d 100644
--- a/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java
+++ b/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java
@@ -28,6 +28,7 @@
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.hamcrest.number.IsCloseTo.closeTo;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -40,7 +41,6 @@
import android.support.test.filters.SmallTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
-import androidx.recyclerview.widget.LinearLayoutManager;
import android.text.InputFilter;
import android.text.TextUtils;
import android.view.View;
@@ -61,6 +61,7 @@
import androidx.car.test.R;
import androidx.car.utils.CarUxRestrictionsTestUtils;
+import androidx.recyclerview.widget.LinearLayoutManager;
/**
* Tests the layout configuration in {@link TextListItem}.
@@ -803,7 +804,7 @@
refreshUi();
assertTrue(Arrays.asList(viewHolder.getBody().getFilters()).contains(filter));
- viewHolder.complyWithUxRestrictions(CarUxRestrictionsTestUtils.getUnrestricted());
+ viewHolder.complyWithUxRestrictions(CarUxRestrictionsTestUtils.getBaseline());
refreshUi();
assertTrue(Arrays.asList(viewHolder.getBody().getFilters()).contains(filter));
}
diff --git a/car/src/main/java/androidx/car/app/CarAlertDialog.java b/car/src/main/java/androidx/car/app/CarAlertDialog.java
index ee67cf7..453ad4e 100644
--- a/car/src/main/java/androidx/car/app/CarAlertDialog.java
+++ b/car/src/main/java/androidx/car/app/CarAlertDialog.java
@@ -21,8 +21,6 @@
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Bundle;
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.MotionEvent;
@@ -33,6 +31,8 @@
import android.widget.Button;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
import androidx.car.R;
/**
@@ -338,7 +338,7 @@
* Builder class that can be used to create a {@link CarAlertDialog} by configuring the options
* for what shows up in the resulting dialog.
*/
- public static class Builder {
+ public static final class Builder {
private final Context mContext;
private final DialogData mDialogData;
@@ -409,8 +409,8 @@
* an "OK" action).
*
* @param textId The resource id of the string to be used for the positive button text.
- * @param listener A {@link OnClickListener} to be invoked when the button is clicked. Can
- * be {@code null} to represent no listener.
+ * @param listener A {@link android.content.DialogInterface.OnClickListener} to be invoked
+ * when the button is clicked. Can be {@code null} to represent no listener.
* @return This {@code Builder} object to allow for chaining of calls.
*/
public Builder setPositiveButton(@StringRes int textId,
@@ -429,8 +429,8 @@
* an "OK" action).
*
* @param text The string to be used for the positive button text.
- * @param listener A {@link OnClickListener} to be invoked when the button is clicked. Can
- * be {@code null} to represent no listener.
+ * @param listener A {@link android.content.DialogInterface.OnClickListener} to be invoked
+ * when the button is clicked. Can be {@code null} to represent no listener.
* @return This {@code Builder} object to allow for chaining of calls.
*/
public Builder setPositiveButton(CharSequence text, @Nullable OnClickListener listener) {
@@ -447,8 +447,8 @@
* <p>The negative button should be used to cancel any actions the dialog represents.
*
* @param textId The resource id of the string to be used for the negative button text.
- * @param listener A {@link OnClickListener} to be invoked when the button is clicked. Can
- * be {@code null} to represent no listener.
+ * @param listener A {@link android.content.DialogInterface.OnClickListener} to be invoked
+ * when the button is clicked. Can be {@code null} to represent no listener.
* @return This {@code Builder} object to allow for chaining of calls.
*/
public Builder setNegativeButton(@StringRes int textId,
@@ -466,8 +466,8 @@
* <p>The negative button should be used to cancel any actions the dialog represents.
*
* @param text The string to be used for the negative button text.
- * @param listener A {@link OnClickListener} to be invoked when the button is clicked. Can
- * be {@code null} to represent no listener.
+ * @param listener A {@link android.content.DialogInterface.OnClickListener} to be invoked
+ * when the button is clicked. Can be {@code null} to represent no listener.
* @return This {@code Builder} object to allow for chaining of calls.
*/
public Builder setNegativeButton(CharSequence text, @Nullable OnClickListener listener) {
@@ -518,8 +518,8 @@
/**
* Creates an {@link CarAlertDialog} with the arguments supplied to this {@code Builder}.
*
- * <p>Calling this method does not display the dialog. If no additional processing is
- * needed, {@link #show()} may be called instead to both create and display the dialog.
+ * <p>Calling this method does not display the dialog. Utilize this dialog within a
+ * {@link androidx.fragment.app.DialogFragment} to show the dialog.
*/
public CarAlertDialog create() {
CarAlertDialog dialog = new CarAlertDialog(mContext, mDialogData);
@@ -531,21 +531,5 @@
return dialog;
}
-
- /**
- * Creates an {@link CarAlertDialog} with the arguments supplied to this {@code Builder}
- * and immediately displays the dialog.
- *
- * <p>Calling this method is functionally identical to:
- * <pre>
- * CarAlertDialog dialog = new CarAlertDialog.Builder().create();
- * dialog.show();
- * </pre>
- */
- public CarAlertDialog show() {
- CarAlertDialog dialog = create();
- dialog.show();
- return dialog;
- }
}
}
diff --git a/car/src/main/java/androidx/car/app/CarListDialog.java b/car/src/main/java/androidx/car/app/CarListDialog.java
index 6261745..4e02d03 100644
--- a/car/src/main/java/androidx/car/app/CarListDialog.java
+++ b/car/src/main/java/androidx/car/app/CarListDialog.java
@@ -20,9 +20,6 @@
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.RecyclerView;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
@@ -31,9 +28,8 @@
import android.view.Window;
import android.view.WindowManager;
-import java.util.ArrayList;
-import java.util.List;
-
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.car.R;
import androidx.car.widget.DayNightStyle;
import androidx.car.widget.ListItem;
@@ -42,6 +38,10 @@
import androidx.car.widget.PagedListView;
import androidx.car.widget.PagedScrollBarView;
import androidx.car.widget.TextListItem;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* A subclass of {@link Dialog} that is tailored for the car environment. This dialog can display a
@@ -167,16 +167,23 @@
mScrollBarView = getWindow().findViewById(R.id.scrollbar);
mScrollBarView.setDayNightStyle(DayNightStyle.FORCE_NIGHT);
- mScrollBarView.setPaginationListener(direction -> {
- switch (direction) {
- case PagedScrollBarView.PaginationListener.PAGE_UP:
- mList.pageUp();
- break;
- case PagedScrollBarView.PaginationListener.PAGE_DOWN:
- mList.pageDown();
- break;
- default:
- Log.e(TAG, "Unknown pagination direction (" + direction + ")");
+ mScrollBarView.setPaginationListener(new PagedScrollBarView.PaginationListener() {
+ @Override
+ public void onPaginate(int direction) {
+ switch (direction) {
+ case PagedScrollBarView.PaginationListener.PAGE_UP:
+ mList.pageUp();
+ break;
+ case PagedScrollBarView.PaginationListener.PAGE_DOWN:
+ mList.pageDown();
+ break;
+ default:
+ Log.e(TAG, "Unknown pagination direction (" + direction + ")");
+ }
+ }
+
+ @Override
+ public void onAlphaJump() {
}
});
}
@@ -276,7 +283,7 @@
* Builder class that can be used to create a {@link CarListDialog} by configuring the
* options for the list and behavior of the dialog.
*/
- public static class Builder {
+ public static final class Builder {
private final Context mContext;
private int mInitialPosition;
private String[] mItems;
@@ -383,8 +390,8 @@
* <p>If {@link #setItems(String[],DialogInterface.OnClickListener)} is never called, then
* calling this method will throw an exception.
*
- * <p>Calling this method does not display the dialog. If no additional processing is
- * needed, {@link #show()} may be called instead to both create and display the dialog.
+ * <p>Calling this method does not display the dialog. Utilize this dialog within a
+ * {@link androidx.fragment.app.DialogFragment} to show the dialog.
*/
public CarListDialog create() {
if (mItems == null || mItems.length == 0) {
@@ -410,21 +417,5 @@
return dialog;
}
-
- /**
- * Creates an {@link CarAlertDialog} with the arguments supplied to this {@code Builder}
- * and immediately displays the dialog.
- *
- * <p>Calling this method is functionally identical to:
- * <pre>{@code
- * CarAlertDialog dialog = new CarAlertDialog.Builder().create();
- * dialog.show();
- * }</pre>
- */
- public CarListDialog show() {
- CarListDialog dialog = create();
- dialog.show();
- return dialog;
- }
}
}
diff --git a/car/src/main/java/androidx/car/drawer/CarDrawerAdapter.java b/car/src/main/java/androidx/car/drawer/CarDrawerAdapter.java
index 3410486..7991cfa 100644
--- a/car/src/main/java/androidx/car/drawer/CarDrawerAdapter.java
+++ b/car/src/main/java/androidx/car/drawer/CarDrawerAdapter.java
@@ -26,7 +26,6 @@
import android.view.View;
import android.view.ViewGroup;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.car.R;
import androidx.car.utils.CarUxRestrictionsHelper;
@@ -37,10 +36,11 @@
* Base adapter for displaying items in the car navigation drawer, which uses a
* {@link PagedListView}.
*
- * <p>Subclasses must set the title that will be displayed when displaying the contents of the
- * drawer via {@link #setTitle(CharSequence)}. The title can be updated at any point later on. The
- * title of the root adapter will also be the main title showed in the toolbar when the drawer is
- * closed. See {@link CarDrawerController#setRootAdapter(CarDrawerAdapter)} for more information.
+ * <p>Subclasses can optionally set the title that will be displayed when displaying the contents
+ * of the drawer via {@link #setTitle(CharSequence)}. The title can be updated at any point later
+ * on. The title of the root adapter will also be the main title showed in the toolbar when the
+ * drawer is closed. See {@link CarDrawerController#setRootAdapter(CarDrawerAdapter)} for more
+ * information.
*
* <p>This class also takes care of implementing the PageListView.ItemCamp contract and subclasses
* should implement {@link #getActualItemCount()}.
@@ -90,7 +90,7 @@
* Called when {@link #setTitle(CharSequence)} has been called and the title has been
* changed.
*/
- void onTitleChanged(CharSequence newTitle);
+ void onTitleChanged(@Nullable CharSequence newTitle);
}
protected CarDrawerAdapter(Context context, boolean showDisabledListOnEmpty) {
@@ -113,11 +113,7 @@
}
/** Updates the title to display in the toolbar for this Adapter. */
- public final void setTitle(@NonNull CharSequence title) {
- if (title == null) {
- throw new IllegalArgumentException("setTitle() cannot be passed a null title!");
- }
-
+ public final void setTitle(@Nullable CharSequence title) {
mTitle = title;
if (mTitleChangeListener != null) {
diff --git a/car/src/main/java/androidx/car/drawer/CarDrawerController.java b/car/src/main/java/androidx/car/drawer/CarDrawerController.java
index c25ac80..ad98e78 100644
--- a/car/src/main/java/androidx/car/drawer/CarDrawerController.java
+++ b/car/src/main/java/androidx/car/drawer/CarDrawerController.java
@@ -19,14 +19,6 @@
import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
-import androidx.annotation.AnimRes;
-import androidx.annotation.ColorInt;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.drawerlayout.widget.DrawerLayout;
-import androidx.appcompat.app.ActionBarDrawerToggle;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.appcompat.widget.Toolbar;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MenuItem;
@@ -34,10 +26,18 @@
import android.view.animation.AnimationUtils;
import android.widget.ProgressBar;
-import java.util.ArrayDeque;
-
+import androidx.annotation.AnimRes;
+import androidx.annotation.ColorInt;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.ActionBarDrawerToggle;
+import androidx.appcompat.widget.Toolbar;
import androidx.car.R;
import androidx.car.widget.PagedListView;
+import androidx.drawerlayout.widget.DrawerLayout;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.ArrayDeque;
/**
* A controller that will handle the set up of the navigation drawer. It will hook up the
@@ -105,6 +105,10 @@
resolveThemeColors();
setupDrawerToggling();
+
+ // Update the colors of the drawer title and arrow. Since this is called when the controller
+ // is first created, the drawer will be closed.
+ updateTitleAndArrowColor(/* drawerOpen= */ false);
}
private void resolveThemeColors() {
@@ -134,9 +138,7 @@
mAdapterStack.removeLast();
}
mAdapterStack.addLast(rootAdapter);
-
- setToolbarTitleFrom(rootAdapter);
- mDrawerList.setAdapter(rootAdapter);
+ setDisplayAdapter(rootAdapter);
}
/**
@@ -200,10 +202,6 @@
* controller's internal Toolbar.
*/
private void setToolbarTitleFrom(CarDrawerAdapter adapter) {
- if (adapter.getTitle() == null) {
- throw new RuntimeException("CarDrawerAdapter must supply a title via setTitle()");
- }
-
mToolbar.setTitle(adapter.getTitle());
adapter.setTitleChangeListener(mToolbar::setTitle);
}
diff --git a/car/src/main/java/androidx/car/widget/AlphaJumpBucketer.java b/car/src/main/java/androidx/car/widget/AlphaJumpBucketer.java
new file mode 100644
index 0000000..1bf50d7
--- /dev/null
+++ b/car/src/main/java/androidx/car/widget/AlphaJumpBucketer.java
@@ -0,0 +1,152 @@
+/*
+ * 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.car.widget;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Predicate;
+
+/**
+ * A helper class for building the list of buckets for alpha jump.
+ */
+public class AlphaJumpBucketer {
+ private static final Character[] DEFAULT_INITIAL_CHARS = {
+ '0', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
+
+ private static final String[] PREFIX_WORDS = {"a", "the"};
+
+ private final List<Bucket> mBuckets;
+
+ public AlphaJumpBucketer() {
+ mBuckets = new ArrayList<>();
+ for (Character ch : DEFAULT_INITIAL_CHARS) {
+ if (ch == '0') {
+ mBuckets.add(new Bucket("123", (String s) -> s.matches("^[0-9]")));
+ } else {
+ String prefix = new String(new char[] {ch});
+ mBuckets.add(new Bucket(prefix, (String s) -> s.startsWith(prefix.toLowerCase())));
+ }
+ }
+ }
+
+ public AlphaJumpBucketer(Bucket[] buckets) {
+ mBuckets = Arrays.asList(buckets);
+ }
+
+ /**
+ * Creates a collection of {@link IAlphaJumpAdapter.Bucket}s from the given list of strings.
+ */
+ public Collection<IAlphaJumpAdapter.Bucket> createBuckets(String[] values) {
+ return createBuckets(Arrays.asList(values));
+ }
+
+ /**
+ * Creates a collection of {@link IAlphaJumpAdapter.Bucket}s from the given iterable collection
+ * of strings.
+ */
+ public Collection<IAlphaJumpAdapter.Bucket> createBuckets(Iterable<String> values) {
+ return createBuckets(values.iterator());
+ }
+
+ /**
+ * Creates the collection of {@link IAlphaJumpAdapter.Bucket}s from the given enumeration of
+ * values.
+ */
+ public Collection<IAlphaJumpAdapter.Bucket> createBuckets(Iterator<String> values) {
+ int index = 0;
+ while (values.hasNext()) {
+ String value = values.next();
+ for (Bucket bucket : mBuckets) {
+ if (bucket.matchString(value, index)) {
+ break;
+ }
+ }
+ index++;
+ }
+ ArrayList<IAlphaJumpAdapter.Bucket> buckets = new ArrayList<>();
+ buckets.addAll(mBuckets);
+ return buckets;
+ }
+
+ /**
+ * "Preprocess" a string so that we remove some common prefixes and so on when performing the
+ * bucketing.
+ *
+ * @param s The string to pre-process.
+ * @return The input string with whitespace trimmed, and also words like "the", "a" and so on
+ * removed.
+ */
+ private static String preprocess(String s) {
+ s = s.trim().toLowerCase();
+
+ for (String word : PREFIX_WORDS) {
+ if (s.startsWith(word + " ")) {
+ s = s.substring(0, word.length() + 1).trim();
+ break;
+ }
+ }
+
+ return s;
+ }
+
+ /**
+ * A basic implementation of {@link IAlphaJumpAdapter.Bucket}.
+ */
+ public static class Bucket implements IAlphaJumpAdapter.Bucket {
+ private CharSequence mLabel;
+ private int mIndex;
+ private Predicate<String> mStringMatcher;
+ private boolean mIsEmpty;
+
+ Bucket(CharSequence label, Predicate<String> stringMatcher) {
+ mLabel = label;
+ mIndex = -1;
+ mIsEmpty = true;
+ mStringMatcher = stringMatcher;
+ }
+
+ boolean matchString(String s, int index) {
+ boolean match = mStringMatcher.test(preprocess(s));
+ if (match) {
+ if (mIndex < 0) {
+ mIndex = index;
+ }
+ mIsEmpty = false;
+ }
+ return match;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return mIsEmpty;
+ }
+
+ @Override
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ @Override
+ public int getIndex() {
+ return mIndex;
+ }
+ }
+}
diff --git a/car/src/main/java/androidx/car/widget/AlphaJumpOverlayView.java b/car/src/main/java/androidx/car/widget/AlphaJumpOverlayView.java
new file mode 100644
index 0000000..5672880
--- /dev/null
+++ b/car/src/main/java/androidx/car/widget/AlphaJumpOverlayView.java
@@ -0,0 +1,87 @@
+/*
+ * 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.car.widget;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+import java.util.Collection;
+
+import androidx.annotation.NonNull;
+import androidx.car.R;
+import androidx.gridlayout.widget.GridLayout;
+
+/**
+ * This view shows a grid of alphabetic letters that you can tap on to advance a list to the
+ * beginning of that list.
+ */
+public class AlphaJumpOverlayView extends GridLayout {
+ private IAlphaJumpAdapter mAdapter;
+ private PagedListView mPagedListView;
+ private Collection<IAlphaJumpAdapter.Bucket> mBuckets;
+
+ public AlphaJumpOverlayView(@NonNull Context context) {
+ super(context);
+ setBackgroundResource(R.color.car_card);
+ setColumnCount(context.getResources().getInteger(R.integer.alpha_jump_button_columns));
+ setUseDefaultMargins(false);
+ }
+
+ void init(PagedListView plv, IAlphaJumpAdapter adapter) {
+ mPagedListView = plv;
+ mAdapter = adapter;
+ mBuckets = adapter.getAlphaJumpBuckets();
+
+ createButtons();
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ // TODO: change the hamburger button into a back button...
+ if (visibility == VISIBLE && changedView == this) {
+ mAdapter.onAlphaJumpEnter();
+ }
+ }
+
+ private void createButtons() {
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ removeAllViews();
+ for (IAlphaJumpAdapter.Bucket bucket : mBuckets) {
+ View container = inflater.inflate(R.layout.car_alpha_jump_button, this, false);
+ TextView btn = container.findViewById(R.id.button);
+ btn.setText(bucket.getLabel());
+ btn.setOnClickListener(this::onButtonClick);
+ btn.setTag(bucket);
+ if (bucket.isEmpty()) {
+ btn.setEnabled(false);
+ }
+ addView(container);
+ }
+ }
+
+ private void onButtonClick(View v) {
+ setVisibility(View.GONE);
+ IAlphaJumpAdapter.Bucket bucket = (IAlphaJumpAdapter.Bucket) v.getTag();
+ if (bucket != null) {
+ mAdapter.onAlphaJumpLeave(bucket);
+
+ mPagedListView.snapToPosition(bucket.getIndex());
+ }
+ }
+}
diff --git a/car/src/main/java/androidx/car/widget/DayNightStyle.java b/car/src/main/java/androidx/car/widget/DayNightStyle.java
index 37d0d51..73f9ce4 100644
--- a/car/src/main/java/androidx/car/widget/DayNightStyle.java
+++ b/car/src/main/java/androidx/car/widget/DayNightStyle.java
@@ -16,8 +16,12 @@
package androidx.car.widget;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import androidx.annotation.IntDef;
+import java.lang.annotation.Retention;
+
/**
* Specifies how the system UI should respond to day/night mode events.
*
@@ -37,6 +41,7 @@
DayNightStyle.FORCE_NIGHT,
DayNightStyle.FORCE_DAY,
})
+@Retention(SOURCE)
public @interface DayNightStyle {
/**
* Sets the foreground color to be automatically changed based on day/night mode, assuming the
diff --git a/car/src/main/java/androidx/car/widget/IAlphaJumpAdapter.java b/car/src/main/java/androidx/car/widget/IAlphaJumpAdapter.java
new file mode 100644
index 0000000..cc84a8c
--- /dev/null
+++ b/car/src/main/java/androidx/car/widget/IAlphaJumpAdapter.java
@@ -0,0 +1,62 @@
+/*
+ * 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.car.widget;
+
+import java.util.Collection;
+
+/**
+ * An interface that you can implement on your Adapter to enable support for Alpha-Jump.
+ */
+public interface IAlphaJumpAdapter {
+ /**
+ * A bucket represents a "button" in the alpha-jump menu.
+ */
+ interface Bucket {
+ /**
+ * Return true if the bucket is empty (and therefore the button should be displayed
+ * disabled).
+ */
+ boolean isEmpty();
+
+ /**
+ * Return the label for this bucket, that is displayed at the text to the user.
+ */
+ CharSequence getLabel();
+
+ /**
+ * Return the index of the first item that this bucket matches in the list.
+ */
+ int getIndex();
+ }
+
+ /**
+ * Generate and populate the buckets used to populate the alpha-jump menu.
+ */
+ Collection<Bucket> getAlphaJumpBuckets();
+
+ /**
+ * Called every time the alpha-jump menu is entered.
+ */
+ void onAlphaJumpEnter();
+
+ /**
+ * Called every time the alpha-jump menu is left.
+ *
+ * @param bucket The {@link Bucket} that the user clicked on, and should be jumped to.
+ */
+ void onAlphaJumpLeave(Bucket bucket);
+}
diff --git a/car/src/main/java/androidx/car/widget/ListItem.java b/car/src/main/java/androidx/car/widget/ListItem.java
index fb8b2f4..8103dd5 100644
--- a/car/src/main/java/androidx/car/widget/ListItem.java
+++ b/car/src/main/java/androidx/car/widget/ListItem.java
@@ -1,17 +1,17 @@
package androidx.car.widget;
import android.car.drivingstate.CarUxRestrictions;
-import androidx.annotation.CallSuper;
-import androidx.annotation.Nullable;
-import androidx.annotation.StyleRes;
-import androidx.recyclerview.widget.RecyclerView;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
+import androidx.annotation.CallSuper;
+import androidx.annotation.Nullable;
+import androidx.annotation.StyleRes;
import androidx.car.R;
+import androidx.recyclerview.widget.RecyclerView;
/**
* Definition of items that can be inserted into {@link ListItemAdapter}.
@@ -41,7 +41,7 @@
*
* @return type of this ListItem.
*/
- abstract int getViewType();
+ public abstract int getViewType();
/**
* Called when ListItem is bound to its ViewHolder.
@@ -134,7 +134,6 @@
return mHideDivider;
};
-
/**
* Does the work that moves the ListItem from dirty state to clean state, i.e. the work required
* the first time this ListItem {@code bind}s to {@link ListItem.ViewHolder}.
@@ -181,8 +180,8 @@
* }
* </pre>
*
- * @params binder to interact with subviews in {@code ViewHolder}.
- * @params cleanUp view binder to revert the effect of {@code binder}. cleanUp binders will be
+ * @param binder to interact with subviews in {@code ViewHolder}.
+ * @param cleanUp view binder to revert the effect of {@code binder}. cleanUp binders will be
* stored in {@link ListItem.ViewHolder} and should be invoked via
* {@link ViewHolder#cleanUp()} before {@code ViewHolder} is recycled.
* This is to avoid changed made to ViewHolder lingers around when ViewHolder is
diff --git a/car/src/main/java/androidx/car/widget/ListItemAdapter.java b/car/src/main/java/androidx/car/widget/ListItemAdapter.java
index 4d58488..b8fc9d9 100644
--- a/car/src/main/java/androidx/car/widget/ListItemAdapter.java
+++ b/car/src/main/java/androidx/car/widget/ListItemAdapter.java
@@ -67,29 +67,37 @@
/**
* Sets the background color of each item.
+ * Background can be configured by {@link R.styleable#ListItem_listItemBackgroundColor}.
*/
- public static final int NONE = 0;
+ public static final int SOLID = 0;
+ /**
+ * Sets the background color of each item to none (transparent).
+ */
+ public static final int NONE = 1;
/**
* Sets each item in {@link CardView} with a rounded corner background and shadow.
*/
- public static final int CARD = 1;
+ public static final int CARD = 2;
/**
* Sets background of each item so the combined list looks like one elongated card, namely
* top and bottom item will have rounded corner at only top/bottom side respectively. If
* only one item exists, it will have both top and bottom rounded corner.
*/
- public static final int PANEL = 2;
+ public static final int PANEL = 3;
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({
+ BackgroundStyle.SOLID,
BackgroundStyle.NONE,
BackgroundStyle.CARD,
- BackgroundStyle.PANEL })
+ BackgroundStyle.PANEL
+ })
private @interface ListBackgroundStyle {}
static final int LIST_ITEM_TYPE_TEXT = 1;
static final int LIST_ITEM_TYPE_SEEKBAR = 2;
+ static final int LIST_ITEM_TYPE_SUBHEADER = 3;
private final SparseIntArray mViewHolderLayoutResIds = new SparseIntArray();
private final SparseArray<Function<View, ListItem.ViewHolder>> mViewHolderCreator =
@@ -109,8 +117,11 @@
private int mMaxItems = PagedListView.ItemCap.UNLIMITED;
+ /**
+ * Defaults {@link BackgroundStyle} to {@link BackgroundStyle#SOLID}.
+ */
public ListItemAdapter(Context context, ListItemProvider itemProvider) {
- this(context, itemProvider, BackgroundStyle.NONE);
+ this(context, itemProvider, BackgroundStyle.SOLID);
}
public ListItemAdapter(Context context, ListItemProvider itemProvider,
@@ -123,6 +134,8 @@
R.layout.car_list_item_text_content, TextListItem::createViewHolder);
registerListItemViewType(LIST_ITEM_TYPE_SEEKBAR,
R.layout.car_list_item_seekbar_content, SeekbarListItem::createViewHolder);
+ registerListItemViewType(LIST_ITEM_TYPE_SUBHEADER,
+ R.layout.car_list_item_subheader_content, SubheaderListItem::createViewHolder);
mUxRestrictionsHelper =
new CarUxRestrictionsHelper(context, carUxRestrictions -> {
@@ -215,31 +228,27 @@
*/
private ViewGroup createListItemContainer() {
ViewGroup container;
- switch (mBackgroundStyle) {
- case BackgroundStyle.NONE:
- case BackgroundStyle.PANEL:
- FrameLayout frameLayout = new FrameLayout(mContext);
- frameLayout.setLayoutParams(new RecyclerView.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+ if (mBackgroundStyle == BackgroundStyle.CARD) {
+ CardView card = new CardView(mContext);
+ RecyclerView.LayoutParams cardLayoutParams = new RecyclerView.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ cardLayoutParams.bottomMargin = mContext.getResources().getDimensionPixelSize(
+ R.dimen.car_padding_3);
+ card.setLayoutParams(cardLayoutParams);
+ card.setRadius(mContext.getResources().getDimensionPixelSize(R.dimen.car_radius_1));
+ card.setCardBackgroundColor(mListItemBackgroundColor);
+
+ container = card;
+ } else {
+ FrameLayout frameLayout = new FrameLayout(mContext);
+ frameLayout.setLayoutParams(new RecyclerView.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+ // Skip setting background color for NONE.
+ if (mBackgroundStyle != BackgroundStyle.NONE) {
frameLayout.setBackgroundColor(mListItemBackgroundColor);
+ }
- container = frameLayout;
- break;
- case BackgroundStyle.CARD:
- CardView card = new CardView(mContext);
- RecyclerView.LayoutParams cardLayoutParams = new RecyclerView.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- cardLayoutParams.bottomMargin = mContext.getResources().getDimensionPixelSize(
- R.dimen.car_padding_3);
- card.setLayoutParams(cardLayoutParams);
- card.setRadius(mContext.getResources().getDimensionPixelSize(R.dimen.car_radius_1));
- card.setCardBackgroundColor(mListItemBackgroundColor);
-
- container = card;
- break;
- default:
- throw new IllegalArgumentException("Unknown background style. "
- + "Expected constants in class ListItemAdapter.BackgroundStyle.");
+ container = frameLayout;
}
return container;
}
diff --git a/car/src/main/java/androidx/car/widget/PagedListView.java b/car/src/main/java/androidx/car/widget/PagedListView.java
index d677489..4d16e0c 100644
--- a/car/src/main/java/androidx/car/widget/PagedListView.java
+++ b/car/src/main/java/androidx/car/widget/PagedListView.java
@@ -18,6 +18,8 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
@@ -28,18 +30,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
-import androidx.annotation.ColorRes;
-import androidx.annotation.IdRes;
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.UiThread;
-import androidx.annotation.VisibleForTesting;
-import androidx.recyclerview.widget.GridLayoutManager;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.OrientationHelper;
-import androidx.recyclerview.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
@@ -48,7 +38,21 @@
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import androidx.annotation.ColorRes;
+import androidx.annotation.IdRes;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.UiThread;
+import androidx.annotation.VisibleForTesting;
import androidx.car.R;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.OrientationHelper;
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.lang.annotation.Retention;
/**
* View that wraps a {@link RecyclerView} and a scroll bar that has
@@ -87,7 +91,6 @@
*/
private static final int PAGINATION_HOLD_DELAY_MS = 400;
-
/**
* When doing a snap, offset the snap by this number of position and then do a smooth scroll to
* the final position.
@@ -104,6 +107,13 @@
@VisibleForTesting
PagedScrollBarView mScrollBarView;
+ /**
+ * AlphaJumpOverlayView that will be null until the first time you tap the alpha jump button, at
+ * which point we'll construct it and add it to the view hierarchy as a child of this frame
+ * layout.
+ */
+ @Nullable private AlphaJumpOverlayView mAlphaJumpView;
+
private int mRowsPerPage = -1;
private RecyclerView.Adapter<? extends RecyclerView.ViewHolder> mAdapter;
@@ -181,6 +191,7 @@
Gutter.END,
Gutter.BOTH,
})
+ @Retention(SOURCE)
public @interface Gutter {
/**
* No gutter on either side of the list items. The items will span the full width of the
@@ -254,7 +265,7 @@
mSnapHelper = new PagedSnapHelper(context);
mSnapHelper.attachToRecyclerView(mRecyclerView);
- mRecyclerView.setOnScrollListener(mRecyclerViewOnScrollListener);
+ mRecyclerView.addOnScrollListener(mRecyclerViewOnScrollListener);
mRecyclerView.getRecycledViewPool().setMaxRecycledViews(0, 12);
int defaultGutterSize = getResources().getDimensionPixelSize(R.dimen.car_margin);
@@ -307,23 +318,31 @@
setFocusable(false);
mScrollBarEnabled = a.getBoolean(R.styleable.PagedListView_scrollBarEnabled, true);
- mScrollBarView = (PagedScrollBarView) findViewById(R.id.paged_scroll_view);
- mScrollBarView.setPaginationListener(direction -> {
- switch (direction) {
- case PagedScrollBarView.PaginationListener.PAGE_UP:
- pageUp();
- if (mOnScrollListener != null) {
- mOnScrollListener.onScrollUpButtonClicked();
- }
- break;
- case PagedScrollBarView.PaginationListener.PAGE_DOWN:
- pageDown();
- if (mOnScrollListener != null) {
- mOnScrollListener.onScrollDownButtonClicked();
- }
- break;
- default:
- Log.e(TAG, "Unknown pagination direction (" + direction + ")");
+ mScrollBarView = findViewById(R.id.paged_scroll_view);
+ mScrollBarView.setPaginationListener(new PagedScrollBarView.PaginationListener() {
+ @Override
+ public void onPaginate(int direction) {
+ switch (direction) {
+ case PagedScrollBarView.PaginationListener.PAGE_UP:
+ pageUp();
+ if (mOnScrollListener != null) {
+ mOnScrollListener.onScrollUpButtonClicked();
+ }
+ break;
+ case PagedScrollBarView.PaginationListener.PAGE_DOWN:
+ pageDown();
+ if (mOnScrollListener != null) {
+ mOnScrollListener.onScrollDownButtonClicked();
+ }
+ break;
+ default:
+ Log.e(TAG, "Unknown pagination direction (" + direction + ")");
+ }
+ }
+
+ @Override
+ public void onAlphaJump() {
+ showAlphaJump();
}
});
@@ -525,7 +544,6 @@
if ((layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
PointF vector = ((RecyclerView.SmoothScroller.ScrollVectorProvider) layoutManager)
.computeScrollVectorForPosition(position);
-
// A positive value in the vector means scrolling down, so should offset by scrolling to
// an item previous in the list.
int offsetDirection = (vector == null || vector.y > 0) ? -1 : 1;
@@ -576,6 +594,7 @@
mRecyclerView.setAdapter(adapter);
updateMaxItems();
+ updateAlphaJump();
}
/**
@@ -836,6 +855,13 @@
int screenSize = mRecyclerView.getHeight();
int scrollDistance = screenSize;
+ // If the last item is partially visible, page down should bring it to the top.
+ View lastChild = mRecyclerView.getChildAt(mRecyclerView.getChildCount() - 1);
+ if (mRecyclerView.getLayoutManager().isViewPartiallyVisible(lastChild,
+ /* completelyVisible= */ false, /* acceptEndPointInclusion= */ false)) {
+ scrollDistance = orientationHelper.getDecoratedStart(lastChild);
+ }
+
// The iteration order matters. In case where there are 2 items longer than screen size, we
// want to focus on upcoming view (the one at the bottom of screen).
for (int i = mRecyclerView.getChildCount() - 1; i >= 0; i--) {
@@ -1109,6 +1135,21 @@
dispatchThawSelfOnly(container);
}
+ private void updateAlphaJump() {
+ boolean supportsAlphaJump = (mAdapter instanceof IAlphaJumpAdapter);
+ mScrollBarView.setShowAlphaJump(supportsAlphaJump);
+ }
+
+ private void showAlphaJump() {
+ if (mAlphaJumpView == null && mAdapter instanceof IAlphaJumpAdapter) {
+ mAlphaJumpView = new AlphaJumpOverlayView(getContext());
+ mAlphaJumpView.init(this, (IAlphaJumpAdapter) mAdapter);
+ addView(mAlphaJumpView);
+ }
+
+ mAlphaJumpView.setVisibility(View.VISIBLE);
+ }
+
private final RecyclerView.OnScrollListener mRecyclerViewOnScrollListener =
new RecyclerView.OnScrollListener() {
@Override
@@ -1162,6 +1203,12 @@
public void onScrollUpButtonClicked() {}
/** Called when scroll down button is clicked */
public void onScrollDownButtonClicked() {}
+ /** Called when the alpha jump button is clicked. */
+ public void onAlphaJumpButtonClicked() {}
+ /** Called when scrolling to the previous page via up gesture */
+ public void onGestureUp() {}
+ /** Called when scrolling to the next page via down gesture */
+ public void onGestureDown() {}
/**
* Called when RecyclerView.OnScrollListener#onScrolled is called. See
diff --git a/car/src/main/java/androidx/car/widget/PagedScrollBarView.java b/car/src/main/java/androidx/car/widget/PagedScrollBarView.java
index 8d26916..aa62813 100644
--- a/car/src/main/java/androidx/car/widget/PagedScrollBarView.java
+++ b/car/src/main/java/androidx/car/widget/PagedScrollBarView.java
@@ -20,9 +20,6 @@
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
-import androidx.annotation.ColorRes;
-import androidx.annotation.VisibleForTesting;
-import androidx.core.content.ContextCompat;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -31,12 +28,15 @@
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.TextView;
+import androidx.annotation.ColorRes;
+import androidx.annotation.VisibleForTesting;
import androidx.car.R;
+import androidx.core.content.ContextCompat;
/** A custom view to provide list scroll behaviour -- up/down buttons and scroll indicator. */
-public class PagedScrollBarView extends FrameLayout
- implements View.OnClickListener, View.OnLongClickListener {
+public class PagedScrollBarView extends FrameLayout {
private static final float BUTTON_DISABLED_ALPHA = 0.2f;
@DayNightStyle private int mDayNightStyle;
@@ -48,10 +48,21 @@
/** Called when the linked view should be paged in the given direction */
void onPaginate(int direction);
+
+ /**
+ * Called when the 'alpha jump' button is clicked and the linked view should switch into
+ * alpha jump mode, where we display a list of buttons to allow the user to quickly scroll
+ * to a certain point in the list, bypassing a lot of manual scrolling.
+ */
+ void onAlphaJump();
}
private final ImageView mUpButton;
+ private final PaginateButtonClickListener mUpButtonClickListener;
private final ImageView mDownButton;
+ private final PaginateButtonClickListener mDownButtonClickListener;
+ private final TextView mAlphaJumpButton;
+ private final AlphaJumpButtonClickListener mAlphaJumpButtonClickListener;
private final View mScrollThumb;
/** The "filler" view between the up and down buttons */
private final View mFiller;
@@ -78,28 +89,21 @@
inflater.inflate(R.layout.car_paged_scrollbar_buttons, this /* root */,
true /* attachToRoot */);
- mUpButton = (ImageView) findViewById(R.id.page_up);
- mUpButton.setOnClickListener(this);
- mUpButton.setOnLongClickListener(this);
- mDownButton = (ImageView) findViewById(R.id.page_down);
- mDownButton.setOnClickListener(this);
- mDownButton.setOnLongClickListener(this);
+ mUpButtonClickListener = new PaginateButtonClickListener(PaginationListener.PAGE_UP);
+ mDownButtonClickListener = new PaginateButtonClickListener(PaginationListener.PAGE_DOWN);
+ mAlphaJumpButtonClickListener = new AlphaJumpButtonClickListener();
+
+ mUpButton = findViewById(R.id.page_up);
+ mUpButton.setOnClickListener(mUpButtonClickListener);
+ mDownButton = findViewById(R.id.page_down);
+ mDownButton.setOnClickListener(mDownButtonClickListener);
+ mAlphaJumpButton = findViewById(R.id.alpha_jump);
+ mAlphaJumpButton.setOnClickListener(mAlphaJumpButtonClickListener);
mScrollThumb = findViewById(R.id.scrollbar_thumb);
mFiller = findViewById(R.id.filler);
}
- @Override
- public void onClick(View v) {
- dispatchPageClick(v);
- }
-
- @Override
- public boolean onLongClick(View v) {
- dispatchPageClick(v);
- return true;
- }
-
/** Sets the icon to be used for the up button. */
public void setUpButtonIcon(Drawable icon) {
mUpButton.setImageDrawable(icon);
@@ -116,7 +120,9 @@
* @param listener The listener to set.
*/
public void setPaginationListener(PaginationListener listener) {
- mPaginationListener = listener;
+ mUpButtonClickListener.setPaginationListener(listener);
+ mDownButtonClickListener.setPaginationListener(listener);
+ mAlphaJumpButtonClickListener.setPaginationListener(listener);
}
/** Returns {@code true} if the "up" button is pressed */
@@ -129,6 +135,10 @@
return mDownButton.isPressed();
}
+ void setShowAlphaJump(boolean show) {
+ mAlphaJumpButton.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+
/**
* Sets the range, offset and extent of the scroll bar. The range represents the size of a
* container for the scrollbar thumb; offset is the distance from the start of the container
@@ -261,7 +271,7 @@
upDownBackgroundResId = R.drawable.car_button_ripple_background_night;
break;
case DayNightStyle.FORCE_DAY:
- tintResId = R.color.car_tint_dark;
+ tintResId = R.color.car_tint_dark;
thumbColorResId = R.color.car_scrollbar_thumb_dark;
upDownBackgroundResId = R.drawable.car_button_ripple_background_day;
break;
@@ -281,6 +291,8 @@
mDownButton.setColorFilter(tint, PorterDuff.Mode.SRC_IN);
mDownButton.setBackgroundResource(upDownBackgroundResId);
+
+ mAlphaJumpButton.setBackgroundResource(upDownBackgroundResId);
}
private void setScrollbarThumbColor(@ColorRes int color) {
@@ -293,18 +305,6 @@
return ((GradientDrawable) mScrollThumb.getBackground()).getColor().getDefaultColor();
}
- private void dispatchPageClick(View v) {
- final PaginationListener listener = mPaginationListener;
- if (listener == null) {
- return;
- }
-
- int direction = v.getId() == R.id.page_up
- ? PaginationListener.PAGE_UP
- : PaginationListener.PAGE_DOWN;
- listener.onPaginate(direction);
- }
-
/** Moves the given view to the specified 'y' position. */
private void moveY(final View view, float newPosition, boolean animate) {
final int duration = animate ? 200 : 0;
@@ -314,4 +314,40 @@
.setInterpolator(mPaginationInterpolator)
.start();
}
+
+ private static class PaginateButtonClickListener implements View.OnClickListener {
+ private final int mPaginateDirection;
+ private PaginationListener mPaginationListener;
+
+ PaginateButtonClickListener(int paginateDirection) {
+ mPaginateDirection = paginateDirection;
+ }
+
+ public void setPaginationListener(PaginationListener listener) {
+ mPaginationListener = listener;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (mPaginationListener != null) {
+ mPaginationListener.onPaginate(mPaginateDirection);
+ }
+ }
+ }
+
+ private static class AlphaJumpButtonClickListener implements View.OnClickListener {
+ private PaginationListener mPaginationListener;
+
+ public void setPaginationListener(PaginationListener listener) {
+ mPaginationListener = listener;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (mPaginationListener != null) {
+ mPaginationListener.onAlphaJump();
+ }
+ }
+
+ }
}
diff --git a/car/src/main/java/androidx/car/widget/SeekbarListItem.java b/car/src/main/java/androidx/car/widget/SeekbarListItem.java
index 027f495..368e6c0 100644
--- a/car/src/main/java/androidx/car/widget/SeekbarListItem.java
+++ b/car/src/main/java/androidx/car/widget/SeekbarListItem.java
@@ -142,7 +142,7 @@
* Used by {@link ListItemAdapter} to choose layout to inflate for view holder.
*/
@Override
- int getViewType() {
+ public int getViewType() {
return ListItemAdapter.LIST_ITEM_TYPE_SEEKBAR;
}
diff --git a/car/src/main/java/androidx/car/widget/SubheaderListItem.java b/car/src/main/java/androidx/car/widget/SubheaderListItem.java
new file mode 100644
index 0000000..dd5a00b
--- /dev/null
+++ b/car/src/main/java/androidx/car/widget/SubheaderListItem.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.car.widget;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.car.drivingstate.CarUxRestrictions;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.DimenRes;
+import androidx.annotation.IntDef;
+import androidx.annotation.StyleRes;
+import androidx.car.R;
+import androidx.car.utils.CarUxRestrictionsUtils;
+
+import java.lang.annotation.Retention;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class to build a sub-header list item.
+ *
+ * <p>A sub-header list item consists of a one-line text. Its margin can be adjusted to match the
+ * rest of {@link ListItem} through {@link #setTextStartMarginType(int)}.
+ */
+public class SubheaderListItem extends ListItem<SubheaderListItem.ViewHolder> {
+
+ /**
+ * Creates a {@link SubheaderListItem.ViewHolder}.
+ */
+ public static ViewHolder createViewHolder(View itemView) {
+ return new ViewHolder(itemView);
+ }
+
+ private final Context mContext;
+
+ private final List<ViewBinder<ViewHolder>> mBinders = new ArrayList<>();
+
+ @StyleRes private int mListItemSubheaderTextAppearance;
+ private String mText;
+
+ public SubheaderListItem(Context context, String text) {
+
+ mContext = context;
+ mText = text;
+ mTextStartMarginType = TEXT_START_MARGIN_TYPE_NONE;
+
+ TypedArray a = context.getTheme().obtainStyledAttributes(R.styleable.ListItem);
+ mListItemSubheaderTextAppearance = a.getResourceId(
+ R.styleable.ListItem_listItemSubheaderTextAppearance,
+ R.style.TextAppearance_Car_Subheader);
+ a.recycle();
+
+ markDirty();
+ }
+
+ @IntDef({
+ TEXT_START_MARGIN_TYPE_NONE, TEXT_START_MARGIN_TYPE_LARGE,
+ TEXT_START_MARGIN_TYPE_SMALL})
+ @Retention(SOURCE)
+ public @interface TextStartMarginType {}
+
+ /**
+ * Sets start margin of text the same as {@link TextListItem#setPrimaryActionNoIcon()}.
+ */
+ public static final int TEXT_START_MARGIN_TYPE_NONE = 0;
+ /**
+ * Sets start margin of text the same as {@link TextListItem#setPrimaryActionIcon(int, boolean)}
+ * with {@code useLargeIcon} set to {@code false}.
+ */
+ public static final int TEXT_START_MARGIN_TYPE_SMALL = 1;
+ /**
+ * Sets start margin of text the same as {@link TextListItem#setPrimaryActionIcon(int, boolean)}
+ * with {@code useLargeIcon} set to {@code true}.
+ */
+ public static final int TEXT_START_MARGIN_TYPE_LARGE = 2;
+ @TextStartMarginType private int mTextStartMarginType;
+
+ /**
+ * Sets the start margin of text. Defaults to {@link #TEXT_START_MARGIN_TYPE_NONE}.
+ */
+ public void setTextStartMarginType(@TextStartMarginType int type) {
+ mTextStartMarginType = type;
+ markDirty();
+ }
+
+ /**
+ * Sets the text to be displayed.
+ */
+ public void setText(String text) {
+ mText = text;
+ markDirty();
+ }
+
+ /**
+ * Used by {@link ListItemAdapter} to choose layout to inflate for view holder.
+ */
+ @Override
+ public int getViewType() {
+ return ListItemAdapter.LIST_ITEM_TYPE_SUBHEADER;
+ }
+
+ /**
+ * Calculates layout params for views in {@link ViewHolder}.
+ */
+ @Override
+ protected void resolveDirtyState() {
+ mBinders.clear();
+
+ setItemLayoutHeight();
+ setText();
+ }
+
+ /**
+ * Applies ViewBinders to adjust view layout params.
+ */
+ @Override
+ protected void onBind(ViewHolder viewHolder) {
+ for (ViewBinder binder : mBinders) {
+ binder.bind(viewHolder);
+ }
+ }
+
+ private void setItemLayoutHeight() {
+ int height = mContext.getResources().getDimensionPixelSize(R.dimen.car_sub_header_height);
+ mBinders.add(vh -> {
+ vh.itemView.getLayoutParams().height = height;
+ vh.itemView.requestLayout();
+ });
+ }
+
+ private void setText() {
+ @DimenRes int textStartMarginDimen;
+ switch (mTextStartMarginType) {
+ case TEXT_START_MARGIN_TYPE_NONE:
+ textStartMarginDimen = R.dimen.car_keyline_1;
+ break;
+ case TEXT_START_MARGIN_TYPE_LARGE:
+ textStartMarginDimen = R.dimen.car_keyline_4;
+ break;
+ case TEXT_START_MARGIN_TYPE_SMALL:
+ textStartMarginDimen = R.dimen.car_keyline_3;
+ break;
+ default:
+ throw new IllegalStateException("Unknown text start margin type.");
+ }
+
+ mBinders.add(vh -> {
+ vh.getText().setText(mText);
+ vh.getText().setTextAppearance(mListItemSubheaderTextAppearance);
+
+ ViewGroup.MarginLayoutParams layoutParams =
+ (ViewGroup.MarginLayoutParams) vh.getText().getLayoutParams();
+ layoutParams.setMarginStart(
+ mContext.getResources().getDimensionPixelSize(textStartMarginDimen));
+ vh.getText().requestLayout();
+ });
+ }
+
+ /**
+ * Holds views of SubHeaderListItem.
+ */
+ public static class ViewHolder extends ListItem.ViewHolder {
+
+ private TextView mText;
+
+ public ViewHolder(View itemView) {
+ super(itemView);
+ mText = itemView.findViewById(R.id.text);
+ }
+
+ /**
+ * Update children views to comply with car UX restrictions.
+ *
+ * <p>{@code Text} might be truncated to meet length limit required by regulation.
+ *
+ * @param restrictions current car UX restrictions.
+ */
+ @Override
+ void complyWithUxRestrictions(CarUxRestrictions restrictions) {
+ CarUxRestrictionsUtils.comply(itemView.getContext(), restrictions, getText());
+ }
+
+ public TextView getText() {
+ return mText;
+ }
+ }
+}
diff --git a/collection/src/main/java/androidx/collection/ContainerHelpers.java b/collection/src/main/java/androidx/collection/ContainerHelpers.java
index 4c969d4..f385f9b 100644
--- a/collection/src/main/java/androidx/collection/ContainerHelpers.java
+++ b/collection/src/main/java/androidx/collection/ContainerHelpers.java
@@ -79,4 +79,7 @@
}
return ~lo; // value not present
}
+
+ private ContainerHelpers() {
+ }
}
diff --git a/compat/OWNERS b/compat/OWNERS
index cf4df58..2dc467e 100644
--- a/compat/OWNERS
+++ b/compat/OWNERS
@@ -1,4 +1,5 @@
aurimas@google.com
cinek@google.com
clarabayarri@google.com
-juliacr@google.com
\ No newline at end of file
+juliacr@google.com
+jmonk@google.com
\ No newline at end of file
diff --git a/compat/api/current.txt b/compat/api/current.txt
index 31d571a..1ceedbe 100644
--- a/compat/api/current.txt
+++ b/compat/api/current.txt
@@ -82,7 +82,7 @@
}
public class AppLaunchChecker {
- ctor public AppLaunchChecker();
+ ctor public deprecated AppLaunchChecker();
method public static boolean hasStartedFromLauncher(android.content.Context);
method public static void onActivityCreate(android.app.Activity);
}
@@ -157,7 +157,7 @@
}
public class NotificationCompat {
- ctor public NotificationCompat();
+ ctor public deprecated NotificationCompat();
method public static androidx.core.app.NotificationCompat.Action getAction(android.app.Notification, int);
method public static int getActionCount(android.app.Notification);
method public static int getBadgeIconType(android.app.Notification);
@@ -444,7 +444,8 @@
public static class NotificationCompat.MessagingStyle extends androidx.core.app.NotificationCompat.Style {
ctor public NotificationCompat.MessagingStyle(java.lang.CharSequence);
method public void addCompatExtras(android.os.Bundle);
- method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
+ method public deprecated androidx.core.app.NotificationCompat.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
+ method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(java.lang.CharSequence, long, androidx.core.app.Person);
method public androidx.core.app.NotificationCompat.MessagingStyle addMessage(androidx.core.app.NotificationCompat.MessagingStyle.Message);
method public static androidx.core.app.NotificationCompat.MessagingStyle extractMessagingStyleFromNotification(android.app.Notification);
method public java.lang.CharSequence getConversationTitle();
@@ -457,11 +458,13 @@
}
public static final class NotificationCompat.MessagingStyle.Message {
- ctor public NotificationCompat.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
+ ctor public NotificationCompat.MessagingStyle.Message(java.lang.CharSequence, long, androidx.core.app.Person);
+ ctor public deprecated NotificationCompat.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
method public java.lang.String getDataMimeType();
method public android.net.Uri getDataUri();
method public android.os.Bundle getExtras();
- method public java.lang.CharSequence getSender();
+ method public androidx.core.app.Person getPerson();
+ method public deprecated java.lang.CharSequence getSender();
method public java.lang.CharSequence getText();
method public long getTimestamp();
method public androidx.core.app.NotificationCompat.MessagingStyle.Message setData(java.lang.String, android.net.Uri);
@@ -571,6 +574,29 @@
field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
}
+ public class Person {
+ method public static androidx.core.app.Person fromBundle(android.os.Bundle);
+ method public android.graphics.Bitmap getIcon();
+ method public java.lang.String getKey();
+ method public java.lang.CharSequence getName();
+ method public java.lang.String getUri();
+ method public boolean isBot();
+ method public boolean isImportant();
+ method public androidx.core.app.Person.Builder toBuilder();
+ method public android.os.Bundle toBundle();
+ }
+
+ public static class Person.Builder {
+ ctor public Person.Builder();
+ method public androidx.core.app.Person build();
+ method public androidx.core.app.Person.Builder setBot(boolean);
+ method public androidx.core.app.Person.Builder setIcon(android.graphics.Bitmap);
+ method public androidx.core.app.Person.Builder setImportant(boolean);
+ method public androidx.core.app.Person.Builder setKey(java.lang.String);
+ method public androidx.core.app.Person.Builder setName(java.lang.CharSequence);
+ method public androidx.core.app.Person.Builder setUri(java.lang.String);
+ }
+
public final class RemoteInput {
method public static void addDataResultToIntent(androidx.core.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
method public static void addResultsToIntent(androidx.core.app.RemoteInput[], android.content.Intent, android.os.Bundle);
@@ -779,6 +805,10 @@
field public static final deprecated int CONFIG_UI_MODE = 512; // 0x200
}
+ public final class PackageInfoCompat {
+ method public static long getLongVersionCode(android.content.pm.PackageInfo);
+ }
+
public class ShortcutInfoCompat {
method public android.content.ComponentName getActivity();
method public java.lang.CharSequence getDisabledMessage();
@@ -1062,6 +1092,7 @@
method public static deprecated boolean isAtLeastO();
method public static deprecated boolean isAtLeastOMR1();
method public static boolean isAtLeastP();
+ method public static boolean isAtLeastQ();
}
public final class CancellationSignal {
@@ -1293,6 +1324,10 @@
method public java.io.FileOutputStream startWrite() throws java.io.IOException;
}
+ public abstract interface Consumer<T> {
+ method public abstract void accept(T);
+ }
+
public class ObjectsCompat {
method public static boolean equals(java.lang.Object, java.lang.Object);
method public static int hash(java.lang.Object...);
@@ -1873,6 +1908,7 @@
method public static deprecated int getScaledPagingTouchSlop(android.view.ViewConfiguration);
method public static float getScaledVerticalScrollFactor(android.view.ViewConfiguration, android.content.Context);
method public static deprecated boolean hasPermanentMenuKey(android.view.ViewConfiguration);
+ method public static boolean shouldShowMenuShortcutsWhenKeyboardPresent(android.view.ViewConfiguration, android.content.Context);
}
public final class ViewGroupCompat {
@@ -2409,7 +2445,7 @@
package androidx.core.view.inputmethod {
public final class EditorInfoCompat {
- ctor public EditorInfoCompat();
+ ctor public deprecated EditorInfoCompat();
method public static java.lang.String[] getContentMimeTypes(android.view.inputmethod.EditorInfo);
method public static void setContentMimeTypes(android.view.inputmethod.EditorInfo, java.lang.String[]);
field public static final int IME_FLAG_FORCE_ASCII = -2147483648; // 0x80000000
@@ -2417,7 +2453,7 @@
}
public final class InputConnectionCompat {
- ctor public InputConnectionCompat();
+ ctor public deprecated InputConnectionCompat();
method public static boolean commitContent(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, androidx.core.view.inputmethod.InputContentInfoCompat, int, android.os.Bundle);
method public static android.view.inputmethod.InputConnection createWrapper(android.view.inputmethod.InputConnection, android.view.inputmethod.EditorInfo, androidx.core.view.inputmethod.InputConnectionCompat.OnCommitContentListener);
field public static final int INPUT_CONTENT_GRANT_READ_URI_PERMISSION = 1; // 0x1
diff --git a/compat/res/values-as/strings.xml b/compat/res/values-as/strings.xml
new file mode 100644
index 0000000..b9c349e
--- /dev/null
+++ b/compat/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="status_bar_notification_info_overflow" msgid="7988687684186075107">"৯৯৯+"</string>
+</resources>
diff --git a/compat/res/values-or/strings.xml b/compat/res/values-or/strings.xml
new file mode 100644
index 0000000..f544aef
--- /dev/null
+++ b/compat/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="status_bar_notification_info_overflow" msgid="7988687684186075107">"999+"</string>
+</resources>
diff --git a/compat/src/androidTest/AndroidManifest.xml b/compat/src/androidTest/AndroidManifest.xml
index 2cad9bf..064f6e9 100644
--- a/compat/src/androidTest/AndroidManifest.xml
+++ b/compat/src/androidTest/AndroidManifest.xml
@@ -52,7 +52,6 @@
<activity android:name="androidx.core.widget.ContentLoadingProgressBarActivity"/>
- <activity android:name="androidx.core.widget.TextViewTestActivity"/>
<activity android:name="androidx.core.app.FrameMetricsActivity"/>
<activity android:name="androidx.core.app.FrameMetricsSubActivity"/>
diff --git a/compat/src/androidTest/java/androidx/core/app/NotificationCompatTest.java b/compat/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
index b4117e3..a882891 100644
--- a/compat/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
+++ b/compat/src/androidTest/java/androidx/core/app/NotificationCompatTest.java
@@ -44,6 +44,8 @@
import android.support.test.runner.AndroidJUnit4;
import android.support.v4.BaseInstrumentationTestCase;
+import androidx.core.app.NotificationCompat.MessagingStyle.Message;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -315,18 +317,18 @@
public void testMessage_setAndGetExtras() throws Throwable {
String extraKey = "extra_key";
CharSequence extraValue = "extra_value";
- NotificationCompat.MessagingStyle.Message m =
- new NotificationCompat.MessagingStyle.Message("text", 0 /*timestamp */, "sender");
+ Message m =
+ new Message("text", 0 /*timestamp */, "sender");
m.getExtras().putCharSequence(extraKey, extraValue);
assertEquals(extraValue, m.getExtras().getCharSequence(extraKey));
- ArrayList<NotificationCompat.MessagingStyle.Message> messages = new ArrayList<>(1);
+ ArrayList<Message> messages = new ArrayList<>(1);
messages.add(m);
Bundle[] bundleArray =
- NotificationCompat.MessagingStyle.Message.getBundleArrayForMessages(messages);
+ Message.getBundleArrayForMessages(messages);
assertEquals(1, bundleArray.length);
- NotificationCompat.MessagingStyle.Message fromBundle =
- NotificationCompat.MessagingStyle.Message.getMessageFromBundle(bundleArray[0]);
+ Message fromBundle =
+ Message.getMessageFromBundle(bundleArray[0]);
assertEquals(extraValue, fromBundle.getExtras().getCharSequence(extraKey));
}
@@ -526,71 +528,119 @@
}
@Test
- public void messagingStyle_isGroupConversation() {
+ public void testMessagingStyle_message() {
+ NotificationCompat.MessagingStyle messagingStyle =
+ new NotificationCompat.MessagingStyle("self name");
+ Person person = new Person.Builder().setName("test name").setKey("key").build();
+ Person person2 = new Person.Builder()
+ .setName("test name 2").setKey("key 2").setImportant(true).build();
+ messagingStyle.addMessage("text", 200, person);
+ messagingStyle.addMessage("text2", 300, person2);
+
+ Notification notification = new NotificationCompat.Builder(mContext, "test id")
+ .setSmallIcon(1)
+ .setContentTitle("test title")
+ .setStyle(messagingStyle)
+ .build();
+
+ List<Message> result = NotificationCompat.MessagingStyle
+ .extractMessagingStyleFromNotification(notification)
+ .getMessages();
+
+ assertEquals(2, result.size());
+ assertEquals("text", result.get(0).getText());
+ assertEquals(200, result.get(0).getTimestamp());
+ assertEquals("test name", result.get(0).getPerson().getName());
+ assertEquals("key", result.get(0).getPerson().getKey());
+ assertEquals("text2", result.get(1).getText());
+ assertEquals(300, result.get(1).getTimestamp());
+ assertEquals("test name 2", result.get(1).getPerson().getName());
+ assertEquals("key 2", result.get(1).getPerson().getKey());
+ assertTrue(result.get(1).getPerson().isImportant());
+ }
+
+ @Test
+ public void testMessagingStyle_isGroupConversation() {
mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
NotificationCompat.MessagingStyle messagingStyle =
new NotificationCompat.MessagingStyle("self name")
.setGroupConversation(true)
.setConversationTitle("test conversation title");
- new NotificationCompat.Builder(mContext, "test id")
+ Notification notification = new NotificationCompat.Builder(mContext, "test id")
.setSmallIcon(1)
.setContentTitle("test title")
.setStyle(messagingStyle)
.build();
- assertTrue(messagingStyle.isGroupConversation());
+ NotificationCompat.MessagingStyle result =
+ NotificationCompat.MessagingStyle
+ .extractMessagingStyleFromNotification(notification);
+
+ assertTrue(result.isGroupConversation());
}
@Test
- public void messagingStyle_isGroupConversation_noConversationTitle() {
+ public void testMessagingStyle_isGroupConversation_noConversationTitle() {
mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
NotificationCompat.MessagingStyle messagingStyle =
new NotificationCompat.MessagingStyle("self name")
.setGroupConversation(true)
.setConversationTitle(null);
- new NotificationCompat.Builder(mContext, "test id")
+ Notification notification = new NotificationCompat.Builder(mContext, "test id")
.setSmallIcon(1)
.setContentTitle("test title")
.setStyle(messagingStyle)
.build();
- assertTrue(messagingStyle.isGroupConversation());
+ NotificationCompat.MessagingStyle result =
+ NotificationCompat.MessagingStyle
+ .extractMessagingStyleFromNotification(notification);
+
+ assertTrue(result.isGroupConversation());
}
@Test
- public void messagingStyle_isGroupConversation_withConversationTitle_legacy() {
+ public void testMessagingStyle_isGroupConversation_withConversationTitle_legacy() {
// In legacy (version < P), isGroupConversation is controlled by conversationTitle.
mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
NotificationCompat.MessagingStyle messagingStyle =
new NotificationCompat.MessagingStyle("self name")
.setConversationTitle("test conversation title");
- new NotificationCompat.Builder(mContext, "test id")
+ Notification notification = new NotificationCompat.Builder(mContext, "test id")
.setSmallIcon(1)
.setContentTitle("test title")
.setStyle(messagingStyle)
.build();
- assertTrue(messagingStyle.isGroupConversation());
+ NotificationCompat.MessagingStyle result =
+ NotificationCompat.MessagingStyle
+ .extractMessagingStyleFromNotification(notification);
+
+ assertTrue(result.isGroupConversation());
}
@Test
- public void messagingStyle_isGroupConversation_withoutConversationTitle_legacy() {
+ public void testMessagingStyle_isGroupConversation_withoutConversationTitle_legacy() {
// In legacy (version < P), isGroupConversation is controlled by conversationTitle.
mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
NotificationCompat.MessagingStyle messagingStyle =
new NotificationCompat.MessagingStyle("self name")
.setConversationTitle(null);
- new NotificationCompat.Builder(mContext, "test id")
+ Notification notification = new NotificationCompat.Builder(mContext, "test id")
.setSmallIcon(1)
.setContentTitle("test title")
.setStyle(messagingStyle)
.build();
- assertFalse(messagingStyle.isGroupConversation());
+ NotificationCompat.MessagingStyle result =
+ NotificationCompat.MessagingStyle
+ .extractMessagingStyleFromNotification(notification);
+
+ assertFalse(result.isGroupConversation());
}
@Test
- public void messagingStyle_isGroupConversation_withConversationTitle_legacyWithOverride() {
+ public void testMessagingStyle_isGroupConversation_withConversationTitle_legacyWithOverride() {
// #setGroupConversation should always take precedence over legacy behavior, so a non-null
// title shouldn't affect #isGroupConversation.
mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
@@ -598,17 +648,21 @@
new NotificationCompat.MessagingStyle("self name")
.setGroupConversation(false)
.setConversationTitle("test conversation title");
- new NotificationCompat.Builder(mContext, "test id")
+ Notification notification = new NotificationCompat.Builder(mContext, "test id")
.setSmallIcon(1)
.setContentTitle("test title")
.setStyle(messagingStyle)
.build();
- assertFalse(messagingStyle.isGroupConversation());
+ NotificationCompat.MessagingStyle result =
+ NotificationCompat.MessagingStyle
+ .extractMessagingStyleFromNotification(notification);
+
+ assertFalse(result.isGroupConversation());
}
@Test
- public void messagingStyle_isGroupConversation_withoutConversationTitle_legacyWithOverride() {
+ public void testMessagingStyle_isGroupConversation_withoutTitle_legacyWithOverride() {
// #setGroupConversation should always take precedence over legacy behavior, so a null
// title shouldn't affect #isGroupConversation.
mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
@@ -616,13 +670,17 @@
new NotificationCompat.MessagingStyle("self name")
.setGroupConversation(true)
.setConversationTitle(null);
- new NotificationCompat.Builder(mContext, "test id")
+ Notification notification = new NotificationCompat.Builder(mContext, "test id")
.setSmallIcon(1)
.setContentTitle("test title")
.setStyle(messagingStyle)
.build();
- assertTrue(messagingStyle.isGroupConversation());
+ NotificationCompat.MessagingStyle result =
+ NotificationCompat.MessagingStyle
+ .extractMessagingStyleFromNotification(notification);
+
+ assertTrue(result.isGroupConversation());
}
@Test
@@ -641,6 +699,17 @@
}
@Test
+ public void testMessagingStyleMessage_bundle_legacySender() {
+ Bundle legacyBundle = new Bundle();
+ legacyBundle.putCharSequence(Message.KEY_TEXT, "message");
+ legacyBundle.putLong(Message.KEY_TIMESTAMP, 100);
+ legacyBundle.putCharSequence(Message.KEY_SENDER, "sender");
+
+ Message result = Message.getMessageFromBundle(legacyBundle);
+ assertEquals("sender", result.getPerson().getName());
+ }
+
+ @Test
public void action_builder_hasDefault() {
NotificationCompat.Action action =
new NotificationCompat.Action.Builder(0, "Test Title", null).build();
diff --git a/compat/src/androidTest/java/androidx/core/app/PersonTest.java b/compat/src/androidTest/java/androidx/core/app/PersonTest.java
new file mode 100644
index 0000000..20b2090
--- /dev/null
+++ b/compat/src/androidTest/java/androidx/core/app/PersonTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.core.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PersonTest {
+ private static final CharSequence TEST_NAME = "Example Name";
+ private static final Bitmap TEST_ICON = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ private static final String TEST_URI = "mailto:example@example.com";
+ private static final String TEST_KEY = "example-key";
+ private static final boolean TEST_IS_BOT = true;
+ private static final boolean TEST_IS_IMPORTANT = true;
+
+ @Test
+ @SdkSuppress(minSdkVersion = 12)
+ public void bundle() {
+ Person person = new Person.Builder()
+ .setImportant(TEST_IS_IMPORTANT)
+ .setBot(TEST_IS_BOT)
+ .setKey(TEST_KEY)
+ .setUri(TEST_URI)
+ .setIcon(TEST_ICON)
+ .setName(TEST_NAME)
+ .build();
+
+ Bundle personBundle = person.toBundle();
+ Person result = Person.fromBundle(personBundle);
+
+ assertEquals(TEST_NAME, result.getName());
+ assertEquals(TEST_URI, result.getUri());
+ assertEquals(TEST_KEY, result.getKey());
+ assertEquals(TEST_IS_BOT, result.isBot());
+ assertEquals(TEST_IS_IMPORTANT, result.isImportant());
+
+ // Requires SDK >= 12
+ assertTrue(TEST_ICON.sameAs(result.getIcon()));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 12)
+ public void toBuilder() {
+ Person person = new Person.Builder()
+ .setImportant(TEST_IS_IMPORTANT)
+ .setBot(TEST_IS_BOT)
+ .setKey(TEST_KEY)
+ .setUri(TEST_URI)
+ .setIcon(TEST_ICON)
+ .setName(TEST_NAME)
+ .build();
+ Person result = person.toBuilder().build();
+
+ assertEquals(TEST_NAME, result.getName());
+ assertEquals(TEST_URI, result.getUri());
+ assertEquals(TEST_KEY, result.getKey());
+ assertEquals(TEST_IS_BOT, result.isBot());
+ assertEquals(TEST_IS_IMPORTANT, result.isImportant());
+
+ // Requires SDK >= 12
+ assertTrue(TEST_ICON.sameAs(result.getIcon()));
+ }
+
+ @Test
+ public void getName() {
+ Person person = new Person.Builder().setName(TEST_NAME).build();
+ assertEquals(TEST_NAME, person.getName());
+ }
+
+ @Test
+ public void getIcon() {
+ Person person = new Person.Builder().setIcon(TEST_ICON).build();
+ assertEquals(TEST_ICON, person.getIcon());
+ }
+
+ @Test
+ public void getUri() {
+ Person person = new Person.Builder().setUri(TEST_URI).build();
+ assertEquals(TEST_URI, person.getUri());
+ }
+
+ @Test
+ public void getKey() {
+ Person person = new Person.Builder().setKey(TEST_KEY).build();
+ assertEquals(TEST_KEY, person.getKey());
+ }
+
+ @Test
+ public void isBot() {
+ Person person = new Person.Builder().setBot(TEST_IS_BOT).build();
+ assertEquals(TEST_IS_BOT, person.isBot());
+ }
+
+ @Test
+ public void isImportant() {
+ Person person = new Person.Builder().setImportant(TEST_IS_IMPORTANT).build();
+ assertEquals(TEST_IS_IMPORTANT, person.isImportant());
+ }
+}
diff --git a/compat/src/androidTest/java/androidx/core/content/pm/PackageInfoCompatTest.java b/compat/src/androidTest/java/androidx/core/content/pm/PackageInfoCompatTest.java
new file mode 100644
index 0000000..e3e5504
--- /dev/null
+++ b/compat/src/androidTest/java/androidx/core/content/pm/PackageInfoCompatTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.core.content.pm;
+
+import static android.os.Build.VERSION_CODES.P;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.pm.PackageInfo;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@SmallTest
+public final class PackageInfoCompatTest {
+ @Test
+ public void getLongVersionCodeLowerBitsOnly() {
+ PackageInfo info = new PackageInfo();
+ info.versionCode = 12345;
+
+ assertEquals(12345L, PackageInfoCompat.getLongVersionCode(info));
+ }
+
+ @SdkSuppress(minSdkVersion = P)
+ @Test
+ public void getLongVersionCodeLowerAndUpperBits() {
+ PackageInfo info = new PackageInfo();
+ info.setLongVersionCode(Long.MAX_VALUE);
+
+ assertEquals(Long.MAX_VALUE, PackageInfoCompat.getLongVersionCode(info));
+ }
+}
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/AndroidManifest.xml b/compat/src/main/AndroidManifest.xml
index d18ee92..6c4338d 100644
--- a/compat/src/main/AndroidManifest.xml
+++ b/compat/src/main/AndroidManifest.xml
@@ -13,4 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<manifest package="androidx.core"/>
+<manifest package="androidx.core" xmlns:android="http://schemas.android.com/apk/res/android">
+ <application
+ android:appComponentFactory="androidx.core.app.CoreComponentFactory" />
+</manifest>
diff --git a/compat/src/main/java/androidx/core/app/ActivityOptionsCompat.java b/compat/src/main/java/androidx/core/app/ActivityOptionsCompat.java
index 49f6c4a..19beb5d 100644
--- a/compat/src/main/java/androidx/core/app/ActivityOptionsCompat.java
+++ b/compat/src/main/java/androidx/core/app/ActivityOptionsCompat.java
@@ -273,17 +273,25 @@
@Override
public void requestUsageTimeReport(PendingIntent receiver) {
- mActivityOptions.requestUsageTimeReport(receiver);
+ if (Build.VERSION.SDK_INT >= 23) {
+ mActivityOptions.requestUsageTimeReport(receiver);
+ }
}
@Override
public ActivityOptionsCompat setLaunchBounds(@Nullable Rect screenSpacePixelRect) {
+ if (Build.VERSION.SDK_INT < 24) {
+ return this;
+ }
return new ActivityOptionsCompatImpl(
mActivityOptions.setLaunchBounds(screenSpacePixelRect));
}
@Override
public Rect getLaunchBounds() {
+ if (Build.VERSION.SDK_INT < 24) {
+ return null;
+ }
return mActivityOptions.getLaunchBounds();
}
}
diff --git a/compat/src/main/java/androidx/core/app/AppLaunchChecker.java b/compat/src/main/java/androidx/core/app/AppLaunchChecker.java
index 8b8d308..8170aeb 100644
--- a/compat/src/main/java/androidx/core/app/AppLaunchChecker.java
+++ b/compat/src/main/java/androidx/core/app/AppLaunchChecker.java
@@ -80,4 +80,10 @@
sp.edit().putBoolean(KEY_STARTED_FROM_LAUNCHER, true).apply();
}
}
+
+ /** @deprecated This type should not be instantiated as it contains only static methods. */
+ @Deprecated
+ @SuppressWarnings("PrivateConstructorForUtilityClass")
+ public AppLaunchChecker() {
+ }
}
diff --git a/compat/src/main/java/androidx/core/app/BundleCompat.java b/compat/src/main/java/androidx/core/app/BundleCompat.java
index c1bb802..7b6968d 100644
--- a/compat/src/main/java/androidx/core/app/BundleCompat.java
+++ b/compat/src/main/java/androidx/core/app/BundleCompat.java
@@ -41,6 +41,9 @@
private static Method sPutIBinderMethod;
private static boolean sPutIBinderMethodFetched;
+ private BundleCompatBaseImpl() {
+ }
+
public static IBinder getBinder(Bundle bundle, String key) {
if (!sGetIBinderMethodFetched) {
try {
diff --git a/compat/src/main/java/androidx/core/app/CoreComponentFactory.java b/compat/src/main/java/androidx/core/app/CoreComponentFactory.java
new file mode 100644
index 0000000..ef6acc1
--- /dev/null
+++ b/compat/src/main/java/androidx/core/app/CoreComponentFactory.java
@@ -0,0 +1,112 @@
+/*
+ * 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.core.app;
+
+import android.app.Activity;
+import android.app.AppComponentFactory;
+import android.app.Application;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.ContentProvider;
+import android.content.Intent;
+
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+
+/**
+ * Instance of AppComponentFactory for support libraries.
+ * @see CompatWrapped
+ * @hide
+ */
+@RequiresApi(api = 28)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class CoreComponentFactory extends AppComponentFactory {
+ private static final String TAG = "CoreComponentFactory";
+
+ @Override
+ public Activity instantiateActivity(ClassLoader cl, String className, Intent intent)
+ throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+ return checkCompatWrapper(super.instantiateActivity(cl, className, intent));
+ }
+
+ @Override
+ public Application instantiateApplication(ClassLoader cl, String className)
+ throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+ return checkCompatWrapper(super.instantiateApplication(cl, className));
+ }
+
+ @Override
+ public BroadcastReceiver instantiateReceiver(ClassLoader cl, String className, Intent intent)
+ throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+ return checkCompatWrapper(super.instantiateReceiver(cl, className, intent));
+ }
+
+ @Override
+ public ContentProvider instantiateProvider(ClassLoader cl, String className)
+ throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+ return checkCompatWrapper(super.instantiateProvider(cl, className));
+ }
+
+ @Override
+ public Service instantiateService(ClassLoader cl, String className, Intent intent)
+ throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+ return checkCompatWrapper(super.instantiateService(cl, className, intent));
+ }
+
+ private <T> T checkCompatWrapper(T obj) {
+ if (obj instanceof CompatWrapped) {
+ T wrapper = (T) ((CompatWrapped) obj).getWrapper();
+ if (wrapper != null) {
+ return wrapper;
+ }
+ }
+ return obj;
+ }
+
+ /**
+ * Implement this interface to allow a different class to be returned when instantiating
+ * on certain API levels.
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public interface CompatWrapped {
+ /**
+ * Called while this class is being instantiated by the OS.
+ *
+ * If an object is returned then it will be used in place of the class.
+ * Note: this will not be called on API <= 27.
+ *
+ * Example:
+ * <pre class="prettyprint">
+ * {@literal
+ * public AndroidXContentProvider extends ContentProvider implements CompatWrapped {
+ * ...
+ *
+ * public Object getWrapper() {
+ * if (SDK_INT >= 29) {
+ * return new AndroidXContentProviderV29(this);
+ * }
+ * return null;
+ * }
+ * }
+ * }
+ * </pre>
+ */
+ Object getWrapper();
+ }
+
+}
diff --git a/compat/src/main/java/androidx/core/app/NotificationCompat.java b/compat/src/main/java/androidx/core/app/NotificationCompat.java
index b8b5205..aba40d1 100644
--- a/compat/src/main/java/androidx/core/app/NotificationCompat.java
+++ b/compat/src/main/java/androidx/core/app/NotificationCompat.java
@@ -2214,9 +2214,14 @@
* @see Message#Message(CharSequence, long, CharSequence)
*
* @return this object for method chaining
+ *
+ * @deprecated Use {@link #addMessage(CharSequence, long, Person)} or
+ * {@link #addMessage(Message)}
*/
+ @Deprecated
public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
- mMessages.add(new Message(text, timestamp, sender));
+ mMessages.add(
+ new Message(text, timestamp, new Person.Builder().setName(sender).build()));
if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
mMessages.remove(0);
}
@@ -2224,8 +2229,23 @@
}
/**
+ * Adds a message for display by this notification. Convenience call for
+ * {@link #addMessage(Message)}.
+ *
+ * @see Message#Message(CharSequence, long, Person)
+ *
+ * @return this for method chaining
+ */
+ public MessagingStyle addMessage(CharSequence text, long timestamp, Person person) {
+ addMessage(new Message(text, timestamp, person));
+ return this;
+ }
+
+ /**
* Adds a {@link Message} for display in this notification.
+ *
* @param message The {@link Message} to be displayed
+ *
* @return this object for method chaining
*/
public MessagingStyle addMessage(Message message) {
@@ -2313,6 +2333,12 @@
@RestrictTo(LIBRARY_GROUP)
@Override
public void apply(NotificationBuilderWithBuilderAccessor builder) {
+ // This is called because we need to apply legacy logic before writing MessagingInfo
+ // data into the bundle. This does nothing in >= P, but in < P this will apply the
+ // correct group conversation status to new fields which will then be decoded properly
+ // by #extractMessagingStyleFromNotification.
+ setGroupConversation(isGroupConversation());
+
if (Build.VERSION.SDK_INT >= 24) {
Notification.MessagingStyle style =
new Notification.MessagingStyle(mUserDisplayName)
@@ -2456,24 +2482,42 @@
}
public static final class Message {
-
static final String KEY_TEXT = "text";
static final String KEY_TIMESTAMP = "time";
static final String KEY_SENDER = "sender";
static final String KEY_DATA_MIME_TYPE = "type";
static final String KEY_DATA_URI= "uri";
static final String KEY_EXTRAS_BUNDLE = "extras";
+ static final String KEY_PERSON = "person";
private final CharSequence mText;
private final long mTimestamp;
- private final CharSequence mSender;
+ @Nullable private final Person mPerson;
private Bundle mExtras = new Bundle();
- private String mDataMimeType;
- private Uri mDataUri;
+ @Nullable private String mDataMimeType;
+ @Nullable private Uri mDataUri;
+
+ /**
+ * Creates a new {@link Message} with the given text, timestamp, and sender.
+ *
+ * @param text A {@link CharSequence} to be displayed as the message content
+ * @param timestamp Time at which the message arrived in ms since Unix epoch
+ * @param person A {@link Person} whose {@link Person#getName()} value is used as the
+ * display name for the sender. This should be {@code null} for messages by the current
+ * user, in which case, the platform will insert
+ * {@link MessagingStyle#getUserDisplayName()}. A {@link Person}'s key should be
+ * consistent during re-posts of the notification.
+ */
+ public Message(CharSequence text, long timestamp, @Nullable Person person) {
+ mText = text;
+ mTimestamp = timestamp;
+ mPerson = person;
+ }
/**
* Constructor
+ *
* @param text A {@link CharSequence} to be displayed as the message content
* @param timestamp Time at which the message arrived in ms since Unix epoch
* @param sender A {@link CharSequence} to be used for displaying the name of the
@@ -2481,17 +2525,19 @@
* the platform will insert {@link MessagingStyle#getUserDisplayName()}.
* Should be unique amongst all individuals in the conversation, and should be
* consistent during re-posts of the notification.
+ *
+ * @deprecated Use the alternative constructor instead.
*/
+ @Deprecated
public Message(CharSequence text, long timestamp, CharSequence sender){
- mText = text;
- mTimestamp = timestamp;
- mSender = sender;
+ this(text, timestamp, new Person.Builder().setName(sender).build());
}
/**
* Sets a binary blob of data and an associated MIME type for a message. In the case
* where the platform doesn't support the MIME type, the original text provided in the
* constructor will be used.
+ *
* @param dataMimeType The MIME type of the content. See
* <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
* types on Android and Android Wear.
@@ -2513,6 +2559,7 @@
* Note that once added to the system MediaStore the content is accessible to any
* app on the device.</li>
* </ol>
+ *
* @return this object for method chaining
*/
public Message setData(String dataMimeType, Uri dataUri) {
@@ -2525,34 +2572,41 @@
* Get the text to be used for this message, or the fallback text if a type and content
* Uri have been set
*/
+ @NonNull
public CharSequence getText() {
return mText;
}
- /**
- * Get the time at which this message arrived in ms since Unix epoch
- */
+ /** Get the time at which this message arrived in ms since Unix epoch. */
public long getTimestamp() {
return mTimestamp;
}
- /**
- * Get the extras Bundle for this message.
- */
+ /** Get the extras Bundle for this message. */
+ @NonNull
public Bundle getExtras() {
return mExtras;
}
/**
* Get the text used to display the contact's name in the messaging experience
+ *
+ * @deprecated Use {@link #getPerson()}
*/
+ @Deprecated
+ @Nullable
public CharSequence getSender() {
- return mSender;
+ return mPerson.getName();
}
- /**
- * Get the MIME type of the data pointed to by the Uri
- */
+ /** Returns the {@link Person} sender of this message. */
+ @Nullable
+ public Person getPerson() {
+ return mPerson;
+ }
+
+ /** Get the MIME type of the data pointed to by the URI. */
+ @Nullable
public String getDataMimeType() {
return mDataMimeType;
}
@@ -2561,6 +2615,7 @@
* Get the the Uri pointing to the content of the message. Can be null, in which case
* {@see #getText()} is used.
*/
+ @Nullable
public Uri getDataUri() {
return mDataUri;
}
@@ -2571,8 +2626,8 @@
bundle.putCharSequence(KEY_TEXT, mText);
}
bundle.putLong(KEY_TIMESTAMP, mTimestamp);
- if (mSender != null) {
- bundle.putCharSequence(KEY_SENDER, mSender);
+ if (mPerson != null) {
+ bundle.putBundle(KEY_PERSON, mPerson.toBundle());
}
if (mDataMimeType != null) {
bundle.putString(KEY_DATA_MIME_TYPE, mDataMimeType);
@@ -2586,6 +2641,7 @@
return bundle;
}
+ @NonNull
static Bundle[] getBundleArrayForMessages(List<Message> messages) {
Bundle[] bundles = new Bundle[messages.size()];
final int N = messages.size();
@@ -2595,6 +2651,7 @@
return bundles;
}
+ @NonNull
static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
List<Message> messages = new ArrayList<>(bundles.length);
for (int i = 0; i < bundles.length; i++) {
@@ -2608,23 +2665,38 @@
return messages;
}
+ @Nullable
static Message getMessageFromBundle(Bundle bundle) {
try {
if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
return null;
- } else {
- Message message = new Message(bundle.getCharSequence(KEY_TEXT),
- bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
- if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
- bundle.containsKey(KEY_DATA_URI)) {
- message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
- (Uri) bundle.getParcelable(KEY_DATA_URI));
- }
- if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
- message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
- }
- return message;
}
+
+ Message message;
+ if (bundle.containsKey(KEY_SENDER)) {
+ // Legacy sender
+ message = new Message(
+ bundle.getCharSequence(KEY_TEXT),
+ bundle.getLong(KEY_TIMESTAMP),
+ new Person.Builder()
+ .setName(bundle.getCharSequence(KEY_SENDER))
+ .build());
+ } else {
+ message = new Message(
+ bundle.getCharSequence(KEY_TEXT),
+ bundle.getLong(KEY_TIMESTAMP),
+ Person.fromBundle(bundle.getBundle(KEY_PERSON)));
+ }
+
+ if (bundle.containsKey(KEY_DATA_MIME_TYPE)
+ && bundle.containsKey(KEY_DATA_URI)) {
+ message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
+ (Uri) bundle.getParcelable(KEY_DATA_URI));
+ }
+ if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
+ message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
+ }
+ return message;
} catch (ClassCastException e) {
return null;
}
@@ -5113,4 +5185,10 @@
return GROUP_ALERT_ALL;
}
}
+
+ /** @deprecated This type should not be instantiated as it contains only static methods. */
+ @Deprecated
+ @SuppressWarnings("PrivateConstructorForUtilityClass")
+ public NotificationCompat() {
+ }
}
diff --git a/compat/src/main/java/androidx/core/app/NotificationCompatJellybean.java b/compat/src/main/java/androidx/core/app/NotificationCompatJellybean.java
index 3157aad..f334fc1 100644
--- a/compat/src/main/java/androidx/core/app/NotificationCompatJellybean.java
+++ b/compat/src/main/java/androidx/core/app/NotificationCompatJellybean.java
@@ -338,4 +338,7 @@
bundle.putParcelableArray(key, typedArray);
return typedArray;
}
+
+ private NotificationCompatJellybean() {
+ }
}
diff --git a/compat/src/main/java/androidx/core/app/Person.java b/compat/src/main/java/androidx/core/app/Person.java
new file mode 100644
index 0000000..3bda510
--- /dev/null
+++ b/compat/src/main/java/androidx/core/app/Person.java
@@ -0,0 +1,246 @@
+/*
+ * 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.core.app;
+
+import android.graphics.Bitmap;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Provides an immutable reference to an entity that appears repeatedly on different surfaces of the
+ * platform. For example, this could represent the sender of a message.
+ */
+public class Person {
+ private static final String NAME_KEY = "name";
+ private static final String ICON_KEY = "icon";
+ private static final String URI_KEY = "uri";
+ private static final String KEY_KEY = "key";
+ private static final String IS_BOT_KEY = "isBot";
+ private static final String IS_IMPORTANT_KEY = "isImportant";
+
+ /**
+ * Extracts and returns the {@link Person} written to the {@code bundle}. A bundle can be
+ * created from a {@link Person} using {@link #toBundle()}.
+ */
+ public static Person fromBundle(Bundle bundle) {
+ return new Builder()
+ .setName(bundle.getCharSequence(NAME_KEY))
+ .setIcon((Bitmap) bundle.getParcelable(ICON_KEY))
+ .setUri(bundle.getString(URI_KEY))
+ .setKey(bundle.getString(KEY_KEY))
+ .setBot(bundle.getBoolean(IS_BOT_KEY))
+ .setImportant(bundle.getBoolean(IS_IMPORTANT_KEY))
+ .build();
+ }
+
+ @Nullable private CharSequence mName;
+ @Nullable private Bitmap mIcon;
+ @Nullable private String mUri;
+ @Nullable private String mKey;
+ private boolean mIsBot;
+ private boolean mIsImportant;
+
+ private Person(Builder builder) {
+ mName = builder.mName;
+ mIcon = builder.mIcon;
+ mUri = builder.mUri;
+ mKey = builder.mKey;
+ mIsBot = builder.mIsBot;
+ mIsImportant = builder.mIsImportant;
+ }
+
+ /**
+ * Writes and returns a new {@link Bundle} that represents this {@link Person}. This bundle can
+ * be converted back by using {@link #fromBundle(Bundle)}.
+ */
+ public Bundle toBundle() {
+ Bundle result = new Bundle();
+ result.putCharSequence(NAME_KEY, mName);
+ result.putParcelable(ICON_KEY, mIcon);
+ result.putString(URI_KEY, mUri);
+ result.putString(KEY_KEY, mKey);
+ result.putBoolean(IS_BOT_KEY, mIsBot);
+ result.putBoolean(IS_IMPORTANT_KEY, mIsImportant);
+ return result;
+ }
+
+ /** Creates and returns a new {@link Builder} initialized with this Person's data. */
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
+ /**
+ * Returns the name for this {@link Person} or {@code null} if no name was provided. This could
+ * be a full name, nickname, username, etc.
+ */
+ @Nullable
+ public CharSequence getName() {
+ return mName;
+ }
+
+ /** Returns the icon for this {@link Person} or {@code null} if no icon was provided. */
+ @Nullable
+ public Bitmap getIcon() {
+ return mIcon;
+ }
+
+ /**
+ * Returns the raw URI for this {@link Person} or {@code null} if no URI was provided. A URI can
+ * be any of the following:
+ * <ul>
+ * <li>The {@code String} representation of a
+ * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}</li>
+ * <li>A {@code mailto:} schema*</li>
+ * <li>A {@code tel:} schema*</li>
+ * </ul>
+ *
+ * <p>*Note for these schemas, the path portion of the URI must exist in the contacts
+ * database in their appropriate column, otherwise the reference should be discarded.
+ */
+ @Nullable
+ public String getUri() {
+ return mUri;
+ }
+
+ /**
+ * Returns the key for this {@link Person} or {@code null} if no key was provided. This is
+ * provided as a unique identifier between other {@link Person}s.
+ */
+ @Nullable
+ public String getKey() {
+ return mKey;
+ }
+
+ /**
+ * Returns whether or not this {@link Person} is a machine rather than a human. Used primarily
+ * to identify automated tooling.
+ */
+ public boolean isBot() {
+ return mIsBot;
+ }
+
+ /**
+ * Returns whether or not this {@link Person} is important to the user of this device with
+ * regards to how frequently they interact.
+ */
+ public boolean isImportant() {
+ return mIsImportant;
+ }
+
+ /** Builder for the immutable {@link Person} class. */
+ public static class Builder {
+ @Nullable private CharSequence mName;
+ @Nullable private Bitmap mIcon;
+ @Nullable private String mUri;
+ @Nullable private String mKey;
+ private boolean mIsBot;
+ private boolean mIsImportant;
+
+ /** Creates a new, empty {@link Builder}. */
+ public Builder() { }
+
+ private Builder(Person person) {
+ mName = person.mName;
+ mIcon = person.mIcon;
+ mUri = person.mUri;
+ mKey = person.mKey;
+ mIsBot = person.mIsBot;
+ mIsImportant = person.mIsImportant;
+ }
+
+ /**
+ * Give this {@link Person} a name to use for display. This can be, for example, a full
+ * name, nickname, username, etc.
+ */
+ @NonNull
+ public Builder setName(@Nullable CharSequence name) {
+ mName = name;
+ return this;
+ }
+
+ /**
+ * Set an icon for this {@link Person}.
+ *
+ * <p>The system will prefer this icon over any images that are resolved from
+ * {@link #setUri(String)}.
+ */
+ @NonNull
+ public Builder setIcon(@Nullable Bitmap icon) {
+ mIcon = icon;
+ return this;
+ }
+
+ /**
+ * Set a URI for this {@link Person} which can be any of the following:
+ * <ul>
+ * <li>The {@code String} representation of a
+ * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}</li>
+ * <li>A {@code mailto:} schema*</li>
+ * <li>A {@code tel:} schema*</li>
+ * </ul>
+ *
+ * <p>*Note for these schemas, the path portion of the URI must exist in the contacts
+ * database in their appropriate column, otherwise the reference will be discarded.
+ */
+ @NonNull
+ public Builder setUri(@Nullable String uri) {
+ mUri = uri;
+ return this;
+ }
+
+ /**
+ * Set a unique identifier for this {@link Person}. This is especially useful if the
+ * {@link #setName(CharSequence)} value isn't unique. This value is preferred for
+ * identification, but if it's not provided, the person's name will be used in its place.
+ */
+ @NonNull
+ public Builder setKey(@Nullable String key) {
+ mKey = key;
+ return this;
+ }
+
+ /**
+ * Sets whether or not this {@link Person} represents a machine rather than a human. This is
+ * used primarily for testing and automated tooling.
+ */
+ @NonNull
+ public Builder setBot(boolean bot) {
+ mIsBot = bot;
+ return this;
+ }
+
+ /**
+ * Sets whether this is an important person. Use this method to denote users who frequently
+ * interact with the user of this device when {@link #setUri(String)} isn't provided with
+ * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, and instead with
+ * the {@code mailto:} or {@code tel:} schemas.
+ */
+ @NonNull
+ public Builder setImportant(boolean important) {
+ mIsImportant = important;
+ return this;
+ }
+
+ /** Creates and returns the {@link Person} this builder represents. */
+ @NonNull
+ public Person build() {
+ return new Person(this);
+ }
+ }
+}
diff --git a/compat/src/main/java/androidx/core/content/pm/PackageInfoCompat.java b/compat/src/main/java/androidx/core/content/pm/PackageInfoCompat.java
new file mode 100644
index 0000000..71c53f2
--- /dev/null
+++ b/compat/src/main/java/androidx/core/content/pm/PackageInfoCompat.java
@@ -0,0 +1,43 @@
+/*
+ * 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.core.content.pm;
+
+import android.content.pm.PackageInfo;
+
+import androidx.annotation.NonNull;
+import androidx.core.os.BuildCompat;
+
+/** Helper for accessing features in {@link PackageInfo}. */
+public final class PackageInfoCompat {
+ /**
+ * Return {@link android.R.attr#versionCode} and {@link android.R.attr#versionCodeMajor}
+ * combined together as a single long value. The {@code versionCodeMajor} is placed in the
+ * upper 32 bits on Android P or newer, otherwise these bits are all set to 0.
+ *
+ * @see PackageInfo#getLongVersionCode()
+ */
+ public static long getLongVersionCode(@NonNull PackageInfo info) {
+ if (BuildCompat.isAtLeastP()) {
+ return info.getLongVersionCode();
+ }
+
+ return info.versionCode;
+ }
+
+ private PackageInfoCompat() {
+ }
+}
diff --git a/compat/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java b/compat/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java
index d2f2f22..dd8d933 100644
--- a/compat/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java
+++ b/compat/src/main/java/androidx/core/content/res/FontResourcesParserCompat.java
@@ -308,4 +308,7 @@
}
}
}
+
+ private FontResourcesParserCompat() {
+ }
}
diff --git a/compat/src/main/java/androidx/core/content/res/TypedArrayUtils.java b/compat/src/main/java/androidx/core/content/res/TypedArrayUtils.java
index 2aa8449..0bb2498 100644
--- a/compat/src/main/java/androidx/core/content/res/TypedArrayUtils.java
+++ b/compat/src/main/java/androidx/core/content/res/TypedArrayUtils.java
@@ -294,4 +294,7 @@
}
return fallbackAttr;
}
+
+ private TypedArrayUtils() {
+ }
}
diff --git a/compat/src/main/java/androidx/core/graphics/PathParser.java b/compat/src/main/java/androidx/core/graphics/PathParser.java
index 6f9d83d..2d0282c 100644
--- a/compat/src/main/java/androidx/core/graphics/PathParser.java
+++ b/compat/src/main/java/androidx/core/graphics/PathParser.java
@@ -758,4 +758,7 @@
}
}
}
+
+ private PathParser() {
+ }
}
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 6dbe266..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,39 +86,11 @@
*/
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
- */
- @RestrictTo(LIBRARY)
- @IntDef({TYPE_BITMAP, TYPE_RESOURCE, TYPE_DATA, TYPE_URI, TYPE_ADAPTIVE_BITMAP})
+ @IntDef({TYPE_UNKOWN, TYPE_BITMAP, TYPE_RESOURCE, TYPE_DATA, TYPE_URI, TYPE_ADAPTIVE_BITMAP})
@Retention(RetentionPolicy.SOURCE)
public @interface IconType {
}
@@ -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/compat/src/main/java/androidx/core/graphics/drawable/WrappedDrawableApi14.java b/compat/src/main/java/androidx/core/graphics/drawable/WrappedDrawableApi14.java
index b57ff37..fde4e85 100644
--- a/compat/src/main/java/androidx/core/graphics/drawable/WrappedDrawableApi14.java
+++ b/compat/src/main/java/androidx/core/graphics/drawable/WrappedDrawableApi14.java
@@ -27,6 +27,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
/**
* Drawable which delegates all calls to its wrapped {@link Drawable}.
@@ -192,11 +193,13 @@
}
@Override
+ @RequiresApi(19)
public void setAutoMirrored(boolean mirrored) {
mDrawable.setAutoMirrored(mirrored);
}
@Override
+ @RequiresApi(19)
public boolean isAutoMirrored() {
return mDrawable.isAutoMirrored();
}
diff --git a/compat/src/main/java/androidx/core/os/BuildCompat.java b/compat/src/main/java/androidx/core/os/BuildCompat.java
index fe3f4be..277a724 100644
--- a/compat/src/main/java/androidx/core/os/BuildCompat.java
+++ b/compat/src/main/java/androidx/core/os/BuildCompat.java
@@ -32,7 +32,7 @@
* @return {@code true} if N APIs are available for use
* @deprecated Android N is a finalized release and this method is no longer necessary. It will
* be removed in a future release of the Support Library. Instead, use
- * {@code Build.SDK_INT >= Build.VERSION_CODES#N}.
+ * {@code Build.SDK_INT >= Build.VERSION_CODES.N}.
*/
@Deprecated
public static boolean isAtLeastN() {
@@ -45,7 +45,7 @@
* @return {@code true} if N MR1 APIs are available for use
* @deprecated Android N MR1 is a finalized release and this method is no longer necessary. It
* will be removed in a future release of the Support Library. Instead, use
- * {@code Build.SDK_INT >= Build.VERSION_CODES#N_MR1}.
+ * {@code Build.SDK_INT >= Build.VERSION_CODES.N_MR1}.
*/
@Deprecated
public static boolean isAtLeastNMR1() {
@@ -58,7 +58,7 @@
* @return {@code true} if O APIs are available for use, {@code false} otherwise
* @deprecated Android O is a finalized release and this method is no longer necessary. It will
* be removed in a future release of the Support Library. Instead use
- * {@code Build.SDK_INT >= Build.VERSION_CODES#O}.
+ * {@code Build.SDK_INT >= Build.VERSION_CODES.O}.
*/
@Deprecated
public static boolean isAtLeastO() {
@@ -71,7 +71,7 @@
* @return {@code true} if O MR1 APIs are available for use, {@code false} otherwise
* @deprecated Android O MR1 is a finalized release and this method is no longer necessary. It
* will be removed in a future release of the Support Library. Instead, use
- * {@code Build.SDK_INT >= Build.VERSION_CODES#O_MR1}.
+ * {@code Build.SDK_INT >= Build.VERSION_CODES.O_MR1}.
*/
@Deprecated
public static boolean isAtLeastOMR1() {
@@ -83,11 +83,24 @@
* <p>
* <strong>Note:</strong> This method will return {@code false} on devices running release
* versions of Android. When Android P is finalized for release, this method will be deprecated
- * and all calls should be replaced with {@code Build.SDK_INT >= Build.VERSION_CODES#P}.
+ * and all calls should be replaced with {@code Build.SDK_INT >= Build.VERSION_CODES.P}.
*
* @return {@code true} if P APIs are available for use, {@code false} otherwise
*/
public static boolean isAtLeastP() {
- return VERSION.CODENAME.equals("P");
+ return VERSION.CODENAME.equals("P") || VERSION.CODENAME.equals("Q");
+ }
+
+ /**
+ * Checks if the device is running on a pre-release version of Android Q or newer.
+ * <p>
+ * <strong>Note:</strong> This method will return {@code false} on devices running release
+ * versions of Android. When Android Q is finalized for release, this method will be deprecated
+ * and all calls should be replaced with {@code Build.SDK_INT >= Build.VERSION_CODES.Q}.
+ *
+ * @return {@code true} if Q APIs are available for use, {@code false} otherwise
+ */
+ public static boolean isAtLeastQ() {
+ return VERSION.CODENAME.equals("Q");
}
}
diff --git a/compat/src/main/java/androidx/core/os/LocaleHelper.java b/compat/src/main/java/androidx/core/os/LocaleHelper.java
index f8fa5c7..001d657 100644
--- a/compat/src/main/java/androidx/core/os/LocaleHelper.java
+++ b/compat/src/main/java/androidx/core/os/LocaleHelper.java
@@ -69,4 +69,7 @@
return buf.toString();
}
+
+ private LocaleHelper() {
+ }
}
diff --git a/compat/src/main/java/androidx/core/text/HtmlCompat.java b/compat/src/main/java/androidx/core/text/HtmlCompat.java
index b846805..a5b97ea 100644
--- a/compat/src/main/java/androidx/core/text/HtmlCompat.java
+++ b/compat/src/main/java/androidx/core/text/HtmlCompat.java
@@ -18,6 +18,8 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import android.annotation.SuppressLint;
import android.graphics.Color;
import android.os.Build;
@@ -33,6 +35,8 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import java.lang.annotation.Retention;
+
/**
* Backwards compatible version of {@link Html}.
*/
@@ -119,6 +123,7 @@
FROM_HTML_MODE_LEGACY
}, flag = true)
@RestrictTo(LIBRARY)
+ @Retention(SOURCE)
@interface FromHtmlFlags {
}
@@ -128,6 +133,7 @@
TO_HTML_PARAGRAPH_LINES_INDIVIDUAL
})
@RestrictTo(LIBRARY)
+ @Retention(SOURCE)
@interface ToHtmlOptions {
}
diff --git a/compat/src/main/java/androidx/core/text/util/FindAddress.java b/compat/src/main/java/androidx/core/text/util/FindAddress.java
index b0523db..d66fa05 100644
--- a/compat/src/main/java/androidx/core/text/util/FindAddress.java
+++ b/compat/src/main/java/androidx/core/text/util/FindAddress.java
@@ -513,4 +513,7 @@
}
return null;
}
+
+ private FindAddress() {
+ }
}
diff --git a/compat/src/main/java/androidx/core/util/Consumer.java b/compat/src/main/java/androidx/core/util/Consumer.java
new file mode 100644
index 0000000..28e0fb8
--- /dev/null
+++ b/compat/src/main/java/androidx/core/util/Consumer.java
@@ -0,0 +1,31 @@
+/*
+ * 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.core.util;
+
+/**
+ * Compat version of {@link java.util.function.Consumer}
+ * @param <T> the type of the input to the operation
+ */
+public interface Consumer<T> {
+
+ /**
+ * Performs this operation on the given argument.
+ *
+ * @param t the input argument
+ */
+ void accept(T t);
+}
diff --git a/compat/src/main/java/androidx/core/util/DebugUtils.java b/compat/src/main/java/androidx/core/util/DebugUtils.java
index f8767b9..20486f2 100644
--- a/compat/src/main/java/androidx/core/util/DebugUtils.java
+++ b/compat/src/main/java/androidx/core/util/DebugUtils.java
@@ -45,4 +45,7 @@
out.append(Integer.toHexString(System.identityHashCode(cls)));
}
}
+
+ private DebugUtils() {
+ }
}
diff --git a/compat/src/main/java/androidx/core/util/Preconditions.java b/compat/src/main/java/androidx/core/util/Preconditions.java
index eb5bdd6..612a93e 100644
--- a/compat/src/main/java/androidx/core/util/Preconditions.java
+++ b/compat/src/main/java/androidx/core/util/Preconditions.java
@@ -462,4 +462,7 @@
return value;
}
+
+ private Preconditions() {
+ }
}
diff --git a/compat/src/main/java/androidx/core/view/AccessibilityDelegateCompat.java b/compat/src/main/java/androidx/core/view/AccessibilityDelegateCompat.java
index 0f36f42..7dfad9c 100644
--- a/compat/src/main/java/androidx/core/view/AccessibilityDelegateCompat.java
+++ b/compat/src/main/java/androidx/core/view/AccessibilityDelegateCompat.java
@@ -25,6 +25,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
+import androidx.annotation.RequiresApi;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityNodeProviderCompat;
@@ -92,6 +93,7 @@
}
@Override
+ @RequiresApi(16)
public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {
AccessibilityNodeProviderCompat provider =
mCompat.getAccessibilityNodeProvider(host);
diff --git a/compat/src/main/java/androidx/core/view/ViewCompat.java b/compat/src/main/java/androidx/core/view/ViewCompat.java
index 60e5f56..7b76c38 100644
--- a/compat/src/main/java/androidx/core/view/ViewCompat.java
+++ b/compat/src/main/java/androidx/core/view/ViewCompat.java
@@ -51,6 +51,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityNodeProviderCompat;
@@ -2195,6 +2196,7 @@
v.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
@Override
+ @RequiresApi(21) // TODO remove https://issuetracker.google.com/issues/76458979
public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) {
WindowInsetsCompat compatInsets = WindowInsetsCompat.wrap(insets);
compatInsets = listener.onApplyWindowInsets(view, compatInsets);
diff --git a/compat/src/main/java/androidx/core/view/ViewConfigurationCompat.java b/compat/src/main/java/androidx/core/view/ViewConfigurationCompat.java
index 89c3ca5..2e47e55 100644
--- a/compat/src/main/java/androidx/core/view/ViewConfigurationCompat.java
+++ b/compat/src/main/java/androidx/core/view/ViewConfigurationCompat.java
@@ -17,6 +17,7 @@
package androidx.core.view;
import android.content.Context;
+import android.content.res.Resources;
import android.os.Build;
import android.util.Log;
import android.util.TypedValue;
@@ -130,5 +131,21 @@
return config.getScaledTouchSlop() / 2;
}
+ /**
+ * Check if shortcuts should be displayed in menus.
+ *
+ * @return {@code True} if shortcuts should be displayed in menus.
+ */
+ public static boolean shouldShowMenuShortcutsWhenKeyboardPresent(ViewConfiguration config,
+ @NonNull Context context) {
+ if (android.os.Build.VERSION.SDK_INT >= 28) {
+ return config.shouldShowMenuShortcutsWhenKeyboardPresent();
+ }
+ final Resources res = context.getResources();
+ final int platformResId = res.getIdentifier(
+ "config_showMenuShortcutsWhenKeyboardPresent", "bool", "android");
+ return platformResId != 0 && res.getBoolean(platformResId);
+ }
+
private ViewConfigurationCompat() {}
}
diff --git a/compat/src/main/java/androidx/core/view/inputmethod/EditorInfoCompat.java b/compat/src/main/java/androidx/core/view/inputmethod/EditorInfoCompat.java
index 3d0eee3..3575d88 100644
--- a/compat/src/main/java/androidx/core/view/inputmethod/EditorInfoCompat.java
+++ b/compat/src/main/java/androidx/core/view/inputmethod/EditorInfoCompat.java
@@ -120,4 +120,9 @@
}
}
+ /** @deprecated This type should not be instantiated as it contains only static methods. */
+ @Deprecated
+ @SuppressWarnings("PrivateConstructorForUtilityClass")
+ public EditorInfoCompat() {
+ }
}
diff --git a/compat/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java b/compat/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java
index e9b10a6..ce1b502 100644
--- a/compat/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java
+++ b/compat/src/main/java/androidx/core/view/inputmethod/InputConnectionCompat.java
@@ -231,4 +231,9 @@
}
}
+ /** @deprecated This type should not be instantiated as it contains only static methods. */
+ @Deprecated
+ @SuppressWarnings("PrivateConstructorForUtilityClass")
+ public InputConnectionCompat() {
+ }
}
diff --git a/compat/src/main/java/androidx/core/widget/TextViewCompat.java b/compat/src/main/java/androidx/core/widget/TextViewCompat.java
index d67d61c..eaae58c 100644
--- a/compat/src/main/java/androidx/core/widget/TextViewCompat.java
+++ b/compat/src/main/java/androidx/core/widget/TextViewCompat.java
@@ -18,6 +18,7 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@@ -489,138 +490,149 @@
// intent after selection to not be displayed in the menu, although they should be.
// Here we fix this, by removing the menu items created by the framework code, and
// adding them (and the missing ones) back correctly.
- textView.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
- // This constant should be correlated with its definition in the
- // android.widget.Editor class.
- private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100;
+ textView.setCustomSelectionActionModeCallback(new OreoCallback(callback, textView));
+ }
- // References to the MenuBuilder class and its removeItemAt(int) method.
- // Since in most cases the menu instance processed by this callback is going
- // to be a MenuBuilder, we keep these references to avoid querying for them
- // frequently by reflection in recomputeProcessTextMenuItems.
- private Class mMenuBuilderClass;
- private Method mMenuBuilderRemoveItemAtMethod;
- private boolean mCanUseMenuBuilderReferences;
- private boolean mInitializedMenuBuilderReferences = false;
+ @TargetApi(26) // TODO was anonymous but https://issuetracker.google.com/issues/76458979
+ private static class OreoCallback implements ActionMode.Callback {
+ // This constant should be correlated with its definition in the
+ // android.widget.Editor class.
+ private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100;
+ private final ActionMode.Callback mCallback;
+ private final TextView mTextView;
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- return callback.onCreateActionMode(mode, menu);
- }
+ // References to the MenuBuilder class and its removeItemAt(int) method.
+ // Since in most cases the menu instance processed by this callback is going
+ // to be a MenuBuilder, we keep these references to avoid querying for them
+ // frequently by reflection in recomputeProcessTextMenuItems.
+ private Class mMenuBuilderClass;
+ private Method mMenuBuilderRemoveItemAtMethod;
+ private boolean mCanUseMenuBuilderReferences;
+ private boolean mInitializedMenuBuilderReferences;
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- recomputeProcessTextMenuItems(menu);
- return callback.onPrepareActionMode(mode, menu);
- }
+ OreoCallback(ActionMode.Callback callback, TextView textView) {
+ mCallback = callback;
+ mTextView = textView;
+ mInitializedMenuBuilderReferences = false;
+ }
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- return callback.onActionItemClicked(mode, item);
- }
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ return mCallback.onCreateActionMode(mode, menu);
+ }
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- callback.onDestroyActionMode(mode);
- }
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ recomputeProcessTextMenuItems(menu);
+ return mCallback.onPrepareActionMode(mode, menu);
+ }
- private void recomputeProcessTextMenuItems(final Menu menu) {
- final Context context = textView.getContext();
- final PackageManager packageManager = context.getPackageManager();
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return mCallback.onActionItemClicked(mode, item);
+ }
- if (!mInitializedMenuBuilderReferences) {
- mInitializedMenuBuilderReferences = true;
- try {
- mMenuBuilderClass =
- Class.forName("com.android.internal.view.menu.MenuBuilder");
- mMenuBuilderRemoveItemAtMethod = mMenuBuilderClass
- .getDeclaredMethod("removeItemAt", Integer.TYPE);
- mCanUseMenuBuilderReferences = true;
- } catch (ClassNotFoundException | NoSuchMethodException e) {
- mMenuBuilderClass = null;
- mMenuBuilderRemoveItemAtMethod = null;
- mCanUseMenuBuilderReferences = false;
- }
- }
- // Remove the menu items created for ACTION_PROCESS_TEXT handlers.
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ mCallback.onDestroyActionMode(mode);
+ }
+
+ private void recomputeProcessTextMenuItems(final Menu menu) {
+ final Context context = mTextView.getContext();
+ final PackageManager packageManager = context.getPackageManager();
+
+ if (!mInitializedMenuBuilderReferences) {
+ mInitializedMenuBuilderReferences = true;
try {
- final Method removeItemAtMethod =
- (mCanUseMenuBuilderReferences && mMenuBuilderClass.isInstance(menu))
- ? mMenuBuilderRemoveItemAtMethod
- : menu.getClass()
- .getDeclaredMethod("removeItemAt", Integer.TYPE);
- for (int i = menu.size() - 1; i >= 0; --i) {
- final MenuItem item = menu.getItem(i);
- if (item.getIntent() != null && Intent.ACTION_PROCESS_TEXT
- .equals(item.getIntent().getAction())) {
- removeItemAtMethod.invoke(menu, i);
- }
- }
- } catch (NoSuchMethodException | IllegalAccessException
- | InvocationTargetException e) {
- // There is a menu custom implementation used which is not providing
- // a removeItemAt(int) menu. There is nothing we can do in this case.
- return;
- }
-
- // Populate the menu again with the ACTION_PROCESS_TEXT handlers.
- final List<ResolveInfo> supportedActivities =
- getSupportedActivities(context, packageManager);
- for (int i = 0; i < supportedActivities.size(); ++i) {
- final ResolveInfo info = supportedActivities.get(i);
- menu.add(Menu.NONE, Menu.NONE,
- MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START + i,
- info.loadLabel(packageManager))
- .setIntent(createProcessTextIntentForResolveInfo(info, textView))
- .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ mMenuBuilderClass =
+ Class.forName("com.android.internal.view.menu.MenuBuilder");
+ mMenuBuilderRemoveItemAtMethod = mMenuBuilderClass
+ .getDeclaredMethod("removeItemAt", Integer.TYPE);
+ mCanUseMenuBuilderReferences = true;
+ } catch (ClassNotFoundException | NoSuchMethodException e) {
+ mMenuBuilderClass = null;
+ mMenuBuilderRemoveItemAtMethod = null;
+ mCanUseMenuBuilderReferences = false;
}
}
-
- private List<ResolveInfo> getSupportedActivities(final Context context,
- final PackageManager packageManager) {
- final List<ResolveInfo> supportedActivities = new ArrayList<>();
- boolean canStartActivityForResult = context instanceof Activity;
- if (!canStartActivityForResult) {
- return supportedActivities;
- }
- final List<ResolveInfo> unfiltered =
- packageManager.queryIntentActivities(createProcessTextIntent(), 0);
- for (ResolveInfo info : unfiltered) {
- if (isSupportedActivity(info, context)) {
- supportedActivities.add(info);
+ // Remove the menu items created for ACTION_PROCESS_TEXT handlers.
+ try {
+ final Method removeItemAtMethod =
+ (mCanUseMenuBuilderReferences && mMenuBuilderClass.isInstance(menu))
+ ? mMenuBuilderRemoveItemAtMethod
+ : menu.getClass()
+ .getDeclaredMethod("removeItemAt", Integer.TYPE);
+ for (int i = menu.size() - 1; i >= 0; --i) {
+ final MenuItem item = menu.getItem(i);
+ if (item.getIntent() != null && Intent.ACTION_PROCESS_TEXT
+ .equals(item.getIntent().getAction())) {
+ removeItemAtMethod.invoke(menu, i);
}
}
+ } catch (NoSuchMethodException | IllegalAccessException
+ | InvocationTargetException e) {
+ // There is a menu custom implementation used which is not providing
+ // a removeItemAt(int) menu. There is nothing we can do in this case.
+ return;
+ }
+
+ // Populate the menu again with the ACTION_PROCESS_TEXT handlers.
+ final List<ResolveInfo> supportedActivities =
+ getSupportedActivities(context, packageManager);
+ for (int i = 0; i < supportedActivities.size(); ++i) {
+ final ResolveInfo info = supportedActivities.get(i);
+ menu.add(Menu.NONE, Menu.NONE,
+ MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START + i,
+ info.loadLabel(packageManager))
+ .setIntent(createProcessTextIntentForResolveInfo(info, mTextView))
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ }
+ }
+
+ private List<ResolveInfo> getSupportedActivities(final Context context,
+ final PackageManager packageManager) {
+ final List<ResolveInfo> supportedActivities = new ArrayList<>();
+ boolean canStartActivityForResult = context instanceof Activity;
+ if (!canStartActivityForResult) {
return supportedActivities;
}
-
- private boolean isSupportedActivity(final ResolveInfo info, final Context context) {
- if (context.getPackageName().equals(info.activityInfo.packageName)) {
- return true;
+ final List<ResolveInfo> unfiltered =
+ packageManager.queryIntentActivities(createProcessTextIntent(), 0);
+ for (ResolveInfo info : unfiltered) {
+ if (isSupportedActivity(info, context)) {
+ supportedActivities.add(info);
}
- if (!info.activityInfo.exported) {
- return false;
- }
- return info.activityInfo.permission == null
- || context.checkSelfPermission(info.activityInfo.permission)
- == PackageManager.PERMISSION_GRANTED;
}
+ return supportedActivities;
+ }
- private Intent createProcessTextIntentForResolveInfo(final ResolveInfo info,
- final TextView textView11) {
- return createProcessTextIntent()
- .putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, !isEditable(textView11))
- .setClassName(info.activityInfo.packageName, info.activityInfo.name);
+ private boolean isSupportedActivity(final ResolveInfo info, final Context context) {
+ if (context.getPackageName().equals(info.activityInfo.packageName)) {
+ return true;
}
+ if (!info.activityInfo.exported) {
+ return false;
+ }
+ return info.activityInfo.permission == null
+ || context.checkSelfPermission(info.activityInfo.permission)
+ == PackageManager.PERMISSION_GRANTED;
+ }
- private boolean isEditable(final TextView textView11) {
- return textView11 instanceof Editable
- && textView11.onCheckIsTextEditor()
- && textView11.isEnabled();
- }
+ private Intent createProcessTextIntentForResolveInfo(final ResolveInfo info,
+ final TextView textView11) {
+ return createProcessTextIntent()
+ .putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, !isEditable(textView11))
+ .setClassName(info.activityInfo.packageName, info.activityInfo.name);
+ }
- private Intent createProcessTextIntent() {
- return new Intent().setAction(Intent.ACTION_PROCESS_TEXT).setType("text/plain");
- }
- });
+ private boolean isEditable(final TextView textView11) {
+ return textView11 instanceof Editable
+ && textView11.onCheckIsTextEditor()
+ && textView11.isEnabled();
+ }
+
+ private Intent createProcessTextIntent() {
+ return new Intent().setAction(Intent.ACTION_PROCESS_TEXT).setType("text/plain");
+ }
}
}
diff --git a/coordinatorlayout/src/main/java/androidx/coordinatorlayout/widget/ViewGroupUtils.java b/coordinatorlayout/src/main/java/androidx/coordinatorlayout/widget/ViewGroupUtils.java
index 8f13761..fc87dd2 100644
--- a/coordinatorlayout/src/main/java/androidx/coordinatorlayout/widget/ViewGroupUtils.java
+++ b/coordinatorlayout/src/main/java/androidx/coordinatorlayout/widget/ViewGroupUtils.java
@@ -92,4 +92,7 @@
m.preConcat(view.getMatrix());
}
}
+
+ private ViewGroupUtils() {
+ }
}
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/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItemVisitor.kt b/core/ktx/src/androidTest/java/androidx/core/TestActivity.kt
similarity index 63%
copy from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItemVisitor.kt
copy to core/ktx/src/androidTest/java/androidx/core/TestActivity.kt
index 7c99fd9..b0132ab 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItemVisitor.kt
+++ b/core/ktx/src/androidTest/java/androidx/core/TestActivity.kt
@@ -11,18 +11,18 @@
* 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.archive
+package androidx.core
-/**
- * Visitor for [ArchiveItem]
- */
-interface ArchiveItemVisitor {
+import android.app.Activity
+import android.os.Bundle
+import androidx.core.ktx.test.R
- fun visit(archive: Archive)
-
- fun visit(archiveFile: ArchiveFile)
-
+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/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt b/core/ktx/src/androidTest/java/androidx/core/text/StringTest.kt
similarity index 69%
copy from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt
copy to core/ktx/src/androidTest/java/androidx/core/text/StringTest.kt
index 67f6d84..2812799 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt
+++ b/core/ktx/src/androidTest/java/androidx/core/text/StringTest.kt
@@ -11,14 +11,16 @@
* 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core
+package androidx.core.text
-import java.io.File
+import org.junit.Assert.assertEquals
+import org.junit.Test
-/**
- * Represents a source file ([from]) to be mapped to a target file ([to]).
- */
-data class FileMapping(val from: File, val to: File)
\ No newline at end of file
+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/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogConsumer.kt b/core/ktx/src/main/java/androidx/core/graphics/Shader.kt
similarity index 61%
copy from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogConsumer.kt
copy to core/ktx/src/main/java/androidx/core/graphics/Shader.kt
index ddebd25..0fb93cb 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogConsumer.kt
+++ b/core/ktx/src/main/java/androidx/core/graphics/Shader.kt
@@ -11,23 +11,20 @@
* 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.utils
+package androidx.core.graphics
+
+import android.graphics.Matrix
+import android.graphics.Shader
/**
- * Interface to plug custom logs consumers to [Log].
+ * Wrap the specified [block] in calls to [Shader.getLocalMatrix] and [Shader.setLocalMatrix].
*/
-interface LogConsumer {
-
- fun error(message: String)
-
- fun info(message: String)
-
- fun verbose(message: String)
-
- fun debug(message: String)
-
+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/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt b/core/ktx/src/main/java/androidx/core/text/String.kt
similarity index 67%
copy from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt
copy to core/ktx/src/main/java/androidx/core/text/String.kt
index 67f6d84..8bffdd0 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt
+++ b/core/ktx/src/main/java/androidx/core/text/String.kt
@@ -11,14 +11,18 @@
* 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core
+@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.
-import java.io.File
+package androidx.core.text
+
+import android.text.TextUtils
/**
- * Represents a source file ([from]) to be mapped to a target file ([to]).
+ * Html-encode the string.
+ *
+ * @see TextUtils.htmlEncode
*/
-data class FileMapping(val from: File, val to: File)
\ No newline at end of file
+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/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt b/core/ktx/src/main/java/androidx/core/util/Locale.kt
similarity index 61%
copy from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt
copy to core/ktx/src/main/java/androidx/core/util/Locale.kt
index 67f6d84..2b4312e 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt
+++ b/core/ktx/src/main/java/androidx/core/util/Locale.kt
@@ -11,14 +11,19 @@
* 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core
+package androidx.core.util
-import java.io.File
+import android.text.TextUtils
+import androidx.annotation.RequiresApi
+import java.util.Locale
/**
- * Represents a source file ([from]) to be mapped to a target file ([to]).
+ * Returns layout direction for a given locale.
+ * @see TextUtils.getLayoutDirectionFromLocale
*/
-data class FileMapping(val from: File, val to: File)
\ No newline at end of file
+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..34f6854
--- /dev/null
+++ b/core/ktx/src/main/java/androidx/core/util/Size.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.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/customview/src/main/java/androidx/customview/widget/FocusStrategy.java b/customview/src/main/java/androidx/customview/widget/FocusStrategy.java
index 6c3f860..02e6ea4 100644
--- a/customview/src/main/java/androidx/customview/widget/FocusStrategy.java
+++ b/customview/src/main/java/androidx/customview/widget/FocusStrategy.java
@@ -450,4 +450,7 @@
V get(T collection, int index);
int size(T collection);
}
+
+ private FocusStrategy() {
+ }
}
diff --git a/documentfile/build.gradle b/documentfile/build.gradle
index 81cabad..ab03322 100644
--- a/documentfile/build.gradle
+++ b/documentfile/build.gradle
@@ -1,3 +1,4 @@
+import static androidx.build.dependencies.DependenciesKt.*
import androidx.build.LibraryGroups
import androidx.build.LibraryVersions
@@ -7,6 +8,8 @@
dependencies {
api(project(":annotation"))
+
+ annotationProcessor(NULLAWAY)
}
supportLibrary {
diff --git a/documentfile/src/main/java/androidx/documentfile/provider/DocumentFile.java b/documentfile/src/main/java/androidx/documentfile/provider/DocumentFile.java
index 6776257..43b5a18 100644
--- a/documentfile/src/main/java/androidx/documentfile/provider/DocumentFile.java
+++ b/documentfile/src/main/java/androidx/documentfile/provider/DocumentFile.java
@@ -23,6 +23,9 @@
import android.os.Build;
import android.provider.DocumentsContract;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import java.io.File;
/**
@@ -79,9 +82,10 @@
public abstract class DocumentFile {
static final String TAG = "DocumentFile";
+ @Nullable
private final DocumentFile mParent;
- DocumentFile(DocumentFile parent) {
+ DocumentFile(@Nullable DocumentFile parent) {
mParent = parent;
}
@@ -93,7 +97,8 @@
* {@link #getUri()} will return {@code file://} Uris for files explored
* through this tree.
*/
- public static DocumentFile fromFile(File file) {
+ @NonNull
+ public static DocumentFile fromFile(@NonNull File file) {
return new RawDocumentFile(null, file);
}
@@ -107,7 +112,8 @@
* {@link Intent#ACTION_OPEN_DOCUMENT} or
* {@link Intent#ACTION_CREATE_DOCUMENT} request.
*/
- public static DocumentFile fromSingleUri(Context context, Uri singleUri) {
+ @Nullable
+ public static DocumentFile fromSingleUri(@NonNull Context context, @NonNull Uri singleUri) {
if (Build.VERSION.SDK_INT >= 19) {
return new SingleDocumentFile(null, context, singleUri);
} else {
@@ -124,7 +130,8 @@
* @param treeUri the {@link Intent#getData()} from a successful
* {@link Intent#ACTION_OPEN_DOCUMENT_TREE} request.
*/
- public static DocumentFile fromTreeUri(Context context, Uri treeUri) {
+ @Nullable
+ public static DocumentFile fromTreeUri(@NonNull Context context, @NonNull Uri treeUri) {
if (Build.VERSION.SDK_INT >= 21) {
return new TreeDocumentFile(null, context,
DocumentsContract.buildDocumentUriUsingTree(treeUri,
@@ -138,7 +145,7 @@
* Test if given Uri is backed by a
* {@link android.provider.DocumentsProvider}.
*/
- public static boolean isDocumentUri(Context context, Uri uri) {
+ public static boolean isDocumentUri(@NonNull Context context, @Nullable Uri uri) {
if (Build.VERSION.SDK_INT >= 19) {
return DocumentsContract.isDocumentUri(context, uri);
} else {
@@ -160,7 +167,8 @@
* @see android.provider.DocumentsContract#createDocument(ContentResolver,
* Uri, String, String)
*/
- public abstract DocumentFile createFile(String mimeType, String displayName);
+ @Nullable
+ public abstract DocumentFile createFile(@NonNull String mimeType, @NonNull String displayName);
/**
* Create a new directory as a direct child of this directory.
@@ -172,7 +180,8 @@
* @see android.provider.DocumentsContract#createDocument(ContentResolver,
* Uri, String, String)
*/
- public abstract DocumentFile createDirectory(String displayName);
+ @Nullable
+ public abstract DocumentFile createDirectory(@NonNull String displayName);
/**
* Return a Uri for the underlying document represented by this file. This
@@ -187,6 +196,7 @@
* @see ContentResolver#openOutputStream(Uri)
* @see ContentResolver#openFileDescriptor(Uri, String)
*/
+ @NonNull
public abstract Uri getUri();
/**
@@ -194,6 +204,7 @@
*
* @see android.provider.DocumentsContract.Document#COLUMN_DISPLAY_NAME
*/
+ @Nullable
public abstract String getName();
/**
@@ -201,6 +212,7 @@
*
* @see android.provider.DocumentsContract.Document#COLUMN_MIME_TYPE
*/
+ @Nullable
public abstract String getType();
/**
@@ -212,6 +224,7 @@
* parent offered here is purely a convenience method, and it may be
* incorrect if the underlying tree structure changes.
*/
+ @Nullable
public DocumentFile getParentFile() {
return mParent;
}
@@ -309,6 +322,7 @@
* @see android.provider.DocumentsContract#buildChildDocumentsUriUsingTree(Uri,
* String)
*/
+ @NonNull
public abstract DocumentFile[] listFiles();
/**
@@ -319,7 +333,8 @@
* @throws UnsupportedOperationException when working with a single document
* created from {@link #fromSingleUri(Context, Uri)}.
*/
- public DocumentFile findFile(String displayName) {
+ @Nullable
+ public DocumentFile findFile(@NonNull String displayName) {
for (DocumentFile doc : listFiles()) {
if (displayName.equals(doc.getName())) {
return doc;
@@ -348,5 +363,5 @@
* @see android.provider.DocumentsContract#renameDocument(ContentResolver,
* Uri, String)
*/
- public abstract boolean renameTo(String displayName);
+ public abstract boolean renameTo(@NonNull String displayName);
}
diff --git a/documentfile/src/main/java/androidx/documentfile/provider/DocumentsContractApi19.java b/documentfile/src/main/java/androidx/documentfile/provider/DocumentsContractApi19.java
index 750958d..45338e5 100644
--- a/documentfile/src/main/java/androidx/documentfile/provider/DocumentsContractApi19.java
+++ b/documentfile/src/main/java/androidx/documentfile/provider/DocumentsContractApi19.java
@@ -26,6 +26,7 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
@RequiresApi(19)
@@ -43,14 +44,17 @@
return (getFlags(context, self) & FLAG_VIRTUAL_DOCUMENT) != 0;
}
+ @Nullable
public static String getName(Context context, Uri self) {
return queryForString(context, self, DocumentsContract.Document.COLUMN_DISPLAY_NAME, null);
}
+ @Nullable
private static String getRawType(Context context, Uri self) {
return queryForString(context, self, DocumentsContract.Document.COLUMN_MIME_TYPE, null);
}
+ @Nullable
public static String getType(Context context, Uri self) {
final String rawType = getRawType(context, self);
if (DocumentsContract.Document.MIME_TYPE_DIR.equals(rawType)) {
@@ -149,8 +153,9 @@
}
}
+ @Nullable
private static String queryForString(Context context, Uri self, String column,
- String defaultValue) {
+ @Nullable String defaultValue) {
final ContentResolver resolver = context.getContentResolver();
Cursor c = null;
@@ -194,7 +199,7 @@
}
}
- private static void closeQuietly(AutoCloseable closeable) {
+ private static void closeQuietly(@Nullable AutoCloseable closeable) {
if (closeable != null) {
try {
closeable.close();
@@ -204,4 +209,7 @@
}
}
}
+
+ private DocumentsContractApi19() {
+ }
}
diff --git a/documentfile/src/main/java/androidx/documentfile/provider/RawDocumentFile.java b/documentfile/src/main/java/androidx/documentfile/provider/RawDocumentFile.java
index 722c7ae..0f5fa4e 100644
--- a/documentfile/src/main/java/androidx/documentfile/provider/RawDocumentFile.java
+++ b/documentfile/src/main/java/androidx/documentfile/provider/RawDocumentFile.java
@@ -20,6 +20,8 @@
import android.util.Log;
import android.webkit.MimeTypeMap;
+import androidx.annotation.Nullable;
+
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -27,12 +29,13 @@
class RawDocumentFile extends DocumentFile {
private File mFile;
- RawDocumentFile(DocumentFile parent, File file) {
+ RawDocumentFile(@Nullable DocumentFile parent, File file) {
super(parent);
mFile = file;
}
@Override
+ @Nullable
public DocumentFile createFile(String mimeType, String displayName) {
// Tack on extension when valid MIME type provided
final String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
@@ -50,6 +53,7 @@
}
@Override
+ @Nullable
public DocumentFile createDirectory(String displayName) {
final File target = new File(mFile, displayName);
if (target.isDirectory() || target.mkdir()) {
@@ -70,6 +74,7 @@
}
@Override
+ @Nullable
public String getType() {
if (mFile.isDirectory()) {
return null;
diff --git a/documentfile/src/main/java/androidx/documentfile/provider/SingleDocumentFile.java b/documentfile/src/main/java/androidx/documentfile/provider/SingleDocumentFile.java
index 0b4e1d5..7cd4b8c 100644
--- a/documentfile/src/main/java/androidx/documentfile/provider/SingleDocumentFile.java
+++ b/documentfile/src/main/java/androidx/documentfile/provider/SingleDocumentFile.java
@@ -20,6 +20,7 @@
import android.net.Uri;
import android.provider.DocumentsContract;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
@RequiresApi(19)
@@ -27,7 +28,7 @@
private Context mContext;
private Uri mUri;
- SingleDocumentFile(DocumentFile parent, Context context, Uri uri) {
+ SingleDocumentFile(@Nullable DocumentFile parent, Context context, Uri uri) {
super(parent);
mContext = context;
mUri = uri;
@@ -49,11 +50,13 @@
}
@Override
+ @Nullable
public String getName() {
return DocumentsContractApi19.getName(mContext, mUri);
}
@Override
+ @Nullable
public String getType() {
return DocumentsContractApi19.getType(mContext, mUri);
}
diff --git a/documentfile/src/main/java/androidx/documentfile/provider/TreeDocumentFile.java b/documentfile/src/main/java/androidx/documentfile/provider/TreeDocumentFile.java
index d429008..9cc20d6 100644
--- a/documentfile/src/main/java/androidx/documentfile/provider/TreeDocumentFile.java
+++ b/documentfile/src/main/java/androidx/documentfile/provider/TreeDocumentFile.java
@@ -23,6 +23,7 @@
import android.provider.DocumentsContract;
import android.util.Log;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.util.ArrayList;
@@ -32,18 +33,20 @@
private Context mContext;
private Uri mUri;
- TreeDocumentFile(DocumentFile parent, Context context, Uri uri) {
+ TreeDocumentFile(@Nullable DocumentFile parent, Context context, Uri uri) {
super(parent);
mContext = context;
mUri = uri;
}
@Override
+ @Nullable
public DocumentFile createFile(String mimeType, String displayName) {
final Uri result = TreeDocumentFile.createFile(mContext, mUri, mimeType, displayName);
return (result != null) ? new TreeDocumentFile(this, mContext, result) : null;
}
+ @Nullable
private static Uri createFile(Context context, Uri self, String mimeType,
String displayName) {
try {
@@ -55,6 +58,7 @@
}
@Override
+ @Nullable
public DocumentFile createDirectory(String displayName) {
final Uri result = TreeDocumentFile.createFile(
mContext, mUri, DocumentsContract.Document.MIME_TYPE_DIR, displayName);
@@ -67,11 +71,13 @@
}
@Override
+ @Nullable
public String getName() {
return DocumentsContractApi19.getName(mContext, mUri);
}
@Override
+ @Nullable
public String getType() {
return DocumentsContractApi19.getType(mContext, mUri);
}
@@ -156,7 +162,7 @@
return resultFiles;
}
- private static void closeQuietly(AutoCloseable closeable) {
+ private static void closeQuietly(@Nullable AutoCloseable closeable) {
if (closeable != null) {
try {
closeable.close();
diff --git a/emoji/core/src/main/java/androidx/emoji/text/MetadataListReader.java b/emoji/core/src/main/java/androidx/emoji/text/MetadataListReader.java
index 28a35f0..039bb6b 100644
--- a/emoji/core/src/main/java/androidx/emoji/text/MetadataListReader.java
+++ b/emoji/core/src/main/java/androidx/emoji/text/MetadataListReader.java
@@ -341,4 +341,7 @@
return mByteBuffer.position();
}
}
+
+ private MetadataListReader() {
+ }
}
diff --git a/fragment/src/main/java/androidx/fragment/app/FragmentManager.java b/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
index 37c480d..f6fc2b8 100644
--- a/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
+++ b/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
@@ -3812,6 +3812,9 @@
public static final int Fragment_id = 1;
public static final int Fragment_name = 0;
public static final int Fragment_tag = 2;
+
+ private FragmentTag() {
+ }
}
/**
diff --git a/fragment/src/main/java/androidx/fragment/app/FragmentTransition.java b/fragment/src/main/java/androidx/fragment/app/FragmentTransition.java
index 8466bb9..f7b3cac 100644
--- a/fragment/src/main/java/androidx/fragment/app/FragmentTransition.java
+++ b/fragment/src/main/java/androidx/fragment/app/FragmentTransition.java
@@ -1254,4 +1254,7 @@
*/
public BackStackRecord firstOutTransaction;
}
+
+ private FragmentTransition() {
+ }
}
diff --git a/gradle.properties b/gradle.properties
index 85bd83e..a21b997 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -3,4 +3,4 @@
org.gradle.configureondemand=true
org.gradle.parallel=true
android.builder.sdkDownload=false
-android.enableD8=true
+
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f05ac0d..e686e68 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=../../../../tools/external/gradle/gradle-4.6-bin.zip
+distributionUrl=../../../../tools/external/gradle/gradle-4.7-20180329152519+0000-bin.zip
diff --git a/graphics/drawable/animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimationUtilsCompat.java b/graphics/drawable/animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimationUtilsCompat.java
index 2782eca..2110f3c 100644
--- a/graphics/drawable/animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimationUtilsCompat.java
+++ b/graphics/drawable/animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimationUtilsCompat.java
@@ -145,4 +145,7 @@
}
return interpolator;
}
+
+ private AnimationUtilsCompat() {
+ }
}
diff --git a/graphics/drawable/animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimatorInflaterCompat.java b/graphics/drawable/animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimatorInflaterCompat.java
index 62e87a0..8c71634 100644
--- a/graphics/drawable/animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimatorInflaterCompat.java
+++ b/graphics/drawable/animated/src/main/java/androidx/vectordrawable/graphics/drawable/AnimatorInflaterCompat.java
@@ -917,5 +917,8 @@
return (type >= TypedValue.TYPE_FIRST_COLOR_INT) && (type
<= TypedValue.TYPE_LAST_COLOR_INT);
}
+
+ private AnimatorInflaterCompat() {
+ }
}
diff --git a/graphics/drawable/static/src/main/java/androidx/vectordrawable/graphics/drawable/AndroidResources.java b/graphics/drawable/static/src/main/java/androidx/vectordrawable/graphics/drawable/AndroidResources.java
index f63f569..409813f 100644
--- a/graphics/drawable/static/src/main/java/androidx/vectordrawable/graphics/drawable/AndroidResources.java
+++ b/graphics/drawable/static/src/main/java/androidx/vectordrawable/graphics/drawable/AndroidResources.java
@@ -146,4 +146,7 @@
public static final int FAST_OUT_LINEAR_IN = 0x010c000f;
public static final int FAST_OUT_SLOW_IN = 0x010c000d;
public static final int LINEAR_OUT_SLOW_IN = 0x010c000e;
+
+ private AndroidResources() {
+ }
}
diff --git a/heifwriter/api/current.txt b/heifwriter/api/current.txt
index c7311ba..3c39776 100644
--- a/heifwriter/api/current.txt
+++ b/heifwriter/api/current.txt
@@ -1,8 +1,6 @@
package androidx.heifwriter {
public final class HeifWriter implements java.lang.AutoCloseable {
- ctor public HeifWriter(java.lang.String, int, int, boolean, int, int, int, int, android.os.Handler) throws java.io.IOException;
- ctor public HeifWriter(java.io.FileDescriptor, int, int, boolean, int, int, int, int, android.os.Handler) throws java.io.IOException;
method public void addBitmap(android.graphics.Bitmap);
method public void addYuvBuffer(int, byte[]);
method public void close();
@@ -15,5 +13,17 @@
field public static final int INPUT_MODE_SURFACE = 1; // 0x1
}
+ public static final class HeifWriter.Builder {
+ ctor public HeifWriter.Builder(java.lang.String, int, int, int);
+ ctor public HeifWriter.Builder(java.io.FileDescriptor, int, int, int);
+ method public androidx.heifwriter.HeifWriter build() throws java.io.IOException;
+ method public androidx.heifwriter.HeifWriter.Builder setGridEnabled(boolean);
+ method public androidx.heifwriter.HeifWriter.Builder setHandler(android.os.Handler);
+ method public androidx.heifwriter.HeifWriter.Builder setMaxImages(int);
+ method public androidx.heifwriter.HeifWriter.Builder setPrimaryIndex(int);
+ method public androidx.heifwriter.HeifWriter.Builder setQuality(int);
+ method public androidx.heifwriter.HeifWriter.Builder setRotation(int);
+ }
+
}
diff --git a/heifwriter/build.gradle b/heifwriter/build.gradle
index 63e55ca..265708f 100644
--- a/heifwriter/build.gradle
+++ b/heifwriter/build.gradle
@@ -6,12 +6,6 @@
id("SupportAndroidLibraryPlugin")
}
-android {
- defaultConfig {
- targetSdkVersion 'P'
- }
-}
-
dependencies {
api(project(":annotation"))
diff --git a/heifwriter/src/androidTest/java/androidx/heifwriter/HeifWriterTest.java b/heifwriter/src/androidTest/java/androidx/heifwriter/HeifWriterTest.java
index d5b339a..127ba1c 100644
--- a/heifwriter/src/androidTest/java/androidx/heifwriter/HeifWriterTest.java
+++ b/heifwriter/src/androidTest/java/androidx/heifwriter/HeifWriterTest.java
@@ -224,7 +224,11 @@
}
private void doTestForVariousNumberImages(TestConfig.Builder builder) throws Exception {
- doTest(builder.setNumImages(4).build());
+ builder.setNumImages(4);
+ doTest(builder.setRotation(270).build());
+ doTest(builder.setRotation(180).build());
+ doTest(builder.setRotation(90).build());
+ doTest(builder.setRotation(0).build());
doTest(builder.setNumImages(1).build());
doTest(builder.setNumImages(8).build());
}
@@ -252,102 +256,109 @@
}
private static class TestConfig {
- final int inputMode;
- final boolean useGrid;
- final boolean useHandler;
- final int maxNumImages;
- final int numImages;
- final int width;
- final int height;
- final int quality;
- final String inputPath;
- final String outputPath;
- final Bitmap[] bitmaps;
+ final int mInputMode;
+ final boolean mUseGrid;
+ final boolean mUseHandler;
+ final int mMaxNumImages;
+ final int mNumImages;
+ final int mWidth;
+ final int mHeight;
+ final int mRotation;
+ final int mQuality;
+ final String mInputPath;
+ final String mOutputPath;
+ final Bitmap[] mBitmaps;
- TestConfig(int _inputMode, boolean _useGrid, boolean _useHandler,
- int _maxNumImage, int _numImages, int _width, int _height, int _quality,
- String _inputPath, String _outputPath, Bitmap[] _bitmaps) {
- inputMode = _inputMode;
- useGrid = _useGrid;
- useHandler = _useHandler;
- maxNumImages = _maxNumImage;
- numImages = _numImages;
- width = _width;
- height = _height;
- quality = _quality;
- inputPath = _inputPath;
- outputPath = _outputPath;
- bitmaps = _bitmaps;
+ TestConfig(int inputMode, boolean useGrid, boolean useHandler,
+ int maxNumImages, int numImages, int width, int height,
+ int rotation, int quality,
+ String inputPath, String outputPath, Bitmap[] bitmaps) {
+ mInputMode = inputMode;
+ mUseGrid = useGrid;
+ mUseHandler = useHandler;
+ mMaxNumImages = maxNumImages;
+ mNumImages = numImages;
+ mWidth = width;
+ mHeight = height;
+ mRotation = rotation;
+ mQuality = quality;
+ mInputPath = inputPath;
+ mOutputPath = outputPath;
+ mBitmaps = bitmaps;
}
static class Builder {
- final int inputMode;
- final boolean useGrid;
- final boolean useHandler;
- int maxNumImages;
- int numImages;
- int width;
- int height;
- int quality;
- String inputPath;
- final String outputPath;
- Bitmap[] bitmaps;
-
- boolean numImagesSetExplicitly;
+ final int mInputMode;
+ final boolean mUseGrid;
+ final boolean mUseHandler;
+ int mMaxNumImages;
+ int mNumImages;
+ int mWidth;
+ int mHeight;
+ int mRotation;
+ final int mQuality;
+ String mInputPath;
+ final String mOutputPath;
+ Bitmap[] mBitmaps;
+ boolean mNumImagesSetExplicitly;
- Builder(int _inputMode, boolean _useGrids, boolean _useHandler) {
- inputMode = _inputMode;
- useGrid = _useGrids;
- useHandler = _useHandler;
- maxNumImages = numImages = 4;
- width = 1920;
- height = 1080;
- quality = 100;
- outputPath = new File(Environment.getExternalStorageDirectory(),
+ Builder(int inputMode, boolean useGrids, boolean useHandler) {
+ mInputMode = inputMode;
+ mUseGrid = useGrids;
+ mUseHandler = useHandler;
+ mMaxNumImages = mNumImages = 4;
+ mWidth = 1920;
+ mHeight = 1080;
+ mRotation = 0;
+ mQuality = 100;
+ mOutputPath = new File(Environment.getExternalStorageDirectory(),
OUTPUT_FILENAME).getAbsolutePath();
}
- Builder setInputPath(String _inputPath) {
- inputPath = (inputMode == INPUT_MODE_BITMAP) ? _inputPath : null;
+ Builder setInputPath(String inputPath) {
+ mInputPath = (mInputMode == INPUT_MODE_BITMAP) ? inputPath : null;
return this;
}
- Builder setNumImages(int _numImages) {
- numImagesSetExplicitly = true;
- numImages = _numImages;
+ Builder setNumImages(int numImages) {
+ mNumImagesSetExplicitly = true;
+ mNumImages = numImages;
+ return this;
+ }
+
+ Builder setRotation(int rotation) {
+ mRotation = rotation;
return this;
}
private void loadBitmapInputs() {
- if (inputMode != INPUT_MODE_BITMAP) {
+ if (mInputMode != INPUT_MODE_BITMAP) {
return;
}
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
- retriever.setDataSource(inputPath);
+ retriever.setDataSource(mInputPath);
String hasImage = retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
if (!"yes".equals(hasImage)) {
throw new IllegalArgumentException("no bitmap found!");
}
- width = Integer.parseInt(retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH));
- height = Integer.parseInt(retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT));
- maxNumImages = Math.min(maxNumImages, Integer.parseInt(retriever.extractMetadata(
+ mMaxNumImages = Math.min(mMaxNumImages, Integer.parseInt(retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT)));
- if (!numImagesSetExplicitly) {
- numImages = maxNumImages;
+ if (!mNumImagesSetExplicitly) {
+ mNumImages = mMaxNumImages;
}
- bitmaps = new Bitmap[maxNumImages];
- for (int i = 0; i < bitmaps.length; i++) {
- bitmaps[i] = retriever.getImageAtIndex(i);
+ mBitmaps = new Bitmap[mMaxNumImages];
+ for (int i = 0; i < mBitmaps.length; i++) {
+ mBitmaps[i] = retriever.getImageAtIndex(i);
}
+ mWidth = mBitmaps[0].getWidth();
+ mHeight = mBitmaps[0].getHeight();
retriever.release();
}
private void cleanupStaleOutputs() {
- File outputFile = new File(outputPath);
+ File outputFile = new File(mOutputPath);
if (outputFile.exists()) {
outputFile.delete();
}
@@ -357,58 +368,61 @@
cleanupStaleOutputs();
loadBitmapInputs();
- return new TestConfig(inputMode, useGrid, useHandler, maxNumImages, numImages,
- width, height, quality, inputPath, outputPath, bitmaps);
+ return new TestConfig(mInputMode, mUseGrid, mUseHandler, mMaxNumImages, mNumImages,
+ mWidth, mHeight, mRotation, mQuality, mInputPath, mOutputPath, mBitmaps);
}
}
@Override
public String toString() {
- return "TestConfig" +
- ": inputMode " + inputMode +
- ", useGrid " + useGrid +
- ", useHandler " + useHandler +
- ", maxNumImages " + maxNumImages +
- ", numImages " + numImages +
- ", width " + width +
- ", height " + height +
- ", quality " + quality +
- ", inputPath " + inputPath +
- ", outputPath " + outputPath;
+ return "TestConfig"
+ + ": mInputMode " + mInputMode
+ + ", mUseGrid " + mUseGrid
+ + ", mUseHandler " + mUseHandler
+ + ", mMaxNumImages " + mMaxNumImages
+ + ", mNumImages " + mNumImages
+ + ", mWidth " + mWidth
+ + ", mHeight " + mHeight
+ + ", mRotation " + mRotation
+ + ", mQuality " + mQuality
+ + ", mInputPath " + mInputPath
+ + ", mOutputPath " + mOutputPath;
}
}
- private void doTest(TestConfig testConfig) throws Exception {
- int width = testConfig.width;
- int height = testConfig.height;
- int numImages = testConfig.numImages;
+ private void doTest(TestConfig config) throws Exception {
+ int width = config.mWidth;
+ int height = config.mHeight;
+ int numImages = config.mNumImages;
mInputIndex = 0;
HeifWriter heifWriter = null;
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
try {
- if (DEBUG) Log.d(TAG, "started: " + testConfig);
+ if (DEBUG) Log.d(TAG, "started: " + config);
- heifWriter = new HeifWriter(testConfig.outputPath, width, height,
- testConfig.useGrid,
- testConfig.quality,
- testConfig.maxNumImages,
- testConfig.maxNumImages - 1,
- testConfig.inputMode,
- testConfig.useHandler ? mHandler : null);
+ heifWriter = new HeifWriter.Builder(
+ config.mOutputPath, width, height, config.mInputMode)
+ .setRotation(config.mRotation)
+ .setGridEnabled(config.mUseGrid)
+ .setMaxImages(config.mMaxNumImages)
+ .setQuality(config.mQuality)
+ .setPrimaryIndex(config.mMaxNumImages - 1)
+ .setHandler(config.mUseHandler ? mHandler : null)
+ .build();
- if (testConfig.inputMode == INPUT_MODE_SURFACE) {
+ if (config.mInputMode == INPUT_MODE_SURFACE) {
mInputEglSurface = new EglWindowSurface(heifWriter.getInputSurface());
}
heifWriter.start();
- if (testConfig.inputMode == INPUT_MODE_BUFFER) {
+ if (config.mInputMode == INPUT_MODE_BUFFER) {
byte[] data = new byte[width * height * 3 / 2];
- if (testConfig.inputPath != null) {
- inputStream = new FileInputStream(testConfig.inputPath);
+ if (config.mInputPath != null) {
+ inputStream = new FileInputStream(config.mInputPath);
}
if (DUMP_YUV_INPUT) {
@@ -426,7 +440,7 @@
}
heifWriter.addYuvBuffer(ImageFormat.YUV_420_888, data);
}
- } else if (testConfig.inputMode == INPUT_MODE_SURFACE) {
+ } else if (config.mInputMode == INPUT_MODE_SURFACE) {
// The input surface is a surface texture using single buffer mode, draws will be
// blocked until onFrameAvailable is done with the buffer, which is dependant on
// how fast MediaCodec processes them, which is further dependent on how fast the
@@ -438,8 +452,8 @@
}
heifWriter.setInputEndOfStreamTimestamp(
1000 * computePresentationTime(numImages - 1));
- } else if (testConfig.inputMode == INPUT_MODE_BITMAP) {
- Bitmap[] bitmaps = testConfig.bitmaps;
+ } else if (config.mInputMode == INPUT_MODE_BITMAP) {
+ Bitmap[] bitmaps = config.mBitmaps;
for (int i = 0; i < Math.min(bitmaps.length, numImages); i++) {
if (DEBUG) Log.d(TAG, "addBitmap: " + i);
heifWriter.addBitmap(bitmaps[i]);
@@ -448,8 +462,8 @@
}
heifWriter.stop(3000);
- verifyResult(testConfig.outputPath, width, height, testConfig.useGrid,
- Math.min(numImages, testConfig.maxNumImages));
+ verifyResult(config.mOutputPath, width, height, config.mRotation, config.mUseGrid,
+ Math.min(numImages, config.mMaxNumImages));
if (DEBUG) Log.d(TAG, "finished: PASS");
} finally {
try {
@@ -532,7 +546,7 @@
}
private void verifyResult(
- String filename, int width, int height, boolean useGrid, int numImages)
+ String filename, int width, int height, int rotation, boolean useGrid, int numImages)
throws Exception {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(filename);
@@ -542,23 +556,26 @@
}
assertEquals("Wrong image count", numImages,
Integer.parseInt(retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT)));
+ MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT)));
assertEquals("Wrong width", width,
Integer.parseInt(retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH)));
+ MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH)));
assertEquals("Wrong height", height,
Integer.parseInt(retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT)));
+ MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT)));
+ assertEquals("Wrong rotation", rotation,
+ Integer.parseInt(retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION)));
retriever.release();
if (useGrid) {
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);
+ 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_COLS);
+ 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",
diff --git a/heifwriter/src/main/java/androidx/heifwriter/HeifEncoder.java b/heifwriter/src/main/java/androidx/heifwriter/HeifEncoder.java
index 3020b96..70b1a2f 100644
--- a/heifwriter/src/main/java/androidx/heifwriter/HeifEncoder.java
+++ b/heifwriter/src/main/java/androidx/heifwriter/HeifEncoder.java
@@ -213,7 +213,14 @@
int gridWidth, gridHeight, gridRows, gridCols;
- useGrid = useGrid && (width > GRID_WIDTH || height > GRID_HEIGHT);
+ MediaCodecInfo.CodecCapabilities caps =
+ mEncoder.getCodecInfo().getCapabilitiesForType(useHeicEncoder
+ ? MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC
+ : MediaFormat.MIMETYPE_VIDEO_HEVC);
+
+ useGrid &= (width > GRID_WIDTH || height > GRID_HEIGHT);
+ // Always enable grid if the size is too large for the HEVC encoder
+ useGrid |= !caps.getVideoCapabilities().isSizeSupported(width, height);
if (useGrid) {
gridWidth = GRID_WIDTH;
@@ -237,9 +244,9 @@
}
if (useGrid) {
- codecFormat.setInteger(MediaFormat.KEY_GRID_WIDTH, gridWidth);
- codecFormat.setInteger(MediaFormat.KEY_GRID_HEIGHT, gridHeight);
- codecFormat.setInteger(MediaFormat.KEY_GRID_COLS, gridCols);
+ 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);
}
@@ -258,15 +265,11 @@
codecFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 0);
codecFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
-
- MediaCodecInfo.CodecCapabilities caps =
- 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);
codecFormat.setInteger(MediaFormat.KEY_CAPTURE_RATE, mNumTiles * 30);
+
+ MediaCodecInfo.EncoderCapabilities encoderCaps = caps.getEncoderCapabilities();
+
if (encoderCaps.isBitrateModeSupported(
MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CQ)) {
Log.d(TAG, "Setting bitrate mode to constant quality");
@@ -278,14 +281,14 @@
(qualityRange.getUpper() - qualityRange.getLower()) * quality / 100.0));
} else {
if (encoderCaps.isBitrateModeSupported(
- MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR)) {
- Log.d(TAG, "Setting bitrate mode to variable bitrate");
- codecFormat.setInteger(MediaFormat.KEY_BITRATE_MODE,
- MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR);
- } else { // assume CBR
+ MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR)) {
Log.d(TAG, "Setting bitrate mode to constant bitrate");
codecFormat.setInteger(MediaFormat.KEY_BITRATE_MODE,
MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR);
+ } else { // assume VBR
+ Log.d(TAG, "Setting bitrate mode to variable bitrate");
+ codecFormat.setInteger(MediaFormat.KEY_BITRATE_MODE,
+ MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR);
}
// Calculate the bitrate based on image dimension, max compression ratio and quality.
// Note that we set the frame rate to the number of tiles, so the bitrate would be the
@@ -806,10 +809,10 @@
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_TILE_WIDTH, mGridWidth);
+ format.setInteger(MediaFormat.KEY_TILE_HEIGHT, mGridHeight);
format.setInteger(MediaFormat.KEY_GRID_ROWS, mGridRows);
- format.setInteger(MediaFormat.KEY_GRID_COLS, mGridCols);
+ format.setInteger(MediaFormat.KEY_GRID_COLUMNS, mGridCols);
}
}
diff --git a/heifwriter/src/main/java/androidx/heifwriter/HeifWriter.java b/heifwriter/src/main/java/androidx/heifwriter/HeifWriter.java
index be7dffb..bc657c4 100644
--- a/heifwriter/src/main/java/androidx/heifwriter/HeifWriter.java
+++ b/heifwriter/src/main/java/androidx/heifwriter/HeifWriter.java
@@ -16,6 +16,8 @@
package androidx.heifwriter;
+import static android.media.MediaMuxer.OutputFormat.MUXER_OUTPUT_HEIF;
+
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.media.MediaCodec;
@@ -25,11 +27,12 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
+import android.util.Log;
+import android.view.Surface;
+
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import android.util.Log;
-import android.view.Surface;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -38,8 +41,6 @@
import java.nio.ByteBuffer;
import java.util.concurrent.TimeoutException;
-import static android.media.MediaMuxer.OutputFormat.MUXER_OUTPUT_HEIF;
-
/**
* This class writes one or more still images (of the same dimensions) into
* a heif file.
@@ -79,7 +80,8 @@
private final HandlerThread mHandlerThread;
private final Handler mHandler;
private int mNumTiles;
- private final int mNumImages;
+ private final int mRotation;
+ private final int mMaxImages;
private final int mPrimaryIndex;
private final ResultWaiter mResultWaiter = new ResultWaiter();
@@ -119,96 +121,200 @@
public @interface InputMode {}
/**
- * Construct a heif writer that writes to a file specified by its path.
- *
- * @param path Path of the file to be written.
- * @param width Width of the image.
- * @param height Height of the image.
- * @param useGrid Whether to encode image into tiles. If enabled, the tile size will be
- * automatically chosen.
- * @param quality A number between 0 and 100 (inclusive), with 100 indicating the best quality
- * supported by this implementation (which often results in larger file size).
- * @param numImages Max number of images to write. Frames exceeding this number will not be
- * written to file. The writing can be stopped earlier before this number of
- * images are written by {@link #stop(long)}, except for the input mode of
- * {@link #INPUT_MODE_SURFACE}, where the EOS timestamp must be specified (via
- * {@link #setInputEndOfStreamTimestamp(long)} and reached.
- * @param primaryIndex Index of the image that should be marked as primary, must be within range
- * [0, numImages - 1] inclusive.
- * @param inputMode Input mode for this writer, must be one of {@link #INPUT_MODE_BUFFER},
- * {@link #INPUT_MODE_SURFACE}, or {@link #INPUT_MODE_BITMAP}.
- * @param handler If not null, client will receive all callbacks on the handler's looper.
- * Otherwise, client will receive callbacks on a looper created by the writer.
- *
- * @throws IOException if failed to construct MediaMuxer or HeifEncoder.
+ * Builder class for constructing a HeifWriter object from specified parameters.
*/
- @SuppressLint("WrongConstant")
- public HeifWriter(@NonNull String path,
- int width, int height, boolean useGrid,
- int quality, int numImages, int primaryIndex,
- @InputMode int inputMode,
- @Nullable Handler handler) throws IOException {
- this(width, height, useGrid, quality, numImages, primaryIndex, inputMode, handler,
- new MediaMuxer(path, MUXER_OUTPUT_HEIF));
+ public static final class Builder {
+ private final String mPath;
+ private final FileDescriptor mFd;
+ private final int mWidth;
+ private final int mHeight;
+ private final @InputMode int mInputMode;
+ private boolean mGridEnabled = true;
+ private int mQuality = 100;
+ private int mMaxImages = 1;
+ private int mPrimaryIndex = 0;
+ private int mRotation = 0;
+ private Handler mHandler;
+
+ /**
+ * Construct a Builder with output specified by its path.
+ *
+ * @param path Path of the file to be written.
+ * @param width Width of the image.
+ * @param height Height of the image.
+ * @param inputMode Input mode for this writer, must be one of {@link #INPUT_MODE_BUFFER},
+ * {@link #INPUT_MODE_SURFACE}, or {@link #INPUT_MODE_BITMAP}.
+ */
+ public Builder(@NonNull String path,
+ int width, int height, @InputMode int inputMode) {
+ this(path, null, width, height, inputMode);
+ }
+
+ /**
+ * Construct a Builder with output specified by its file descriptor.
+ *
+ * @param fd File descriptor of the file to be written.
+ * @param width Width of the image.
+ * @param height Height of the image.
+ * @param inputMode Input mode for this writer, must be one of {@link #INPUT_MODE_BUFFER},
+ * {@link #INPUT_MODE_SURFACE}, or {@link #INPUT_MODE_BITMAP}.
+ */
+ public Builder(@NonNull FileDescriptor fd,
+ int width, int height, @InputMode int inputMode) {
+ this(null, fd, width, height, inputMode);
+ }
+
+ private Builder(String path, FileDescriptor fd,
+ int width, int height, @InputMode int inputMode) {
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("Invalid image size: " + width + "x" + height);
+ }
+ mPath = path;
+ mFd = fd;
+ mWidth = width;
+ mHeight = height;
+ mInputMode = inputMode;
+ }
+
+ /**
+ * Set the image rotation in degrees.
+ *
+ * @param rotation Rotation angle (clockwise) of the image, must be 0, 90, 180 or 270.
+ * Default is 0.
+ * @return this Builder object.
+ */
+ public Builder setRotation(int rotation) {
+ if (rotation != 0 && rotation != 90 && rotation != 180 && rotation != 270) {
+ throw new IllegalArgumentException("Invalid rotation angle: " + rotation);
+ }
+ mRotation = rotation;
+ return this;
+ }
+
+ /**
+ * Set whether to enable grid option.
+ *
+ * @param gridEnabled Whether to enable grid option. If enabled, the tile size will be
+ * automatically chosen. Default is to enable.
+ * @return this Builder object.
+ */
+ public Builder setGridEnabled(boolean gridEnabled) {
+ mGridEnabled = gridEnabled;
+ return this;
+ }
+
+ /**
+ * Set the quality for encoding images.
+ *
+ * @param quality A number between 0 and 100 (inclusive), with 100 indicating the best
+ * quality supported by this implementation. Default is 100.
+ * @return this Builder object.
+ */
+ public Builder setQuality(int quality) {
+ if (quality < 0 || quality > 100) {
+ throw new IllegalArgumentException("Invalid quality: " + quality);
+ }
+ mQuality = quality;
+ return this;
+ }
+
+ /**
+ * Set the maximum number of images to write.
+ *
+ * @param maxImages Max number of images to write. Frames exceeding this number will not be
+ * written to file. The writing can be stopped earlier before this number
+ * of images are written by {@link #stop(long)}, except for the input mode
+ * of {@link #INPUT_MODE_SURFACE}, where the EOS timestamp must be
+ * specified (via {@link #setInputEndOfStreamTimestamp(long)} and reached.
+ * Default is 1.
+ * @return this Builder object.
+ */
+ public Builder setMaxImages(int maxImages) {
+ if (maxImages <= 0) {
+ throw new IllegalArgumentException("Invalid maxImage: " + maxImages);
+ }
+ mMaxImages = maxImages;
+ return this;
+ }
+
+ /**
+ * Set the primary image index.
+ *
+ * @param primaryIndex Index of the image that should be marked as primary, must be within
+ * range [0, maxImages - 1] inclusive. Default is 0.
+ * @return this Builder object.
+ */
+ public Builder setPrimaryIndex(int primaryIndex) {
+ if (primaryIndex < 0) {
+ throw new IllegalArgumentException("Invalid primaryIndex: " + primaryIndex);
+ }
+ mPrimaryIndex = primaryIndex;
+ return this;
+ }
+
+ /**
+ * Provide a handler for the HeifWriter to use.
+ *
+ * @param handler If not null, client will receive all callbacks on the handler's looper.
+ * Otherwise, client will receive callbacks on a looper created by the
+ * writer. Default is null.
+ * @return this Builder object.
+ */
+ public Builder setHandler(@Nullable Handler handler) {
+ mHandler = handler;
+ return this;
+ }
+
+ /**
+ * Build a HeifWriter object.
+ *
+ * @return a HeifWriter object built according to the specifications.
+ * @throws IOException if failed to create the writer, possibly due to failure to create
+ * {@link android.media.MediaMuxer} or {@link android.media.MediaCodec}.
+ */
+ public HeifWriter build() throws IOException {
+ return new HeifWriter(mPath, mFd, mWidth, mHeight, mRotation, mGridEnabled, mQuality,
+ mMaxImages, mPrimaryIndex, mInputMode, mHandler);
+ }
}
- /**
- * Construct a heif writer that writes to a file specified by file descriptor.
- *
- * @param fd File descriptor of the file to be written.
- * @param width Width of the image.
- * @param height Height of the image.
- * @param useGrid Whether to encode image into tiles. If enabled, the tile size will be
- * automatically chosen.
- * @param quality A number between 0 and 100 (inclusive), with 100 indicating the best quality
- * supported by this implementation (which often results in larger file size).
- * @param numImages Max number of images to write. Frames exceeding this number will not be
- * written to file. The writing can be stopped earlier before this number of
- * images are written by {@link #stop(long)}, except for the input mode of
- * {@link #INPUT_MODE_SURFACE}, where the EOS timestamp must be specified (via
- * {@link #setInputEndOfStreamTimestamp(long)} and reached.
- * @param primaryIndex Index of the image that should be marked as primary, must be within range
- * [0, numImages - 1] inclusive.
- * @param inputMode Input mode for this writer, must be one of {@link #INPUT_MODE_BUFFER},
- * {@link #INPUT_MODE_SURFACE}, or {@link #INPUT_MODE_BITMAP}.
- * @param handler If not null, client will receive all callbacks on the handler's looper.
- * Otherwise, client will receive callbacks on a looper created by the writer.
- *
- * @throws IOException if failed to construct MediaMuxer or HeifEncoder.
- */
@SuppressLint("WrongConstant")
- public HeifWriter(@NonNull FileDescriptor fd,
- int width, int height, boolean useGrid,
- int quality, int numImages, int primaryIndex,
- @InputMode int inputMode,
- @Nullable Handler handler) throws IOException {
- this(width, height, useGrid, quality, numImages, primaryIndex, inputMode, handler,
- new MediaMuxer(fd, MUXER_OUTPUT_HEIF));
- }
-
- private HeifWriter(int width, int height, boolean useGrid,
- int quality, int numImages, int primaryIndex,
+ private HeifWriter(@NonNull String path,
+ @NonNull FileDescriptor fd,
+ int width,
+ int height,
+ int rotation,
+ boolean gridEnabled,
+ int quality,
+ int maxImages,
+ int primaryIndex,
@InputMode int inputMode,
- @Nullable Handler handler,
- @NonNull MediaMuxer muxer) throws IOException {
- if (numImages <= 0 || primaryIndex < 0 || primaryIndex >= numImages) {
+ @Nullable Handler handler) throws IOException {
+ if (primaryIndex >= maxImages) {
throw new IllegalArgumentException(
- "Invalid numImages (" + numImages + ") or primaryIndex (" + primaryIndex + ")");
+ "Invalid maxImages (" + maxImages + ") or primaryIndex (" + primaryIndex + ")");
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "width: " + width
+ + ", height: " + height
+ + ", rotation: " + rotation
+ + ", gridEnabled: " + gridEnabled
+ + ", quality: " + quality
+ + ", maxImages: " + maxImages
+ + ", primaryIndex: " + primaryIndex
+ + ", inputMode: " + inputMode);
}
MediaFormat format = MediaFormat.createVideoFormat(
MediaFormat.MIMETYPE_IMAGE_ANDROID_HEIC, width, height);
- if (DEBUG) {
- Log.d(TAG, "format: " + format + ", inputMode: " + inputMode +
- ", numImage: " + numImages + ", primaryIndex: " + primaryIndex);
- }
-
// set to 1 initially, and wait for output format to know for sure
mNumTiles = 1;
+ mRotation = rotation;
mInputMode = inputMode;
- mNumImages = numImages;
+ mMaxImages = maxImages;
mPrimaryIndex = primaryIndex;
Looper looper = (handler != null) ? handler.getLooper() : null;
@@ -222,9 +328,10 @@
}
mHandler = new Handler(looper);
- mMuxer = muxer;
+ mMuxer = (path != null) ? new MediaMuxer(path, MUXER_OUTPUT_HEIF)
+ : new MediaMuxer(fd, MUXER_OUTPUT_HEIF);
- mHeifEncoder = new HeifEncoder(width, height, useGrid, quality,
+ mHeifEncoder = new HeifEncoder(width, height, gridEnabled, quality,
mInputMode, mHandler, new HeifCallback());
}
@@ -400,19 +507,23 @@
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;
}
- // add mNumImages image tracks of the same format
- mTrackIndexArray = new int[mNumImages];
+ // add mMaxImages image tracks of the same format
+ mTrackIndexArray = new int[mMaxImages];
+
+ // set rotation angle
+ if (mRotation > 0) {
+ Log.d(TAG, "setting rotation: " + mRotation);
+ mMuxer.setOrientationHint(mRotation);
+ }
for (int i = 0; i < mTrackIndexArray.length; i++) {
// mark primary
- if (i == mPrimaryIndex) {
- format.setInteger(MediaFormat.KEY_IS_DEFAULT, 1);
- }
+ format.setInteger(MediaFormat.KEY_IS_DEFAULT, (i == mPrimaryIndex) ? 1 : 0);
mTrackIndexArray[i] = mMuxer.addTrack(format);
}
mMuxer.start();
@@ -436,7 +547,7 @@
return;
}
- if (mOutputIndex < mNumImages * mNumTiles) {
+ if (mOutputIndex < mMaxImages * mNumTiles) {
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
info.set(byteBuffer.position(), byteBuffer.remaining(), 0, 0);
mMuxer.writeSampleData(
@@ -446,7 +557,7 @@
mOutputIndex++;
// post EOS if reached max number of images allowed.
- if (mOutputIndex == mNumImages * mNumTiles) {
+ if (mOutputIndex == mMaxImages * mNumTiles) {
stopAndNotify(null);
}
}
diff --git a/jetifier/jetifier/core/build.gradle b/jetifier/jetifier/core/build.gradle
index 315b41e..68f3a00 100644
--- a/jetifier/jetifier/core/build.gradle
+++ b/jetifier/jetifier/core/build.gradle
@@ -14,20 +14,18 @@
* limitations under the License
*/
-import static androidx.build.dependencies.DependenciesKt.KOTLIN_STDLIB
import androidx.build.LibraryGroups
import androidx.build.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.KOTLIN_STDLIB
+
plugins {
id("SupportKotlinLibraryPlugin")
}
dependencies {
- compile("org.ow2.asm:asm:5.2")
- compile("org.ow2.asm:asm-commons:5.2")
compile("com.google.code.gson:gson:2.8.0")
- compile("org.jdom:jdom2:2.0.6")
compile(KOTLIN_STDLIB)
testCompile("junit:junit:4.12")
testCompile("com.google.truth:truth:0.34")
@@ -35,7 +33,7 @@
supportLibrary {
name = "Android Jetifier Core"
- publish = false
+ publish = true
mavenVersion = LibraryVersions.JETIFIER
mavenGroup = LibraryGroups.JETIFIER
generateDocs = false
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/MapGeneratorRemapper.kt b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/MapGeneratorRemapper.kt
deleted file mode 100644
index 97574e7..0000000
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/MapGeneratorRemapper.kt
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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 android.support.tools.jetifier.core.map
-
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.transform.bytecode.CoreRemapper
-import android.support.tools.jetifier.core.transform.bytecode.asm.CustomRemapper
-import android.support.tools.jetifier.core.utils.Log
-import org.objectweb.asm.ClassVisitor
-import org.objectweb.asm.commons.ClassRemapper
-
-/**
- * Hooks to asm remapping to collect data for [TypesMap] by applying all the [RewriteRule]s from the
- * given [config] on all discovered and eligible types.
- */
-class MapGeneratorRemapper(private val config: Config) : CoreRemapper {
-
- companion object {
- private const val TAG: String = "MapGeneratorRemapper"
- }
-
- private val typesRewritesMap = mutableMapOf<JavaType, JavaType>()
-
- var isMapNotComplete = false
- private set
-
- fun createClassRemapper(visitor: ClassVisitor): ClassRemapper {
- return ClassRemapper(visitor, CustomRemapper(this))
- }
-
- override fun rewriteType(typeToMap: JavaType): JavaType {
- if (!isTypeSupported(typeToMap)) {
- return typeToMap
- }
-
- if (typesRewritesMap.contains(typeToMap)) {
- return typeToMap
- }
-
- // Try to find a rule
- val type = typeToMap.getRootType()
- for (rule in config.rewriteRules) {
- val typeRewriteResult = rule.apply(type)
- if (typeRewriteResult.isIgnored) {
- Log.i(TAG, "Ignoring: " + type)
- return type
- }
- if (typeRewriteResult.result == null) {
- continue
- }
- typesRewritesMap.put(type, typeRewriteResult.result)
- Log.i(TAG, " map: %s -> %s", type, typeRewriteResult.result)
- return typeRewriteResult.result
- }
-
- isMapNotComplete = true
- Log.e(TAG, "No rule for: " + type)
- typesRewritesMap.put(type, type) // Identity
- return type
- }
-
- override fun rewriteString(value: String): String {
- // We don't build map from strings
- return value
- }
-
- fun createTypesMap(): TypesMap {
- return TypesMap(typesRewritesMap)
- }
-
- private fun isTypeSupported(type: JavaType): Boolean {
- return config.restrictToPackagePrefixes.any { type.fullName.startsWith(it) }
- }
-}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/TransformationContext.kt b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/TransformationContext.kt
deleted file mode 100644
index d6705af..0000000
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/TransformationContext.kt
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * 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 android.support.tools.jetifier.core.transform
-
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.rules.RewriteRule.TypeRewriteResult
-import android.support.tools.jetifier.core.transform.proguard.ProGuardType
-import java.util.regex.Pattern
-
-/**
- * Context to share the transformation state between individual [Transformer]s.
- */
-class TransformationContext(
- val config: Config,
- val rewritingSupportLib: Boolean = false,
- val isInReversedMode: Boolean = false,
- /**
- * Whether to use identity if type in our scope is missing instead of throwing an exception.
- */
- val useIdentityIfTypeIsMissing: Boolean = true
-) {
-
- // Merges all packages prefixes into one regEx pattern
- private val packagePrefixPattern = Pattern.compile(
- "^(" + config.restrictToPackagePrefixes.map { "($it)" }.joinToString("|") + ").*$")
-
- /**
- * Whether to skip verification of dependency version match in pom files.
- */
- val ignorePomVersionCheck = rewritingSupportLib || isInReversedMode
-
- /** Counter for [reportNoMappingFoundFailure] calls. */
- var mappingNotFoundFailuresCount = 0
- private set
-
- /** Counter for [reportNoProGuardMappingFoundFailure] calls. */
- var proGuardMappingNotFoundFailuresCount = 0
- private set
-
- /** Counter for [reportNoPackageMappingFoundFailure] calls. */
- var packageMappingNotFoundFailuresCounts = 0
-
- var libraryName: String = ""
-
- private var runtimeIgnoreRules =
- (
- if (rewritingSupportLib) {
- config.slRules
- } else {
- config.rewriteRules
- }
- )
- .filter { it.isRuntimeIgnoreRule() }
- .toTypedArray()
-
- /** Total amount of errors found during the transformation process */
- fun errorsTotal() = mappingNotFoundFailuresCount + proGuardMappingNotFoundFailuresCount +
- packageMappingNotFoundFailuresCounts
-
- /**
- * Returns whether the given type is eligible for rewrite.
- *
- * If not, the transformers should ignore it.
- */
- fun isEligibleForRewrite(type: JavaType): Boolean {
- if (!isEligibleForRewriteInternal(type.fullName)) {
- return false
- }
-
- val isIgnored = runtimeIgnoreRules.any { it.apply(type) == TypeRewriteResult.IGNORED }
- return !isIgnored
- }
-
- /**
- * Returns whether the given ProGuard type reference is eligible for rewrite.
- *
- * Keep in mind that his has limited capabilities - mainly when * is used as a prefix. Rules
- * like *.v7 are not matched by prefix support.v7. So don't rely on it and use
- * the [ProGuardTypesMap] as first.
- */
- fun isEligibleForRewrite(type: ProGuardType): Boolean {
- if (!isEligibleForRewriteInternal(type.value)) {
- return false
- }
-
- val isIgnored = runtimeIgnoreRules.any { it.doesThisIgnoreProGuard(type) }
- return !isIgnored
- }
-
- private fun isEligibleForRewriteInternal(type: String): Boolean {
- if (config.restrictToPackagePrefixes.isEmpty()) {
- return false
- }
- return packagePrefixPattern.matcher(type).matches()
- }
-
- /**
- * Reports that there was a reference found that satisfies [isEligibleForRewrite] but no
- * mapping was found to rewrite it.
- */
- fun reportNoMappingFoundFailure() {
- mappingNotFoundFailuresCount++
- }
-
- /**
- * Reports that there was a reference found in a ProGuard file that satisfies
- * [isEligibleForRewrite] but no mapping was found to rewrite it.
- */
- fun reportNoProGuardMappingFoundFailure() {
- proGuardMappingNotFoundFailuresCount++
- }
-
- /**
- * Reports that there was a package reference found in a manifest file during a support library
- * artifact rewrite but no mapping was found for it.
- */
- fun reportNoPackageMappingFoundFailure() {
- packageMappingNotFoundFailuresCounts++
- }
-}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomDependency.kt b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomDependency.kt
deleted file mode 100644
index 8e480d0..0000000
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomDependency.kt
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * 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 android.support.tools.jetifier.core.transform.pom
-
-import com.google.gson.annotations.SerializedName
-import org.jdom2.Document
-import org.jdom2.Element
-
-/**
- * Represents a '<dependency>' XML node of a POM file.
- *
- * See documentation of the content at https://maven.apache.org/pom.html#Dependencies
- */
-data class PomDependency(
- @SerializedName("groupId")
- val groupId: String?,
-
- @SerializedName("artifactId")
- val artifactId: String?,
-
- @SerializedName("version")
- var version: String? = null,
-
- @SerializedName("classifier")
- val classifier: String? = null,
-
- @SerializedName("type")
- val type: String? = null,
-
- @SerializedName("scope")
- val scope: String? = null,
-
- @SerializedName("systemPath")
- val systemPath: String? = null,
-
- @SerializedName("optional")
- val optional: String? = null) {
-
- companion object {
-
- /**
- * Creates a new [PomDependency] from the given XML [Element].
- */
- fun fromXmlElement(node: Element, properties: Map<String, String>): PomDependency {
- var groupId: String? = null
- var artifactId: String? = null
- var version: String? = null
- var classifier: String? = null
- var type: String? = null
- var scope: String? = null
- var systemPath: String? = null
- var optional: String? = null
-
- for (childNode in node.children) {
- when (childNode.name) {
- "groupId" -> groupId = XmlUtils.resolveValue(childNode.value, properties)
- "artifactId" -> artifactId = XmlUtils.resolveValue(childNode.value, properties)
- "version" -> version = XmlUtils.resolveValue(childNode.value, properties)
- "classifier" -> classifier = XmlUtils.resolveValue(childNode.value, properties)
- "type" -> type = XmlUtils.resolveValue(childNode.value, properties)
- "scope" -> scope = XmlUtils.resolveValue(childNode.value, properties)
- "systemPath" -> systemPath = XmlUtils.resolveValue(childNode.value, properties)
- "optional" -> optional = XmlUtils.resolveValue(childNode.value, properties)
- }
- }
-
- return PomDependency(
- groupId = groupId,
- artifactId = artifactId,
- version = version,
- classifier = classifier,
- type = type,
- scope = scope,
- systemPath = systemPath,
- optional = optional)
- }
- }
-
- init {
- if (version != null) {
- version = version!!.toLowerCase()
- }
- }
-
- /**
- * Whether this dependency should be skipped from the rewriting process
- */
- fun shouldSkipRewrite(): Boolean {
- return scope != null && scope.toLowerCase() == "test"
- }
-
- /**
- * Returns a new dependency created by taking all the items from the [input] dependency and then
- * overwriting these with all of its non-null items.
- */
- fun rewrite(input: PomDependency): PomDependency {
- return PomDependency(
- groupId = groupId ?: input.groupId,
- artifactId = artifactId ?: input.artifactId,
- version = version ?: input.version,
- classifier = classifier ?: input.classifier,
- type = type ?: input.type,
- scope = scope ?: input.scope,
- systemPath = systemPath ?: input.systemPath,
- optional = optional ?: input.optional
- )
- }
-
- /**
- * Transforms the current data into XML '<dependency>' node.
- */
- fun toXmlElement(document: Document): Element {
- val node = Element("dependency")
- node.namespace = document.rootElement.namespace
-
- XmlUtils.addStringNodeToNode(node, "groupId", groupId)
- XmlUtils.addStringNodeToNode(node, "artifactId", artifactId)
- XmlUtils.addStringNodeToNode(node, "version", version)
- XmlUtils.addStringNodeToNode(node, "classifier", classifier)
- XmlUtils.addStringNodeToNode(node, "type", type)
- XmlUtils.addStringNodeToNode(node, "scope", scope)
- XmlUtils.addStringNodeToNode(node, "systemPath", systemPath)
- XmlUtils.addStringNodeToNode(node, "optional", optional)
-
- return node
- }
-
- /**
- * Returns the dependency in format "groupId:artifactId:version".
- */
- fun toStringNotation(): String {
- return "$groupId:$artifactId:$version"
- }
-}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/XmlUtils.kt b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/XmlUtils.kt
deleted file mode 100644
index 67b7a3d..0000000
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/XmlUtils.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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 android.support.tools.jetifier.core.transform.pom
-
-import android.support.tools.jetifier.core.utils.Log
-import org.jdom2.Document
-import org.jdom2.Element
-import org.jdom2.input.SAXBuilder
-import org.jdom2.output.Format
-import org.jdom2.output.XMLOutputter
-import java.io.ByteArrayOutputStream
-import java.util.regex.Pattern
-
-/**
- * Utilities for handling XML documents.
- */
-class XmlUtils {
-
- companion object {
-
- private val variablePattern = Pattern.compile("\\$\\{([^}]*)}")
-
- /** Saves the given [Document] to a new byte array */
- fun convertDocumentToByteArray(document : Document) : ByteArray {
- val xmlOutput = XMLOutputter()
- ByteArrayOutputStream().use {
- xmlOutput.format = Format.getPrettyFormat()
- xmlOutput.output(document, it)
- return it.toByteArray()
- }
- }
-
- /** Creates a new [Document] from the given [ByteArray] */
- fun createDocumentFromByteArray(data: ByteArray) : Document {
- val builder = SAXBuilder()
- data.inputStream().use {
- return builder.build(it)
- }
- }
-
- /**
- * Creates a new XML element with the given [id] and text given in [value] and puts it under
- * the given [parent]. Nothing is created if the [value] argument is null or empty.
- */
- fun addStringNodeToNode(parent: Element, id: String, value: String?) {
- if (value.isNullOrEmpty()) {
- return
- }
-
- val element = Element(id)
- element.text = value
- element.namespace = parent.namespace
- parent.children.add(element)
- }
-
-
- fun resolveValue(value: String?, properties: Map<String, String>) : String? {
- if (value == null) {
- return null
- }
-
- val matcher = variablePattern.matcher(value)
- if (matcher.matches()) {
- val variableName = matcher.group(1)
- val varValue = properties[variableName]
- if (varValue == null) {
- Log.e("TAG", "Failed to resolve variable '%s'", value)
- return value
- }
- return varValue
- }
-
- return value
- }
- }
-
-}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTransformer.kt b/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTransformer.kt
deleted file mode 100644
index b7536c7..0000000
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTransformer.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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 android.support.tools.jetifier.core.transform.proguard
-
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.Transformer
-import android.support.tools.jetifier.core.transform.proguard.patterns.ReplacersRunner
-import java.nio.charset.StandardCharsets
-
-/**
- * The [Transformer] responsible for ProGuard files refactoring.
- */
-class ProGuardTransformer internal constructor(context: TransformationContext) : Transformer {
-
- private val mapper = ProGuardTypesMapper(context)
-
- val replacer = ReplacersRunner(listOf(
- ProGuardClassSpecParser(mapper).replacer,
- ProGuardClassFilterParser(mapper).replacer
- ))
-
- override fun canTransform(file: ArchiveFile): Boolean {
- return file.isProGuardFile()
- }
-
- override fun runTransform(file: ArchiveFile) {
- val content = StringBuilder(file.data.toString(StandardCharsets.UTF_8)).toString()
- val result = replacer.applyReplacers(content)
-
- if (result == content) {
- return
- }
-
- file.setNewData(result.toByteArray())
- }
-}
-
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/PackageMap.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/PackageMap.kt
similarity index 95%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/PackageMap.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/PackageMap.kt
index 23888d3..7a1e300 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/PackageMap.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/PackageMap.kt
@@ -1,22 +1,22 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform
+package com.android.tools.build.jetifier.core
-import android.support.tools.jetifier.core.rules.PackageName
+import com.android.tools.build.jetifier.core.type.PackageName
/**
* Package map to be used to rewrite packages. The rewrite rules allow duplicities where the
@@ -304,6 +304,14 @@
PackageRule(
from = "android/arch/lifecycle/reactivestreams",
to = "androidx/lifecycle/reactivestreams"
+ ),
+ PackageRule(
+ from = "android/support/multidex/instrumentation",
+ to = "androidx/multidex/instrumentation"
+ ),
+ PackageRule(
+ from = "android/support/multidex",
+ to = "androidx/multidex"
)
)
diff --git a/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/TypeRewriter.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/TypeRewriter.kt
new file mode 100644
index 0000000..9f85d38
--- /dev/null
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/TypeRewriter.kt
@@ -0,0 +1,102 @@
+/*
+ * 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 com.android.tools.build.jetifier.core
+
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.proguard.ProGuardType
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.utils.Log
+import java.util.regex.Pattern
+
+/**
+ * Wraps capabilities of [TypesMap] and [RewriteRulesMap] into one place.
+ */
+class TypeRewriter(private val config: Config, private val useFallback: Boolean) {
+
+ companion object {
+ private const val TAG = "TypeRewriter"
+ }
+
+ // Merges all packages prefixes into one regEx pattern
+ private val packagePrefixPattern = Pattern.compile(
+ "^(" + config.restrictToPackagePrefixes.map { "($it)" }.joinToString("|") + ").*$")
+
+ fun rewriteType(type: JavaType): JavaType? {
+ if (!isEligibleForRewrite(type)) {
+ return type
+ }
+
+ val result = config.typesMap.mapType(type)
+ if (result != null) {
+ Log.i(TAG, "Map: %s -> %s", type, result)
+ return result
+ }
+
+ if (!useFallback) {
+ Log.e(TAG, "No mapping for: " + type)
+ return null
+ }
+
+ val rulesResult = config.rulesMap.rewriteType(type)
+ if (rulesResult != null) {
+ Log.i(TAG, "Using fallback: %s -> %s", type, rulesResult)
+ return rulesResult
+ }
+
+ Log.e(TAG, "No mapping for: " + type)
+ return null
+ }
+
+ /**
+ * Returns whether the given type is eligible for rewrite.
+ *
+ * If not, the transformers should ignore it.
+ */
+ fun isEligibleForRewrite(type: JavaType): Boolean {
+ if (!isEligibleForRewriteInternal(type.fullName)) {
+ return false
+ }
+
+ val isIgnored = config.rulesMap.runtimeIgnoreRules
+ .any { it.apply(type) == RewriteRule.TypeRewriteResult.IGNORED }
+ return !isIgnored
+ }
+
+ /**
+ * Returns whether the given ProGuard type reference is eligible for rewrite.
+ *
+ * Keep in mind that his has limited capabilities - mainly when * is used as a prefix. Rules
+ * like *.v7 are not matched by prefix support.v7. So don't rely on it and use
+ * the [ProGuardTypesMap] as first.
+ */
+ fun isEligibleForRewrite(type: ProGuardType): Boolean {
+ if (!isEligibleForRewriteInternal(type.value)) {
+ return false
+ }
+
+ val isIgnored = config.rulesMap.runtimeIgnoreRules.any { it.doesThisIgnoreProGuard(type) }
+ return !isIgnored
+ }
+
+ private fun isEligibleForRewriteInternal(type: String): Boolean {
+ if (config.restrictToPackagePrefixes.isEmpty()) {
+ return false
+ }
+ return packagePrefixPattern.matcher(type).matches()
+ }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/config/Config.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/config/Config.kt
similarity index 69%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/config/Config.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/config/Config.kt
index 673fbf6..e9414bb 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/config/Config.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/config/Config.kt
@@ -1,33 +1,34 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.config
+package com.android.tools.build.jetifier.core.config
-import android.support.tools.jetifier.core.rules.RewriteRule
-import android.support.tools.jetifier.core.transform.pom.PomRewriteRule
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.transform.PackageMap
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.PackageMap
+import com.android.tools.build.jetifier.core.pom.PomRewriteRule
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.TypesMap
import com.google.gson.annotations.SerializedName
/**
* The main and only one configuration that is used by the tool and all its transformers.
*
* [restrictToPackagePrefixes] Package prefixes that limit the scope of the rewriting
- * [rewriteRules] Rules to scan support libraries to generate [TypesMap]
+ * [rulesMap] Rules to scan support libraries to generate [TypesMap]
* [slRules] List of rules used when rewriting the support library itself in the reversed mode to
* ignore packages that don't need rewriting anymore.
* [pomRewriteRules] Rules to rewrite POM files
@@ -36,9 +37,9 @@
* rewrite.
*/
data class Config(
- val restrictToPackagePrefixes: List<String>,
- val rewriteRules: List<RewriteRule>,
- val slRules: List<RewriteRule>,
+ val restrictToPackagePrefixes: Set<String>,
+ val rulesMap: RewriteRulesMap,
+ val slRules: Set<RewriteRule>,
val pomRewriteRules: Set<PomRewriteRule>,
val typesMap: TypesMap,
val proGuardMap: ProGuardTypesMap,
@@ -62,18 +63,28 @@
companion object {
/** Path to the default config file located within the jar file. */
const val DEFAULT_CONFIG_RES_PATH = "/default.generated.config"
+
+ val EMPTY = Config(
+ restrictToPackagePrefixes = emptySet(),
+ rulesMap = RewriteRulesMap.EMPTY,
+ slRules = emptySet(),
+ pomRewriteRules = emptySet(),
+ typesMap = TypesMap.EMPTY,
+ proGuardMap = ProGuardTypesMap.EMPTY,
+ packageMap = PackageMap.EMPTY
+ )
}
fun setNewMap(mappings: TypesMap): Config {
- return Config(restrictToPackagePrefixes, rewriteRules, slRules, pomRewriteRules,
+ return Config(restrictToPackagePrefixes, rulesMap, slRules, pomRewriteRules,
mappings, proGuardMap)
}
/** Returns JSON data model of this class */
fun toJson(): JsonData {
return JsonData(
- restrictToPackagePrefixes,
- rewriteRules.map { it.toJson() }.toList(),
+ restrictToPackagePrefixes.toList(),
+ rulesMap.toJson().rules.toList(),
slRules.map { it.toJson() }.toList(),
pomRewriteRules.map { it.toJson() }.toList(),
typesMap.toJson(),
@@ -89,7 +100,7 @@
val restrictToPackages: List<String?>,
@SerializedName("rules")
- val rules: List<RewriteRule.JsonData?>,
+ val rules: List<RewriteRule.JsonData?>?,
@SerializedName("slRules")
val slRules: List<RewriteRule.JsonData?>?,
@@ -107,9 +118,10 @@
fun toConfig(): Config {
return Config(
- restrictToPackagePrefixes = restrictToPackages.filterNotNull(),
- rewriteRules = rules.filterNotNull().map { it.toRule() },
- slRules = slRules?.filterNotNull()?.map { it.toRule() } ?: listOf(),
+ restrictToPackagePrefixes = restrictToPackages.filterNotNull().toSet(),
+ rulesMap = RewriteRulesMap(
+ rules?.filterNotNull()?.map { it.toRule() }?.toSet() ?: emptySet()),
+ slRules = slRules?.filterNotNull()?.map { it.toRule() }?.toSet() ?: emptySet(),
pomRewriteRules = pomRules.filterNotNull().map { it.toRule() }.toSet(),
typesMap = mappings?.toMappings() ?: TypesMap.EMPTY,
proGuardMap = proGuardMap?.toMappings() ?: ProGuardTypesMap.EMPTY
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/config/ConfigParser.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/config/ConfigParser.kt
similarity index 78%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/config/ConfigParser.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/config/ConfigParser.kt
index 50d510c..74fd3d1 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/config/ConfigParser.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/config/ConfigParser.kt
@@ -1,22 +1,22 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.config
+package com.android.tools.build.jetifier.core.config
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.utils.Log
import com.google.gson.GsonBuilder
import java.io.FileNotFoundException
import java.io.FileWriter
@@ -25,11 +25,11 @@
object ConfigParser {
- private const val TAG : String = "Config"
+ private const val TAG: String = "Config"
private val gson = GsonBuilder().setPrettyPrinting().create()
- fun writeToString(config: Config) : String {
+ fun writeToString(config: Config): String {
return gson.toJson(config.toJson())
}
@@ -39,22 +39,22 @@
}
}
- fun parseFromString(inputText: String) : Config? {
+ fun parseFromString(inputText: String): Config? {
return gson.fromJson(inputText, Config.JsonData::class.java).toConfig()
}
- fun loadFromFile(configPath: Path) : Config? {
+ fun loadFromFile(configPath: Path): Config? {
return loadConfigFileInternal(configPath)
}
- fun loadDefaultConfig() : Config? {
+ fun loadDefaultConfig(): Config? {
Log.v(TAG, "Using the default config '%s'", Config.DEFAULT_CONFIG_RES_PATH)
val inputStream = javaClass.getResourceAsStream(Config.DEFAULT_CONFIG_RES_PATH)
return parseFromString(inputStream.reader().readText())
}
- fun loadConfigOrFail(configPath: Path?) : Config {
+ fun loadConfigOrFail(configPath: Path?): Config {
if (configPath != null) {
val config = loadConfigFileInternal(configPath)
if (config != null) {
@@ -70,7 +70,7 @@
throw AssertionError("The default config could not be found!")
}
- private fun loadConfigFileInternal(configPath: Path) : Config? {
+ private fun loadConfigFileInternal(configPath: Path): Config? {
if (!Files.isReadable(configPath)) {
Log.e(TAG, "Cannot access the config file: '%s'", configPath)
return null
diff --git a/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/pom/PomDependency.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/pom/PomDependency.kt
new file mode 100644
index 0000000..6109064
--- /dev/null
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/pom/PomDependency.kt
@@ -0,0 +1,87 @@
+/*
+ * 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 com.android.tools.build.jetifier.core.pom
+
+import com.google.gson.annotations.SerializedName
+
+/**
+ * Represents a '<dependency>' XML node of a POM file.
+ *
+ * See documentation of the content at https://maven.apache.org/pom.html#Dependencies
+ */
+data class PomDependency(
+ @SerializedName("groupId")
+ val groupId: String?,
+
+ @SerializedName("artifactId")
+ val artifactId: String?,
+
+ @SerializedName("version")
+ var version: String? = null,
+
+ @SerializedName("classifier")
+ val classifier: String? = null,
+
+ @SerializedName("type")
+ val type: String? = null,
+
+ @SerializedName("scope")
+ val scope: String? = null,
+
+ @SerializedName("systemPath")
+ val systemPath: String? = null,
+
+ @SerializedName("optional")
+ val optional: String? = null) {
+
+ init {
+ if (version != null) {
+ version = version!!.toLowerCase()
+ }
+ }
+
+ /**
+ * Whether this dependency should be skipped from the rewriting process
+ */
+ fun shouldSkipRewrite(): Boolean {
+ return scope != null && scope.toLowerCase() == "test"
+ }
+
+ /**
+ * Returns a new dependency created by taking all the items from the [input] dependency and then
+ * overwriting these with all of its non-null items.
+ */
+ fun rewrite(input: PomDependency): PomDependency {
+ return PomDependency(
+ groupId = groupId ?: input.groupId,
+ artifactId = artifactId ?: input.artifactId,
+ version = version ?: input.version,
+ classifier = classifier ?: input.classifier,
+ type = type ?: input.type,
+ scope = scope ?: input.scope,
+ systemPath = systemPath ?: input.systemPath,
+ optional = optional ?: input.optional
+ )
+ }
+
+ /**
+ * Returns the dependency in format "groupId:artifactId:version".
+ */
+ fun toStringNotation(): String {
+ return "$groupId:$artifactId:$version"
+ }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteRule.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/pom/PomRewriteRule.kt
similarity index 85%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteRule.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/pom/PomRewriteRule.kt
index 49bf8de..326b323 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteRule.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/pom/PomRewriteRule.kt
@@ -1,23 +1,24 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.pom
+package com.android.tools.build.jetifier.core.pom
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.utils.Log
import com.google.gson.annotations.SerializedName
+import java.nio.file.Path
/**
* Rule that defines how to rewrite a dependency element in a POM file.
@@ -61,7 +62,7 @@
/**
* Validates that the given [input] dependency has a valid version.
*/
- fun validateVersion(input: PomDependency, document: PomDocument? = null): Boolean {
+ fun validateVersion(input: PomDependency, pomPath: Path? = null): Boolean {
if (from.version == null || input.version == null) {
return true
}
@@ -72,8 +73,8 @@
if (!areVersionsMatching(from.version!!, input.version!!)) {
Log.e(TAG, "Version mismatch! Expected version '%s' but found version '%s' for " +
- "'%s:%s' in '%s' file.", from.version, input.version, input.groupId,
- input.artifactId, document?.file?.relativePath)
+ "'%s:%s' in '%s' file.", from.version, input.version, input.groupId,
+ input.artifactId, pomPath.toString())
return false
}
@@ -107,8 +108,8 @@
}
/** Returns JSON data model of this class */
- fun toJson(): PomRewriteRule.JsonData {
- return PomRewriteRule.JsonData(from, to)
+ fun toJson(): JsonData {
+ return JsonData(from, to)
}
/**
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardType.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/proguard/ProGuardType.kt
similarity index 78%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardType.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/proguard/ProGuardType.kt
index be15fbf..fd45004 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardType.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/proguard/ProGuardType.kt
@@ -1,22 +1,22 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.core.proguard
-import android.support.tools.jetifier.core.rules.JavaType
+import com.android.tools.build.jetifier.core.type.JavaType
/**
* Represents a type reference in ProGuard file. This type is similar to the regular java type but
@@ -32,7 +32,7 @@
companion object {
/** Creates the type reference from notation where packages are separated using '.' */
- fun fromDotNotation(type: String) : ProGuardType {
+ fun fromDotNotation(type: String): ProGuardType {
return ProGuardType(type.replace('.', '/'))
}
}
@@ -42,7 +42,7 @@
*/
fun isTrivial() = value == "*" || value == "**" || value == "***" || value == "%"
- fun toJavaType() : JavaType? {
+ fun toJavaType(): JavaType? {
if (value.contains('*') || value.contains('?')) {
return null
}
@@ -50,7 +50,7 @@
}
/** Returns the type reference as a string where packages are separated using '.' */
- fun toDotNotation() : String {
+ fun toDotNotation(): String {
return value.replace('/', '.')
}
}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMap.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/proguard/ProGuardTypesMap.kt
similarity index 89%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMap.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/proguard/ProGuardTypesMap.kt
index f232a35..1aa81fe 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMap.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/proguard/ProGuardTypesMap.kt
@@ -1,22 +1,22 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.core.proguard
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.utils.Log
/**
* Contains custom mappings to map support library types referenced in ProGuard to new ones.
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/RewriteRule.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRule.kt
similarity index 85%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/RewriteRule.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRule.kt
index 0a7fbde..8846080 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/RewriteRule.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRule.kt
@@ -1,22 +1,23 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.rules
+package com.android.tools.build.jetifier.core.rule
-import android.support.tools.jetifier.core.transform.proguard.ProGuardType
+import com.android.tools.build.jetifier.core.proguard.ProGuardType
+import com.android.tools.build.jetifier.core.type.JavaType
import com.google.gson.annotations.SerializedName
import java.util.regex.Pattern
@@ -30,9 +31,7 @@
* @param to A string to be used as a replacement if the 'from' pattern is matched. It can also
* apply groups matched from the original pattern using {x} annotation, e.g. {0}.
*/
-class RewriteRule(
- private val from: String,
- private val to: String) {
+class RewriteRule(private val from: String, private val to: String) {
companion object {
const val IGNORE_RUNTIME = "ignore"
@@ -86,6 +85,12 @@
return TypeRewriteResult(JavaType(result))
}
+ fun reverse(): RewriteRule {
+ val newFrom = to.replace("{0}", "(.*)")
+ val newTo = from.replace("(.*)", "{0}")
+ return RewriteRule(newFrom, newTo)
+ }
+
/*
* Returns whether this rule is an ignore rule and applies to the given proGuard type.
*/
@@ -111,11 +116,11 @@
* JSON data model for [RewriteRule].
*/
data class JsonData(
- @SerializedName("from")
- val from: String,
+ @SerializedName("from")
+ val from: String,
- @SerializedName("to")
- val to: String) {
+ @SerializedName("to")
+ val to: String) {
/** Creates instance of [RewriteRule] */
fun toRule(): RewriteRule {
diff --git a/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRulesMap.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRulesMap.kt
new file mode 100644
index 0000000..daf5b97
--- /dev/null
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRulesMap.kt
@@ -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 com.android.tools.build.jetifier.core.rule
+
+import com.android.tools.build.jetifier.core.type.JavaType
+
+/**
+ * Contains all [RewriteRule]s.
+ */
+class RewriteRulesMap(val rewriteRules: Set<RewriteRule>) {
+
+ companion object {
+ private const val TAG = "RewriteRulesMap"
+
+ val EMPTY = RewriteRulesMap(emptySet())
+ }
+
+ constructor(vararg rules: RewriteRule) : this(rules.toSet())
+
+ val runtimeIgnoreRules = rewriteRules.filter { it.isRuntimeIgnoreRule() }.toSet()
+
+ /**
+ * Tries to rewrite the given given type using the rules. If
+ */
+ fun rewriteType(type: JavaType): JavaType? {
+ // Try to find a rule
+ for (rule in rewriteRules) {
+ if (rule.isIgnoreRule()) {
+ continue
+ }
+ val typeRewriteResult = rule.apply(type)
+ if (typeRewriteResult.result == null) {
+ continue
+ }
+ return typeRewriteResult.result!!
+ }
+
+ return null
+ }
+
+ fun reverse(): RewriteRulesMap {
+ return RewriteRulesMap(rewriteRules
+ .filter { !it.isIgnoreRule() }
+ .map { it.reverse() }
+ .toSet())
+ }
+
+ fun appendRules(rules: Set<RewriteRule>): RewriteRulesMap {
+ return RewriteRulesMap(rewriteRules + rules)
+ }
+
+ fun toJson(): JsonData {
+ return JsonData(rewriteRules.map { it.toJson() }.toSet())
+ }
+
+ /**
+ * JSON data model for [RewriteRulesMap].
+ */
+ data class JsonData(val rules: Set<RewriteRule.JsonData>)
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/JavaType.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/JavaType.kt
similarity index 92%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/JavaType.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/JavaType.kt
index 734f46d..66a67cc 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/JavaType.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/JavaType.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.rules
+package com.android.tools.build.jetifier.core.type
/**
* Wrapper for Java type declaration.
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/PackageName.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/PackageName.kt
similarity index 86%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/PackageName.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/PackageName.kt
index c42f995..b89738e 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/rules/PackageName.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/PackageName.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.rules
+package com.android.tools.build.jetifier.core.type
/**
* Wrapper for Java package name declaration.
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/TypesMap.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/TypesMap.kt
similarity index 88%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/TypesMap.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/TypesMap.kt
index 8e497f5..e592a71 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/TypesMap.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/type/TypesMap.kt
@@ -1,24 +1,22 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.map
+package com.android.tools.build.jetifier.core.type
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.rules.RewriteRule
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.utils.Log
/**
* Contains all the mappings needed to rewrite java types.
@@ -41,6 +39,8 @@
}
}
+ constructor(vararg types: Pair<JavaType, JavaType>) : this(types.toMap())
+
/** Returns JSON data model of this class */
fun toJson(): JsonData {
return JsonData(types.map { it.key.fullName to it.value.fullName }.toMap())
@@ -66,7 +66,7 @@
"See the log for more details.")
}
- return TypesMap(types= typesReversed)
+ return TypesMap(types = typesReversed)
}
/** Maps the given type using this map. */
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/Log.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt
similarity index 82%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/Log.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt
index b1bb0af..fa558c5 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/Log.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/Log.kt
@@ -1,33 +1,34 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.utils
+package com.android.tools.build.jetifier.core.utils
object Log {
- var currentLevel: LogLevel = LogLevel.INFO
+ var currentLevel: LogLevel = LogLevel.ERROR
var logConsumer: LogConsumer = StdOutLogConsumer()
fun setLevel(level: String?) {
currentLevel = when (level) {
+ "info" -> LogLevel.INFO
"error" -> LogLevel.ERROR
"debug" -> LogLevel.DEBUG
"verbose" -> LogLevel.VERBOSE
- else -> LogLevel.INFO
+ else -> LogLevel.ERROR
}
}
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogConsumer.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/LogConsumer.kt
similarity index 78%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogConsumer.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/LogConsumer.kt
index ddebd25..0489274 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogConsumer.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/LogConsumer.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.utils
+package com.android.tools.build.jetifier.core.utils
/**
* Interface to plug custom logs consumers to [Log].
@@ -28,6 +28,5 @@
fun verbose(message: String)
fun debug(message: String)
-
}
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogLevel.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/LogLevel.kt
similarity index 70%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogLevel.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/LogLevel.kt
index f46b8f6..3017304 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/LogLevel.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/LogLevel.kt
@@ -1,22 +1,22 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.utils
+package com.android.tools.build.jetifier.core.utils
-enum class LogLevel(val priority : Int) {
+enum class LogLevel(val priority: Int) {
ERROR(0),
INFO(1),
VERBOSE(2),
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/StdOutLogConsumer.kt b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/StdOutLogConsumer.kt
similarity index 82%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/StdOutLogConsumer.kt
rename to jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/StdOutLogConsumer.kt
index 7cfd25e..f63cb32 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/utils/StdOutLogConsumer.kt
+++ b/jetifier/jetifier/core/src/main/kotlin/com/android/tools/build/jetifier/core/utils/StdOutLogConsumer.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.utils
+package com.android.tools.build.jetifier.core.utils
/**
* Prints logs to the standard output.
diff --git a/jetifier/jetifier/core/src/main/resources/default.config b/jetifier/jetifier/core/src/main/resources/default.config
index 4dc5fda..bec56c3 100644
--- a/jetifier/jetifier/core/src/main/resources/default.config
+++ b/jetifier/jetifier/core/src/main/resources/default.config
@@ -665,6 +665,12 @@
"from": "android/databinding/(.*)",
"to": "androidx/databinding/{0}"
},
+
+ #Multidex
+ {
+ "from": "android/support/multidex/(.*)",
+ "to": "androidx/multidex/{0}"
+ }
],
"slRules": [
# Ignore
@@ -699,7 +705,7 @@
{
"from": "androidx/textclassifier/(.*)",
"to": "ignore"
- },
+ }
],
"pomRules": [
{
@@ -739,12 +745,12 @@
"to": [{ "groupId": "androidx.mediarouter", "artifactId": "mediarouter", "version": "1.0.0" }]
},
{
- "from": { "groupId": "com.android.support", "artifactId": "multidex", "version": "28.0.0" },
- "to": [{ "groupId": "androidx.multidex", "artifactId": "multidex", "version": "1.0.0" }]
+ "from": { "groupId": "com.android.support", "artifactId": "multidex", "version": "1.0.3" },
+ "to": [{ "groupId": "androidx.multidex", "artifactId": "multidex", "version": "2.0.0" }]
},
{
- "from": { "groupId": "com.android.support", "artifactId": "multidex-instrumentation", "version": "28.0.0" },
- "to": [{ "groupId": "androidx.multidex", "artifactId": "multidex-instrumentation", "version": "1.0.0" }]
+ "from": { "groupId": "com.android.support", "artifactId": "multidex-instrumentation", "version": "1.0.3" },
+ "to": [{ "groupId": "androidx.multidex", "artifactId": "multidex-instrumentation", "version": "2.0.0" }]
},
{
"from": { "groupId": "com.android.support", "artifactId": "palette-v7", "version": "28.0.0" },
@@ -1108,16 +1114,44 @@
},
# Keep it same
{
- "from": { "groupId": "androidx.slice", "artifactId": "slices-core", "version": "1.0.0" },
+ "from": { "groupId": "com.android.support", "artifactId": "slices-core", "version": "28.0.0" },
"to": [{ "groupId": "androidx.slice", "artifactId": "slices-core", "version": "1.0.0" }]
},
{
- "from": { "groupId": "androidx.slice", "artifactId": "slices-builders", "version": "1.0.0" },
+ "from": { "groupId": "com.android.support", "artifactId": "slices-builders", "version": "28.0.0" },
"to": [{ "groupId": "androidx.slice", "artifactId": "slices-builders", "version": "1.0.0" }]
},
{
- "from": { "groupId": "androidx.slice", "artifactId": "slices-view", "version": "1.0.0" },
+ "from": { "groupId": "com.android.support", "artifactId": "slices-view", "version": "28.0.0" },
"to": [{ "groupId": "androidx.slice", "artifactId": "slices-view", "version": "1.0.0" }]
+ },
+ {
+ "from": { "groupId": "com.android.support", "artifactId": "heifwriter", "version": "28.0.0" },
+ "to": [{ "groupId": "androidx.heifwriter", "artifactId": "heifwriter", "version": "1.0.0" }]
+ },
+ {
+ "from": { "groupId": "com.android.support", "artifactId": "recyclerview-selection", "version": "28.0.0" },
+ "to": [{ "groupId": "androidx.recyclerview", "artifactId": "recyclerview-selection", "version": "1.0.0" }]
+ },
+ {
+ "from": { "groupId": "com.android.support", "artifactId": "webkit", "version": "28.0.0" },
+ "to": [{ "groupId": "androidx.webkit", "artifactId": "webkit", "version": "1.0.0" }]
+ },
+ {
+ "from": { "groupId": "com.android.support", "artifactId": "palette-ktx", "version": "28.0.0" },
+ "to": [{ "groupId": "androidx.palette", "artifactId": "palette-ktx", "version": "1.0.0" }]
+ },
+ {
+ "from": { "groupId": "com.android.support", "artifactId": "fragment-ktx", "version": "28.0.0" },
+ "to": [{ "groupId": "androidx.fragment", "artifactId": "fragment-ktx", "version": "1.0.0" }]
+ },
+ {
+ "from": { "groupId": "com.android.support", "artifactId": "core-ktx", "version": "28.0.0" },
+ "to": [{ "groupId": "androidx.core", "artifactId": "core-ktx", "version": "1.0.0" }]
+ },
+ {
+ "from": { "groupId": "com.android.support", "artifactId": "collection-ktx", "version": "28.0.0" },
+ "to": [{ "groupId": "androidx.collection", "artifactId": "collection-ktx", "version": "1.0.0" }]
}
],
"map": {
diff --git a/jetifier/jetifier/core/src/main/resources/default.generated.config b/jetifier/jetifier/core/src/main/resources/default.generated.config
index ca86505..57590dd 100644
--- a/jetifier/jetifier/core/src/main/resources/default.generated.config
+++ b/jetifier/jetifier/core/src/main/resources/default.generated.config
@@ -646,6 +646,10 @@
{
"from": "android/databinding/(.*)",
"to": "androidx/databinding/{0}"
+ },
+ {
+ "from": "android/support/multidex/(.*)",
+ "to": "androidx/multidex/{0}"
}
],
"slRules": [
@@ -813,13 +817,13 @@
"from": {
"groupId": "com.android.support",
"artifactId": "multidex",
- "version": "28.0.0"
+ "version": "1.0.3"
},
"to": [
{
"groupId": "androidx.multidex",
"artifactId": "multidex",
- "version": "1.0.0"
+ "version": "2.0.0"
}
]
},
@@ -827,13 +831,13 @@
"from": {
"groupId": "com.android.support",
"artifactId": "multidex-instrumentation",
- "version": "28.0.0"
+ "version": "1.0.3"
},
"to": [
{
"groupId": "androidx.multidex",
"artifactId": "multidex-instrumentation",
- "version": "1.0.0"
+ "version": "2.0.0"
}
]
},
@@ -2099,9 +2103,9 @@
},
{
"from": {
- "groupId": "androidx.slice",
+ "groupId": "com.android.support",
"artifactId": "slices-core",
- "version": "1.0.0"
+ "version": "28.0.0"
},
"to": [
{
@@ -2113,9 +2117,9 @@
},
{
"from": {
- "groupId": "androidx.slice",
+ "groupId": "com.android.support",
"artifactId": "slices-builders",
- "version": "1.0.0"
+ "version": "28.0.0"
},
"to": [
{
@@ -2127,9 +2131,9 @@
},
{
"from": {
- "groupId": "androidx.slice",
+ "groupId": "com.android.support",
"artifactId": "slices-view",
- "version": "1.0.0"
+ "version": "28.0.0"
},
"to": [
{
@@ -2138,6 +2142,104 @@
"version": "1.0.0"
}
]
+ },
+ {
+ "from": {
+ "groupId": "com.android.support",
+ "artifactId": "heifwriter",
+ "version": "28.0.0"
+ },
+ "to": [
+ {
+ "groupId": "androidx.heifwriter",
+ "artifactId": "heifwriter",
+ "version": "1.0.0"
+ }
+ ]
+ },
+ {
+ "from": {
+ "groupId": "com.android.support",
+ "artifactId": "recyclerview-selection",
+ "version": "28.0.0"
+ },
+ "to": [
+ {
+ "groupId": "androidx.recyclerview",
+ "artifactId": "recyclerview-selection",
+ "version": "1.0.0"
+ }
+ ]
+ },
+ {
+ "from": {
+ "groupId": "com.android.support",
+ "artifactId": "webkit",
+ "version": "28.0.0"
+ },
+ "to": [
+ {
+ "groupId": "androidx.webkit",
+ "artifactId": "webkit",
+ "version": "1.0.0"
+ }
+ ]
+ },
+ {
+ "from": {
+ "groupId": "com.android.support",
+ "artifactId": "palette-ktx",
+ "version": "28.0.0"
+ },
+ "to": [
+ {
+ "groupId": "androidx.palette",
+ "artifactId": "palette-ktx",
+ "version": "1.0.0"
+ }
+ ]
+ },
+ {
+ "from": {
+ "groupId": "com.android.support",
+ "artifactId": "fragment-ktx",
+ "version": "28.0.0"
+ },
+ "to": [
+ {
+ "groupId": "androidx.fragment",
+ "artifactId": "fragment-ktx",
+ "version": "1.0.0"
+ }
+ ]
+ },
+ {
+ "from": {
+ "groupId": "com.android.support",
+ "artifactId": "core-ktx",
+ "version": "28.0.0"
+ },
+ "to": [
+ {
+ "groupId": "androidx.core",
+ "artifactId": "core-ktx",
+ "version": "1.0.0"
+ }
+ ]
+ },
+ {
+ "from": {
+ "groupId": "com.android.support",
+ "artifactId": "collection-ktx",
+ "version": "28.0.0"
+ },
+ "to": [
+ {
+ "groupId": "androidx.collection",
+ "artifactId": "collection-ktx",
+ "version": "1.0.0"
+ }
+ ]
}
],
"map": {
@@ -2313,6 +2415,10 @@
"android/support/v4/content/res/ResourcesCompat": "androidx/core/content/res/ResourcesCompat",
"android/arch/lifecycle/DefaultLifecycleObserver": "androidx/lifecycle/DefaultLifecycleObserver",
"android/arch/lifecycle/FullLifecycleObserver": "androidx/lifecycle/FullLifecycleObserver",
+ "android/support/multidex/ZipUtil": "androidx/multidex/ZipUtil",
+ "android/support/multidex/MultiDexExtractor": "androidx/multidex/MultiDexExtractor",
+ "android/support/multidex/MultiDex": "androidx/multidex/MultiDex",
+ "android/support/multidex/MultiDexApplication": "androidx/multidex/MultiDexApplication",
"android/support/v7/widget/CardView": "androidx/cardview/widget/CardView",
"android/support/v7/widget/CardViewDelegate": "androidx/cardview/widget/CardViewDelegate",
"android/support/v7/cardview/R": "androidx/cardview/R",
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/DependencyMappingTest.kt b/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/DependencyMappingTest.kt
deleted file mode 100644
index 3499fef..0000000
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/DependencyMappingTest.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-package android.support.tools.jetifier.core.transform
-
-import android.support.tools.jetifier.core.Processor
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.transform.pom.PomDependency
-import android.support.tools.jetifier.core.transform.pom.PomRewriteRule
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
-import com.google.common.truth.Truth
-import org.junit.Test
-
-class DependencyMappingTest {
-
- @Test
- fun mapTest_oneToOne_shouldMap() {
- MappingTester
- .testRewrite(
- from = "hello:world:1.0.0",
- to = setOf("hi:all:2.0.0"),
- rules = setOf(
- PomRewriteRule(
- from = PomDependency(groupId = "hello", artifactId = "world"),
- to = setOf(
- PomDependency(groupId = "hi", artifactId = "all", version = "2.0.0")
- )
- ))
- )
- }
-
- @Test
- fun mapTest_oneToTwo_shouldMap() {
- MappingTester
- .testRewrite(
- from = "hello:world:1.0.0",
- to = setOf("hi:all:2.0.0", "hey:all:3.0.0"),
- rules = setOf(
- PomRewriteRule(
- from = PomDependency(groupId = "hello", artifactId = "world"),
- to = setOf(
- PomDependency(groupId = "hi", artifactId = "all", version = "2.0.0"),
- PomDependency(groupId = "hey", artifactId = "all", version = "3.0.0")
- )
- ))
- )
- }
-
- @Test
- fun mapTest_oneToNone_shouldMapToEmpty() {
- MappingTester
- .testRewrite(
- from = "hello:world:1.0.0",
- to = setOf(),
- rules = setOf(
- PomRewriteRule(
- from = PomDependency(groupId = "hello", artifactId = "world"),
- to = setOf()
- ))
- )
- }
-
- @Test
- fun mapTest_oneToNull_ruleNotFound_returnNull() {
- MappingTester
- .testRewrite(
- from = "hello:world:1.0.0",
- to = null,
- rules = setOf(
- PomRewriteRule(
- from = PomDependency(groupId = "hello", artifactId = "me", version = "1.0"),
- to = setOf()
- ))
- )
- }
-
- object MappingTester {
-
- fun testRewrite(
- from: String,
- to: Set<String>?,
- rules: Set<PomRewriteRule>
- ) {
- val config = Config(
- restrictToPackagePrefixes = emptyList(),
- rewriteRules = emptyList(),
- slRules = emptyList(),
- pomRewriteRules = rules,
- typesMap = TypesMap.EMPTY,
- proGuardMap = ProGuardTypesMap.EMPTY,
- packageMap = PackageMap.EMPTY
- )
-
- val processor = Processor.createProcessor(config)
- val result = processor.mapDependency(from)
-
- if (to == null) {
- Truth.assertThat(result).isNull()
- } else {
- Truth.assertThat(result).containsExactlyElementsIn(to)
- }
- }
- }
-}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapperImplTest.kt b/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapperImplTest.kt
deleted file mode 100644
index 4dcbd39..0000000
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapperImplTest.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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 android.support.tools.jetifier.core.transform.bytecode
-
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.transform.PackageMap
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
-import com.google.common.truth.Truth
-import org.junit.Test
-import org.objectweb.asm.ClassWriter
-
-class CoreRemapperImplTest {
-
- @Test
- fun remapString_shouldUseFallbackForField() {
- val remapper = prepareRemapper(
- TypesMap(mapOf(
- JavaType.fromDotVersion("androidx.test.InputConnectionCompat")
- to JavaType.fromDotVersion("android.support.test.InputConnectionCompat")
- )),
- "androidx/")
-
- val given = "androidx.test.InputConnectionCompat.CONTENT_URI"
- val expected = "android.support.test.InputConnectionCompat.CONTENT_URI"
-
- Truth.assertThat(remapper.rewriteString(given)).isEqualTo(expected)
- }
-
- private fun prepareRemapper(
- typesMap: TypesMap,
- restrictToPackagePrefix: String? = null
- ): CoreRemapperImpl {
- val prefixes = if (restrictToPackagePrefix == null) {
- emptyList()
- } else {
- listOf(restrictToPackagePrefix)
- }
-
- val config = Config(
- restrictToPackagePrefixes = prefixes,
- rewriteRules = emptyList(),
- typesMap = typesMap,
- slRules = emptyList(),
- pomRewriteRules = emptySet(),
- proGuardMap = ProGuardTypesMap.EMPTY,
- packageMap = PackageMap.EMPTY)
-
- val context = TransformationContext(config,
- isInReversedMode = true)
-
- val writer = ClassWriter(0 /* flags */)
- return CoreRemapperImpl(context, writer)
- }
-}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/TypeRewriterTest.kt b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/TypeRewriterTest.kt
new file mode 100644
index 0000000..72556a9
--- /dev/null
+++ b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/TypeRewriterTest.kt
@@ -0,0 +1,153 @@
+/*
+ * 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 com.android.tools.build.jetifier.core
+
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.google.common.truth.Truth
+import org.junit.Test
+
+class TypeRewriterTest {
+
+ @Test fun simpleRewrite_typesMap() {
+ testRewrite(
+ from = "test.sample.Class",
+ to = "test.sample2.Class2",
+ typesMap = TypesMap(
+ JavaType.fromDotVersion("test.sample.Class")
+ to JavaType.fromDotVersion("test.sample2.Class2")
+ )
+ )
+ }
+
+ @Test fun prefixNotAllowedForRewrite() {
+ testRewrite(
+ from = "test.sample.Class",
+ to = "test.sample.Class",
+ packagePrefix = "notTest/",
+ typesMap = TypesMap(
+ JavaType.fromDotVersion("test.sample.Class")
+ to JavaType.fromDotVersion("test.sample2.Class2")
+ )
+ )
+ }
+
+ @Test fun typeMissingInMap_returnNull() {
+ testRewrite(
+ from = "test.sample.Class",
+ to = null
+ )
+ }
+
+ @Test fun typeMissingInMap_useFallback_shouldRewrite() {
+ testRewrite(
+ from = "test.sample.Class",
+ to = "test.sample2.Class2",
+ rewriteRulesMap = RewriteRulesMap(RewriteRule(
+ "test/sample/Cl(.*)",
+ "test/sample2/Cl{0}2"
+ )),
+ useFallback = true
+ )
+ }
+
+ @Test fun typeMissingInMap_useFallback_innerClass_shouldRewrite() {
+ testRewrite(
+ from = "test.sample.Class\$Inner",
+ to = "test.sample2.Class2\$Inner",
+ rewriteRulesMap = RewriteRulesMap(RewriteRule(
+ "test/sample/Class(.*)",
+ "test/sample2/Class2{0}"
+ )),
+ useFallback = true
+ )
+ }
+
+ @Test fun typeMissingInMap_useFallback_reversedMap_shouldRewrite() {
+ testRewrite(
+ from = "test.sample.Class",
+ to = "test.sample2.Class2",
+ rewriteRulesMap = RewriteRulesMap(RewriteRule(
+ "test/sample2/Cl(.*)2",
+ "test/sample/Cl{0}"
+ )).reverse(),
+ useFallback = true
+ )
+ }
+
+ @Test fun useBothMaps_typesMapHasPriority() {
+ testRewrite(
+ from = "test.sample.Class",
+ to = "test.sample2.Class2",
+ typesMap = TypesMap(
+ JavaType.fromDotVersion("test.sample.Class")
+ to JavaType.fromDotVersion("test.sample2.Class2")
+ ),
+ rewriteRulesMap = RewriteRulesMap(RewriteRule(
+ "test/sample/Cl(.*)",
+ "test/sample3/Cl{0}3"
+ )),
+ useFallback = true
+ )
+ }
+
+ @Test fun ignoreRule_shouldNotRewrite() {
+ testRewrite(
+ from = "test.sample.Class",
+ to = "test.sample2.Class2",
+ typesMap = TypesMap(
+ JavaType.fromDotVersion("test.sample.Class")
+ to JavaType.fromDotVersion("test.sample2.Class2")
+ ),
+ rewriteRulesMap = RewriteRulesMap(RewriteRule(
+ "test/sample/Cl(.*)",
+ "ignoreInRuntime"
+ ))
+ )
+ }
+
+ fun testRewrite(
+ from: String,
+ to: String?,
+ packagePrefix: String = "test/",
+ typesMap: TypesMap = TypesMap.EMPTY,
+ rewriteRulesMap: RewriteRulesMap = RewriteRulesMap.EMPTY,
+ useFallback: Boolean = false) {
+ val config = Config(
+ restrictToPackagePrefixes = setOf(packagePrefix),
+ rulesMap = rewriteRulesMap,
+ slRules = emptySet(),
+ pomRewriteRules = emptySet(),
+ typesMap = typesMap,
+ proGuardMap = ProGuardTypesMap.EMPTY,
+ packageMap = PackageMap.EMPTY
+ )
+
+ val rewriter = TypeRewriter(config, useFallback)
+ val result = rewriter.rewriteType(JavaType.fromDotVersion(from))
+
+ if (to == null) {
+ Truth.assertThat(result).isNull()
+ } else {
+ Truth.assertThat(result).isEqualTo(JavaType.fromDotVersion(to))
+ }
+ }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/config/ConfigParserTest.kt b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/config/ConfigParserTest.kt
similarity index 92%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/config/ConfigParserTest.kt
rename to jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/config/ConfigParserTest.kt
index 49ef6f8..794b342 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/config/ConfigParserTest.kt
+++ b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/config/ConfigParserTest.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.config
+package com.android.tools.build.jetifier.core.config
import com.google.common.truth.Truth
import org.junit.Test
@@ -55,8 +55,8 @@
val config = ConfigParser.parseFromString(confStr)
Truth.assertThat(config).isNotNull()
- Truth.assertThat(config!!.restrictToPackagePrefixes[0]).isEqualTo("android/support/")
- Truth.assertThat(config.rewriteRules.size).isEqualTo(2)
+ Truth.assertThat(config!!.restrictToPackagePrefixes.first()).isEqualTo("android/support/")
+ Truth.assertThat(config.rulesMap.rewriteRules.size).isEqualTo(2)
Truth.assertThat(config.proGuardMap.rules.size).isEqualTo(1)
}
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/RewriteRuleTest.kt b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRuleTest.kt
similarity index 81%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/RewriteRuleTest.kt
rename to jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRuleTest.kt
index ebb48dd..404b0bd 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/RewriteRuleTest.kt
+++ b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/rule/RewriteRuleTest.kt
@@ -1,24 +1,23 @@
-
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform
+package android.support.tools.jetifier.processor.transform
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.rules.RewriteRule
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.type.JavaType
import com.google.common.truth.Truth
import org.junit.Test
@@ -79,13 +78,14 @@
class RuleTesterStep1(val from: String, val to: String) {
- fun rewritesType(inputType: String)
- = RuleTesterFinalTypeStep(from, to, inputType)
+ fun rewritesType(inputType: String) = RuleTesterFinalTypeStep(from, to, inputType)
}
- class RuleTesterFinalTypeStep(val fromType: String,
- val toType: String,
- val inputType: String) {
+ class RuleTesterFinalTypeStep(
+ val fromType: String,
+ val toType: String,
+ val inputType: String
+ ) {
fun into(expectedResult: String) {
val fieldRule = RewriteRule(fromType, toType)
@@ -103,7 +103,6 @@
Truth.assertThat(result.isIgnored).isTrue()
}
}
-
}
}
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/rules/JavaTypeTest.kt b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/type/JavaTypeTest.kt
similarity index 96%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/rules/JavaTypeTest.kt
rename to jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/type/JavaTypeTest.kt
index a5c0e89..2a73c21 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/rules/JavaTypeTest.kt
+++ b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/type/JavaTypeTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.support.tools.jetifier.core.rules
+package com.android.tools.build.jetifier.core.type
import com.google.common.truth.Truth
import org.junit.Test
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/map/TypesMapTest.kt b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/type/TypesMapTest.kt
similarity index 94%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/map/TypesMapTest.kt
rename to jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/type/TypesMapTest.kt
index 7aa60df..49fae50 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/map/TypesMapTest.kt
+++ b/jetifier/jetifier/core/src/test/kotlin/com/android/tools/build/jetifier/core/type/TypesMapTest.kt
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-package android.support.tools.jetifier.core.map
+package com.android.tools.build.jetifier.core.type
-import android.support.tools.jetifier.core.rules.JavaType
import com.google.common.truth.Truth
import org.junit.Test
diff --git a/jetifier/jetifier/gradle-plugin/build.gradle b/jetifier/jetifier/gradle-plugin/build.gradle
index deeb722..526901b 100644
--- a/jetifier/jetifier/gradle-plugin/build.gradle
+++ b/jetifier/jetifier/gradle-plugin/build.gradle
@@ -14,17 +14,18 @@
* limitations under the License
*/
-import static androidx.build.dependencies.DependenciesKt.KOTLIN_STDLIB
import androidx.build.LibraryGroups
import androidx.build.LibraryVersions
+import static androidx.build.dependencies.DependenciesKt.KOTLIN_STDLIB
+
plugins {
id("SupportKotlinLibraryPlugin")
}
dependencies {
- compile project(':jetifier-core')
+ compile project(':jetifier-processor')
compile(KOTLIN_STDLIB)
compileOnly gradleApi()
}
diff --git a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierExtension.kt b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierExtension.kt
similarity index 95%
rename from jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierExtension.kt
rename to jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierExtension.kt
index e0eb60c..4270e22 100644
--- a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierExtension.kt
+++ b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierExtension.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.plugin.gradle
+package com.android.tools.build.jetifier.plugin.gradle
import groovy.lang.Closure
import org.gradle.api.Project
diff --git a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierLoggerAdapter.kt b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierLoggerAdapter.kt
similarity index 79%
rename from jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierLoggerAdapter.kt
rename to jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierLoggerAdapter.kt
index 9d85851..a3ee44b 100644
--- a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierLoggerAdapter.kt
+++ b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierLoggerAdapter.kt
@@ -1,22 +1,22 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.plugin.gradle
+package com.android.tools.build.jetifier.plugin.gradle
-import android.support.tools.jetifier.core.utils.LogConsumer
+import com.android.tools.build.jetifier.core.utils.LogConsumer
import org.gradle.api.logging.Logger
/**
@@ -39,5 +39,4 @@
override fun debug(message: String) {
gradleLogger.debug(message)
}
-
}
\ No newline at end of file
diff --git a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierPlugin.kt b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierPlugin.kt
similarity index 87%
rename from jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierPlugin.kt
rename to jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierPlugin.kt
index 4c26406..5680875 100644
--- a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifierPlugin.kt
+++ b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifierPlugin.kt
@@ -1,22 +1,22 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.plugin.gradle
+package com.android.tools.build.jetifier.plugin.gradle
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.utils.Log
import org.gradle.api.Plugin
import org.gradle.api.Project
diff --git a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifyGlobalTask.kt b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifyGlobalTask.kt
similarity index 84%
rename from jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifyGlobalTask.kt
rename to jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifyGlobalTask.kt
index 62e7e21..b562299 100644
--- a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifyGlobalTask.kt
+++ b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifyGlobalTask.kt
@@ -1,7 +1,23 @@
-package android.support.tools.jetifier.plugin.gradle
+/*
+ * 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 android.support.tools.jetifier.core.FileMapping
-import android.support.tools.jetifier.core.config.ConfigParser
+package com.android.tools.build.jetifier.plugin.gradle
+
+import com.android.tools.build.jetifier.core.config.ConfigParser
+import com.android.tools.build.jetifier.processor.FileMapping
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
@@ -117,10 +133,10 @@
// Process the files using Jetifier
val result = TasksCommon.processFiles(config,
- dependenciesMap.keys.map {
- FileMapping(it, File(outputDir, it.name))
- }.toSet(),
- project.logger)
+ dependenciesMap.keys.map {
+ FileMapping(it, File(outputDir, it.name))
+ }.toSet(),
+ project.logger)
configurationsToProcess.forEach { conf ->
// Remove files that we don't need anymore
diff --git a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifyLibsTask.kt b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifyLibsTask.kt
similarity index 91%
rename from jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifyLibsTask.kt
rename to jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifyLibsTask.kt
index 34230c0..d4fd941 100644
--- a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/JetifyLibsTask.kt
+++ b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/JetifyLibsTask.kt
@@ -1,23 +1,23 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.plugin.gradle
+package com.android.tools.build.jetifier.plugin.gradle
-import android.support.tools.jetifier.core.FileMapping
-import android.support.tools.jetifier.core.config.ConfigParser
+import com.android.tools.build.jetifier.core.config.ConfigParser
+import com.android.tools.build.jetifier.processor.FileMapping
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
@@ -52,7 +52,6 @@
}
return project.tasks.create(TASK_NAME, JetifyLibsTask::class.java)
}
-
}
private val outputDir = File(project.buildDir, OUTPUT_DIR_APPENDIX)
diff --git a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/TasksCommon.kt b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/TasksCommon.kt
similarity index 78%
rename from jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/TasksCommon.kt
rename to jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/TasksCommon.kt
index de75cbb..6364870 100644
--- a/jetifier/jetifier/gradle-plugin/src/main/kotlin/android/support/tools/jetifier/plugin/gradle/TasksCommon.kt
+++ b/jetifier/jetifier/gradle-plugin/src/main/kotlin/com/android/tools/build/jetifier/plugin/gradle/TasksCommon.kt
@@ -1,25 +1,25 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.plugin.gradle
+package com.android.tools.build.jetifier.plugin.gradle
-import android.support.tools.jetifier.core.FileMapping
-import android.support.tools.jetifier.core.Processor
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.FileMapping
+import com.android.tools.build.jetifier.processor.Processor
import org.gradle.api.logging.LogLevel
import org.gradle.api.logging.Logger
import java.io.File
diff --git a/jetifier/jetifier/gradle-plugin/src/main/resources/META-INF/gradle-plugins/androidx.tools.jetifier.properties b/jetifier/jetifier/gradle-plugin/src/main/resources/META-INF/gradle-plugins/androidx.tools.jetifier.properties
index 7ee7e73..555bd14 100644
--- a/jetifier/jetifier/gradle-plugin/src/main/resources/META-INF/gradle-plugins/androidx.tools.jetifier.properties
+++ b/jetifier/jetifier/gradle-plugin/src/main/resources/META-INF/gradle-plugins/androidx.tools.jetifier.properties
@@ -14,4 +14,4 @@
# limitations under the License
#
-implementation-class=android.support.tools.jetifier.plugin.gradle.JetifierPlugin
\ No newline at end of file
+implementation-class=com.android.tools.build.jetifier.plugin.gradle.JetifierPlugin
\ No newline at end of file
diff --git a/jetifier/jetifier/preprocessor/build.gradle b/jetifier/jetifier/preprocessor/build.gradle
index 893b763..24fbb69 100644
--- a/jetifier/jetifier/preprocessor/build.gradle
+++ b/jetifier/jetifier/preprocessor/build.gradle
@@ -19,9 +19,9 @@
id("application")
}
-mainClassName = "android.support.tools.jetifier.preprocessor.MainKt"
+mainClassName = "com.android.tools.build.jetifier.preprocessor.MainKt"
dependencies {
- compile project(':jetifier-core')
+ compile project(':jetifier-processor')
compile group: 'commons-cli', name: 'commons-cli', version: '1.3.1'
}
diff --git a/jetifier/jetifier/preprocessor/scripts/processDefaultConfig.sh b/jetifier/jetifier/preprocessor/scripts/processDefaultConfig.sh
index 19dcc7e..aa9c264 100755
--- a/jetifier/jetifier/preprocessor/scripts/processDefaultConfig.sh
+++ b/jetifier/jetifier/preprocessor/scripts/processDefaultConfig.sh
@@ -87,6 +87,8 @@
unzip -oj "$SUPPORT_LIBS_DOWNLOADED/support-lib.zip" -d "$SUPPORT_LIBS_UNPACKED"
unzip -oj "$SUPPORT_LIBS_DOWNLOADED/arch.zip" -d "$SUPPORT_LIBS_UNPACKED"
find "$CHECKOUT_DIR/prebuilts/maven_repo/android/com/android/support/" -type f -name "*design-*28.0.0*.aar" -exec cp '{}' -t "$SUPPORT_LIBS_UNPACKED" \;
+ cp "$CHECKOUT_DIR/prebuilts/maven_repo/android/com/android/support/multidex/1.0.3/multidex-1.0.3.aar" "$SUPPORT_LIBS_UNPACKED/multidex.aar"
+ cp "$CHECKOUT_DIR/prebuilts/maven_repo/android/com/android/support/multidex-instrumentation/1.0.3/multidex-instrumentation-1.0.3.aar" "$SUPPORT_LIBS_UNPACKED/multidex-instrumentation.aar"
find "$SUPPORT_LIBS_UNPACKED" -type f -name "jetifier*" -exec rm -f {} \;
}
diff --git a/jetifier/jetifier/preprocessor/src/main/kotlin/android/support/tools/jetifier/preprocessor/ConfigGenerator.kt b/jetifier/jetifier/preprocessor/src/main/kotlin/com/android/tools/build/jetifier/preprocessor/ConfigGenerator.kt
similarity index 86%
rename from jetifier/jetifier/preprocessor/src/main/kotlin/android/support/tools/jetifier/preprocessor/ConfigGenerator.kt
rename to jetifier/jetifier/preprocessor/src/main/kotlin/com/android/tools/build/jetifier/preprocessor/ConfigGenerator.kt
index 0e9f63a..1444203 100644
--- a/jetifier/jetifier/preprocessor/src/main/kotlin/android/support/tools/jetifier/preprocessor/ConfigGenerator.kt
+++ b/jetifier/jetifier/preprocessor/src/main/kotlin/com/android/tools/build/jetifier/preprocessor/ConfigGenerator.kt
@@ -1,25 +1,25 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.preprocessor
+package com.android.tools.build.jetifier.preprocessor
-import android.support.tools.jetifier.core.archive.Archive
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.config.ConfigParser
-import android.support.tools.jetifier.core.map.LibraryMapGenerator
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.config.ConfigParser
+import com.android.tools.build.jetifier.processor.archive.Archive
+import com.android.tools.build.jetifier.processor.type.LibraryMapGenerator
import java.io.File
import java.nio.file.Path
diff --git a/jetifier/jetifier/preprocessor/src/main/kotlin/android/support/tools/jetifier/preprocessor/Main.kt b/jetifier/jetifier/preprocessor/src/main/kotlin/com/android/tools/build/jetifier/preprocessor/Main.kt
similarity index 78%
rename from jetifier/jetifier/preprocessor/src/main/kotlin/android/support/tools/jetifier/preprocessor/Main.kt
rename to jetifier/jetifier/preprocessor/src/main/kotlin/com/android/tools/build/jetifier/preprocessor/Main.kt
index 58cb20f..ac35ab7 100644
--- a/jetifier/jetifier/preprocessor/src/main/kotlin/android/support/tools/jetifier/preprocessor/Main.kt
+++ b/jetifier/jetifier/preprocessor/src/main/kotlin/com/android/tools/build/jetifier/preprocessor/Main.kt
@@ -1,23 +1,23 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.preprocessor
+package com.android.tools.build.jetifier.preprocessor
-import android.support.tools.jetifier.core.config.ConfigParser
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.config.ConfigParser
+import com.android.tools.build.jetifier.core.utils.Log
import org.apache.commons.cli.CommandLine
import org.apache.commons.cli.DefaultParser
import org.apache.commons.cli.HelpFormatter
@@ -40,10 +40,12 @@
val OPTION_LOG_LEVEL = createOption("l", "Logging level. debug, verbose, default",
isRequired = false)
- private fun createOption(argName: String,
- desc: String,
- isRequired: Boolean = true,
- multiple: Boolean = false) : Option {
+ private fun createOption(
+ argName: String,
+ desc: String,
+ isRequired: Boolean = true,
+ multiple: Boolean = false
+ ): Option {
val op = Option(argName, true, desc)
op.isRequired = isRequired
if (multiple) {
@@ -54,7 +56,7 @@
}
}
- fun run(args : Array<String>) {
+ fun run(args: Array<String>) {
val cmd = parseCmdLine(args)
if (cmd == null) {
System.exit(1)
@@ -77,7 +79,7 @@
generator.generateMapping(config, inputLibraries, outputConfigPath)
}
- private fun parseCmdLine(args : Array<String>) : CommandLine? {
+ private fun parseCmdLine(args: Array<String>): CommandLine? {
try {
return DefaultParser().parse(OPTIONS, args)
} catch (e: ParseException) {
@@ -86,10 +88,8 @@
}
return null
}
-
}
-
-fun main(args : Array<String>) {
+fun main(args: Array<String>) {
Main().run(args)
}
\ No newline at end of file
diff --git a/jetifier/jetifier/processor/build.gradle b/jetifier/jetifier/processor/build.gradle
new file mode 100644
index 0000000..dbcc0ac
--- /dev/null
+++ b/jetifier/jetifier/processor/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 androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+
+import static androidx.build.dependencies.DependenciesKt.KOTLIN_STDLIB
+
+plugins {
+ id("SupportKotlinLibraryPlugin")
+}
+
+dependencies {
+ compile project(':jetifier-core')
+ compile("org.ow2.asm:asm:5.2")
+ compile("org.ow2.asm:asm-commons:5.2")
+ compile("org.jdom:jdom2:2.0.6")
+ compile(KOTLIN_STDLIB)
+ testCompile("junit:junit:4.12")
+ testCompile("com.google.truth:truth:0.34")
+}
+
+supportLibrary {
+ name = "Android Jetifier Processor"
+ publish = true
+ mavenVersion = LibraryVersions.JETIFIER
+ mavenGroup = LibraryGroups.JETIFIER
+ generateDocs = false
+ inceptionYear = "2018"
+ description = "Android Jetifier Processor"
+}
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/FileMapping.kt
similarity index 77%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/FileMapping.kt
index 67f6d84..d807aeb 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/FileMapping.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/FileMapping.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core
+package com.android.tools.build.jetifier.processor
import java.io.File
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/Processor.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt
similarity index 81%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/Processor.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt
index 61415da..c9eaf25 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/Processor.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/Processor.kt
@@ -1,35 +1,35 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core
+package com.android.tools.build.jetifier.processor
-import android.support.tools.jetifier.core.archive.Archive
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.archive.ArchiveItemVisitor
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.Transformer
-import android.support.tools.jetifier.core.transform.bytecode.ByteCodeTransformer
-import android.support.tools.jetifier.core.transform.metainf.MetaInfTransformer
-import android.support.tools.jetifier.core.transform.pom.PomDependency
-import android.support.tools.jetifier.core.transform.pom.PomDocument
-import android.support.tools.jetifier.core.transform.pom.PomScanner
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTransformer
-import android.support.tools.jetifier.core.transform.resource.XmlResourcesTransformer
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.archive.Archive
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.archive.ArchiveItemVisitor
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.android.tools.build.jetifier.processor.transform.Transformer
+import com.android.tools.build.jetifier.processor.transform.bytecode.ByteCodeTransformer
+import com.android.tools.build.jetifier.processor.transform.metainf.MetaInfTransformer
+import com.android.tools.build.jetifier.processor.transform.pom.PomDocument
+import com.android.tools.build.jetifier.processor.transform.pom.PomScanner
+import com.android.tools.build.jetifier.processor.transform.proguard.ProGuardTransformer
+import com.android.tools.build.jetifier.processor.transform.resource.XmlResourcesTransformer
import java.io.File
import java.io.FileNotFoundException
@@ -38,7 +38,7 @@
* the registered [Transformer]s over the set and creates new archives that will contain the
* transformed files.
*/
-class Processor private constructor (
+class Processor private constructor(
private val context: TransformationContext,
private val transformers: List<Transformer>
) : ArchiveItemVisitor {
@@ -49,7 +49,7 @@
/**
* Value of "restrictToPackagePrefixes" config for reversed jetification.
*/
- private val REVERSE_RESTRICT_TO_PACKAGE = listOf(
+ private val REVERSE_RESTRICT_TO_PACKAGE = setOf(
"androidx/",
"com/google/android/material/"
)
@@ -93,7 +93,7 @@
if (reversedMode) {
newConfig = Config(
restrictToPackagePrefixes = REVERSE_RESTRICT_TO_PACKAGE,
- rewriteRules = config.rewriteRules,
+ rulesMap = config.rulesMap.reverse().appendRules(config.slRules),
slRules = config.slRules,
pomRewriteRules = config.pomRewriteRules.map { it.getReversed() }.toSet(),
typesMap = config.typesMap.reverseMapOrDie(),
@@ -106,7 +106,7 @@
config = newConfig,
rewritingSupportLib = rewritingSupportLib,
isInReversedMode = reversedMode,
- useIdentityIfTypeIsMissing = useIdentityIfTypeIsMissing)
+ useFallbackIfTypeIsMissing = useIdentityIfTypeIsMissing)
val transformers = if (rewritingSupportLib) {
createSLTransformers(context)
} else {
@@ -149,7 +149,7 @@
if (context.errorsTotal() > 0) {
throw IllegalArgumentException("There were ${context.errorsTotal()}" +
- " errors found during the remapping. Check the logs for more details.")
+ " errors found during the remapping. Check the logs for more details.")
}
// TODO: Here we might need to modify the POM files if they point at a library that we have
@@ -160,11 +160,11 @@
// 5) Repackage the libraries back to archive files
val generatedLibraries = libraries
- .filter { copyUnmodifiedLibsAlso || it.wasChanged }
- .map {
- it.writeSelf()
- }
- .toSet()
+ .filter { copyUnmodifiedLibsAlso || it.wasChanged }
+ .map {
+ it.writeSelf()
+ }
+ .toSet()
if (copyUnmodifiedLibsAlso) {
return generatedLibraries
@@ -172,9 +172,9 @@
// 6) Create a set of files that should be removed (because they've been changed).
val filesToRemove = libraries
- .filter { it.wasChanged }
- .map { it.relativePath.toFile() }
- .toSet()
+ .filter { it.wasChanged }
+ .map { it.relativePath.toFile() }
+ .toSet()
return inputLibraries.minus(filesToRemove).plus(generatedLibraries)
}
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/TransformationResult.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/TransformationResult.kt
similarity index 81%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/TransformationResult.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/TransformationResult.kt
index 3e90483..cc7f422 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/TransformationResult.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/TransformationResult.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core
+package com.android.tools.build.jetifier.processor
import java.io.File
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/Archive.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/Archive.kt
similarity index 91%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/Archive.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/Archive.kt
index 3564ff3..448eb4d 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/Archive.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/Archive.kt
@@ -1,22 +1,22 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.archive
+package com.android.tools.build.jetifier.processor.archive
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.utils.Log
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileInputStream
@@ -35,9 +35,9 @@
* Represents an archive (zip, jar, aar ...)
*/
class Archive(
- override val relativePath: Path,
- val files: List<ArchiveItem>)
- : ArchiveItem {
+ override val relativePath: Path,
+ val files: List<ArchiveItem>
+) : ArchiveItem {
companion object {
/** Defines file extensions that are recognized as archives */
@@ -126,9 +126,9 @@
@Throws(IOException::class)
private fun extractArchive(
- inputStream: InputStream,
- relativePath: Path,
- recursive: Boolean
+ inputStream: InputStream,
+ relativePath: Path,
+ recursive: Boolean
): Archive {
val zipIn = ZipInputStream(inputStream)
val files = mutableListOf<ArchiveItem>()
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveFile.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveFile.kt
similarity index 60%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveFile.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveFile.kt
index 81c166e..06e332c 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveFile.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveFile.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.archive
+package com.android.tools.build.jetifier.processor.archive
import java.io.IOException
import java.io.OutputStream
@@ -55,8 +55,25 @@
fileName = relativePath.fileName.toString()
}
+ /**
+ * Sets new data while also marking this file as changed. This will result into the parent
+ * archive also being considered as changed thus marking it as dependent on the Support library.
+ */
fun setNewData(newData: ByteArray) {
data = newData
wasChanged = true
}
+
+ /**
+ * Sets a potentially new data without triggering a change. Useful in cases the change is not
+ * significant for the refactoring because it occurred due to some optimization or
+ * formatting change.
+ *
+ * If there was at least one genuine change in any file of the parent archive this won't prevent
+ * this file from being updated. However this will prevent the change to propagate to
+ * the parent archive which would otherwise mark it as dependent on the Support Library.
+ */
+ fun setNewDataSilently(newData: ByteArray) {
+ data = newData
+ }
}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItem.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveItem.kt
similarity index 84%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItem.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveItem.kt
index 16a8afa..9e188ca 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItem.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveItem.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.archive
+package com.android.tools.build.jetifier.processor.archive
import java.io.OutputStream
import java.nio.file.Path
@@ -39,7 +39,9 @@
val fileName: String
/**
- * Whether the item's content or its children were changed by Jetifier.
+ * Whether the item's content or its children were changed by Jetifier. This determines
+ * whether the parent archive is going to be marked as changed thus having a dependency on
+ * support.
*/
val wasChanged: Boolean
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItemVisitor.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveItemVisitor.kt
similarity index 76%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItemVisitor.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveItemVisitor.kt
index 7c99fd9..9e6a6ce 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/archive/ArchiveItemVisitor.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/archive/ArchiveItemVisitor.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.archive
+package com.android.tools.build.jetifier.processor.archive
/**
* Visitor for [ArchiveItem]
@@ -24,5 +24,4 @@
fun visit(archive: Archive)
fun visit(archiveFile: ArchiveFile)
-
}
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/TransformationContext.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/TransformationContext.kt
new file mode 100644
index 0000000..5c53e88
--- /dev/null
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/TransformationContext.kt
@@ -0,0 +1,87 @@
+/*
+ * 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 com.android.tools.build.jetifier.processor.transform
+
+import com.android.tools.build.jetifier.core.TypeRewriter
+import com.android.tools.build.jetifier.core.config.Config
+import java.util.regex.Pattern
+
+/**
+ * Context to share the transformation state between individual [Transformer]s.
+ */
+class TransformationContext(
+ val config: Config,
+ val rewritingSupportLib: Boolean = false,
+ val isInReversedMode: Boolean = false,
+ /**
+ * Whether to use fallback if type in our scope is missing instead of throwing an exception.
+ */
+ val useFallbackIfTypeIsMissing: Boolean = true
+) {
+
+ // Merges all packages prefixes into one regEx pattern
+ private val packagePrefixPattern = Pattern.compile(
+ "^(" + config.restrictToPackagePrefixes.map { "($it)" }.joinToString("|") + ").*$")
+
+ val typeRewriter: TypeRewriter = TypeRewriter(config, useFallbackIfTypeIsMissing)
+
+ /**
+ * Whether to skip verification of dependency version match in pom files.
+ */
+ val ignorePomVersionCheck = rewritingSupportLib || isInReversedMode
+
+ /** Counter for [reportNoMappingFoundFailure] calls. */
+ var mappingNotFoundFailuresCount = 0
+ private set
+
+ /** Counter for [reportNoProGuardMappingFoundFailure] calls. */
+ var proGuardMappingNotFoundFailuresCount = 0
+ private set
+
+ /** Counter for [reportNoPackageMappingFoundFailure] calls. */
+ var packageMappingNotFoundFailuresCounts = 0
+
+ var libraryName: String = ""
+
+ /** Total amount of errors found during the transformation process */
+ fun errorsTotal() = mappingNotFoundFailuresCount + proGuardMappingNotFoundFailuresCount +
+ packageMappingNotFoundFailuresCounts
+
+ /**
+ * Reports that there was a reference found that satisfies [isEligibleForRewrite] but no
+ * mapping was found to rewrite it.
+ */
+ fun reportNoMappingFoundFailure() {
+ mappingNotFoundFailuresCount++
+ }
+
+ /**
+ * Reports that there was a reference found in a ProGuard file that satisfies
+ * [isEligibleForRewrite] but no mapping was found to rewrite it.
+ */
+ fun reportNoProGuardMappingFoundFailure() {
+ proGuardMappingNotFoundFailuresCount++
+ }
+
+ /**
+ * Reports that there was a package reference found in a manifest file during a support library
+ * artifact rewrite but no mapping was found for it.
+ */
+ fun reportNoPackageMappingFoundFailure() {
+ packageMappingNotFoundFailuresCounts++
+ }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/Transformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/Transformer.kt
similarity index 75%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/Transformer.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/Transformer.kt
index 51335e2..3e9c460 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/Transformer.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/Transformer.kt
@@ -1,22 +1,22 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform
+package com.android.tools.build.jetifier.processor.transform
-import android.support.tools.jetifier.core.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
/**
* Interface to be implemented by any class that wants process files.
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/ByteCodeTransformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/ByteCodeTransformer.kt
similarity index 72%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/ByteCodeTransformer.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/ByteCodeTransformer.kt
index 9024ff1..553ac05 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/ByteCodeTransformer.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/ByteCodeTransformer.kt
@@ -1,24 +1,24 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.bytecode
+package com.android.tools.build.jetifier.processor.transform.bytecode
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.Transformer
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.android.tools.build.jetifier.processor.transform.Transformer
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
@@ -39,6 +39,7 @@
reader.accept(remapper.classRemapper, 0 /* flags */)
if (!remapper.changesDone) {
+ file.setNewDataSilently(writer.toByteArray())
return
}
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapper.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapper.kt
similarity index 72%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapper.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapper.kt
index 1b91088..9f39381 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapper.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapper.kt
@@ -1,22 +1,22 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.bytecode
+package com.android.tools.build.jetifier.processor.transform.bytecode
-import android.support.tools.jetifier.core.rules.JavaType
+import com.android.tools.build.jetifier.core.type.JavaType
/**
* High-level re-mapping interface to provide only the refactorings needed by jetifier.
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapperImpl.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImpl.kt
similarity index 67%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapperImpl.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImpl.kt
index dc400e3..aa769dd 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/CoreRemapperImpl.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImpl.kt
@@ -1,26 +1,26 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.bytecode
+package com.android.tools.build.jetifier.processor.transform.bytecode
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.bytecode.asm.CustomRemapper
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.android.tools.build.jetifier.processor.transform.bytecode.asm.CustomRemapper
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.commons.ClassRemapper
import java.nio.file.Path
@@ -45,22 +45,14 @@
val classRemapper = ClassRemapper(visitor, CustomRemapper(this))
override fun rewriteType(type: JavaType): JavaType {
- if (!context.isEligibleForRewrite(type)) {
- return type
- }
-
- val result = typesMap.mapType(type)
+ val result = context.typeRewriter.rewriteType(type)
if (result != null) {
changesDone = changesDone || result != type
- Log.i(TAG, " map: %s -> %s", type, result)
return result
}
- if (context.useIdentityIfTypeIsMissing) {
- Log.i(TAG, "No mapping for %s - using identity", type)
- } else {
+ if (!context.useFallbackIfTypeIsMissing) {
context.reportNoMappingFoundFailure()
- Log.e(TAG, "No mapping for: " + type)
}
return type
}
@@ -69,14 +61,14 @@
val startsWithAndroidX = context.isInReversedMode && value.startsWith("androidx")
val type = JavaType.fromDotVersion(value)
- if (!context.isEligibleForRewrite(type)) {
+ if (!context.typeRewriter.isEligibleForRewrite(type)) {
if (startsWithAndroidX) {
- Log.i(TAG, "Found string '%s' but failed to rewrite", value)
+ Log.i(TAG, "Found & ignored string '%s'", value)
}
return value
}
- val result = typesMap.mapType(type)
+ val result = context.config.typesMap.mapType(type)
if (result != null) {
changesDone = changesDone || result != type
Log.i(TAG, "Map string: '%s' -> '%s'", type, result)
@@ -86,14 +78,23 @@
// We might be working with an internal type or field reference, e.g.
// AccessibilityNodeInfoCompat.PANE_TITLE_KEY. So we try to remove last segment to help it.
if (value.contains(".")) {
- val subTypeResult = typesMap.mapType(type.getParentType())
+ val subTypeResult = context.config.typesMap.mapType(type.getParentType())
if (subTypeResult != null) {
val result = subTypeResult.toDotNotation() + '.' + value.substringAfterLast('.')
- Log.i(TAG, "Map string: '%s' -> '%s' via fallback", value, result)
+ Log.i(TAG, "Map string: '%s' -> '%s' via type fallback", value, result)
return result
}
}
+ // Try rewrite rules
+ if (context.useFallbackIfTypeIsMissing) {
+ val result = context.config.rulesMap.rewriteType(type)
+ if (result != null) {
+ Log.i(TAG, "Map string: '%s' -> '%s' via fallback", value, result)
+ return result.toDotNotation()
+ }
+ }
+
// We do not treat string content mismatches as errors
Log.i(TAG, "Found string '%s' but failed to rewrite", value)
return value
@@ -106,23 +107,16 @@
val owner = path.toFile().path.replace('\\', '/').removeSuffix(".class")
val type = JavaType(owner)
- if (!context.isEligibleForRewrite(type)) {
- return path
- }
-
val result = rewriteType(type)
if (result != type) {
changesDone = true
return path.fileSystem.getPath(result.fullName + ".class")
}
- if (context.useIdentityIfTypeIsMissing) {
- Log.i(TAG, "No mapping for: %s", type)
- return path
+ if (!context.useFallbackIfTypeIsMissing) {
+ context.reportNoMappingFoundFailure()
}
- context.reportNoMappingFoundFailure()
- Log.e(TAG, "No mapping for: %s", type)
return path
}
}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/asm/CustomRemapper.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/CustomRemapper.kt
similarity index 74%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/asm/CustomRemapper.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/CustomRemapper.kt
index a54e8bd..b3f096b 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/bytecode/asm/CustomRemapper.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/asm/CustomRemapper.kt
@@ -1,23 +1,23 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.bytecode.asm
+package com.android.tools.build.jetifier.processor.transform.bytecode.asm
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.transform.bytecode.CoreRemapper
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.processor.transform.bytecode.CoreRemapper
import org.objectweb.asm.commons.Remapper
/**
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
similarity index 80%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformer.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
index 333d776..146daf2 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformer.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
@@ -1,24 +1,24 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.metainf
+package com.android.tools.build.jetifier.processor.transform.metainf
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.Transformer
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.android.tools.build.jetifier.processor.transform.Transformer
import java.nio.charset.StandardCharsets
/**
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDependencyExtensions.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDependencyExtensions.kt
new file mode 100644
index 0000000..b56d896
--- /dev/null
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDependencyExtensions.kt
@@ -0,0 +1,39 @@
+/*
+ * 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 com.android.tools.build.jetifier.processor.transform.pom
+
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import org.jdom2.Document
+import org.jdom2.Element
+
+/**
+ * Transforms the current data into XML '<dependency>' node.
+ */
+fun PomDependency.toXmlElement(document: Document): Element {
+ val node = Element("dependency")
+ node.namespace = document.rootElement.namespace
+
+ XmlUtils.addStringNodeToNode(node, "groupId", groupId)
+ XmlUtils.addStringNodeToNode(node, "artifactId", artifactId)
+ XmlUtils.addStringNodeToNode(node, "version", version)
+ XmlUtils.addStringNodeToNode(node, "classifier", classifier)
+ XmlUtils.addStringNodeToNode(node, "type", type)
+ XmlUtils.addStringNodeToNode(node, "scope", scope)
+ XmlUtils.addStringNodeToNode(node, "systemPath", systemPath)
+ XmlUtils.addStringNodeToNode(node, "optional", optional)
+ return node
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomDocument.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDocument.kt
similarity index 90%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomDocument.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDocument.kt
index 1c10e02..ae9a296 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomDocument.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDocument.kt
@@ -1,24 +1,26 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.pom
+package com.android.tools.build.jetifier.processor.transform.pom
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import com.android.tools.build.jetifier.core.pom.PomRewriteRule
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
import org.jdom2.Document
import org.jdom2.Element
@@ -55,7 +57,7 @@
dependenciesGroup = document.rootElement
.getChild("dependencies", document.rootElement.namespace) ?: return
dependenciesGroup!!.children.mapTo(dependencies) {
- PomDependency.fromXmlElement(it, properties)
+ XmlUtils.createDependencyFrom(it, properties)
}
}
@@ -156,7 +158,7 @@
}
if (matchesPrefix) {
- if (context.useIdentityIfTypeIsMissing) {
+ if (context.useFallbackIfTypeIsMissing) {
Log.i(TAG, "No mapping found for '%s' - using identity",
dependency.toStringNotation())
} else {
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomScanner.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomScanner.kt
similarity index 77%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomScanner.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomScanner.kt
index 45b695c..ac15488 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/pom/PomScanner.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomScanner.kt
@@ -1,26 +1,26 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.pom
+package com.android.tools.build.jetifier.processor.transform.pom
-import android.support.tools.jetifier.core.archive.Archive
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.archive.ArchiveItemVisitor
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.archive.Archive
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.archive.ArchiveItemVisitor
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
/**
* Helper to scan [Archive]s to find their POM files.
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/XmlUtils.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/XmlUtils.kt
new file mode 100644
index 0000000..f6283b7
--- /dev/null
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/pom/XmlUtils.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright 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 com.android.tools.build.jetifier.processor.transform.pom
+
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import com.android.tools.build.jetifier.core.utils.Log
+import org.jdom2.Document
+import org.jdom2.Element
+import org.jdom2.input.SAXBuilder
+import org.jdom2.output.Format
+import org.jdom2.output.XMLOutputter
+import java.io.ByteArrayOutputStream
+import java.util.regex.Pattern
+
+/**
+ * Utilities for handling XML documents.
+ */
+class XmlUtils {
+
+ companion object {
+
+ private val variablePattern = Pattern.compile("\\$\\{([^}]*)}")
+
+ /** Saves the given [Document] to a new byte array */
+ fun convertDocumentToByteArray(document: Document): ByteArray {
+ val xmlOutput = XMLOutputter()
+ ByteArrayOutputStream().use {
+ xmlOutput.format = Format.getPrettyFormat()
+ xmlOutput.output(document, it)
+ return it.toByteArray()
+ }
+ }
+
+ /** Creates a new [Document] from the given [ByteArray] */
+ fun createDocumentFromByteArray(data: ByteArray): Document {
+ val builder = SAXBuilder()
+ data.inputStream().use {
+ return builder.build(it)
+ }
+ }
+
+ /**
+ * Creates a new XML element with the given [id] and text given in [value] and puts it under
+ * the given [parent]. Nothing is created if the [value] argument is null or empty.
+ */
+ fun addStringNodeToNode(parent: Element, id: String, value: String?) {
+ if (value.isNullOrEmpty()) {
+ return
+ }
+
+ val element = Element(id)
+ element.text = value
+ element.namespace = parent.namespace
+ parent.children.add(element)
+ }
+
+ fun resolveValue(value: String?, properties: Map<String, String>): String? {
+ if (value == null) {
+ return null
+ }
+
+ val matcher = variablePattern.matcher(value)
+ if (matcher.matches()) {
+ val variableName = matcher.group(1)
+ val varValue = properties[variableName]
+ if (varValue == null) {
+ Log.e("TAG", "Failed to resolve variable '%s'", value)
+ return value
+ }
+ return varValue
+ }
+
+ return value
+ }
+
+ /**
+ * Creates a new [PomDependency] from the given XML [Element].
+ */
+ fun createDependencyFrom(node: Element, properties: Map<String, String>): PomDependency {
+ var groupId: String? = null
+ var artifactId: String? = null
+ var version: String? = null
+ var classifier: String? = null
+ var type: String? = null
+ var scope: String? = null
+ var systemPath: String? = null
+ var optional: String? = null
+
+ for (childNode in node.children) {
+ when (childNode.name) {
+ "groupId" -> groupId = resolveValue(childNode.value, properties)
+ "artifactId" -> artifactId = resolveValue(childNode.value, properties)
+ "version" -> version = resolveValue(childNode.value, properties)
+ "classifier" -> classifier = resolveValue(childNode.value, properties)
+ "type" -> type = resolveValue(childNode.value, properties)
+ "scope" -> scope = resolveValue(childNode.value, properties)
+ "systemPath" -> systemPath = resolveValue(childNode.value, properties)
+ "optional" -> optional = resolveValue(childNode.value, properties)
+ }
+ }
+
+ return PomDependency(
+ groupId = groupId,
+ artifactId = artifactId,
+ version = version,
+ classifier = classifier,
+ type = type,
+ scope = scope,
+ systemPath = systemPath,
+ optional = optional)
+ }
+ }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardClassFilterParser.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardClassFilterParser.kt
similarity index 68%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardClassFilterParser.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardClassFilterParser.kt
index c431572..ba67b29 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardClassFilterParser.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardClassFilterParser.kt
@@ -1,30 +1,30 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
-import android.support.tools.jetifier.core.transform.proguard.patterns.GroupsReplacer
-import android.support.tools.jetifier.core.transform.proguard.patterns.PatternHelper
+import com.android.tools.build.jetifier.processor.transform.proguard.patterns.GroupsReplacer
+import com.android.tools.build.jetifier.processor.transform.proguard.patterns.PatternHelper
import java.util.regex.Pattern
/**
* Parses and rewrites ProGuard rules that contain class filters. See ProGuard documentation
* https://www.guardsquare.com/en/proguard/manual/usage#filters
*/
-class ProGuardClassFilterParser(private val mapper : ProGuardTypesMapper) {
+class ProGuardClassFilterParser(private val mapper: ProGuardTypesMapper) {
companion object {
private const val RULES = "(adaptclassstrings|dontnote|dontwarn)"
@@ -33,11 +33,11 @@
val replacer = GroupsReplacer(
pattern = PatternHelper.build("^ *-$RULES ⦅[^-]+⦆ *$", Pattern.MULTILINE),
groupsMap = listOf(
- { filter : String -> rewriteClassFilter(filter) }
+ { filter: String -> rewriteClassFilter(filter) }
)
)
- private fun rewriteClassFilter(classFilter: String) : String {
+ private fun rewriteClassFilter(classFilter: String): String {
return classFilter
.splitToSequence(",")
.filterNotNull()
@@ -47,7 +47,7 @@
.joinToString(separator = ", ")
}
- private fun replaceTypeInClassFilter(type: String) : String {
+ private fun replaceTypeInClassFilter(type: String): String {
if (!type.startsWith('!')) {
return mapper.replaceType(type)
}
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardClassSpecParser.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardClassSpecParser.kt
similarity index 69%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardClassSpecParser.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardClassSpecParser.kt
index 933ff08..b0487fb 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardClassSpecParser.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardClassSpecParser.kt
@@ -1,29 +1,29 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
-import android.support.tools.jetifier.core.transform.proguard.patterns.GroupsReplacer
-import android.support.tools.jetifier.core.transform.proguard.patterns.PatternHelper
+import com.android.tools.build.jetifier.processor.transform.proguard.patterns.GroupsReplacer
+import com.android.tools.build.jetifier.processor.transform.proguard.patterns.PatternHelper
/**
* Parses and rewrites ProGuard rules that contain class specification. See ProGuard documentation
* https://www.guardsquare.com/en/proguard/manual/usage#classspecification
*/
-class ProGuardClassSpecParser(private val mapper : ProGuardTypesMapper) {
+class ProGuardClassSpecParser(private val mapper: ProGuardTypesMapper) {
companion object {
private const val RULES = "(keep[a-z]*|whyareyoukeeping|assumenosideeffects)"
@@ -53,10 +53,10 @@
"-$RULES ($RULES_MODIFIERS )*(@⦅$ANNOTATION_TYPE⦆ )?($CLASS_MODIFIERS )*$CLASS_TYPES " +
"⦅$CLASS_NAME⦆( (extends|implements) ⦅$CLASS_NAME⦆)?+ *( *\\{⦅[^}]*⦆\\} *)?+"),
groupsMap = listOf(
- { annotation : String -> mapper.replaceType(annotation) },
- { className : String -> mapper.replaceType(className) },
- { className2 : String -> mapper.replaceType(className2) },
- { bodyGroup : String -> rewriteBodyGroup(bodyGroup) }
+ { annotation: String -> mapper.replaceType(annotation) },
+ { className: String -> mapper.replaceType(className) },
+ { className2: String -> mapper.replaceType(className2) },
+ { bodyGroup: String -> rewriteBodyGroup(bodyGroup) }
)
)
@@ -66,7 +66,7 @@
pattern = PatternHelper.build(
"^ *(@⦅$ANNOTATION_TYPE⦆ )?($FIELD_MODIFIERS )*<fields> *$"),
groupsMap = listOf(
- { annotation : String -> mapper.replaceType(annotation) }
+ { annotation: String -> mapper.replaceType(annotation) }
)),
// [@annotation] [[!]public|private|etc...] fieldType fieldName;
@@ -74,8 +74,8 @@
pattern = PatternHelper.build(
"^ *(@⦅$ANNOTATION_TYPE⦆ )?($FIELD_MODIFIERS )*(⦅$FIELD_TYPE⦆ $FIELD_NAME) *$"),
groupsMap = listOf(
- { annotation : String -> mapper.replaceType(annotation) },
- { fieldType : String -> mapper.replaceType(fieldType) }
+ { annotation: String -> mapper.replaceType(annotation) },
+ { fieldType: String -> mapper.replaceType(fieldType) }
)),
// [@annotation] [[!]public|private|etc...] <methods>;
@@ -83,7 +83,7 @@
pattern = PatternHelper.build(
"^ *(@⦅$ANNOTATION_TYPE⦆ )?($METHOD_MODIFIERS )*<methods> *$"),
groupsMap = listOf(
- { annotation : String -> mapper.replaceType(annotation) }
+ { annotation: String -> mapper.replaceType(annotation) }
)),
// [@annotation] [[!]public|private|etc...] className(argumentType,...));
@@ -91,33 +91,33 @@
pattern = PatternHelper.build(
"^ *(@⦅$ANNOTATION_TYPE⦆ )?($METHOD_MODIFIERS )*⦅$CLASS_NAME⦆ *\\(⦅$ARGS⦆\\) *$"),
groupsMap = listOf(
- { annotation : String -> mapper.replaceType(annotation) },
- { className : String -> mapper.replaceType(className) },
- { argsType : String -> mapper.replaceMethodArgs(argsType) }
- )
- ),
+ { annotation: String -> mapper.replaceType(annotation) },
+ { className: String -> mapper.replaceType(className) },
+ { argsType: String -> mapper.replaceMethodArgs(argsType) }
+ )),
// [@annotation] [[!]public|private|etc...] <init>(argumentType,...));
GroupsReplacer(
pattern = PatternHelper.build(
"^ *(@⦅$ANNOTATION_TYPE⦆ )?($METHOD_MODIFIERS )*<init> *\\(⦅$ARGS⦆\\) *$"),
groupsMap = listOf(
- { annotation : String -> mapper.replaceType(annotation) },
- { argsType : String -> mapper.replaceMethodArgs(argsType) }
+ { annotation: String -> mapper.replaceType(annotation) },
+ { argsType: String -> mapper.replaceMethodArgs(argsType) }
)),
// [@annotation] [[!]public|private|etc...] returnType methodName(argumentType,...));
GroupsReplacer(
- pattern = PatternHelper.build("^ *(@⦅$ANNOTATION_TYPE⦆ )?($METHOD_MODIFIERS )*" +
+ pattern = PatternHelper.build(
+ "^ *(@⦅$ANNOTATION_TYPE⦆ )?($METHOD_MODIFIERS )*" +
"⦅$RETURN_TYPE_NAME⦆ $METHOD_NAME *\\(⦅$ARGS⦆\\) *$"),
groupsMap = listOf(
- { annotation : String -> mapper.replaceType(annotation) },
- { returnType : String -> mapper.replaceType(returnType) },
- { argsType : String -> mapper.replaceMethodArgs(argsType) }
+ { annotation: String -> mapper.replaceType(annotation) },
+ { returnType: String -> mapper.replaceType(returnType) },
+ { argsType: String -> mapper.replaceMethodArgs(argsType) }
))
)
- private fun rewriteBodyGroup(bodyGroup: String) : String {
+ private fun rewriteBodyGroup(bodyGroup: String): String {
if (bodyGroup == "*" || bodyGroup == "**") {
return bodyGroup
}
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTransformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTransformer.kt
new file mode 100644
index 0000000..129df0b
--- /dev/null
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTransformer.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 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 com.android.tools.build.jetifier.processor.transform.proguard
+
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.android.tools.build.jetifier.processor.transform.Transformer
+import com.android.tools.build.jetifier.processor.transform.proguard.patterns.ReplacersRunner
+import java.nio.charset.StandardCharsets
+
+/**
+ * The [Transformer] responsible for ProGuard files refactoring.
+ */
+class ProGuardTransformer internal constructor(context: TransformationContext) : Transformer {
+
+ private val mapper = ProGuardTypesMapper(
+ context)
+
+ val replacer = ReplacersRunner(
+ listOf(
+ ProGuardClassSpecParser(mapper).replacer,
+ ProGuardClassFilterParser(mapper).replacer
+ ))
+
+ override fun canTransform(file: ArchiveFile): Boolean {
+ return file.isProGuardFile()
+ }
+
+ override fun runTransform(file: ArchiveFile) {
+ val content = StringBuilder(file.data.toString(StandardCharsets.UTF_8)).toString()
+ val result = replacer.applyReplacers(content)
+
+ if (result == content) {
+ return
+ }
+
+ file.setNewData(result.toByteArray())
+ }
+}
+
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMapper.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTypesMapper.kt
similarity index 74%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMapper.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTypesMapper.kt
index 779384e..9cc261b 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMapper.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTypesMapper.kt
@@ -1,23 +1,24 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.proguard.ProGuardType
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
/**
* Maps ProGuard types using [TypesMap] and [ProGuardTypesMap].
@@ -42,22 +43,11 @@
val javaType = type.toJavaType()
if (javaType != null) {
- // We are dealing with an explicit type definition
- if (!context.isEligibleForRewrite(javaType)) {
- return typeToReplace
- }
-
- val result = config.typesMap.mapType(javaType)
+ val result = context.typeRewriter.rewriteType(javaType)
if (result != null) {
- Log.i(TAG, " map: %s -> %s", type, result)
return result.toDotNotation()
}
- if (context.useIdentityIfTypeIsMissing) {
- Log.i(TAG, "No mapping for: %s - using identity")
- return typeToReplace
- }
-
context.reportNoProGuardMappingFoundFailure()
Log.e(TAG, "No mapping for: %s", type)
return typeToReplace
@@ -71,7 +61,7 @@
}
// Report error only when we are sure
- if (context.isEligibleForRewrite(type)) {
+ if (context.typeRewriter.isEligibleForRewrite(type)) {
context.reportNoProGuardMappingFoundFailure()
Log.e(TAG, "No mapping for: " + type)
}
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/GroupsReplacer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/GroupsReplacer.kt
similarity index 74%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/GroupsReplacer.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/GroupsReplacer.kt
index 6213a55..a76f104 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/GroupsReplacer.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/GroupsReplacer.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard.patterns
+package com.android.tools.build.jetifier.processor.transform.proguard.patterns
import java.util.regex.Matcher
import java.util.regex.Pattern
@@ -23,21 +23,23 @@
* Applies replacements on a matched string using the given [pattern] and its groups. Each group is
* mapped using a lambda from [groupsMap].
*/
-class GroupsReplacer(val pattern: Pattern,
- private val groupsMap: List<(String) -> String>) {
+class GroupsReplacer(
+ val pattern: Pattern,
+ private val groupsMap: List<(String) -> String>
+) {
/**
* Takes the given [matcher] and replace its matched groups using mapping functions given in
* [groupsMap].
*/
- fun runReplacements(matcher: Matcher) : String {
+ fun runReplacements(matcher: Matcher): String {
var result = matcher.group(0)
// We go intentionally backwards to replace using indexes
for (i in groupsMap.size - 1 downTo 0) {
val groupVal = matcher.group(i + 1) ?: continue
val localStart = matcher.start(i + 1) - matcher.start()
- val localEnd = matcher.end(i + 1) - matcher.start()
+ val localEnd = matcher.end(i + 1) - matcher.start()
result = result.replaceRange(
startIndex = localStart,
@@ -46,5 +48,4 @@
}
return result
}
-
}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/PatternHelper.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/PatternHelper.kt
similarity index 85%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/PatternHelper.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/PatternHelper.kt
index 3171185..faa46ab 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/PatternHelper.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/PatternHelper.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard.patterns
+package com.android.tools.build.jetifier.processor.transform.proguard.patterns
import java.util.regex.Pattern
@@ -42,7 +42,7 @@
* Transforms the given [toReplace] according to the rules defined in documentation of this
* class and compiles it to a [Pattern].
*/
- fun build(toReplace: String, flags : Int = 0) : Pattern {
+ fun build(toReplace: String, flags: Int = 0): Pattern {
var result = toReplace
result = result.replace("(?<!\\\\)\\(".toRegex(), "(?:")
rewrites.forEach { result = result.replace(it.first, it.second) }
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/ReplacersRunner.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/ReplacersRunner.kt
similarity index 89%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/ReplacersRunner.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/ReplacersRunner.kt
index bd2fc12..431ccbb 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/proguard/patterns/ReplacersRunner.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/patterns/ReplacersRunner.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard.patterns
+package com.android.tools.build.jetifier.processor.transform.proguard.patterns
/**
* Runs multiple [GroupsReplacer]s on given strings.
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/resource/XmlResourcesTransformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/resource/XmlResourcesTransformer.kt
similarity index 83%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/resource/XmlResourcesTransformer.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/resource/XmlResourcesTransformer.kt
index c7c6b1d..e78a11d 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/transform/resource/XmlResourcesTransformer.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/resource/XmlResourcesTransformer.kt
@@ -1,28 +1,28 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.resource
+package com.android.tools.build.jetifier.processor.transform.resource
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.rules.PackageName
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.Transformer
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.PackageName
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.android.tools.build.jetifier.processor.transform.Transformer
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.util.regex.Pattern
@@ -72,8 +72,6 @@
Pattern.compile("<manifest[^>]*package=\"([a-zA-Z0-9._]+)\"[^>]*>")
)
- private val typesMap = context.config.typesMap
-
override fun canTransform(file: ArchiveFile) = file.isXmlFile() && !file.isPomFile()
override fun runTransform(file: ArchiveFile) {
@@ -157,23 +155,15 @@
private fun rewriteType(typeName: String): String {
val type = JavaType.fromDotVersion(typeName)
- if (!context.isEligibleForRewrite(type)) {
- return typeName
- }
-
- val result = typesMap.mapType(type)
+ val result = context.typeRewriter.rewriteType(type)
if (result != null) {
- Log.i(TAG, " map: %s -> %s", type, result)
return result.toDotNotation()
}
- if (context.useIdentityIfTypeIsMissing) {
- Log.i(TAG, "No mapping for: %s - using identity", type)
- return typeName
+ if (!context.useFallbackIfTypeIsMissing) {
+ context.reportNoMappingFoundFailure()
}
- context.reportNoMappingFoundFailure()
- Log.e(TAG, "No mapping for: %s", type)
return typeName
}
@@ -183,7 +173,7 @@
if (result != null) {
return result.toDotNotation()
}
- if (context.useIdentityIfTypeIsMissing) {
+ if (context.useFallbackIfTypeIsMissing) {
Log.i(TAG, "No mapping for package: '%s' in artifact: '%s' - using identity",
pckg, archiveName)
return packageName
diff --git a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/LibraryMapGenerator.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/type/LibraryMapGenerator.kt
similarity index 76%
rename from jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/LibraryMapGenerator.kt
rename to jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/type/LibraryMapGenerator.kt
index c9d8f9f..3ad3cf0 100644
--- a/jetifier/jetifier/core/src/main/kotlin/android/support/tools/jetifier/core/map/LibraryMapGenerator.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/type/LibraryMapGenerator.kt
@@ -1,25 +1,26 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.map
+package com.android.tools.build.jetifier.processor.type
-import android.support.tools.jetifier.core.archive.Archive
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.archive.ArchiveItemVisitor
-import android.support.tools.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.archive.Archive
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.archive.ArchiveItemVisitor
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/type/MapGeneratorRemapper.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/type/MapGeneratorRemapper.kt
new file mode 100644
index 0000000..aa6f077
--- /dev/null
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/type/MapGeneratorRemapper.kt
@@ -0,0 +1,90 @@
+/*
+ * 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 com.android.tools.build.jetifier.processor.type
+
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.transform.bytecode.CoreRemapper
+import com.android.tools.build.jetifier.processor.transform.bytecode.asm.CustomRemapper
+import org.objectweb.asm.ClassVisitor
+import org.objectweb.asm.commons.ClassRemapper
+
+/**
+ * Hooks to asm remapping to collect data for [TypesMap] by applying all the [RewriteRule]s from the
+ * given [config] on all discovered and eligible types.
+ */
+class MapGeneratorRemapper(private val config: Config) : CoreRemapper {
+
+ companion object {
+ private const val TAG: String = "MapGeneratorRemapper"
+ }
+
+ private val typesRewritesMap = mutableMapOf<JavaType, JavaType>()
+
+ var isMapNotComplete = false
+ private set
+
+ fun createClassRemapper(visitor: ClassVisitor): ClassRemapper {
+ return ClassRemapper(visitor, CustomRemapper(this))
+ }
+
+ override fun rewriteType(type: JavaType): JavaType {
+ if (!isTypeSupported(type)) {
+ return type
+ }
+
+ if (typesRewritesMap.contains(type)) {
+ return type
+ }
+
+ // Try to find a rule
+ val typeToMap = type.getRootType()
+ for (rule in config.rulesMap.rewriteRules) {
+ val typeRewriteResult = rule.apply(typeToMap)
+ if (typeRewriteResult.isIgnored) {
+ Log.i(TAG, "Ignoring: " + typeToMap)
+ return typeToMap
+ }
+ if (typeRewriteResult.result == null) {
+ continue
+ }
+ typesRewritesMap.put(typeToMap, typeRewriteResult.result!!)
+ Log.i(TAG, " map: %s -> %s", typeToMap, typeRewriteResult.result)
+ return typeRewriteResult.result!!
+ }
+
+ isMapNotComplete = true
+ Log.e(TAG, "No rule for: " + typeToMap)
+ typesRewritesMap.put(typeToMap, typeToMap) // Identity
+ return typeToMap
+ }
+
+ override fun rewriteString(value: String): String {
+ // We don't build map from strings
+ return value
+ }
+
+ fun createTypesMap(): TypesMap {
+ return TypesMap(typesRewritesMap)
+ }
+
+ private fun isTypeSupported(type: JavaType): Boolean {
+ return config.restrictToPackagePrefixes.any { type.fullName.startsWith(it) }
+ }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/ChangeDetectionTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/ChangeDetectionTest.kt
similarity index 81%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/ChangeDetectionTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/ChangeDetectionTest.kt
index fb7a92d..92cb371 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/ChangeDetectionTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/ChangeDetectionTest.kt
@@ -1,31 +1,32 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core
+package com.android.tools.build.jetifier.processor
-import android.support.tools.jetifier.core.archive.Archive
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.rules.RewriteRule
-import android.support.tools.jetifier.core.transform.PackageMap
-import android.support.tools.jetifier.core.transform.pom.PomDependency
-import android.support.tools.jetifier.core.transform.pom.PomRewriteRule
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.PackageMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import com.android.tools.build.jetifier.core.pom.PomRewriteRule
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.archive.Archive
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
import com.google.common.truth.Truth
import org.junit.Test
import java.io.File
@@ -37,24 +38,15 @@
* was something to rewrite or not.
*/
class ChangeDetectionTest {
- private val emptyConfig = Config(
- restrictToPackagePrefixes = emptyList(),
- rewriteRules = emptyList(),
- slRules = emptyList(),
- pomRewriteRules = emptySet(),
- typesMap = TypesMap.EMPTY,
- proGuardMap = ProGuardTypesMap.EMPTY,
- packageMap = PackageMap.EMPTY
- )
private val prefRewriteConfig = Config(
- restrictToPackagePrefixes = listOf("android/support/v7/preference"),
- rewriteRules =
- listOf(
+ restrictToPackagePrefixes = setOf("android/support/v7/preference"),
+ rulesMap =
+ RewriteRulesMap(
RewriteRule(from = "android/support/v7/preference/Preference(.+)", to = "ignore"),
RewriteRule(from = "(.*)/R(.*)", to = "ignore")
),
- slRules = emptyList(),
+ slRules = setOf(),
pomRewriteRules = setOf(
PomRewriteRule(
PomDependency(
@@ -63,11 +55,11 @@
PomDependency(
groupId = "testGroup", artifactId = "testArtifact", version = "1.0")
)
- )),
- typesMap = TypesMap(mapOf(
+ )),
+ typesMap = TypesMap(
JavaType("android/support/v7/preference/Preference")
- to JavaType("android/test/pref/Preference")
- )),
+ to JavaType("android/test/pref/Preference")
+ ),
proGuardMap = ProGuardTypesMap.EMPTY,
packageMap = PackageMap.EMPTY
)
@@ -87,7 +79,7 @@
@Test
fun xmlRewrite_archiveNotChanged() {
testChange(
- config = emptyConfig,
+ config = Config.EMPTY,
fileContent =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<android.support.v7.preference.Preference/>",
@@ -112,7 +104,7 @@
@Test
fun proGuard_archiveNotChanged() {
testChange(
- config = emptyConfig,
+ config = Config.EMPTY,
fileContent =
"-keep public class * extends android.support.v7.preference.Preference { \n" +
" <fields>; \n" +
@@ -148,7 +140,7 @@
@Test
fun pom_archiveNotChanged() {
testChange(
- config = emptyConfig,
+ config = Config.EMPTY,
fileContent =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<project xmlns=\"http://maven.apache.org/POM/4.0.0\" " +
@@ -186,7 +178,7 @@
val inputFile = File(javaClass.getResource(inputClassPath).file)
testChange(
- config = emptyConfig,
+ config = Config.EMPTY,
file = ArchiveFile(Paths.get("/", "preference.class"), inputFile.readBytes()),
areChangesExpected = false
)
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/map/MapGenerationTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/map/MapGenerationTest.kt
similarity index 85%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/map/MapGenerationTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/map/MapGenerationTest.kt
index 9f56676..653f9f6 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/map/MapGenerationTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/map/MapGenerationTest.kt
@@ -1,25 +1,28 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.map
+package com.android.tools.build.jetifier.processor.map
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.rules.RewriteRule
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.type.MapGeneratorRemapper
import com.google.common.truth.Truth
import org.junit.Test
@@ -30,8 +33,7 @@
.testThatRules(
RewriteRule("android/support/v7/(.*)", "android/test/{0}")
)
- .withAllowedPrefixes(
- "android/support/"
+ .withAllowedPrefixes("android/support/"
)
.forGivenTypes(
JavaType("android/support/v7/pref/Preference")
@@ -137,31 +139,27 @@
object ScanTester {
- fun testThatRules(vararg rules: RewriteRule) = Step1(rules.toList())
+ fun testThatRules(vararg rules: RewriteRule) = Step1(rules.toSet())
+ class Step1(private val rules: Set<RewriteRule>) {
- class Step1(private val rules: List<RewriteRule>) {
+ fun withAllowedPrefixes(vararg prefixes: String) = Step2(rules, prefixes.toSet())
- fun withAllowedPrefixes(vararg prefixes: String) = Step2(rules, prefixes.toList())
-
-
- class Step2(private val rules: List<RewriteRule>, private val prefixes: List<String>) {
+ class Step2(private val rules: Set<RewriteRule>, private val prefixes: Set<String>) {
private val allTypes: MutableList<JavaType> = mutableListOf()
private var wasMapIncomplete = false
-
fun forGivenTypes(vararg types: JavaType): Step2 {
allTypes.addAll(types)
return this
}
-
fun mapInto(types: Map<String, String>): Step2 {
val config = Config(
restrictToPackagePrefixes = prefixes,
- rewriteRules = rules,
- slRules = emptyList(),
+ rulesMap = RewriteRulesMap(rules),
+ slRules = emptySet(),
pomRewriteRules = emptySet(),
typesMap = TypesMap.EMPTY,
proGuardMap = ProGuardTypesMap.EMPTY)
@@ -184,9 +182,7 @@
Truth.assertThat(wasMapIncomplete).isFalse()
}
}
-
}
-
}
}
diff --git a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/DependencyMappingTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/DependencyMappingTest.kt
new file mode 100644
index 0000000..bc70508
--- /dev/null
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/DependencyMappingTest.kt
@@ -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 com.android.tools.build.jetifier.processor.transform
+
+import com.android.tools.build.jetifier.core.PackageMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import com.android.tools.build.jetifier.core.pom.PomRewriteRule
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.Processor
+import com.google.common.truth.Truth
+import org.junit.Test
+
+class DependencyMappingTest {
+
+ @Test fun mapTest_oneToOne_shouldMap() {
+ MappingTester.testRewrite(
+ from = "hello:world:1.0.0",
+ to = setOf("hi:all:2.0.0"),
+ rules = setOf(
+ PomRewriteRule(
+ from = PomDependency(groupId = "hello", artifactId = "world"),
+ to = setOf(
+ PomDependency(groupId = "hi", artifactId = "all", version = "2.0.0")
+ )
+ ))
+ )
+ }
+
+ @Test fun mapTest_oneToTwo_shouldMap() {
+ MappingTester.testRewrite(
+ from = "hello:world:1.0.0",
+ to = setOf("hi:all:2.0.0", "hey:all:3.0.0"),
+ rules = setOf(
+ PomRewriteRule(
+ from = PomDependency(groupId = "hello", artifactId = "world"),
+ to = setOf(
+ PomDependency(groupId = "hi", artifactId = "all", version = "2.0.0"),
+ PomDependency(groupId = "hey", artifactId = "all", version = "3.0.0")
+ )
+ ))
+ )
+ }
+
+ @Test fun mapTest_oneToNone_shouldMapToEmpty() {
+ MappingTester.testRewrite(
+ from = "hello:world:1.0.0",
+ to = setOf(),
+ rules = setOf(
+ PomRewriteRule(
+ from = PomDependency(groupId = "hello", artifactId = "world"),
+ to = setOf()
+ ))
+ )
+ }
+
+ @Test fun mapTest_oneToNull_ruleNotFound_returnNull() {
+ MappingTester.testRewrite(
+ from = "hello:world:1.0.0",
+ to = null,
+ rules = setOf(
+ PomRewriteRule(
+ from = PomDependency(groupId = "hello", artifactId = "me", version = "1.0"),
+ to = setOf()
+ ))
+ )
+ }
+
+ object MappingTester {
+
+ fun testRewrite(from: String, to: Set<String>?, rules: Set<PomRewriteRule>) {
+ val config = Config(
+ restrictToPackagePrefixes = emptySet(),
+ rulesMap = RewriteRulesMap.EMPTY,
+ slRules = emptySet(),
+ pomRewriteRules = rules,
+ typesMap = TypesMap.EMPTY,
+ proGuardMap = ProGuardTypesMap.EMPTY,
+ packageMap = PackageMap.EMPTY
+ )
+
+ val processor = Processor.createProcessor(config)
+ val result = processor.mapDependency(from)
+
+ if (to == null) {
+ Truth.assertThat(result).isNull()
+ } else {
+ Truth.assertThat(result).containsExactlyElementsIn(to)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/bytecode/ClassFilesMoveTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/ClassFilesMoveTest.kt
similarity index 75%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/bytecode/ClassFilesMoveTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/ClassFilesMoveTest.kt
index 1bebfc4..63a7d1e 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/bytecode/ClassFilesMoveTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/ClassFilesMoveTest.kt
@@ -1,31 +1,32 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.bytecode
+package com.android.tools.build.jetifier.processor.transform.bytecode
-import android.support.tools.jetifier.core.FileMapping
-import android.support.tools.jetifier.core.Processor
-import android.support.tools.jetifier.core.archive.Archive
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.archive.ArchiveItemVisitor
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.rules.RewriteRule
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.FileMapping
+import com.android.tools.build.jetifier.processor.Processor
+import com.android.tools.build.jetifier.processor.archive.Archive
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.archive.ArchiveItemVisitor
import com.google.common.truth.Truth
import org.junit.Test
import java.io.File
@@ -37,13 +38,13 @@
companion object {
private val TEST_CONFIG = Config(
- restrictToPackagePrefixes = listOf("android/support"),
- rewriteRules = listOf(
+ restrictToPackagePrefixes = setOf("android/support"),
+ rulesMap = RewriteRulesMap(
RewriteRule("android/support/annotation/(.*)", "ignore"),
RewriteRule("android/support/v7/preference/R(.*)", "ignore"),
RewriteRule("android/support/v4/(.*)", "ignore")
),
- slRules = listOf(
+ slRules = setOf(
RewriteRule("android/support/annotation/(.*)", "ignore"),
RewriteRule("android/support/v7/preference/R(.*)", "ignore"),
RewriteRule("android/support/v4/(.*)", "ignore")
@@ -51,19 +52,19 @@
pomRewriteRules = emptySet(),
typesMap = TypesMap(mapOf(
"android/support/v7/preference/Preference"
- to "androidx/support/preference/Preference",
+ to "androidx/support/preference/Preference",
"android/support/v7/preference/TwoStatePreference"
- to "androidx/support/preference/TwoStatePreference",
+ to "androidx/support/preference/TwoStatePreference",
"android/support/v7/preference/PreferenceGroup"
- to "androidx/support/preference/PreferenceGroup",
+ to "androidx/support/preference/PreferenceGroup",
"android/support/v7/preference/PreferenceViewHolder"
- to "androidx/support/preference/PreferenceViewHolder",
+ to "androidx/support/preference/PreferenceViewHolder",
"android/support/v7/preference/PreferenceManager"
- to "androidx/support/preference/PreferenceManager",
+ to "androidx/support/preference/PreferenceManager",
"android/support/v14/preference/SwitchPreference"
- to "androidx/support/preference/SwitchPreference",
+ to "androidx/support/preference/SwitchPreference",
"android/support/v7/preference/PreferenceDataStore"
- to "androidx/support/preference/PreferenceDataStore"
+ to "androidx/support/preference/PreferenceDataStore"
).map { JavaType(it.key) to JavaType(it.value) }.toMap()),
proGuardMap = ProGuardTypesMap.EMPTY
)
@@ -91,8 +92,7 @@
val resultFiles = processor.transform(setOf(FileMapping(inputFile, expectedFile)))
Truth.assertThat(resultFiles).hasSize(1)
- testArchivesAreSame(resultFiles.first(),
- File(javaClass.getResource(expectedZipPath).file))
+ testArchivesAreSame(resultFiles.first(), File(javaClass.getResource(expectedZipPath).file))
tempDir.delete()
}
@@ -114,8 +114,7 @@
val resultFiles = processor.transform(setOf(FileMapping(inputFile, expectedFile)))
Truth.assertThat(resultFiles).hasSize(1)
- testArchivesAreSame(resultFiles.first(),
- File(javaClass.getResource(expectedZipPath).file))
+ testArchivesAreSame(resultFiles.first(), File(javaClass.getResource(expectedZipPath).file))
tempDir.delete()
}
@@ -130,8 +129,7 @@
val inputZipPath = "/fileRenameTest/inputTestLib.zip"
// Transform forward
- val processor = Processor.createProcessor(TEST_CONFIG,
- rewritingSupportLib = true)
+ val processor = Processor.createProcessor(TEST_CONFIG, rewritingSupportLib = true)
val inputFile = File(javaClass.getResource(inputZipPath).file)
val tempDir = createTempDir()
@@ -140,15 +138,15 @@
val resultFiles = processor.transform(setOf(FileMapping(inputFile, expectedFile)))
// Take previous result & reverse it
- val processor2 = Processor.createProcessor(TEST_CONFIG,
+ val processor2 = Processor.createProcessor(
+ TEST_CONFIG,
rewritingSupportLib = true,
reversedMode = true)
val expectedFile2 = File(createTempDir(), "test2.zip")
val resultFiles2 = processor2.transform(setOf(
- FileMapping(resultFiles.first(), expectedFile2)))
+ FileMapping(resultFiles.first(), expectedFile2)))
- testArchivesAreSame(resultFiles2.first(),
- File(javaClass.getResource(inputZipPath).file))
+ testArchivesAreSame(resultFiles2.first(), File(javaClass.getResource(inputZipPath).file))
tempDir.delete()
}
@@ -169,8 +167,7 @@
val resultFiles = processor.transform(setOf(FileMapping(inputFile, expectedFile)))
Truth.assertThat(resultFiles).hasSize(1)
- testArchivesAreSame(resultFiles.first(),
- File(javaClass.getResource(inputZipPath).file))
+ testArchivesAreSame(resultFiles.first(), File(javaClass.getResource(inputZipPath).file))
tempDir.delete()
}
diff --git a/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImplTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImplTest.kt
new file mode 100644
index 0000000..1c23991
--- /dev/null
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/bytecode/CoreRemapperImplTest.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 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 com.android.tools.build.jetifier.processor.transform.bytecode
+
+import com.android.tools.build.jetifier.core.PackageMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.objectweb.asm.ClassWriter
+
+class CoreRemapperImplTest {
+
+ @Test
+ fun remapString_shouldUseFallbackForField() {
+ val remapper = prepareRemapper(
+ TypesMap(mapOf(
+ JavaType.fromDotVersion("androidx.test.InputConnectionCompat")
+ to JavaType.fromDotVersion(
+ "android.support.test.InputConnectionCompat")
+ )),
+ "androidx/")
+
+ val given = "androidx.test.InputConnectionCompat.CONTENT_URI"
+ val expected = "android.support.test.InputConnectionCompat.CONTENT_URI"
+
+ Truth.assertThat(remapper.rewriteString(given)).isEqualTo(expected)
+ }
+
+ private fun prepareRemapper(
+ typesMap: TypesMap,
+ restrictToPackagePrefix: String? = null
+ ): CoreRemapperImpl {
+ val prefixes = if (restrictToPackagePrefix == null) {
+ emptySet()
+ } else {
+ setOf(restrictToPackagePrefix)
+ }
+
+ val config = Config(
+ restrictToPackagePrefixes = prefixes,
+ rulesMap = RewriteRulesMap.EMPTY,
+ typesMap = typesMap,
+ slRules = emptySet(),
+ pomRewriteRules = emptySet(),
+ proGuardMap = ProGuardTypesMap.EMPTY,
+ packageMap = PackageMap.EMPTY)
+
+ val context = TransformationContext(config, isInReversedMode = true)
+
+ val writer = ClassWriter(0 /* flags */)
+ return CoreRemapperImpl(context, writer)
+ }
+}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformerTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformerTest.kt
similarity index 76%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformerTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformerTest.kt
index c88c08d..4db1a6e 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/metainf/MetaInfTransformerTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformerTest.kt
@@ -1,27 +1,24 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.metainf
+package com.android.tools.build.jetifier.processor.transform.metainf
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.transform.PackageMap
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
import com.google.common.truth.Truth
import org.junit.Test
import java.nio.charset.Charset
@@ -99,16 +96,7 @@
expectedCanTransform: Boolean = true,
rewritingSupportLib: Boolean = true
) {
- val config = Config(
- restrictToPackagePrefixes = emptyList(),
- rewriteRules = emptyList(),
- slRules = emptyList(),
- pomRewriteRules = emptySet(),
- packageMap = PackageMap.EMPTY,
- typesMap = TypesMap.EMPTY,
- proGuardMap = ProGuardTypesMap.EMPTY
- )
- val context = TransformationContext(config,
+ val context = TransformationContext(Config.EMPTY,
rewritingSupportLib = rewritingSupportLib,
isInReversedMode = reverseMode)
val transformer = MetaInfTransformer(context)
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomDocumentTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDocumentTest.kt
similarity index 88%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomDocumentTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDocumentTest.kt
index 567097d..a805f56 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomDocumentTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomDocumentTest.kt
@@ -1,28 +1,30 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.pom
+package com.android.tools.build.jetifier.processor.transform.pom
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.config.ConfigParser
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.transform.PackageMap
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.PackageMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import com.android.tools.build.jetifier.core.pom.PomRewriteRule
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
import com.google.common.truth.Truth
import org.junit.Test
import java.nio.charset.StandardCharsets
@@ -75,7 +77,7 @@
PomRewriteRule(
PomDependency(
groupId = "supportGroup", artifactId = "supportArtifact",
- version = "4.0"),
+ version = "4.0"),
setOf(
PomDependency(
groupId = "testGroup", artifactId = "testArtifact",
@@ -101,7 +103,7 @@
PomRewriteRule(
PomDependency(
groupId = "supportGroup", artifactId = "supportArtifact",
- version = "4.0"),
+ version = "4.0"),
setOf(
PomDependency(
groupId = "testGroup", artifactId = "testArtifact",
@@ -126,7 +128,7 @@
PomRewriteRule(
PomDependency(
groupId = "supportGroup", artifactId = "supportArtifact2",
- version = "4.0"),
+ version = "4.0"),
setOf(
PomDependency(
groupId = "testGroup", artifactId = "testArtifact",
@@ -173,7 +175,7 @@
PomRewriteRule(
PomDependency(
groupId = "supportGroup", artifactId = "supportArtifact",
- version = "4.0"),
+ version = "4.0"),
setOf(
PomDependency(
groupId = "testGroup", artifactId = "testArtifact",
@@ -211,7 +213,7 @@
PomRewriteRule(
PomDependency(
groupId = "supportGroup", artifactId = "supportArtifact",
- version = "4.0"),
+ version = "4.0"),
setOf(
PomDependency(
groupId = "testGroup", artifactId = "testArtifact",
@@ -256,7 +258,7 @@
PomRewriteRule(
PomDependency(
groupId = "supportGroup", artifactId = "supportArtifact",
- version = "4.0"),
+ version = "4.0"),
setOf(
PomDependency(
groupId = "testGroup", artifactId = "testArtifact",
@@ -269,7 +271,7 @@
PomRewriteRule(
PomDependency(
groupId = "supportGroup", artifactId = "supportArtifact2",
- version = "4.0"),
+ version = "4.0"),
setOf(
PomDependency(
groupId = "testGroup", artifactId = "testArtifact",
@@ -314,7 +316,7 @@
PomRewriteRule(
PomDependency(
groupId = "supportGroup", artifactId = "supportArtifact",
- version = "4.0"),
+ version = "4.0"),
setOf(
PomDependency(
groupId = "testGroup", artifactId = "testArtifact",
@@ -362,17 +364,17 @@
Truth.assertThat(dependency.artifactId).isEqualTo("supportArtifact")
}
-
private fun testRewriteToTheSame(givenAndExpectedXml: String, rules: Set<PomRewriteRule>) {
testRewrite(givenAndExpectedXml, givenAndExpectedXml, rules)
}
- private fun testRewrite(givenXml: String, expectedXml : String, rules: Set<PomRewriteRule>) {
+ private fun testRewrite(givenXml: String, expectedXml: String, rules: Set<PomRewriteRule>) {
val given =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<project xmlns=\"http://maven.apache.org/POM/4.0.0\" " +
- "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
- "xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" +
+ "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
+ "xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 " +
+ "http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" +
" <!-- Some comment -->\n" +
" <groupId>test.group</groupId>\n" +
" <artifactId>test.artifact.id</artifactId>\n" +
@@ -383,8 +385,9 @@
var expected =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<project xmlns=\"http://maven.apache.org/POM/4.0.0\" " +
- "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
- "xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" +
+ "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
+ "xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 " +
+ "http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" +
" <!-- Some comment -->\n" +
" <groupId>test.group</groupId>\n" +
" <artifactId>test.artifact.id</artifactId>\n" +
@@ -395,13 +398,13 @@
val file = ArchiveFile(Paths.get("pom.xml"), given.toByteArray())
val pomDocument = PomDocument.loadFrom(file)
val config = Config(
- restrictToPackagePrefixes = emptyList(),
- rewriteRules = emptyList(),
- typesMap = TypesMap.EMPTY,
- slRules = emptyList(),
- pomRewriteRules = rules,
- proGuardMap = ProGuardTypesMap.EMPTY,
- packageMap = PackageMap.EMPTY);
+ restrictToPackagePrefixes = emptySet(),
+ rulesMap = RewriteRulesMap.EMPTY,
+ typesMap = TypesMap.EMPTY,
+ slRules = emptySet(),
+ pomRewriteRules = rules,
+ proGuardMap = ProGuardTypesMap.EMPTY,
+ packageMap = PackageMap.EMPTY)
val context = TransformationContext(config)
pomDocument.applyRules(context)
pomDocument.saveBackToFileIfNeeded()
@@ -420,12 +423,13 @@
Truth.assertThat(strResult).isEqualTo(expected)
}
- private fun loadDocument(givenXml : String) : PomDocument {
+ private fun loadDocument(givenXml: String): PomDocument {
val given =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<project xmlns=\"http://maven.apache.org/POM/4.0.0\" " +
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
- "xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" +
+ "xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 " +
+ "http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n" +
" <!-- Some comment -->\n" +
" <groupId>test.group</groupId>\n" +
" <artifactId>test.artifact.id</artifactId>\n" +
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteInZipTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomRewriteInZipTest.kt
similarity index 78%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteInZipTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomRewriteInZipTest.kt
index 77dc196..ac95d1c 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteInZipTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomRewriteInZipTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 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.
@@ -14,15 +14,18 @@
* limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.pom
+package com.android.tools.build.jetifier.processor.transform.pom
-import android.support.tools.jetifier.core.FileMapping
-import android.support.tools.jetifier.core.Processor
-import android.support.tools.jetifier.core.archive.Archive
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import com.android.tools.build.jetifier.core.pom.PomRewriteRule
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.FileMapping
+import com.android.tools.build.jetifier.processor.Processor
+import com.android.tools.build.jetifier.processor.archive.Archive
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
import com.google.common.truth.Truth
import org.junit.Test
import java.io.File
@@ -34,16 +37,16 @@
companion object {
private val TEST_CONFIG = Config(
- restrictToPackagePrefixes = listOf("com/sample"),
- rewriteRules = listOf(),
- slRules = listOf(),
+ restrictToPackagePrefixes = setOf("com/sample"),
+ rulesMap = RewriteRulesMap.EMPTY,
+ slRules = setOf(),
pomRewriteRules = setOf(
PomRewriteRule(
from = PomDependency(
groupId = "old.group",
artifactId = "myOldArtifact",
version = "0.1.0"),
- to = setOf(
+ to = setOf(
PomDependency(
groupId = "com.sample.my.group",
artifactId = "myArtifact",
@@ -94,9 +97,9 @@
val inputZipPath = "/pomRefactorTest/pomTest.zip"
val processor = Processor.createProcessor(
- TEST_CONFIG,
- rewritingSupportLib = false,
- reversedMode = true)
+ TEST_CONFIG,
+ rewritingSupportLib = false,
+ reversedMode = true)
val inputFile = File(javaClass.getResource(inputZipPath).file)
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteRuleTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomRewriteRuleTest.kt
similarity index 91%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteRuleTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomRewriteRuleTest.kt
index 94ed6e3..43cc1a7 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/pom/PomRewriteRuleTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/pom/PomRewriteRuleTest.kt
@@ -1,21 +1,23 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.pom
+package com.android.tools.build.jetifier.processor.transform.pom
+import com.android.tools.build.jetifier.core.pom.PomDependency
+import com.android.tools.build.jetifier.core.pom.PomRewriteRule
import com.google.common.truth.Truth
import org.junit.Test
@@ -138,5 +140,4 @@
Truth.assertThat(rule.validateVersion(pom)).isFalse()
}
-
}
\ No newline at end of file
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassFilterTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassFilterTest.kt
similarity index 92%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassFilterTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassFilterTest.kt
index 86b1599..25c4af6 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassFilterTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassFilterTest.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
import org.junit.Test
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest.kt
similarity index 95%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest.kt
index dc3e470..45eaa91 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
import org.junit.Test
@@ -85,8 +85,10 @@
"-keep allowshrinking class support.Activity \n" +
"-keep allowoptimization class support.Activity \n" +
"-keep allowobfuscation class support.Activity \n" +
- "-keep allowshrinking allowoptimization allowobfuscation class support.Activity \n" +
- "-keep allowshrinking allowoptimization allowobfuscation class support.Activity"
+ "-keep allowshrinking allowoptimization allowobfuscation class support.Activity" +
+ " \n" +
+ "-keep allowshrinking allowoptimization allowobfuscation class " +
+ "support.Activity"
)
.rewritesTo(
"-keep includedescriptorclasses class test.Activity \n" +
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_FieldTypeSelector.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_FieldTypeSelector.kt
similarity index 95%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_FieldTypeSelector.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_FieldTypeSelector.kt
index d08bbbe..f388e88 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_FieldTypeSelector.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_FieldTypeSelector.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
import org.junit.Test
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_FieldsSelector.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_FieldsSelector.kt
similarity index 93%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_FieldsSelector.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_FieldsSelector.kt
index 2a0f752..e637308 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_FieldsSelector.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_FieldsSelector.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
import org.junit.Test
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_MethodInitSelector.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_MethodInitSelector.kt
similarity index 97%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_MethodInitSelector.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_MethodInitSelector.kt
index d681bda..ee58fd1 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_MethodInitSelector.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_MethodInitSelector.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
import org.junit.Test
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_MethodSelectorWithReturnType.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_MethodSelectorWithReturnType.kt
similarity index 97%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_MethodSelectorWithReturnType.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_MethodSelectorWithReturnType.kt
index 1cee765..dde9ad2 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_MethodSelectorWithReturnType.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_MethodSelectorWithReturnType.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
import org.junit.Test
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_NamedCtorSelector.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_NamedCtorSelector.kt
similarity index 96%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_NamedCtorSelector.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_NamedCtorSelector.kt
index 671be61..825a027 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ClassSpecTest_NamedCtorSelector.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ClassSpecTest_NamedCtorSelector.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
import org.junit.Test
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTester.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTester.kt
similarity index 75%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTester.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTester.kt
index 7d13826..a4b38b5 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTester.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTester.kt
@@ -1,27 +1,30 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.rules.RewriteRule
-import android.support.tools.jetifier.core.transform.TransformationContext
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.proguard.ProGuardType
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRule
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
import com.google.common.truth.Truth
import java.nio.charset.StandardCharsets
import java.nio.file.Paths
@@ -34,10 +37,10 @@
private var javaTypes = emptyList<Pair<String, String>>()
private var rewriteRules = emptyList<Pair<String, String>>()
private var proGuardTypes = emptyList<Pair<ProGuardType, ProGuardType>>()
- private var prefixes = emptyList<String>()
+ private var prefixes = emptySet<String>()
fun forGivenPrefixes(vararg prefixes: String): ProGuardTester {
- this.prefixes = prefixes.toList()
+ this.prefixes = prefixes.toSet()
return this
}
@@ -53,8 +56,8 @@
fun forGivenProGuardMap(vararg rules: Pair<String, String>): ProGuardTester {
this.proGuardTypes = rules.map {
- ProGuardType.fromDotNotation(it.first) to ProGuardType.fromDotNotation(it.second) }
- .toList()
+ ProGuardType.fromDotNotation(it.first) to ProGuardType.fromDotNotation(it.second)
+ }.toList()
return this
}
@@ -73,8 +76,10 @@
private fun createConfig(): Config {
return Config(
restrictToPackagePrefixes = prefixes,
- rewriteRules = rewriteRules.map { RewriteRule(it.first, it.second) },
- slRules = emptyList(),
+ rulesMap = RewriteRulesMap(rewriteRules
+ .map { RewriteRule(it.first, it.second) }
+ .toSet()),
+ slRules = emptySet(),
pomRewriteRules = emptySet(),
typesMap = TypesMap(
types = javaTypes.map { JavaType(it.first) to JavaType(it.second) }.toMap()
@@ -100,7 +105,7 @@
class ProGuardTesterForType(private val config: Config, private val given: String) {
fun getsRewrittenTo(expectedType: String) {
- val context = TransformationContext(config, useIdentityIfTypeIsMissing = false)
+ val context = TransformationContext(config, useFallbackIfTypeIsMissing = false)
val mapper = ProGuardTypesMapper(context)
val result = mapper.replaceType(given)
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMapperTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTypesMapperTest.kt
similarity index 96%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMapperTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTypesMapperTest.kt
index 3249d0a..1d1ffd9 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProGuardTypesMapperTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProGuardTypesMapperTest.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
import org.junit.Test
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProguardSamplesTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProguardSamplesTest.kt
similarity index 97%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProguardSamplesTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProguardSamplesTest.kt
index f9554f1..1bbcb7b 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/proguard/ProguardSamplesTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/proguard/ProguardSamplesTest.kt
@@ -1,20 +1,20 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.proguard
+package com.android.tools.build.jetifier.processor.transform.proguard
import org.junit.Test
diff --git a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/resource/XmlResourcesTransformerTest.kt b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/resource/XmlResourcesTransformerTest.kt
similarity index 86%
rename from jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/resource/XmlResourcesTransformerTest.kt
rename to jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/resource/XmlResourcesTransformerTest.kt
index 3549559..36718a9 100644
--- a/jetifier/jetifier/core/src/test/kotlin/android/support/tools/jetifier/core/transform/resource/XmlResourcesTransformerTest.kt
+++ b/jetifier/jetifier/processor/src/test/kotlin/com/android/tools/build/jetifier/processor/transform/resource/XmlResourcesTransformerTest.kt
@@ -1,28 +1,29 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.core.transform.resource
+package com.android.tools.build.jetifier.processor.transform.resource
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.rules.JavaType
-import android.support.tools.jetifier.core.map.TypesMap
-import android.support.tools.jetifier.core.transform.PackageMap
-import android.support.tools.jetifier.core.transform.TransformationContext
-import android.support.tools.jetifier.core.transform.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.PackageMap
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.proguard.ProGuardTypesMap
+import com.android.tools.build.jetifier.core.rule.RewriteRulesMap
+import com.android.tools.build.jetifier.core.type.JavaType
+import com.android.tools.build.jetifier.core.type.TypesMap
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.transform.TransformationContext
import com.google.common.truth.Truth
import org.junit.Test
import java.nio.charset.Charset
@@ -35,7 +36,7 @@
givenAndExpectedXml =
"<android.support.v7.preference.Preference>\n" +
"</android.support.v7.preference.Preference>",
- prefixes = listOf(),
+ prefixes = setOf(),
map = mapOf(
"android/support/v7/preference/Preference" to "android/test/pref/Preference"
)
@@ -47,7 +48,7 @@
givenAndExpectedXml =
"<android.support.v7.preference.Preference>\n" +
"</android.support.v7.preference.Preference>",
- prefixes = listOf("android/support/v7/"),
+ prefixes = setOf("android/support/v7/"),
map = mapOf(),
errorsExpected = true
)
@@ -58,7 +59,7 @@
givenAndExpectedXml =
"<android.support.v7.preference.Preference>\n" +
"</android.support.v7.preference.Preference>",
- prefixes = listOf("android/support/v14/"),
+ prefixes = setOf("android/support/v14/"),
map = mapOf(
"android/support/v7/preference/Preference" to "android/test/pref/Preference"
)
@@ -70,7 +71,7 @@
givenAndExpectedXml =
"<my.android.support.v7.preference.Preference>\n" +
"</my.android.support.v7.preference.Preference>",
- prefixes = listOf("android/support/v7/"),
+ prefixes = setOf("android/support/v7/"),
map = mapOf(
"android/support/v7/preference/Preference" to "android/test/pref/Preference"
)
@@ -82,7 +83,7 @@
givenAndExpectedXml =
"<android.support.v7.preference.Preference>\n" +
"</android.support.v7.preference.Preference>",
- prefixes = listOf("android/support/"),
+ prefixes = setOf("android/support/"),
map = mapOf(
"android/support2/v7/preference/Preference" to "android/test/pref/Preference"
),
@@ -96,7 +97,7 @@
"<android.support.v7.preference.Preference/>",
expectedXml =
"<android.test.pref.Preference/>",
- prefixes = listOf("android/support/"),
+ prefixes = setOf("android/support/"),
typesMap = mapOf(
"android/support/v7/preference/Preference" to "android/test/pref/Preference"
)
@@ -111,7 +112,7 @@
expectedXml =
"<android.test.pref.Preference \n" +
" someAttribute=\"android.support.v7.preference.Preference\"/>",
- prefixes = listOf("android/support/"),
+ prefixes = setOf("android/support/"),
typesMap = mapOf(
"android/support/v7/preference/Preference" to "android/test/pref/Preference"
)
@@ -126,7 +127,7 @@
expectedXml =
"<android.test.pref.Preference>\n" +
"</android.test.pref.Preference>",
- prefixes = listOf("android/support/"),
+ prefixes = setOf("android/support/"),
typesMap = mapOf(
"android/support/v7/preference/Preference" to "android/test/pref/Preference"
)
@@ -139,7 +140,7 @@
"<view class=\"android.support.v7.preference.Preference\">",
expectedXml =
"<view class=\"android.test.pref.Preference\">",
- prefixes = listOf("android/support/"),
+ prefixes = setOf("android/support/"),
typesMap = mapOf(
"android/support/v7/preference/Preference" to "android/test/pref/Preference"
)
@@ -156,7 +157,7 @@
"<view notRelated=\"true\" " +
" class=\"android.test.pref.Preference\"" +
" ignoreMe=\"android.support.v7.preference.Preference\">",
- prefixes = listOf("android/support/"),
+ prefixes = setOf("android/support/"),
typesMap = mapOf(
"android/support/v7/preference/Preference" to "android/test/pref/Preference"
)
@@ -167,7 +168,7 @@
testRewriteToTheSame(
givenAndExpectedXml =
"<test attribute=\"view\" class=\"android.support.v7.preference.Preference\">",
- prefixes = listOf("android/support/"),
+ prefixes = setOf("android/support/"),
map = mapOf(
"android/support/v7/preference/Preference" to "android/test/pref/Preference"
)
@@ -182,7 +183,7 @@
expectedXml =
"<android.support.v7.preference.Preference>\n" +
"</android.support.v7.preference.Preference>",
- prefixes = listOf("android/support/"),
+ prefixes = setOf("android/support/"),
typesMap = mapOf(
"android/support/v7/preference/Preference"
to "android/support/v7/preference/Preference"
@@ -216,7 +217,7 @@
"</android.test.pref.Preference>\n" +
"\n" +
"<android.test.pref.ListPref/>",
- prefixes = listOf(
+ prefixes = setOf(
"android/support/v7/",
"android/support/v14/"
),
@@ -245,7 +246,7 @@
" package=\"androidx.preference\">\n" +
" <uses-sdk android:minSdkVersion=\"14\"/>\n" +
"</manifest>",
- prefixes = listOf(
+ prefixes = setOf(
),
typesMap = mapOf(
),
@@ -262,16 +263,16 @@
@Test fun manifestFile_packageRewrite_chooseBasedOnArtifactName() {
testRewrite(
givenXml =
- "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
" package=\"androidx.preference\">\n" +
" <uses-sdk android:minSdkVersion=\"14\"/>\n" +
"</manifest>",
expectedXml =
- "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
" package=\"android.support.v14.preference\">\n" +
" <uses-sdk android:minSdkVersion=\"14\"/>\n" +
"</manifest>",
- prefixes = listOf(
+ prefixes = setOf(
),
typesMap = mapOf(
),
@@ -293,7 +294,7 @@
private fun testRewriteToTheSame(
givenAndExpectedXml: String,
- prefixes: List<String>,
+ prefixes: Set<String>,
map: Map<String, String>,
errorsExpected: Boolean = false
) {
@@ -304,7 +305,7 @@
private fun testRewrite(
givenXml: String,
expectedXml: String,
- prefixes: List<String>,
+ prefixes: Set<String>,
typesMap: Map<String, String>,
packageMap: PackageMap = PackageMap.EMPTY,
rewritingSupportLib: Boolean = false,
@@ -323,8 +324,8 @@
val typeMap = TypesMap(typesMap.map { JavaType(it.key) to JavaType(it.value) }.toMap())
val config = Config(
restrictToPackagePrefixes = prefixes,
- rewriteRules = emptyList(),
- slRules = emptyList(),
+ rulesMap = RewriteRulesMap.EMPTY,
+ slRules = emptySet(),
pomRewriteRules = emptySet(),
typesMap = typeMap,
proGuardMap = ProGuardTypesMap.EMPTY,
@@ -333,7 +334,7 @@
val context = TransformationContext(
config,
rewritingSupportLib = rewritingSupportLib,
- useIdentityIfTypeIsMissing = false)
+ useFallbackIfTypeIsMissing = false)
context.libraryName = libraryName
val processor = XmlResourcesTransformer(context)
val fileName = if (isManifestFile) {
diff --git a/jetifier/jetifier/core/src/test/resources/changeDetectionTest/testPreference.class b/jetifier/jetifier/processor/src/test/resources/changeDetectionTest/testPreference.class
similarity index 100%
rename from jetifier/jetifier/core/src/test/resources/changeDetectionTest/testPreference.class
rename to jetifier/jetifier/processor/src/test/resources/changeDetectionTest/testPreference.class
Binary files differ
diff --git a/jetifier/jetifier/core/src/test/resources/fileRenameTest/expectedTestLib.zip b/jetifier/jetifier/processor/src/test/resources/fileRenameTest/expectedTestLib.zip
similarity index 100%
rename from jetifier/jetifier/core/src/test/resources/fileRenameTest/expectedTestLib.zip
rename to jetifier/jetifier/processor/src/test/resources/fileRenameTest/expectedTestLib.zip
Binary files differ
diff --git a/jetifier/jetifier/core/src/test/resources/fileRenameTest/expectedTestLibNested.zip b/jetifier/jetifier/processor/src/test/resources/fileRenameTest/expectedTestLibNested.zip
similarity index 100%
rename from jetifier/jetifier/core/src/test/resources/fileRenameTest/expectedTestLibNested.zip
rename to jetifier/jetifier/processor/src/test/resources/fileRenameTest/expectedTestLibNested.zip
Binary files differ
diff --git a/jetifier/jetifier/core/src/test/resources/fileRenameTest/inputTestLib.zip b/jetifier/jetifier/processor/src/test/resources/fileRenameTest/inputTestLib.zip
similarity index 100%
rename from jetifier/jetifier/core/src/test/resources/fileRenameTest/inputTestLib.zip
rename to jetifier/jetifier/processor/src/test/resources/fileRenameTest/inputTestLib.zip
Binary files differ
diff --git a/jetifier/jetifier/core/src/test/resources/fileRenameTest/inputTestLibNested.zip b/jetifier/jetifier/processor/src/test/resources/fileRenameTest/inputTestLibNested.zip
similarity index 100%
rename from jetifier/jetifier/core/src/test/resources/fileRenameTest/inputTestLibNested.zip
rename to jetifier/jetifier/processor/src/test/resources/fileRenameTest/inputTestLibNested.zip
Binary files differ
diff --git a/jetifier/jetifier/core/src/test/resources/pomRefactorTest/pomTest.zip b/jetifier/jetifier/processor/src/test/resources/pomRefactorTest/pomTest.zip
similarity index 100%
rename from jetifier/jetifier/core/src/test/resources/pomRefactorTest/pomTest.zip
rename to jetifier/jetifier/processor/src/test/resources/pomRefactorTest/pomTest.zip
Binary files differ
diff --git a/jetifier/jetifier/source-transformer/rewriteAll.sh b/jetifier/jetifier/source-transformer/rewriteAll.sh
new file mode 100755
index 0000000..c402d5e
--- /dev/null
+++ b/jetifier/jetifier/source-transformer/rewriteAll.sh
@@ -0,0 +1,7 @@
+set -e
+work_path="$PWD"
+
+script_path="$(cd $(dirname $0) && pwd)"
+
+"$script_path/rewriteMake.py"
+"$script_path/rewritePackageNames.py"
diff --git a/jetifier/jetifier/source-transformer/rewriteMake.py b/jetifier/jetifier/source-transformer/rewriteMake.py
new file mode 100755
index 0000000..5954a2f
--- /dev/null
+++ b/jetifier/jetifier/source-transformer/rewriteMake.py
@@ -0,0 +1,112 @@
+#!/usr/bin/python
+
+import csv, subprocess
+
+class StringBuilder(object):
+ def __init__(self, item=None):
+ self.items = []
+ if item is not None:
+ self.add(item)
+
+ def add(self, item):
+ self.items.append(str(item))
+ return self
+
+ def __str__(self):
+ return "".join(self.items)
+
+target_map = """android-slices-builders,androidx.slice_slice-builders
+android-slices-core,androidx.slice_slice-core
+android-slices-view,androidx.slice_slice-view
+android-support-animatedvectordrawable,androidx.vectordrawable_vectordrawable-animated
+android-support-annotations,androidx.annotation_annotation
+android-support-asynclayoutinflater,androidx.asynclayoutinflater_asynclayoutinflater
+android-support-car,androidx.car_car
+android-support-collections,androidx.collection_collection
+android-support-compat,androidx.core_core
+android-support-contentpaging,androidx.contentpaging_contentpaging
+android-support-coordinatorlayout,androidx.coordinatorlayout_coordinatorlayout
+android-support-core-ui,androidx.legacy_legacy-support-core-ui
+android-support-core-utils,androidx.legacy_legacy-support-core-utils
+android-support-cursoradapter,androidx.cursoradapter_cursoradapter
+android-support-customtabs,androidx.browser_browser
+android-support-customview,androidx.customview_customview
+android-support-documentfile,androidx.documentfile_documentfile
+android-support-drawerlayout,androidx.drawerlayout_drawerlayout
+android-support-dynamic-animation,androidx.dynamicanimation_dynamicanimation
+android-support-emoji,androidx.emoji_emoji
+android-support-emoji-appcompat,androidx.emoji_emoji-appcompat
+android-support-emoji-bundled,androidx.emoji_emoji-bundled
+android-support-exifinterface,androidx.exifinterface_exifinterface
+android-support-fragment,androidx.fragment_fragment
+android-support-heifwriter,androidx.heifwriter_heifwriter
+android-support-interpolator,androidx.interpolator_interpolator
+android-support-loader,androidx.loader_loader
+android-support-localbroadcastmanager,androidx.localbroadcastmanager_localbroadcastmanager
+android-support-media-compat,androidx.media_media
+android-support-percent,androidx.percentlayout_percentlayout
+android-support-print,androidx.print_print
+android-support-recommendation,androidx.recommendation_recommendation
+android-support-recyclerview-selection,androidx.recyclerview_recyclerview-selection
+android-support-slidingpanelayout,androidx.slidingpanelayout_slidingpanelayout
+android-support-swiperefreshlayout,androidx.swiperefreshlayout_swiperefreshlayout
+android-support-textclassifier,androidx.textclassifier_textclassifier
+android-support-transition,androidx.transition_transition
+android-support-tv-provider,androidx.tvprovider_tvprovider
+android-support-v13,androidx.legacy_legacy-support-v13
+android-support-v14-preference,androidx.legacy_legacy-preference-v14
+android-support-v17-leanback,androidx.leanback_leanback
+android-support-v17-preference-leanback,androidx.leanback_leanback-preference
+android-support-v4,androidx.legacy_legacy-support-v4
+android-support-v7-appcompat,androidx.appcompat_appcompat
+android-support-v7-cardview,androidx.cardview_cardview
+android-support-v7-gridlayout,androidx.gridlayout_gridlayout
+android-support-v7-mediarouter,androidx.mediarouter_mediarouter
+android-support-v7-palette,androidx.palette_palette
+android-support-v7-preference,androidx.preference_preference
+android-support-v7-recyclerview,androidx.recyclerview_recyclerview
+android-support-vectordrawable,androidx.vectordrawable_vectordrawable
+android-support-viewpager,androidx.viewpager_viewpager
+android-support-wear,androidx.wear_wear
+android-support-webkit,androidx.webkit_webkit
+android-arch-core-common,androidx.arch.core_core-common
+android-arch-core-runtime,androidx.arch.core_core-runtime
+android-arch-lifecycle-common,androidx.lifecycle_lifecycle-common
+android-arch-lifecycle-common-java8,androidx.lifecycle_lifecycle-common-java8
+android-arch-lifecycle-extensions,androidx.lifecycle_lifecycle-extensions
+android-arch-lifecycle-livedata,androidx.lifecycle_lifecycle-livedata
+android-arch-lifecycle-livedata-core,androidx.lifecycle_lifecycle-livedata-core
+android-arch-lifecycle-runtime,androidx.lifecycle_lifecycle-runtime
+android-arch-lifecycle-viewmodel,androidx.lifecycle_lifecycle-viewmodel
+android-arch-paging-common,androidx.paging_paging-common
+android-arch-paging-runtime,androidx.paging_paging-runtime
+android-arch-persistence-db,androidx.sqlite_sqlite
+android-arch-persistence-db-framework,androidx.sqlite_sqlite-framework
+android-arch-room-common,androidx.room_room-common
+android-arch-room-migration,androidx.room_room-migration
+android-arch-room-runtime,androidx.room_room-runtime
+android-arch-room-testing,androidx.room_room-testing
+$(ANDROID_SUPPORT_DESIGN_TARGETS),androidx.design_design"""
+
+reader = csv.reader(target_map.split('\n'), delimiter=',')
+
+rewriterTextBuilder = StringBuilder()
+for row in reader:
+ rewriterTextBuilder.add("s|").add(row[0]).add("|").add(row[1]).add("|g\n")
+scriptPath = "/tmp/jetifier-make-sed-script.txt"
+print("Writing " + scriptPath)
+with open(scriptPath, 'w') as scriptFile:
+ scriptFile.write(str(rewriterTextBuilder))
+
+rewriteCommand = "time find . -name out -prune -o -name .git -prune -o -name .repo -prune -o -iregex '.*\.mk' -print | xargs -n 1 --no-run-if-empty -P 64 sed -i -f /tmp/jetifier-make-sed-script.txt"
+
+print("""
+Will run command:
+
+""" + rewriteCommand + """
+
+""")
+response = raw_input("Ok? [y/n]")
+if response == "y":
+ subprocess.check_output(rewriteCommand, shell=True)
+
diff --git a/jetifier/jetifier/source-transformer/rewritePackageNames.py b/jetifier/jetifier/source-transformer/rewritePackageNames.py
new file mode 100755
index 0000000..5cfea14
--- /dev/null
+++ b/jetifier/jetifier/source-transformer/rewritePackageNames.py
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+
+# This script does package renamings on source code (and .xml files and build files, etc), as part of migrating code to Jetpack.
+# For example, this script may replace the text
+# import android.support.annotation.RequiresApi;
+# with
+# import androidx.annotation.RequiresApi;
+
+# See also b/74074903
+import json
+import os.path
+import subprocess
+
+class StringBuilder(object):
+ def __init__(self, item=None):
+ self.items = []
+ if item is not None:
+ self.add(item)
+
+ def add(self, item):
+ self.items.append(str(item))
+ return self
+
+ def __str__(self):
+ return "".join(self.items)
+
+class ExecutionConfig(object):
+ """Stores configuration about this execution of the package renaming.
+ For example, file paths of the source code.
+ This config could potentially be affected by command-line arguments.
+ """
+ def __init__(self, jetifierConfig, sourceRoots, excludeDirs):
+ self.jetifierConfig = jetifierConfig
+ self.sourceRoots = sourceRoots
+ self.excludeDirs = excludeDirs
+
+
+class SourceRewriteRule(object):
+ def __init__(self, fromName, toName):
+ self.fromName = fromName
+ self.toName = toName
+
+ def serialize(self):
+ return self.fromName + ":" + self.toName
+
+class JetifierConfig(object):
+ """Stores configuration about the renaming itself, such as package rename rules.
+ This config isn't supposed to be affected by command-line arguments.
+ """
+ @staticmethod
+ def parse(filePath):
+ with open(filePath) as f:
+ lines = f.readlines()
+ nonCommentLines = [line for line in lines if not line.strip().startswith("#")]
+ parsed = json.loads("".join(nonCommentLines))
+ return JetifierConfig(parsed)
+
+ def __init__(self, parsedJson):
+ self.json = parsedJson
+
+ def getTypesMap(self):
+ rules = []
+ for rule in self.json["rules"]:
+ fromName = rule["from"].replace("/", ".").replace("(.*)", "")
+ toName = rule["to"].replace("/", ".").replace("{0}", "")
+ if not toName.startswith("ignore"):
+ rules.append(SourceRewriteRule(fromName, toName))
+
+ return rules
+
+
+def createRewriteCommand(executionConfig):
+ # create command to find source files
+ finderTextBuilder = StringBuilder("find")
+ for sourceRoot in executionConfig.sourceRoots:
+ finderTextBuilder.add(" ").add(sourceRoot)
+ for exclusion in executionConfig.excludeDirs:
+ finderTextBuilder.add(" -name ").add(exclusion).add(" -prune -o")
+ finderTextBuilder.add(" -iregex '.*\.java\|.*\.xml' -print")
+
+ # create command to rewrite one source
+ print("Building sed instructions")
+ rewriterTextBuilder = StringBuilder()
+ rewriteRules = executionConfig.jetifierConfig.getTypesMap()
+ for rule in rewriteRules:
+ rewriterTextBuilder.add("s|").add(rule.fromName).add("|").add(rule.toName).add("|g\n")
+ scriptPath = "/tmp/jetifier-sed-script.txt"
+ print("Writing " + scriptPath)
+ with open(scriptPath, 'w') as scriptFile:
+ scriptFile.write(str(rewriterTextBuilder))
+
+ # create the command to do the rewrites
+ fullCommand = "time " + str(finderTextBuilder) + " | xargs -n 1 --no-run-if-empty -P 64 sed -i -f " + scriptPath
+
+ return fullCommand
+
+def processConfig(executionConfig):
+ print("Building rewrite command")
+ rewriteCommand = createRewriteCommand(executionConfig)
+ commandLength = len(rewriteCommand)
+ print("""
+Will run command:
+
+""" + rewriteCommand + """
+
+""")
+ response = raw_input("Ok? [y/n]")
+ if response == "y":
+ subprocess.check_output(rewriteCommand, shell=True)
+
+
+def main():
+ pathOfThisFile = os.path.realpath(__file__)
+ jetifierPath = os.path.abspath(os.path.join(pathOfThisFile, "..", ".."))
+
+ jetifierConfigPath = os.path.join(jetifierPath, "core", "src", "main", "resources", "default.config")
+ print("Parsing " + jetifierConfigPath)
+ jetifierConfig = JetifierConfig.parse(jetifierConfigPath)
+
+ sourceRoot = os.getcwd()
+ excludeDirs = ["out", ".git", ".repo"]
+
+ executionConfig = ExecutionConfig(jetifierConfig, [sourceRoot], excludeDirs)
+
+ processConfig(executionConfig)
+
+main()
+
+
diff --git a/jetifier/jetifier/standalone/build.gradle b/jetifier/jetifier/standalone/build.gradle
index eed727c..ded5432 100644
--- a/jetifier/jetifier/standalone/build.gradle
+++ b/jetifier/jetifier/standalone/build.gradle
@@ -19,10 +19,10 @@
id("application")
}
-mainClassName = "android.support.tools.jetifier.standalone.MainKt"
+mainClassName = "com.android.tools.build.jetifier.standalone.Main"
dependencies {
- compile project(':jetifier-core')
+ compile project(':jetifier-processor')
compile group: 'commons-cli', name: 'commons-cli', version: '1.3.1'
}
diff --git a/jetifier/jetifier/standalone/src/main/kotlin/android/support/tools/jetifier/standalone/Main.kt b/jetifier/jetifier/standalone/src/main/kotlin/com/android/tools/build/jetifier/standalone/Main.kt
similarity index 82%
rename from jetifier/jetifier/standalone/src/main/kotlin/android/support/tools/jetifier/standalone/Main.kt
rename to jetifier/jetifier/standalone/src/main/kotlin/com/android/tools/build/jetifier/standalone/Main.kt
index e426621..132b90c 100644
--- a/jetifier/jetifier/standalone/src/main/kotlin/android/support/tools/jetifier/standalone/Main.kt
+++ b/jetifier/jetifier/standalone/src/main/kotlin/com/android/tools/build/jetifier/standalone/Main.kt
@@ -1,26 +1,26 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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
+ * 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
+ * limitations under the License.
*/
-package android.support.tools.jetifier.standalone
+package com.android.tools.build.jetifier.standalone
-import android.support.tools.jetifier.core.FileMapping
-import android.support.tools.jetifier.core.Processor
-import android.support.tools.jetifier.core.config.Config
-import android.support.tools.jetifier.core.config.ConfigParser
-import android.support.tools.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.core.config.Config
+import com.android.tools.build.jetifier.core.config.ConfigParser
+import com.android.tools.build.jetifier.core.utils.Log
+import com.android.tools.build.jetifier.processor.FileMapping
+import com.android.tools.build.jetifier.processor.Processor
import org.apache.commons.cli.CommandLine
import org.apache.commons.cli.DefaultParser
import org.apache.commons.cli.HelpFormatter
@@ -39,21 +39,21 @@
val OPTIONS = Options()
val OPTION_INPUT = createOption("i", "Input libraries paths", multiple = true)
val OPTION_OUTPUT_DIR = createOption("outputdir", "Output directory path",
- isRequired = false)
+ isRequired = false)
val OPTION_OUTPUT_FILE = createOption("outputfile", "Output file", isRequired = false)
val OPTION_CONFIG = createOption("c", "Input config path", isRequired = false)
val OPTION_LOG_LEVEL = createOption("l", "Logging level. debug, verbose, error, info " +
- "(default)", isRequired = false)
+ "(default)", isRequired = false)
val OPTION_REVERSED = createOption("r", "Run reversed process", hasArgs = false,
- isRequired = false)
+ isRequired = false)
val OPTION_REWRITE_SUPPORT_LIB = createOption("s", "If set, all libraries being rewritten" +
- " are assumed to be part of Support Library. Otherwise only general dependencies " +
- " are expected.", hasArgs = false, isRequired = false)
+ " are assumed to be part of Support Library. Otherwise only general dependencies " +
+ " are expected.", hasArgs = false, isRequired = false)
val OPTION_STRICT = createOption("strict",
- "Don't fallback in case rules are missing", hasArgs = false, isRequired = false)
+ "Don't fallback in case rules are missing", hasArgs = false, isRequired = false)
val OPTION_REBUILD_TOP_OF_TREE = createOption("rebuildTopOfTree",
- "Rebuild the zip of maven distribution according to the generated pom file",
- hasArgs = false, isRequired = false)
+ "Rebuild the zip of maven distribution according to the generated pom file",
+ hasArgs = false, isRequired = false)
private fun createOption(
argName: String,
@@ -70,6 +70,10 @@
OPTIONS.addOption(op)
return op
}
+
+ @JvmStatic fun main(args: Array<String>) {
+ Main().run(args)
+ }
}
fun run(args: Array<String>) {
@@ -156,6 +160,3 @@
}
}
-fun main(args: Array<String>) {
- Main().run(args)
-}
\ No newline at end of file
diff --git a/jetifier/jetifier/standalone/src/main/kotlin/android/support/tools/jetifier/standalone/TopOfTreeBuilder.kt b/jetifier/jetifier/standalone/src/main/kotlin/com/android/tools/build/jetifier/standalone/TopOfTreeBuilder.kt
similarity index 76%
rename from jetifier/jetifier/standalone/src/main/kotlin/android/support/tools/jetifier/standalone/TopOfTreeBuilder.kt
rename to jetifier/jetifier/standalone/src/main/kotlin/com/android/tools/build/jetifier/standalone/TopOfTreeBuilder.kt
index 893ee65..aac3fc2 100644
--- a/jetifier/jetifier/standalone/src/main/kotlin/android/support/tools/jetifier/standalone/TopOfTreeBuilder.kt
+++ b/jetifier/jetifier/standalone/src/main/kotlin/com/android/tools/build/jetifier/standalone/TopOfTreeBuilder.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package android.support.tools.jetifier.standalone
+package com.android.tools.build.jetifier.standalone
-import android.support.tools.jetifier.core.archive.Archive
-import android.support.tools.jetifier.core.archive.ArchiveFile
-import android.support.tools.jetifier.core.archive.ArchiveItemVisitor
-import android.support.tools.jetifier.core.transform.pom.PomDocument
+import com.android.tools.build.jetifier.processor.archive.Archive
+import com.android.tools.build.jetifier.processor.archive.ArchiveFile
+import com.android.tools.build.jetifier.processor.archive.ArchiveItemVisitor
+import com.android.tools.build.jetifier.processor.transform.pom.PomDocument
import java.io.File
import java.nio.file.Paths
import java.security.MessageDigest
@@ -44,8 +44,8 @@
// Find archives
val archivesFilter = FileFilter({
return@FileFilter it.fileName.endsWith(".aar")
- || (it.fileName.endsWith("jar")
- && !it.fileName.contains("sources") && !it.fileName.contains("javadoc"))
+ || (it.fileName.endsWith("jar")
+ && !it.fileName.contains("sources") && !it.fileName.contains("javadoc"))
})
archive.accept(archivesFilter)
val libFiles = archivesFilter.files
@@ -54,10 +54,15 @@
val newFiles = mutableSetOf<ArchiveFile>()
pomFiles.forEach {
pomFile -> run {
- val name = pomFile.relativePath.toFile().nameWithoutExtension
- val artifactFile = libFiles.first { it.relativePath.toString().contains(name) }
- process(pomFile, artifactFile, newFiles)
- }
+ val name = pomFile.relativePath.toFile().nameWithoutExtension
+ val nameAar = name + ".aar"
+ val nameJar = name + ".jar"
+ val artifactFile = libFiles.first {
+ it.fileName == nameAar || it.fileName == nameJar
+ }
+
+ process(pomFile, artifactFile, newFiles)
+ }
}
// Write the result
@@ -78,7 +83,7 @@
val baseFileName = "${pomDep.artifactId}-${pomDep.version}"
val artifactDir = Paths.get(
- DIR_PREFIX, groupAsPath, pomDep.artifactId, pomDep.version!!.toUpperCase())
+ DIR_PREFIX, groupAsPath, pomDep.artifactId, pomDep.version!!.toUpperCase())
val newLibFilePath = Paths.get(artifactDir.toString(), "$baseFileName.$packaging")
val newPomFilePath = Paths.get(artifactDir.toString(), "$baseFileName.pom")
@@ -98,7 +103,7 @@
val md = MessageDigest.getInstance(hashType)
val result = md.digest(file.data)
return ArchiveFile(Paths.get(
- file.relativePath.toString() + "." + hashType.toLowerCase()), result)
+ file.relativePath.toString() + "." + hashType.toLowerCase()), result)
}
private class FileFilter(private val filter: (ArchiveFile) -> Boolean) : ArchiveItemVisitor {
diff --git a/leanback-preference/api21/androidx/leanback/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java b/leanback-preference/api21/androidx/leanback/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java
index ebcad93..0f74626 100644
--- a/leanback-preference/api21/androidx/leanback/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java
+++ b/leanback-preference/api21/androidx/leanback/preference/LeanbackPreferenceFragmentTransitionHelperApi21.java
@@ -44,4 +44,6 @@
}
+ private LeanbackPreferenceFragmentTransitionHelperApi21() {
+ }
}
diff --git a/leanback/api/current.txt b/leanback/api/current.txt
index 138509e..dd8202e 100644
--- a/leanback/api/current.txt
+++ b/leanback/api/current.txt
@@ -1703,7 +1703,7 @@
}
public class FocusHighlightHelper {
- ctor public FocusHighlightHelper();
+ ctor public deprecated FocusHighlightHelper();
method public static void setupBrowseItemFocusHighlight(androidx.leanback.widget.ItemBridgeAdapter, int, boolean);
method public static deprecated void setupHeaderItemFocusHighlight(androidx.leanback.widget.VerticalGridView);
method public static deprecated void setupHeaderItemFocusHighlight(androidx.leanback.widget.VerticalGridView, boolean);
@@ -2113,6 +2113,7 @@
method protected void onCreate(androidx.leanback.widget.ItemBridgeAdapter.ViewHolder);
method public final androidx.recyclerview.widget.RecyclerView.ViewHolder onCreateViewHolder(android.view.ViewGroup, int);
method protected void onDetachedFromWindow(androidx.leanback.widget.ItemBridgeAdapter.ViewHolder);
+ method public final boolean onFailedToRecycleView(androidx.recyclerview.widget.RecyclerView.ViewHolder);
method protected void onUnbind(androidx.leanback.widget.ItemBridgeAdapter.ViewHolder);
method public final void onViewAttachedToWindow(androidx.recyclerview.widget.RecyclerView.ViewHolder);
method public final void onViewDetachedFromWindow(androidx.recyclerview.widget.RecyclerView.ViewHolder);
diff --git a/leanback/api21/androidx/leanback/transition/TranslationAnimationCreator.java b/leanback/api21/androidx/leanback/transition/TranslationAnimationCreator.java
index 7ad7faa..455a60d 100644
--- a/leanback/api21/androidx/leanback/transition/TranslationAnimationCreator.java
+++ b/leanback/api21/androidx/leanback/transition/TranslationAnimationCreator.java
@@ -154,4 +154,6 @@
}
}
+ private TranslationAnimationCreator() {
+ }
}
diff --git a/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java b/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
index d4a751f..99e6fc7 100644
--- a/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
+++ b/leanback/src/androidTest/java/androidx/leanback/widget/GridWidgetTest.java
@@ -71,6 +71,7 @@
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
@LargeTest
@RunWith(AndroidJUnit4.class)
@@ -4648,6 +4649,58 @@
}
@Test
+ public void testAccessibilityBug77292190() throws Throwable {
+ Intent intent = new Intent();
+ final int numItems = 1000;
+ intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
+ intent.putExtra(GridActivity.EXTRA_CHILD_LAYOUT_ID, R.layout.item_full_width);
+ intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 1000);
+ intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+ initActivity(intent);
+ mOrientation = BaseGridView.VERTICAL;
+ mNumRows = 1;
+
+ setSelectedPosition(0);
+
+ final RecyclerViewAccessibilityDelegate delegateCompat = mGridView
+ .getCompatAccessibilityDelegate();
+ final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info);
+ }
+ });
+ if (Build.VERSION.SDK_INT >= 21) {
+ assertFalse(hasAction(info,
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_UP));
+ assertTrue(hasAction(info,
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_DOWN));
+ } else {
+ assertFalse(hasAction(info, AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD));
+ assertTrue(hasAction(info, AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD));
+ }
+
+ setSelectedPosition(numItems - 1);
+ final AccessibilityNodeInfoCompat info2 = AccessibilityNodeInfoCompat.obtain();
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ delegateCompat.onInitializeAccessibilityNodeInfo(mGridView, info2);
+ }
+ });
+ if (Build.VERSION.SDK_INT >= 21) {
+ assertTrue(hasAction(info2,
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_UP));
+ assertFalse(hasAction(info2,
+ AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_DOWN));
+ } else {
+ assertTrue(hasAction(info2, AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD));
+ assertFalse(hasAction(info2, AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD));
+ }
+ }
+
+ @Test
public void testAccessibilityWhenScrollDisabled() throws Throwable {
Intent intent = new Intent();
intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_linear);
@@ -4681,12 +4734,18 @@
});
assertEquals(RecyclerView.SCROLL_STATE_IDLE, mGridView.getScrollState());
}
-
private boolean hasAction(AccessibilityNodeInfoCompat info, Object action) {
if (Build.VERSION.SDK_INT >= 21) {
AccessibilityNodeInfoCompat.AccessibilityActionCompat convertedAction =
(AccessibilityNodeInfoCompat.AccessibilityActionCompat) action;
- return ((info.getActions() & convertedAction.getId()) != 0);
+ List<AccessibilityNodeInfoCompat.AccessibilityActionCompat> actions =
+ info.getActionList();
+ for (int i = 0; i < actions.size(); i++) {
+ if (actions.get(i).getId() == convertedAction.getId()) {
+ return true;
+ }
+ }
+ return false;
} else {
int convertedAction = (int) action;
return ((info.getActions() & convertedAction) != 0);
diff --git a/leanback/src/androidTest/java/androidx/leanback/widget/ItemBridgeAdapterTest.java b/leanback/src/androidTest/java/androidx/leanback/widget/ItemBridgeAdapterTest.java
new file mode 100644
index 0000000..40c741c
--- /dev/null
+++ b/leanback/src/androidTest/java/androidx/leanback/widget/ItemBridgeAdapterTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.leanback.widget;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class ItemBridgeAdapterTest {
+ private static final int sViewWidth = 100;
+ private static final int sViewHeight = 100;
+
+ public static class BasePresenter extends Presenter {
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent) {
+ View view = new View(parent.getContext());
+ view.setLayoutParams(new ViewGroup.LayoutParams(sViewWidth, sViewHeight));
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder viewHolder, Object item) {
+ }
+
+ @Override
+ public void onUnbindViewHolder(ViewHolder viewHolder) {
+
+ }
+ }
+
+ private ItemBridgeAdapter mItemBridgeAdapter;
+
+ private ArrayObjectAdapter mAdapter;
+
+ private RecyclerView mRecyclerView;
+
+ private Presenter mPresenter;
+
+ @Before
+ public void setup() {
+ mPresenter = spy(BasePresenter.class);
+
+ mItemBridgeAdapter = new ItemBridgeAdapter();
+ mAdapter = new ArrayObjectAdapter(mPresenter);
+ mAdapter.setItems(populateData(), null);
+ mItemBridgeAdapter.setAdapter(mAdapter);
+
+ Context context = InstrumentationRegistry.getTargetContext();
+ mRecyclerView = new RecyclerView(context);
+ mRecyclerView.setItemViewCacheSize(0);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(context,
+ LinearLayoutManager.VERTICAL, false));
+ mRecyclerView.setHasFixedSize(false); // force layout items in layout pass
+ mRecyclerView.setAdapter(mItemBridgeAdapter);
+ measureAndLayoutRecycleView(mRecyclerView);
+ }
+
+ List populateData() {
+ List data = new ArrayList();
+ for (int i = 0; i < 10000; i++) {
+ data.add(i);
+ }
+ return data;
+ }
+
+ static void measureAndLayoutRecycleView(RecyclerView recyclerView) {
+ measureAndLayout(recyclerView, 1920, 1080);
+ }
+
+ static void measureAndLayout(View view, int expectedWidth, int expectedHeight) {
+ view.measure(View.MeasureSpec.makeMeasureSpec(expectedWidth, View.MeasureSpec.EXACTLY),
+ View.MeasureSpec.makeMeasureSpec(expectedHeight, View.MeasureSpec.EXACTLY));
+ view.layout(0, 0, expectedWidth, expectedHeight);
+ }
+
+ @Test
+ public void onCreateAndOnBind() {
+ int childCount = mRecyclerView.getChildCount();
+ assertTrue(childCount > 0);
+ // Assert number of invokes of onCreateViewHolder and onBindViewHolder.
+ Mockito.verify(mPresenter, times(childCount)).onCreateViewHolder(any(ViewGroup.class));
+ Mockito.verify(mPresenter, times(childCount))
+ .onBindViewHolder(any(Presenter.ViewHolder.class), any());
+ }
+ @Test
+ public void onUnbind() {
+ assertTrue(mRecyclerView.getChildCount() > 0);
+ // When scroll one item offscreen, assert onUnbindViewHolder called once.
+ mRecyclerView.scrollBy(0, mRecyclerView.getChildAt(0).getHeight());
+ Mockito.verify(mPresenter, times(1))
+ .onUnbindViewHolder(any(Presenter.ViewHolder.class));
+ }
+
+ @Test
+ public void onUnbindWithTransientState() {
+ assertTrue(mRecyclerView.getChildCount() > 0);
+ // Set TransientState to true to simulate the view is running custom ViewPropertyAnimation.
+ mRecyclerView.getChildAt(0).setHasTransientState(true);
+ // When scroll one item with custom animation offscreen, assert onUnbindViewHolder
+ // was called once when ViewHolder is still having transient state.
+ mRecyclerView.scrollBy(0, mRecyclerView.getChildAt(0).getHeight());
+ Mockito.verify(mPresenter, times(1))
+ .onUnbindViewHolder(any(Presenter.ViewHolder.class));
+ }
+}
diff --git a/leanback/src/androidTest/res/layout/item_full_width.xml b/leanback/src/androidTest/res/layout/item_full_width.xml
new file mode 100644
index 0000000..75bf9b2
--- /dev/null
+++ b/leanback/src/androidTest/res/layout/item_full_width.xml
@@ -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.
+ -->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="400dp"
+ >
+ <TextView
+ android:layout_alignParentTop="true"
+ android:text="unfocusable text"
+ android:layout_width="match_parent"
+ android:layout_height="50dp"/>
+ <Button
+ android:layout_alignParentBottom="true"
+ android:text="button"
+ android:focusable="true"
+ android:layout_width="match_parent"
+ android:layout_height="50dp"/>
+</RelativeLayout>
diff --git a/leanback/src/main/java/androidx/leanback/app/FragmentUtil.java b/leanback/src/main/java/androidx/leanback/app/FragmentUtil.java
index 8d042e8..9ff380e 100644
--- a/leanback/src/main/java/androidx/leanback/app/FragmentUtil.java
+++ b/leanback/src/main/java/androidx/leanback/app/FragmentUtil.java
@@ -26,4 +26,7 @@
}
return fragment.getActivity();
}
+
+ private FragmentUtil() {
+ }
}
diff --git a/leanback/src/main/java/androidx/leanback/app/PermissionHelper.java b/leanback/src/main/java/androidx/leanback/app/PermissionHelper.java
index 73d1a05..a7e56bb 100644
--- a/leanback/src/main/java/androidx/leanback/app/PermissionHelper.java
+++ b/leanback/src/main/java/androidx/leanback/app/PermissionHelper.java
@@ -32,4 +32,6 @@
}
}
+ private PermissionHelper() {
+ }
}
diff --git a/leanback/src/main/java/androidx/leanback/transition/LeanbackTransitionHelper.java b/leanback/src/main/java/androidx/leanback/transition/LeanbackTransitionHelper.java
index a259050..9693013 100644
--- a/leanback/src/main/java/androidx/leanback/transition/LeanbackTransitionHelper.java
+++ b/leanback/src/main/java/androidx/leanback/transition/LeanbackTransitionHelper.java
@@ -55,4 +55,7 @@
slide.addTarget(R.id.browse_title_group);
return slide;
}
+
+ private LeanbackTransitionHelper() {
+ }
}
diff --git a/leanback/src/main/java/androidx/leanback/transition/TransitionHelper.java b/leanback/src/main/java/androidx/leanback/transition/TransitionHelper.java
index 084e1e7..4d8c877 100644
--- a/leanback/src/main/java/androidx/leanback/transition/TransitionHelper.java
+++ b/leanback/src/main/java/androidx/leanback/transition/TransitionHelper.java
@@ -464,4 +464,7 @@
}
}
}
+
+ private TransitionHelper() {
+ }
}
diff --git a/leanback/src/main/java/androidx/leanback/widget/BackgroundHelper.java b/leanback/src/main/java/androidx/leanback/widget/BackgroundHelper.java
index c50399a..f72ff44 100644
--- a/leanback/src/main/java/androidx/leanback/widget/BackgroundHelper.java
+++ b/leanback/src/main/java/androidx/leanback/widget/BackgroundHelper.java
@@ -40,4 +40,7 @@
view.setBackground(drawable);
}
}
+
+ private BackgroundHelper() {
+ }
}
diff --git a/leanback/src/main/java/androidx/leanback/widget/FocusHighlightHelper.java b/leanback/src/main/java/androidx/leanback/widget/FocusHighlightHelper.java
index ca5a4fd..7ca9e6f 100644
--- a/leanback/src/main/java/androidx/leanback/widget/FocusHighlightHelper.java
+++ b/leanback/src/main/java/androidx/leanback/widget/FocusHighlightHelper.java
@@ -339,4 +339,10 @@
}
}
+
+ /** @deprecated This type should not be instantiated as it contains only static methods. */
+ @Deprecated
+ @SuppressWarnings("PrivateConstructorForUtilityClass")
+ public FocusHighlightHelper() {
+ }
}
diff --git a/leanback/src/main/java/androidx/leanback/widget/ForegroundHelper.java b/leanback/src/main/java/androidx/leanback/widget/ForegroundHelper.java
index 3742c63..57c2741 100644
--- a/leanback/src/main/java/androidx/leanback/widget/ForegroundHelper.java
+++ b/leanback/src/main/java/androidx/leanback/widget/ForegroundHelper.java
@@ -25,4 +25,7 @@
view.setForeground(drawable);
}
}
+
+ private ForegroundHelper() {
+ }
}
diff --git a/leanback/src/main/java/androidx/leanback/widget/GridLayoutManager.java b/leanback/src/main/java/androidx/leanback/widget/GridLayoutManager.java
index 6ef40e5..62b7bb2 100644
--- a/leanback/src/main/java/androidx/leanback/widget/GridLayoutManager.java
+++ b/leanback/src/main/java/androidx/leanback/widget/GridLayoutManager.java
@@ -3427,8 +3427,9 @@
if (vh == null) {
return false;
}
- return vh.itemView.getLeft() >= 0 && vh.itemView.getRight() < mBaseGridView.getWidth()
- && vh.itemView.getTop() >= 0 && vh.itemView.getBottom() < mBaseGridView.getHeight();
+ return vh.itemView.getLeft() >= 0 && vh.itemView.getRight() <= mBaseGridView.getWidth()
+ && vh.itemView.getTop() >= 0 && vh.itemView.getBottom()
+ <= mBaseGridView.getHeight();
}
boolean canScrollTo(View view) {
diff --git a/leanback/src/main/java/androidx/leanback/widget/ItemAlignmentFacetHelper.java b/leanback/src/main/java/androidx/leanback/widget/ItemAlignmentFacetHelper.java
index 6e80100..5ca7458 100644
--- a/leanback/src/main/java/androidx/leanback/widget/ItemAlignmentFacetHelper.java
+++ b/leanback/src/main/java/androidx/leanback/widget/ItemAlignmentFacetHelper.java
@@ -105,4 +105,6 @@
return alignPos;
}
+ private ItemAlignmentFacetHelper() {
+ }
}
diff --git a/leanback/src/main/java/androidx/leanback/widget/ItemBridgeAdapter.java b/leanback/src/main/java/androidx/leanback/widget/ItemBridgeAdapter.java
index faa5a69..eaa2787 100644
--- a/leanback/src/main/java/androidx/leanback/widget/ItemBridgeAdapter.java
+++ b/leanback/src/main/java/androidx/leanback/widget/ItemBridgeAdapter.java
@@ -417,6 +417,12 @@
}
@Override
+ public final boolean onFailedToRecycleView(RecyclerView.ViewHolder holder) {
+ onViewRecycled(holder);
+ return false;
+ }
+
+ @Override
public final void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
ViewHolder viewHolder = (ViewHolder) holder;
onAttachedToWindow(viewHolder);
diff --git a/leanback/src/main/java/androidx/leanback/widget/RoundedRectHelperApi21.java b/leanback/src/main/java/androidx/leanback/widget/RoundedRectHelperApi21.java
index 031b980..470f645 100644
--- a/leanback/src/main/java/androidx/leanback/widget/RoundedRectHelperApi21.java
+++ b/leanback/src/main/java/androidx/leanback/widget/RoundedRectHelperApi21.java
@@ -59,4 +59,7 @@
}
view.setClipToOutline(clip);
}
+
+ private RoundedRectHelperApi21() {
+ }
}
diff --git a/leanback/src/main/java/androidx/leanback/widget/ShadowHelperApi21.java b/leanback/src/main/java/androidx/leanback/widget/ShadowHelperApi21.java
index 0d97cf9..e32d165 100644
--- a/leanback/src/main/java/androidx/leanback/widget/ShadowHelperApi21.java
+++ b/leanback/src/main/java/androidx/leanback/widget/ShadowHelperApi21.java
@@ -58,4 +58,7 @@
ShadowImpl impl = (ShadowImpl) object;
impl.mShadowContainer.setZ(impl.mNormalZ + level * (impl.mFocusedZ - impl.mNormalZ));
}
+
+ private ShadowHelperApi21() {
+ }
}
diff --git a/leanback/src/main/java/androidx/leanback/widget/Util.java b/leanback/src/main/java/androidx/leanback/widget/Util.java
index aa81715..bda8ac6 100644
--- a/leanback/src/main/java/androidx/leanback/widget/Util.java
+++ b/leanback/src/main/java/androidx/leanback/widget/Util.java
@@ -43,4 +43,7 @@
}
return false;
}
-}
\ No newline at end of file
+
+ private Util() {
+ }
+}
diff --git a/leanback/src/main/java/androidx/leanback/widget/picker/PickerUtility.java b/leanback/src/main/java/androidx/leanback/widget/picker/PickerUtility.java
index 5347971..c520646 100644
--- a/leanback/src/main/java/androidx/leanback/widget/picker/PickerUtility.java
+++ b/leanback/src/main/java/androidx/leanback/widget/picker/PickerUtility.java
@@ -95,4 +95,7 @@
return newCalendar;
}
}
+
+ private PickerUtility() {
+ }
}
diff --git a/leanback/src/main/res/values-as/strings.xml b/leanback/src/main/res/values-as/strings.xml
new file mode 100644
index 0000000..eb3b2b0
--- /dev/null
+++ b/leanback/src/main/res/values-as/strings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2014 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="lb_navigation_menu_contentDescription" msgid="8126335323963415494">"নেভিগেশ্বন মেনু"</string>
+ <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_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>
+ <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$dX"</string>
+ <string name="lb_playback_controls_rewind" msgid="1412664391757869774">"ৰিৱাইণ্ড কৰক"</string>
+ <string name="lb_playback_controls_rewind_multiplier" msgid="8651612807713092781">"ৰিৱাইণ্ড কৰক %1$dX"</string>
+ <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_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_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_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>
+ <string name="lb_playback_time_separator" msgid="6549544638083578695">"/"</string>
+ <string name="lb_playback_controls_shown" msgid="7794717158616536936">"মিডিয়াৰ নিয়ন্ত্ৰণসমূহ দেখুওৱা হ\'ল"</string>
+ <string name="lb_playback_controls_hidden" msgid="619396299825306757">"মিডিয়াৰ নিয়ন্ত্ৰণসমূহ লুকুৱাই ৰখা হৈছে, দেখুওৱাবলৈ ডি-পেডত টিপক"</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_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 5d85c92..53e6797 100644
--- a/leanback/src/main/res/values-es/strings.xml
+++ b/leanback/src/main/res/values-es/strings.xml
@@ -47,7 +47,7 @@
<string name="lb_playback_controls_high_quality_disable" msgid="3000046054608531995">"Inhabilitar alta calidad"</string>
<string name="lb_playback_controls_closed_captioning_enable" msgid="3934392140182327163">"Habilitar subtítulos"</string>
<string name="lb_playback_controls_closed_captioning_disable" msgid="5508271941331836786">"Inhabilitar subtítulos"</string>
- <string name="lb_playback_controls_picture_in_picture" msgid="8800305194045609275">"Activar modo pantalla en pantalla"</string>
+ <string name="lb_playback_controls_picture_in_picture" msgid="8800305194045609275">"Activar modo imagen en imagen"</string>
<string name="lb_playback_time_separator" msgid="6549544638083578695">"/"</string>
<string name="lb_playback_controls_shown" msgid="7794717158616536936">"Controles multimedia mostrados"</string>
<string name="lb_playback_controls_hidden" msgid="619396299825306757">"Controles multimedia ocultos (pulsa la cruceta para mostrarlos)"</string>
diff --git a/leanback/src/main/res/values-or/strings.xml b/leanback/src/main/res/values-or/strings.xml
new file mode 100644
index 0000000..ce6d1ea
--- /dev/null
+++ b/leanback/src/main/res/values-or/strings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2014 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="lb_navigation_menu_contentDescription" msgid="8126335323963415494">"ନେଭିଗେଶନ୍ ମେନୁ"</string>
+ <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_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>
+ <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$dX ବେଗରେ ଫାଷ୍ଟ ଫରୱାର୍ଡ"</string>
+ <string name="lb_playback_controls_rewind" msgid="1412664391757869774">"ରିୱାଇଣ୍ଡ"</string>
+ <string name="lb_playback_controls_rewind_multiplier" msgid="8651612807713092781">"%1$dX ବେଗରେ ରିୱାଇଣ୍ଡ କରନ୍ତୁ"</string>
+ <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_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_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_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>
+ <string name="lb_playback_time_separator" msgid="6549544638083578695">"/"</string>
+ <string name="lb_playback_controls_shown" msgid="7794717158616536936">"ମିଡିଆ ନିୟନ୍ତ୍ରଣ ଦେଖାଯାଇଛି"</string>
+ <string name="lb_playback_controls_hidden" msgid="619396299825306757">"ମିଡିଆ ନିୟନ୍ତ୍ରଣ ଲୁଚିଯାଇଛି, ଦେଖାଇବାକୁ ଡି-ପ୍ୟାଡ୍ ଦବାନ୍ତୁ"</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_onboarding_get_started" msgid="7674487829030291492">"ଆରମ୍ଭ କରନ୍ତୁ"</string>
+ <string name="lb_onboarding_accessibility_next" msgid="4213611627196077555">"ପରବର୍ତ୍ତୀ"</string>
+</resources>
diff --git a/lifecycle/common/src/main/java/androidx/lifecycle/Lifecycling.java b/lifecycle/common/src/main/java/androidx/lifecycle/Lifecycling.java
index 47c66c8..aeebf90 100644
--- a/lifecycle/common/src/main/java/androidx/lifecycle/Lifecycling.java
+++ b/lifecycle/common/src/main/java/androidx/lifecycle/Lifecycling.java
@@ -178,4 +178,7 @@
public static String getAdapterName(String className) {
return className.replace(".", "_") + "_LifecycleAdapter";
}
+
+ private Lifecycling() {
+ }
}
diff --git a/lifecycle/extensions/api/current.txt b/lifecycle/extensions/api/current.txt
index dad4ab8..e8c0334 100644
--- a/lifecycle/extensions/api/current.txt
+++ b/lifecycle/extensions/api/current.txt
@@ -33,9 +33,9 @@
ctor public deprecated ViewModelProviders.DefaultFactory(android.app.Application);
}
- public class ViewModelStores {
- method public static androidx.lifecycle.ViewModelStore of(androidx.fragment.app.FragmentActivity);
- method public static androidx.lifecycle.ViewModelStore of(androidx.fragment.app.Fragment);
+ public deprecated class ViewModelStores {
+ method public static deprecated androidx.lifecycle.ViewModelStore of(androidx.fragment.app.FragmentActivity);
+ method public static deprecated androidx.lifecycle.ViewModelStore of(androidx.fragment.app.Fragment);
}
}
diff --git a/lifecycle/extensions/src/main/java/androidx/lifecycle/HolderFragment.java b/lifecycle/extensions/src/main/java/androidx/lifecycle/HolderFragment.java
deleted file mode 100644
index 24fa504..0000000
--- a/lifecycle/extensions/src/main/java/androidx/lifecycle/HolderFragment.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * 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.lifecycle;
-
-import android.app.Activity;
-import android.app.Application.ActivityLifecycleCallbacks;
-import android.os.Bundle;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @hide
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class HolderFragment extends Fragment implements ViewModelStoreOwner {
- private static final String LOG_TAG = "ViewModelStores";
-
- private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();
-
- /**
- * @hide
- */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public static final String HOLDER_TAG =
- "androidx.lifecycle.state.StateProviderHolderFragment";
-
- private ViewModelStore mViewModelStore = new ViewModelStore();
-
- public HolderFragment() {
- setRetainInstance(true);
- }
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- sHolderFragmentManager.holderFragmentCreated(this);
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- mViewModelStore.clear();
- }
-
- @NonNull
- @Override
- public ViewModelStore getViewModelStore() {
- return mViewModelStore;
- }
-
- /**
- * @hide
- */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public static HolderFragment holderFragmentFor(FragmentActivity activity) {
- return sHolderFragmentManager.holderFragmentFor(activity);
- }
-
- /**
- * @hide
- */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public static HolderFragment holderFragmentFor(Fragment fragment) {
- return sHolderFragmentManager.holderFragmentFor(fragment);
- }
-
- @SuppressWarnings("WeakerAccess")
- static class HolderFragmentManager {
- private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>();
- private Map<Fragment, HolderFragment> mNotCommittedFragmentHolders = new HashMap<>();
-
- private ActivityLifecycleCallbacks mActivityCallbacks =
- new EmptyActivityLifecycleCallbacks() {
- @Override
- public void onActivityDestroyed(Activity activity) {
- HolderFragment fragment = mNotCommittedActivityHolders.remove(activity);
- if (fragment != null) {
- Log.e(LOG_TAG, "Failed to save a ViewModel for " + activity);
- }
- }
- };
-
- private boolean mActivityCallbacksIsAdded = false;
-
- private FragmentLifecycleCallbacks mParentDestroyedCallback =
- new FragmentLifecycleCallbacks() {
- @Override
- public void onFragmentDestroyed(FragmentManager fm, Fragment parentFragment) {
- super.onFragmentDestroyed(fm, parentFragment);
- HolderFragment fragment = mNotCommittedFragmentHolders.remove(
- parentFragment);
- if (fragment != null) {
- Log.e(LOG_TAG, "Failed to save a ViewModel for " + parentFragment);
- }
- }
- };
-
- void holderFragmentCreated(Fragment holderFragment) {
- Fragment parentFragment = holderFragment.getParentFragment();
- if (parentFragment != null) {
- mNotCommittedFragmentHolders.remove(parentFragment);
- parentFragment.getFragmentManager().unregisterFragmentLifecycleCallbacks(
- mParentDestroyedCallback);
- } else {
- mNotCommittedActivityHolders.remove(holderFragment.getActivity());
- }
- }
-
- private static HolderFragment findHolderFragment(FragmentManager manager) {
- if (manager.isDestroyed()) {
- throw new IllegalStateException("Can't access ViewModels from onDestroy");
- }
-
- Fragment fragmentByTag = manager.findFragmentByTag(HOLDER_TAG);
- if (fragmentByTag != null && !(fragmentByTag instanceof HolderFragment)) {
- throw new IllegalStateException("Unexpected "
- + "fragment instance was returned by HOLDER_TAG");
- }
- return (HolderFragment) fragmentByTag;
- }
-
- private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
- HolderFragment holder = new HolderFragment();
- fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
- return holder;
- }
-
- HolderFragment holderFragmentFor(FragmentActivity activity) {
- FragmentManager fm = activity.getSupportFragmentManager();
- HolderFragment holder = findHolderFragment(fm);
- if (holder != null) {
- return holder;
- }
- holder = mNotCommittedActivityHolders.get(activity);
- if (holder != null) {
- return holder;
- }
-
- if (!mActivityCallbacksIsAdded) {
- mActivityCallbacksIsAdded = true;
- activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
- }
- holder = createHolderFragment(fm);
- mNotCommittedActivityHolders.put(activity, holder);
- return holder;
- }
-
- HolderFragment holderFragmentFor(Fragment parentFragment) {
- FragmentManager fm = parentFragment.getChildFragmentManager();
- HolderFragment holder = findHolderFragment(fm);
- if (holder != null) {
- return holder;
- }
- holder = mNotCommittedFragmentHolders.get(parentFragment);
- if (holder != null) {
- return holder;
- }
-
- parentFragment.getFragmentManager()
- .registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false);
- holder = createHolderFragment(fm);
- mNotCommittedFragmentHolders.put(parentFragment, holder);
- return holder;
- }
- }
-}
diff --git a/lifecycle/extensions/src/main/java/androidx/lifecycle/LifecycleDispatcher.java b/lifecycle/extensions/src/main/java/androidx/lifecycle/LifecycleDispatcher.java
index f241276..fa1550b 100644
--- a/lifecycle/extensions/src/main/java/androidx/lifecycle/LifecycleDispatcher.java
+++ b/lifecycle/extensions/src/main/java/androidx/lifecycle/LifecycleDispatcher.java
@@ -180,4 +180,7 @@
dispatchIfLifecycleOwner(f, ON_RESUME);
}
}
+
+ private LifecycleDispatcher() {
+ }
}
diff --git a/lifecycle/extensions/src/main/java/androidx/lifecycle/ViewModelProviders.java b/lifecycle/extensions/src/main/java/androidx/lifecycle/ViewModelProviders.java
index ad72a54..0b83b92 100644
--- a/lifecycle/extensions/src/main/java/androidx/lifecycle/ViewModelProviders.java
+++ b/lifecycle/extensions/src/main/java/androidx/lifecycle/ViewModelProviders.java
@@ -102,7 +102,7 @@
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
- return new ViewModelProvider(ViewModelStores.of(fragment), factory);
+ return new ViewModelProvider(fragment.getViewModelStore(), factory);
}
/**
@@ -123,7 +123,7 @@
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
- return new ViewModelProvider(ViewModelStores.of(activity), factory);
+ return new ViewModelProvider(activity.getViewModelStore(), factory);
}
/**
diff --git a/lifecycle/extensions/src/main/java/androidx/lifecycle/ViewModelStores.java b/lifecycle/extensions/src/main/java/androidx/lifecycle/ViewModelStores.java
index 6627239..ba9e15a 100644
--- a/lifecycle/extensions/src/main/java/androidx/lifecycle/ViewModelStores.java
+++ b/lifecycle/extensions/src/main/java/androidx/lifecycle/ViewModelStores.java
@@ -16,8 +16,6 @@
package androidx.lifecycle;
-import static androidx.lifecycle.HolderFragment.holderFragmentFor;
-
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
@@ -25,8 +23,13 @@
/**
* Factory methods for {@link ViewModelStore} class.
+ *
+ * @deprecated Use {@link FragmentActivity#getViewModelStore()} or
+ * {@link Fragment#getViewModelStore()} to retrieve a {@code ViewModelStore} directly from
+ * activities and fragments.
*/
@SuppressWarnings("WeakerAccess")
+@Deprecated
public class ViewModelStores {
private ViewModelStores() {
@@ -37,14 +40,13 @@
*
* @param activity an activity whose {@code ViewModelStore} is requested
* @return a {@code ViewModelStore}
+ * @deprecated Use {@link FragmentActivity#getViewModelStore()}
*/
+ @Deprecated
@NonNull
@MainThread
public static ViewModelStore of(@NonNull FragmentActivity activity) {
- if (activity instanceof ViewModelStoreOwner) {
- return ((ViewModelStoreOwner) activity).getViewModelStore();
- }
- return holderFragmentFor(activity).getViewModelStore();
+ return activity.getViewModelStore();
}
/**
@@ -52,13 +54,12 @@
*
* @param fragment a fragment whose {@code ViewModelStore} is requested
* @return a {@code ViewModelStore}
+ * @deprecated Use {@link Fragment#getViewModelStore()}
*/
+ @Deprecated
@NonNull
@MainThread
public static ViewModelStore of(@NonNull Fragment fragment) {
- if (fragment instanceof ViewModelStoreOwner) {
- return ((ViewModelStoreOwner) fragment).getViewModelStore();
- }
- return holderFragmentFor(fragment).getViewModelStore();
+ return fragment.getViewModelStore();
}
}
diff --git a/lifecycle/viewmodel/src/main/java/androidx/lifecycle/ViewModelStore.java b/lifecycle/viewmodel/src/main/java/androidx/lifecycle/ViewModelStore.java
index c831563..29ea55e 100644
--- a/lifecycle/viewmodel/src/main/java/androidx/lifecycle/ViewModelStore.java
+++ b/lifecycle/viewmodel/src/main/java/androidx/lifecycle/ViewModelStore.java
@@ -30,7 +30,7 @@
* then it should call {@link #clear()} on this {@code ViewModelStore}, so {@code ViewModels} would
* be notified that they are no longer used.
* <p>
- * {@link androidx.lifecycle.ViewModelStores} provides a {@code ViewModelStore} for
+ * Use {@link ViewModelStoreOwner#getViewModelStore()} to retrieve a {@code ViewModelStore} for
* activities and fragments.
*/
public class ViewModelStore {
diff --git a/media/api21/android/support/v4/media/MediaBrowserCompatApi21.java b/media/api21/android/support/v4/media/MediaBrowserCompatApi21.java
index 4f040df..acd8bf8 100644
--- a/media/api21/android/support/v4/media/MediaBrowserCompatApi21.java
+++ b/media/api21/android/support/v4/media/MediaBrowserCompatApi21.java
@@ -148,5 +148,11 @@
public static Object getDescription(Object itemObj) {
return ((MediaBrowser.MediaItem) itemObj).getDescription();
}
+
+ private MediaItem() {
+ }
+ }
+
+ private MediaBrowserCompatApi21() {
}
}
diff --git a/media/api21/android/support/v4/media/MediaDescriptionCompatApi21.java b/media/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
index 4179518..bed7f01 100644
--- a/media/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
+++ b/media/api21/android/support/v4/media/MediaDescriptionCompatApi21.java
@@ -99,5 +99,11 @@
public static Object build(Object builderObj) {
return ((MediaDescription.Builder) builderObj).build();
}
+
+ private Builder() {
+ }
+ }
+
+ private MediaDescriptionCompatApi21() {
}
}
diff --git a/media/api21/android/support/v4/media/MediaMetadataCompatApi21.java b/media/api21/android/support/v4/media/MediaMetadataCompatApi21.java
index 4095a39..5ce873a 100644
--- a/media/api21/android/support/v4/media/MediaMetadataCompatApi21.java
+++ b/media/api21/android/support/v4/media/MediaMetadataCompatApi21.java
@@ -83,5 +83,11 @@
public static Object build(Object builderObj) {
return ((MediaMetadata.Builder)builderObj).build();
}
+
+ private Builder() {
+ }
+ }
+
+ private MediaMetadataCompatApi21() {
}
}
diff --git a/media/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java b/media/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java
index d1d81eb..30d0225 100644
--- a/media/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java
+++ b/media/api21/android/support/v4/media/ParceledListSliceAdapterApi21.java
@@ -48,4 +48,7 @@
}
return result;
}
+
+ private ParceledListSliceAdapterApi21() {
+ }
}
diff --git a/media/api21/android/support/v4/media/session/MediaControllerCompatApi21.java b/media/api21/android/support/v4/media/session/MediaControllerCompatApi21.java
index 6c5f860..0cd1cd9 100644
--- a/media/api21/android/support/v4/media/session/MediaControllerCompatApi21.java
+++ b/media/api21/android/support/v4/media/session/MediaControllerCompatApi21.java
@@ -186,6 +186,9 @@
public static void sendCustomAction(Object controlsObj, String action, Bundle args) {
((MediaController.TransportControls) controlsObj).sendCustomAction(action, args);
}
+
+ private TransportControls() {
+ }
}
public static class PlaybackInfo {
@@ -258,6 +261,9 @@
return AudioManager.STREAM_MUSIC;
}
}
+
+ private PlaybackInfo() {
+ }
}
public static interface Callback {
@@ -320,4 +326,7 @@
info.getMaxVolume(), info.getCurrentVolume());
}
}
+
+ private MediaControllerCompatApi21() {
+ }
}
diff --git a/media/api21/android/support/v4/media/session/MediaSessionCompatApi21.java b/media/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
index f0c542b..a359704 100644
--- a/media/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
+++ b/media/api21/android/support/v4/media/session/MediaSessionCompatApi21.java
@@ -269,5 +269,11 @@
public static long getQueueId(Object queueItem) {
return ((MediaSession.QueueItem) queueItem).getQueueId();
}
+
+ private QueueItem() {
+ }
+ }
+
+ private MediaSessionCompatApi21() {
}
}
diff --git a/media/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java b/media/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java
index 85cd8d0..f4aa9fc 100644
--- a/media/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java
+++ b/media/api21/android/support/v4/media/session/PlaybackStateCompatApi21.java
@@ -100,5 +100,11 @@
customActionObj.setExtras(extras);
return customActionObj.build();
}
+
+ private CustomAction() {
+ }
+ }
+
+ private PlaybackStateCompatApi21() {
}
}
diff --git a/media/api21/androidx/media/AudioAttributesCompatApi21.java b/media/api21/androidx/media/AudioAttributesCompatApi21.java
index 19144c1..ba3d37c 100644
--- a/media/api21/androidx/media/AudioAttributesCompatApi21.java
+++ b/media/api21/androidx/media/AudioAttributesCompatApi21.java
@@ -65,4 +65,7 @@
return mWrapped;
}
}
+
+ private AudioAttributesCompatApi21() {
+ }
}
diff --git a/media/api21/androidx/media/MediaBrowserServiceCompatApi21.java b/media/api21/androidx/media/MediaBrowserServiceCompatApi21.java
index ca80e08..79c591b 100644
--- a/media/api21/androidx/media/MediaBrowserServiceCompatApi21.java
+++ b/media/api21/androidx/media/MediaBrowserServiceCompatApi21.java
@@ -129,4 +129,7 @@
mServiceProxy.onLoadChildren(parentId, new ResultWrapper<List<Parcel>>(result));
}
}
+
+ private MediaBrowserServiceCompatApi21() {
+ }
}
diff --git a/media/api21/androidx/media/VolumeProviderCompatApi21.java b/media/api21/androidx/media/VolumeProviderCompatApi21.java
index 6df87d0..6d540b3 100644
--- a/media/api21/androidx/media/VolumeProviderCompatApi21.java
+++ b/media/api21/androidx/media/VolumeProviderCompatApi21.java
@@ -45,4 +45,7 @@
void onSetVolumeTo(int volume);
void onAdjustVolume(int delta);
}
+
+ private VolumeProviderCompatApi21() {
+ }
}
diff --git a/media/api22/android/support/v4/media/session/MediaSessionCompatApi22.java b/media/api22/android/support/v4/media/session/MediaSessionCompatApi22.java
index 8650400..9a44d8c 100644
--- a/media/api22/android/support/v4/media/session/MediaSessionCompatApi22.java
+++ b/media/api22/android/support/v4/media/session/MediaSessionCompatApi22.java
@@ -25,4 +25,7 @@
public static void setRatingType(Object sessionObj, int type) {
((MediaSession) sessionObj).setRatingType(type);
}
+
+ private MediaSessionCompatApi22() {
+ }
}
diff --git a/media/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java b/media/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java
index 53b76ce..1be825f 100644
--- a/media/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java
+++ b/media/api22/android/support/v4/media/session/PlaybackStateCompatApi22.java
@@ -45,4 +45,7 @@
stateObj.setExtras(extras);
return stateObj.build();
}
+
+ private PlaybackStateCompatApi22() {
+ }
}
diff --git a/media/api23/android/support/v4/media/MediaBrowserCompatApi23.java b/media/api23/android/support/v4/media/MediaBrowserCompatApi23.java
index 6f94d5d..514b1e9 100644
--- a/media/api23/android/support/v4/media/MediaBrowserCompatApi23.java
+++ b/media/api23/android/support/v4/media/MediaBrowserCompatApi23.java
@@ -61,4 +61,7 @@
mItemCallback.onError(itemId);
}
}
+
+ private MediaBrowserCompatApi23() {
+ }
}
diff --git a/media/api23/android/support/v4/media/MediaDescriptionCompatApi23.java b/media/api23/android/support/v4/media/MediaDescriptionCompatApi23.java
index b702617..ee1b1a1 100644
--- a/media/api23/android/support/v4/media/MediaDescriptionCompatApi23.java
+++ b/media/api23/android/support/v4/media/MediaDescriptionCompatApi23.java
@@ -21,14 +21,20 @@
import androidx.annotation.RequiresApi;
@RequiresApi(23)
-class MediaDescriptionCompatApi23 extends MediaDescriptionCompatApi21 {
+class MediaDescriptionCompatApi23 {
public static Uri getMediaUri(Object descriptionObj) {
return ((MediaDescription) descriptionObj).getMediaUri();
}
- static class Builder extends MediaDescriptionCompatApi21.Builder {
+ static class Builder {
public static void setMediaUri(Object builderObj, Uri mediaUri) {
((MediaDescription.Builder)builderObj).setMediaUri(mediaUri);
}
+
+ private Builder() {
+ }
+ }
+
+ private MediaDescriptionCompatApi23() {
}
}
diff --git a/media/api23/android/support/v4/media/session/MediaControllerCompatApi23.java b/media/api23/android/support/v4/media/session/MediaControllerCompatApi23.java
index ca424d1..c5137b5 100644
--- a/media/api23/android/support/v4/media/session/MediaControllerCompatApi23.java
+++ b/media/api23/android/support/v4/media/session/MediaControllerCompatApi23.java
@@ -25,9 +25,15 @@
@RequiresApi(23)
class MediaControllerCompatApi23 {
- public static class TransportControls extends MediaControllerCompatApi21.TransportControls {
+ public static class TransportControls {
public static void playFromUri(Object controlsObj, Uri uri, Bundle extras) {
((MediaController.TransportControls) controlsObj).playFromUri(uri, extras);
}
+
+ private TransportControls() {
+ }
+ }
+
+ private MediaControllerCompatApi23() {
}
}
diff --git a/media/api23/android/support/v4/media/session/MediaSessionCompatApi23.java b/media/api23/android/support/v4/media/session/MediaSessionCompatApi23.java
index de55764..586ffda 100644
--- a/media/api23/android/support/v4/media/session/MediaSessionCompatApi23.java
+++ b/media/api23/android/support/v4/media/session/MediaSessionCompatApi23.java
@@ -42,4 +42,7 @@
mCallback.onPlayFromUri(uri, extras);
}
}
+
+ private MediaSessionCompatApi23() {
+ }
}
diff --git a/media/api23/androidx/media/MediaBrowserServiceCompatApi23.java b/media/api23/androidx/media/MediaBrowserServiceCompatApi23.java
index 624b98a..0292fb9 100644
--- a/media/api23/androidx/media/MediaBrowserServiceCompatApi23.java
+++ b/media/api23/androidx/media/MediaBrowserServiceCompatApi23.java
@@ -45,4 +45,7 @@
new MediaBrowserServiceCompatApi21.ResultWrapper<Parcel>(result));
}
}
+
+ private MediaBrowserServiceCompatApi23() {
+ }
}
diff --git a/media/api24/android/support/v4/media/session/MediaControllerCompatApi24.java b/media/api24/android/support/v4/media/session/MediaControllerCompatApi24.java
index 9faeff6..f130ed0 100644
--- a/media/api24/android/support/v4/media/session/MediaControllerCompatApi24.java
+++ b/media/api24/android/support/v4/media/session/MediaControllerCompatApi24.java
@@ -25,7 +25,7 @@
@RequiresApi(24)
class MediaControllerCompatApi24 {
- public static class TransportControls extends MediaControllerCompatApi23.TransportControls {
+ public static class TransportControls {
public static void prepare(Object controlsObj) {
((MediaController.TransportControls) controlsObj).prepare();
}
@@ -41,5 +41,11 @@
public static void prepareFromUri(Object controlsObj, Uri uri, Bundle extras) {
((MediaController.TransportControls) controlsObj).prepareFromUri(uri, extras);
}
+
+ private TransportControls() {
+ }
+ }
+
+ private MediaControllerCompatApi24() {
}
}
diff --git a/media/api24/android/support/v4/media/session/MediaSessionCompatApi24.java b/media/api24/android/support/v4/media/session/MediaSessionCompatApi24.java
index 2522e5b..818277f 100644
--- a/media/api24/android/support/v4/media/session/MediaSessionCompatApi24.java
+++ b/media/api24/android/support/v4/media/session/MediaSessionCompatApi24.java
@@ -78,4 +78,7 @@
mCallback.onPrepareFromUri(uri, extras);
}
}
+
+ private MediaSessionCompatApi24() {
+ }
}
diff --git a/media/api26/android/support/v4/media/MediaBrowserCompatApi26.java b/media/api26/android/support/v4/media/MediaBrowserCompatApi26.java
index 5d556d3..e156c06 100644
--- a/media/api26/android/support/v4/media/MediaBrowserCompatApi26.java
+++ b/media/api26/android/support/v4/media/MediaBrowserCompatApi26.java
@@ -64,4 +64,7 @@
mSubscriptionCallback.onError(parentId, options);
}
}
+
+ private MediaBrowserCompatApi26() {
+ }
}
diff --git a/media/api26/androidx/media/MediaBrowserServiceCompatApi26.java b/media/api26/androidx/media/MediaBrowserServiceCompatApi26.java
index 0f250dd..99b13a3 100644
--- a/media/api26/androidx/media/MediaBrowserServiceCompatApi26.java
+++ b/media/api26/androidx/media/MediaBrowserServiceCompatApi26.java
@@ -106,4 +106,7 @@
parentId, new ResultWrapper(result), options);
}
}
+
+ private MediaBrowserServiceCompatApi26() {
+ }
}
diff --git a/media/src/main/java/androidx/media/MediaBrowserCompatUtils.java b/media/src/main/java/androidx/media/MediaBrowserCompatUtils.java
index d3f8bbb..c553256 100644
--- a/media/src/main/java/androidx/media/MediaBrowserCompatUtils.java
+++ b/media/src/main/java/androidx/media/MediaBrowserCompatUtils.java
@@ -77,4 +77,7 @@
}
return false;
}
+
+ private MediaBrowserCompatUtils() {
+ }
}
diff --git a/media/src/main/java/androidx/media/MediaBrowserProtocol.java b/media/src/main/java/androidx/media/MediaBrowserProtocol.java
index 251158f..5c85880 100644
--- a/media/src/main/java/androidx/media/MediaBrowserProtocol.java
+++ b/media/src/main/java/androidx/media/MediaBrowserProtocol.java
@@ -200,4 +200,7 @@
* - replyTo : Callback messenger
*/
public static final int CLIENT_MSG_SEND_CUSTOM_ACTION = 9;
+
+ private MediaBrowserProtocol() {
+ }
}
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/lib/src/main/java/android/support/mediacompat/testlib/MediaBrowserConstants.java b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaBrowserConstants.java
index f961308..b4bea4e 100644
--- a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaBrowserConstants.java
+++ b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaBrowserConstants.java
@@ -63,4 +63,7 @@
"test_media_id_children_2", "test_media_id_children_3",
MEDIA_ID_CHILDREN_DELAYED
};
+
+ private MediaBrowserConstants() {
+ }
}
diff --git a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaControllerConstants.java b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaControllerConstants.java
index 4978888..e81f7d9 100644
--- a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaControllerConstants.java
+++ b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaControllerConstants.java
@@ -52,4 +52,7 @@
public static final int SET_CAPTIONING_ENABLED = 320;
public static final int SET_REPEAT_MODE = 321;
public static final int SET_SHUFFLE_MODE = 322;
+
+ private MediaControllerConstants() {
+ }
}
diff --git a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaSessionConstants.java b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaSessionConstants.java
index c0a64d4..ffc24a6 100644
--- a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaSessionConstants.java
+++ b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaSessionConstants.java
@@ -57,4 +57,7 @@
public static final int TEST_ERROR_CODE = 0x3;
public static final String TEST_ERROR_MSG = "test-error-msg";
+
+ private MediaSessionConstants() {
+ }
}
diff --git a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/VersionConstants.java b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/VersionConstants.java
index 4b217b1..5d877f2 100644
--- a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/VersionConstants.java
+++ b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/VersionConstants.java
@@ -25,4 +25,7 @@
public static final String VERSION_TOT = "tot";
public static final String VERSION_PREVIOUS = "previous";
+
+ private VersionConstants() {
+ }
}
diff --git a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/IntentUtil.java b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/IntentUtil.java
index 8d58a6f..14ba14c 100644
--- a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/IntentUtil.java
+++ b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/IntentUtil.java
@@ -129,4 +129,7 @@
}
return intent;
}
+
+ private IntentUtil() {
+ }
}
diff --git a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/TestUtil.java b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/TestUtil.java
index 21fd223..b06b219 100644
--- a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/TestUtil.java
+++ b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/TestUtil.java
@@ -38,4 +38,7 @@
assertEquals(expected.get(key), observed.get(key));
}
}
+
+ private TestUtil() {
+ }
}
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/mediarouter/api24/androidx/mediarouter/media/MediaRouterApi24.java b/mediarouter/api24/androidx/mediarouter/media/MediaRouterApi24.java
index f47b873..800ea22 100644
--- a/mediarouter/api24/androidx/mediarouter/media/MediaRouterApi24.java
+++ b/mediarouter/api24/androidx/mediarouter/media/MediaRouterApi24.java
@@ -24,5 +24,11 @@
public static int getDeviceType(Object routeObj) {
return ((android.media.MediaRouter.RouteInfo)routeObj).getDeviceType();
}
+
+ private RouteInfo() {
+ }
+ }
+
+ private MediaRouterApi24() {
}
}
diff --git a/mediarouter/jellybean-mr1/androidx/mediarouter/media/MediaRouterJellybeanMr1.java b/mediarouter/jellybean-mr1/androidx/mediarouter/media/MediaRouterJellybeanMr1.java
index c08f79e..9ca6313 100644
--- a/mediarouter/jellybean-mr1/androidx/mediarouter/media/MediaRouterJellybeanMr1.java
+++ b/mediarouter/jellybean-mr1/androidx/mediarouter/media/MediaRouterJellybeanMr1.java
@@ -52,6 +52,9 @@
}
return null;
}
+
+ private RouteInfo() {
+ }
}
public static interface Callback extends MediaRouterJellybean.Callback {
@@ -183,4 +186,7 @@
mCallback.onRoutePresentationDisplayChanged(route);
}
}
+
+ private MediaRouterJellybeanMr1() {
+ }
}
diff --git a/mediarouter/jellybean-mr2/androidx/mediarouter/media/MediaRouterJellybeanMr2.java b/mediarouter/jellybean-mr2/androidx/mediarouter/media/MediaRouterJellybeanMr2.java
index be796ac..bbb5f64 100644
--- a/mediarouter/jellybean-mr2/androidx/mediarouter/media/MediaRouterJellybeanMr2.java
+++ b/mediarouter/jellybean-mr2/androidx/mediarouter/media/MediaRouterJellybeanMr2.java
@@ -37,11 +37,20 @@
public static boolean isConnecting(Object routeObj) {
return ((android.media.MediaRouter.RouteInfo)routeObj).isConnecting();
}
+
+ private RouteInfo() {
+ }
}
public static final class UserRouteInfo {
public static void setDescription(Object routeObj, CharSequence description) {
((android.media.MediaRouter.UserRouteInfo)routeObj).setDescription(description);
}
+
+ private UserRouteInfo() {
+ }
+ }
+
+ private MediaRouterJellybeanMr2() {
}
}
diff --git a/mediarouter/jellybean/androidx/mediarouter/media/MediaRouterJellybean.java b/mediarouter/jellybean/androidx/mediarouter/media/MediaRouterJellybean.java
index c3f79e8..d73edb7 100644
--- a/mediarouter/jellybean/androidx/mediarouter/media/MediaRouterJellybean.java
+++ b/mediarouter/jellybean/androidx/mediarouter/media/MediaRouterJellybean.java
@@ -197,6 +197,9 @@
public static boolean isGroup(Object routeObj) {
return routeObj instanceof android.media.MediaRouter.RouteGroup;
}
+
+ private RouteInfo() {
+ }
}
public static final class RouteGroup {
@@ -211,6 +214,9 @@
}
return out;
}
+
+ private RouteGroup() {
+ }
}
public static final class UserRouteInfo {
@@ -255,6 +261,9 @@
((android.media.MediaRouter.UserRouteInfo)routeObj).setRemoteControlClient(
(android.media.RemoteControlClient)rccObj);
}
+
+ private UserRouteInfo() {
+ }
}
public static final class RouteCategory {
@@ -276,6 +285,9 @@
public static boolean isGroupable(Object categoryObj) {
return ((android.media.MediaRouter.RouteCategory)categoryObj).isGroupable();
}
+
+ private RouteCategory() {
+ }
}
public static interface Callback {
@@ -461,4 +473,7 @@
mCallback.onVolumeUpdateRequest(route, direction);
}
}
+
+ private MediaRouterJellybean() {
+ }
}
diff --git a/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteDialogHelper.java b/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteDialogHelper.java
index a418d5b..c5e53ee 100644
--- a/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteDialogHelper.java
+++ b/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteDialogHelper.java
@@ -149,4 +149,7 @@
view.draw(canvas);
return new BitmapDrawable(context.getResources(), bitmap);
}
+
+ private MediaRouteDialogHelper() {
+ }
}
diff --git a/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderProtocol.java b/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderProtocol.java
index 805e9f4..fcf4ba5 100644
--- a/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderProtocol.java
+++ b/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderProtocol.java
@@ -227,4 +227,7 @@
return false;
}
}
+
+ private MediaRouteProviderProtocol() {
+ }
}
diff --git a/paging/common/src/main/java/androidx/paging/PagedList.java b/paging/common/src/main/java/androidx/paging/PagedList.java
index 2878290..a11a731 100644
--- a/paging/common/src/main/java/androidx/paging/PagedList.java
+++ b/paging/common/src/main/java/androidx/paging/PagedList.java
@@ -138,7 +138,7 @@
private final AtomicBoolean mDetached = new AtomicBoolean(false);
- protected final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
+ private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
PagedList(@NonNull PagedStorage<T> storage,
@NonNull Executor mainThreadExecutor,
@@ -158,10 +158,10 @@
*
*
* @param dataSource DataSource providing data to the PagedList
- * @param mainThreadExecutor Thread that will use and consume data from the PagedList.
- * Generally, this is the UI/main thread.
- * @param backgroundThreadExecutor Data loading will be done via this executor - should be a
- * background thread.
+ * @param notifyExecutor Thread that will use and consume data from the PagedList.
+ * Generally, this is the UI/main thread.
+ * @param fetchExecutor Data loading will be done via this executor -
+ * should be a background thread.
* @param boundaryCallback Optional boundary callback to attach to the list.
* @param config PagedList Config, which defines how the PagedList will load data.
* @param <K> Key type that indicates to the DataSource what data to load.
@@ -171,8 +171,8 @@
*/
@NonNull
private static <K, T> PagedList<T> create(@NonNull DataSource<K, T> dataSource,
- @NonNull Executor mainThreadExecutor,
- @NonNull Executor backgroundThreadExecutor,
+ @NonNull Executor notifyExecutor,
+ @NonNull Executor fetchExecutor,
@Nullable BoundaryCallback<T> boundaryCallback,
@NonNull Config config,
@Nullable K key) {
@@ -188,16 +188,16 @@
}
ContiguousDataSource<K, T> contigDataSource = (ContiguousDataSource<K, T>) dataSource;
return new ContiguousPagedList<>(contigDataSource,
- mainThreadExecutor,
- backgroundThreadExecutor,
+ notifyExecutor,
+ fetchExecutor,
boundaryCallback,
config,
key,
lastLoad);
} else {
return new TiledPagedList<>((PositionalDataSource<T>) dataSource,
- mainThreadExecutor,
- backgroundThreadExecutor,
+ notifyExecutor,
+ fetchExecutor,
boundaryCallback,
config,
(key != null) ? (Integer) key : 0);
@@ -224,8 +224,8 @@
public static final class Builder<Key, Value> {
private final DataSource<Key, Value> mDataSource;
private final Config mConfig;
- private Executor mMainThreadExecutor;
- private Executor mBackgroundThreadExecutor;
+ private Executor mNotifyExecutor;
+ private Executor mFetchExecutor;
private BoundaryCallback mBoundaryCallback;
private Key mInitialKey;
@@ -264,30 +264,31 @@
this(dataSource, new PagedList.Config.Builder().setPageSize(pageSize).build());
}
/**
- * The executor defining where main/UI thread for page loading updates.
+ * The executor defining where page loading updates are dispatched.
*
- * @param mainThreadExecutor Executor for main/UI thread to receive {@link Callback} calls.
+ * @param notifyExecutor Executor that receives PagedList updates, and where
+ * {@link Callback} calls are dispatched. Generally, this is the ui/main thread.
* @return this
*/
@NonNull
- public Builder<Key, Value> setMainThreadExecutor(@NonNull Executor mainThreadExecutor) {
- mMainThreadExecutor = mainThreadExecutor;
+ public Builder<Key, Value> setNotifyExecutor(@NonNull Executor notifyExecutor) {
+ mNotifyExecutor = notifyExecutor;
return this;
}
/**
- * The executor on which background loading will be run.
- * <p>
- * Does not affect initial load, which will be done on whichever thread the PagedList is
- * created on.
+ * The executor used to fetch additional pages from the DataSource.
*
- * @param backgroundThreadExecutor Executor for background DataSource loading.
+ * Does not affect initial load, which will be done immediately on whichever thread the
+ * PagedList is created on.
+ *
+ * @param fetchExecutor Executor used to fetch from DataSources, generally a background
+ * thread pool for e.g. I/O or network loading.
* @return this
*/
@NonNull
- public Builder<Key, Value> setBackgroundThreadExecutor(
- @NonNull Executor backgroundThreadExecutor) {
- mBackgroundThreadExecutor = backgroundThreadExecutor;
+ public Builder<Key, Value> setFetchExecutor(@NonNull Executor fetchExecutor) {
+ mFetchExecutor = fetchExecutor;
return this;
}
@@ -350,18 +351,18 @@
@NonNull
public PagedList<Value> build() {
// TODO: define defaults, once they can be used in module without android dependency
- if (mMainThreadExecutor == null) {
+ if (mNotifyExecutor == null) {
throw new IllegalArgumentException("MainThreadExecutor required");
}
- if (mBackgroundThreadExecutor == null) {
+ if (mFetchExecutor == null) {
throw new IllegalArgumentException("BackgroundThreadExecutor required");
}
//noinspection unchecked
return PagedList.create(
mDataSource,
- mMainThreadExecutor,
- mBackgroundThreadExecutor,
+ mNotifyExecutor,
+ mFetchExecutor,
mBoundaryCallback,
mConfig,
mInitialKey);
@@ -731,7 +732,7 @@
* Callback signaling when content is loaded into the list.
* <p>
* Can be used to listen to items being paged in and out. These calls will be dispatched on
- * the executor defined by {@link Builder#setMainThreadExecutor(Executor)}, which defaults to
+ * the executor defined by {@link Builder#setNotifyExecutor(Executor)}, which is generally
* the main/UI thread.
*/
public abstract static class Callback {
diff --git a/paging/common/src/test/java/androidx/paging/TiledPagedListTest.kt b/paging/common/src/test/java/androidx/paging/TiledPagedListTest.kt
index 2baf0aa..83eb20c 100644
--- a/paging/common/src/test/java/androidx/paging/TiledPagedListTest.kt
+++ b/paging/common/src/test/java/androidx/paging/TiledPagedListTest.kt
@@ -303,8 +303,8 @@
.setEnablePlaceholders(false)
.build()
val pagedList = PagedList.Builder<Int, Item>(ListDataSource(ITEMS), config)
- .setMainThreadExecutor(mMainThread)
- .setBackgroundThreadExecutor(mBackgroundThread)
+ .setNotifyExecutor(mMainThread)
+ .setFetchExecutor(mBackgroundThread)
.setInitialKey(20)
.build()
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagedListDifferTest.kt b/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagedListDifferTest.kt
index 169779d..8a53b78 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagedListDifferTest.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/AsyncPagedListDifferTest.kt
@@ -58,8 +58,8 @@
config: PagedList.Config, data: List<V>, initialKey: Int): PagedList<V> {
return PagedList.Builder<Int, V>(ListDataSource(data), config)
.setInitialKey(initialKey)
- .setMainThreadExecutor(mMainThread)
- .setBackgroundThreadExecutor(mPageLoadingThread)
+ .setNotifyExecutor(mMainThread)
+ .setFetchExecutor(mPageLoadingThread)
.build()
}
diff --git a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
index 54955a0..14809c8 100644
--- a/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
+++ b/paging/runtime/src/androidTest/java/androidx/paging/LivePagedListBuilderTest.kt
@@ -96,7 +96,7 @@
// overriding default arch IO executor
val livePagedList = LivePagedListBuilder(
MockDataSource.Factory(), 2)
- .setBackgroundThreadExecutor(backgroundExecutor)
+ .setFetchExecutor(backgroundExecutor)
.build()
val pagedListHolder: Array<PagedList<String>?> = arrayOfNulls(1)
diff --git a/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.java b/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.java
index 75c632f..f8af819 100644
--- a/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.java
+++ b/paging/runtime/src/main/java/androidx/paging/LivePagedListBuilder.java
@@ -41,7 +41,7 @@
private PagedList.Config mConfig;
private DataSource.Factory<Key, Value> mDataSourceFactory;
private PagedList.BoundaryCallback mBoundaryCallback;
- private Executor mBackgroundThreadExecutor;
+ private Executor mFetchExecutor = ArchTaskExecutor.getIOThreadExecutor();
/**
* Creates a LivePagedListBuilder with required parameters.
@@ -51,6 +51,15 @@
*/
public LivePagedListBuilder(@NonNull DataSource.Factory<Key, Value> dataSourceFactory,
@NonNull PagedList.Config config) {
+ //noinspection ConstantConditions
+ if (config == null) {
+ throw new IllegalArgumentException("PagedList.Config must be provided");
+ }
+ //noinspection ConstantConditions
+ if (dataSourceFactory == null) {
+ throw new IllegalArgumentException("DataSource.Factory must be provided");
+ }
+
mDataSourceFactory = dataSourceFactory;
mConfig = config;
}
@@ -116,21 +125,18 @@
}
/**
- * Sets executor which will be used for background loading of pages.
+ * Sets executor used for background fetching of PagedLists, and the pages within.
* <p>
* If not set, defaults to the Arch components I/O thread.
- * <p>
- * Does not affect initial load, which will be always be done on done on the Arch components
- * I/O thread.
*
- * @param backgroundThreadExecutor Executor for background DataSource loading.
+ * @param fetchExecutor Executor for fetching data from DataSources.
* @return this
*/
@SuppressWarnings("unused")
@NonNull
- public LivePagedListBuilder<Key, Value> setBackgroundThreadExecutor(
- @NonNull Executor backgroundThreadExecutor) {
- mBackgroundThreadExecutor = backgroundThreadExecutor;
+ public LivePagedListBuilder<Key, Value> setFetchExecutor(
+ @NonNull Executor fetchExecutor) {
+ mFetchExecutor = fetchExecutor;
return this;
}
@@ -144,18 +150,8 @@
*/
@NonNull
public LiveData<PagedList<Value>> build() {
- if (mConfig == null) {
- throw new IllegalArgumentException("PagedList.Config must be provided");
- }
- if (mDataSourceFactory == null) {
- throw new IllegalArgumentException("DataSource.Factory must be provided");
- }
- if (mBackgroundThreadExecutor == null) {
- mBackgroundThreadExecutor = ArchTaskExecutor.getIOThreadExecutor();
- }
-
return create(mInitialLoadKey, mConfig, mBoundaryCallback, mDataSourceFactory,
- ArchTaskExecutor.getMainThreadExecutor(), mBackgroundThreadExecutor);
+ ArchTaskExecutor.getMainThreadExecutor(), mFetchExecutor);
}
@AnyThread
@@ -165,9 +161,9 @@
@NonNull final PagedList.Config config,
@Nullable final PagedList.BoundaryCallback boundaryCallback,
@NonNull final DataSource.Factory<Key, Value> dataSourceFactory,
- @NonNull final Executor mainThreadExecutor,
- @NonNull final Executor backgroundThreadExecutor) {
- return new ComputableLiveData<PagedList<Value>>(backgroundThreadExecutor) {
+ @NonNull final Executor notifyExecutor,
+ @NonNull final Executor fetchExecutor) {
+ return new ComputableLiveData<PagedList<Value>>(fetchExecutor) {
@Nullable
private PagedList<Value> mList;
@Nullable
@@ -198,8 +194,8 @@
mDataSource.addInvalidatedCallback(mCallback);
mList = new PagedList.Builder<>(mDataSource, config)
- .setMainThreadExecutor(mainThreadExecutor)
- .setBackgroundThreadExecutor(backgroundThreadExecutor)
+ .setNotifyExecutor(notifyExecutor)
+ .setFetchExecutor(fetchExecutor)
.setBoundaryCallback(boundaryCallback)
.setInitialKey(initializeKey)
.build();
diff --git a/persistence/db/build.gradle b/persistence/db/build.gradle
index 5c9cb2d..f2e6c8c 100644
--- a/persistence/db/build.gradle
+++ b/persistence/db/build.gradle
@@ -25,6 +25,8 @@
dependencies {
api(SUPPORT_ANNOTATIONS)
+ testImplementation(JUNIT)
+ testImplementation(MOCKITO_CORE)
}
// Used by testCompile in room-compiler
diff --git a/persistence/db/src/main/java/androidx/sqlite/db/SimpleSQLiteQuery.java b/persistence/db/src/main/java/androidx/sqlite/db/SimpleSQLiteQuery.java
index bdcbb9f..58c330c 100644
--- a/persistence/db/src/main/java/androidx/sqlite/db/SimpleSQLiteQuery.java
+++ b/persistence/db/src/main/java/androidx/sqlite/db/SimpleSQLiteQuery.java
@@ -16,12 +16,15 @@
package androidx.sqlite.db;
+import androidx.annotation.Nullable;
+
/**
* A basic implementation of {@link SupportSQLiteQuery} which receives a query and its args and
* binds args based on the passed in Object type.
*/
public final class SimpleSQLiteQuery implements SupportSQLiteQuery {
private final String mQuery;
+ @Nullable
private final Object[] mBindArgs;
/**
@@ -30,7 +33,7 @@
* @param query The query string, can include bind arguments (.e.g ?).
* @param bindArgs The bind argument value that will replace the placeholders in the query.
*/
- public SimpleSQLiteQuery(String query, Object[] bindArgs) {
+ public SimpleSQLiteQuery(String query, @Nullable Object[] bindArgs) {
mQuery = query;
mBindArgs = bindArgs;
}
@@ -56,7 +59,7 @@
@Override
public int getArgCount() {
- return mBindArgs.length;
+ return mBindArgs == null ? 0 : mBindArgs.length;
}
/**
diff --git a/persistence/db/src/test/java/androidx/sqlite/db/SimpleSQLiteQueryTestTest.java b/persistence/db/src/test/java/androidx/sqlite/db/SimpleSQLiteQueryTestTest.java
new file mode 100644
index 0000000..7b04a2b
--- /dev/null
+++ b/persistence/db/src/test/java/androidx/sqlite/db/SimpleSQLiteQueryTestTest.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.sqlite.db;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
+
+@RunWith(JUnit4.class)
+public class SimpleSQLiteQueryTestTest {
+
+ @Test
+ public void getSql() {
+ SimpleSQLiteQuery query = new SimpleSQLiteQuery("foo");
+ assertThat(query.getSql(), is("foo"));
+ }
+
+ @Test
+ public void bindTo_noArgs() {
+ SimpleSQLiteQuery query = new SimpleSQLiteQuery("foo");
+ SupportSQLiteProgram program = Mockito.mock(SupportSQLiteProgram.class);
+ query.bindTo(program);
+ verifyNoMoreInteractions(program);
+ }
+
+ @Test
+ public void bindTo_withArgs() {
+ byte[] bytes = new byte[3];
+ SimpleSQLiteQuery query = new SimpleSQLiteQuery("foo",
+ new Object[]{"bar", 2, true, .5f, null, bytes});
+ SupportSQLiteProgram program = Mockito.mock(SupportSQLiteProgram.class);
+ query.bindTo(program);
+ verify(program).bindString(1, "bar");
+ verify(program).bindLong(2, 2);
+ verify(program).bindLong(3, 1);
+ verify(program).bindDouble(4, .5f);
+ verify(program).bindNull(5);
+ verify(program).bindBlob(6, bytes);
+ verifyNoMoreInteractions(program);
+ }
+
+ @Test
+ public void getArgCount_withArgs() {
+ SimpleSQLiteQuery query = new SimpleSQLiteQuery("foo",
+ new Object[]{"bar", 2, true});
+ assertThat(query.getArgCount(), is(3));
+ }
+
+ @Test
+ public void getArgCount_noArgs() {
+ SimpleSQLiteQuery query = new SimpleSQLiteQuery("foo");
+ assertThat(query.getArgCount(), is(0));
+ }
+}
diff --git a/preference/res/layout-v17/preference_category_material.xml b/preference/res/layout-v17/preference_category_material.xml
index db3abfe..fc39db3 100644
--- a/preference/res/layout-v17/preference_category_material.xml
+++ b/preference/res/layout-v17/preference_category_material.xml
@@ -15,13 +15,49 @@
~ limitations under the License
-->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/title"
+<FrameLayout
+ 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="wrap_content"
- android:layout_marginBottom="16dip"
- android:textAppearance="@style/Preference_TextAppearanceMaterialBody2"
- android:textColor="@color/preference_fallback_accent_color"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingTop="16dip" />
+ android:layout_marginBottom="8dp"
+ android:layout_marginTop="8dp"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:orientation="horizontal">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:maxHeight="18dp"
+ app:maxWidth="18dp"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingStart="@dimen/preference_category_padding_start">
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:textAlignment="viewStart"
+ android:textColor="@color/preference_fallback_accent_color"/>
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textColor="?android:attr/textColorSecondary"/>
+ </LinearLayout>
+
+</FrameLayout>
diff --git a/preference/res/layout-v17/preference_widget_seekbar_material.xml b/preference/res/layout-v17/preference_widget_seekbar_material.xml
new file mode 100644
index 0000000..bc426b9
--- /dev/null
+++ b/preference/res/layout-v17/preference_widget_seekbar_material.xml
@@ -0,0 +1,115 @@
+<?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.
+-->
+
+<!-- Layout used by SeekBarPreference for the seekbar widget style. -->
+<LinearLayout
+ 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="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="-4dp"
+ android:minWidth="60dp"
+ android:gravity="start|center_vertical"
+ android:orientation="horizontal"
+ android:paddingEnd="12dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:maxWidth="48dp"
+ app:maxHeight="48dp" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dip"
+ android:layout_marginTop="6dip"
+ android:layout_marginBottom="6dip"
+ android:layout_weight="1"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="@style/Preference_TextAppearanceMaterialSubhead"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"/>
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="4"/>
+
+ <!-- Using UnPressableLinearLayout as a workaround to disable the pressed state propagation
+ to the children of this container layout. Otherwise, the animated pressed state will also
+ play for the thumb in the AbsSeekBar in addition to the preference's ripple background.
+ The background of the SeekBar is also set to null to disable the ripple background -->
+ <androidx.preference.UnPressableLinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/summary"
+ android:layout_alignStart="@android:id/title"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+ <SeekBar
+ android:id="@+id/seekbar"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/preference_seekbar_padding_start"
+ android:paddingEnd="@dimen/preference_seekbar_padding_end"
+ android:focusable="false"
+ android:clickable="false"
+ android:background="@null" />
+
+ <TextView
+ android:id="@+id/seekbar_value"
+ android:layout_width="@dimen/preference_seekbar_value_width"
+ android:layout_height="match_parent"
+ android:gravity="right|center_vertical"
+ android:fontFamily="sans-serif-condensed"
+ android:singleLine="true"
+ android:textAppearance="@style/Preference_TextAppearanceMaterialSubhead"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:scrollbars="none"/>
+ </androidx.preference.UnPressableLinearLayout>
+
+ </RelativeLayout>
+
+</LinearLayout>
diff --git a/preference/res/layout-v21/preference_category_material.xml b/preference/res/layout-v21/preference_category_material.xml
index dad9a5c..bea9a05 100644
--- a/preference/res/layout-v21/preference_category_material.xml
+++ b/preference/res/layout-v21/preference_category_material.xml
@@ -15,13 +15,52 @@
~ limitations under the License
-->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/title"
+<FrameLayout
+ 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="wrap_content"
- android:layout_marginBottom="16dip"
- android:textAppearance="@android:style/TextAppearance.Material.Body2"
- android:textColor="?android:attr/colorAccent"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingTop="16dip" />
+ android:layout_marginBottom="8dp"
+ android:layout_marginTop="8dp"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:orientation="horizontal">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:maxHeight="18dp"
+ app:maxWidth="18dp"
+ android:tint="?android:attr/textColorPrimary"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingStart="@dimen/preference_category_padding_start">
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:textAlignment="viewStart"
+ android:textAppearance="@android:style/TextAppearance.Material.Body2"
+ android:textColor="?android:attr/colorAccent"/>
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"/>
+ </LinearLayout>
+
+</FrameLayout>
diff --git a/preference/res/layout-v21/preference_dropdown_material.xml b/preference/res/layout-v21/preference_dropdown_material.xml
index bffd3b3..fcafcae 100644
--- a/preference/res/layout-v21/preference_dropdown_material.xml
+++ b/preference/res/layout-v21/preference_dropdown_material.xml
@@ -15,74 +15,18 @@
~ limitations under the License
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:gravity="center_vertical"
- android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
- android:paddingRight="?android:attr/listPreferredItemPaddingRight"
- android:background="?android:attr/selectableItemBackground"
- android:clipToPadding="false"
- android:focusable="true" >
+ android:layout_height="wrap_content">
<Spinner
android:id="@+id/spinner"
+ android:layout_marginStart="@dimen/preference_no_icon_padding_start"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="invisible" />
- <LinearLayout
- android:id="@+id/icon_frame"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="-4dp"
- android:minWidth="60dp"
- android:gravity="start|center_vertical"
- android:orientation="horizontal"
- android:paddingRight="12dp"
- android:paddingTop="4dp"
- android:paddingBottom="4dp">
- <androidx.preference.internal.PreferenceImageView
- android:id="@android:id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:maxWidth="48dp"
- app:maxHeight="48dp" />
- </LinearLayout>
+ <include layout="@layout/preference_material"/>
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingTop="16dp"
- android:paddingBottom="16dp">
-
- <TextView android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:textAppearance="@style/Preference_TextAppearanceMaterialSubhead"
- android:ellipsize="marquee" />
-
- <TextView android:id="@android:id/summary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@android:id/title"
- android:layout_alignStart="@android:id/title"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorSecondary"
- android:maxLines="10" />
-
- </RelativeLayout>
-
- <!-- Preference should place its actual preference widget here. -->
- <LinearLayout android:id="@android:id/widget_frame"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="end|center_vertical"
- android:paddingLeft="16dp"
- android:orientation="vertical" />
-
-</LinearLayout>
+</FrameLayout>
diff --git a/preference/res/layout-v21/preference_widget_seekbar_material.xml b/preference/res/layout-v21/preference_widget_seekbar_material.xml
new file mode 100644
index 0000000..b31400d
--- /dev/null
+++ b/preference/res/layout-v21/preference_widget_seekbar_material.xml
@@ -0,0 +1,116 @@
+<?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.
+-->
+
+<!-- Layout used by SeekBarPreference for the seekbar widget style. -->
+<LinearLayout
+ 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="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="-4dp"
+ android:minWidth="60dp"
+ android:gravity="start|center_vertical"
+ android:orientation="horizontal"
+ android:paddingStart="4dp"
+ android:paddingEnd="8dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:maxWidth="48dp"
+ app:maxHeight="48dp" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dip"
+ android:layout_marginTop="6dip"
+ android:layout_marginBottom="6dip"
+ android:layout_weight="1"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="@style/Preference_TextAppearanceMaterialSubhead"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"/>
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="4"/>
+
+ <!-- Using UnPressableLinearLayout as a workaround to disable the pressed state propagation
+ to the children of this container layout. Otherwise, the animated pressed state will also
+ play for the thumb in the AbsSeekBar in addition to the preference's ripple background.
+ The background of the SeekBar is also set to null to disable the ripple background -->
+ <androidx.preference.UnPressableLinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/summary"
+ android:layout_alignStart="@android:id/title"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+ <SeekBar
+ android:id="@+id/seekbar"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/preference_seekbar_padding_start"
+ android:paddingEnd="@dimen/preference_seekbar_padding_end"
+ android:focusable="false"
+ android:clickable="false"
+ android:background="@null" />
+
+ <TextView
+ android:id="@+id/seekbar_value"
+ android:layout_width="@dimen/preference_seekbar_value_width"
+ android:layout_height="match_parent"
+ android:gravity="right|center_vertical"
+ android:fontFamily="sans-serif-condensed"
+ android:singleLine="true"
+ android:textAppearance="@style/Preference_TextAppearanceMaterialSubhead"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:scrollbars="none"/>
+ </androidx.preference.UnPressableLinearLayout>
+
+ </RelativeLayout>
+
+</LinearLayout>
diff --git a/preference/res/layout/expand_button.xml b/preference/res/layout/expand_button.xml
index 16c8143..bb99ebf 100644
--- a/preference/res/layout/expand_button.xml
+++ b/preference/res/layout/expand_button.xml
@@ -57,7 +57,8 @@
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceListItem"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorPrimary"
android:ellipsize="marquee"/>
<TextView
diff --git a/preference/res/layout/preference_category_material.xml b/preference/res/layout/preference_category_material.xml
index e366e7a..3c48792 100644
--- a/preference/res/layout/preference_category_material.xml
+++ b/preference/res/layout/preference_category_material.xml
@@ -15,13 +15,49 @@
~ limitations under the License
-->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/title"
+<FrameLayout
+ 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="wrap_content"
- android:layout_marginBottom="16dip"
- android:textAppearance="@style/Preference_TextAppearanceMaterialBody2"
- android:textColor="@color/preference_fallback_accent_color"
- android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
- android:paddingRight="?android:attr/listPreferredItemPaddingRight"
- android:paddingTop="16dip" />
+ android:layout_marginBottom="8dp"
+ android:layout_marginTop="8dp"
+ android:paddingLeft="?android:attr/listPreferredItemPaddingLeft">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:orientation="horizontal">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:maxHeight="18dp"
+ app:maxWidth="18dp"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="@dimen/preference_category_padding_start">
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+ android:textAlignment="viewStart"
+ android:textColor="@color/preference_fallback_accent_color"/>
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textColor="?android:attr/textColorSecondary"/>
+ </LinearLayout>
+
+</FrameLayout>
diff --git a/preference/res/layout/preference_widget_seekbar_material.xml b/preference/res/layout/preference_widget_seekbar_material.xml
index 8bb80d1..bae0a75 100644
--- a/preference/res/layout/preference_widget_seekbar_material.xml
+++ b/preference/res/layout/preference_widget_seekbar_material.xml
@@ -16,25 +16,38 @@
<!-- Layout used by SeekBarPreference for the seekbar widget style. -->
<LinearLayout 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="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
- android:paddingEnd="?android:attr/scrollbarSize"
+ android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+ android:paddingRight="?android:attr/listPreferredItemPaddingRight"
android:clipChildren="false"
android:clipToPadding="false">
- <ImageView
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="-4dp"
+ android:minWidth="60dp"
+ android:gravity="start|center_vertical"
+ android:orientation="horizontal"
+ android:paddingRight="12dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <androidx.preference.internal.PreferenceImageView
android:id="@android:id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:minWidth="@dimen/preference_icon_minWidth"/>
+ app:maxWidth="48dp"
+ app:maxHeight="48dp" />
+ </LinearLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="16dip"
android:layout_marginEnd="8dip"
android:layout_marginTop="6dip"
android:layout_marginBottom="6dip"
@@ -89,7 +102,8 @@
android:singleLine="true"
android:textAppearance="@style/Preference_TextAppearanceMaterialSubhead"
android:ellipsize="marquee"
- android:fadingEdge="horizontal"/>
+ android:fadingEdge="horizontal"
+ android:scrollbars="none"/>
</androidx.preference.UnPressableLinearLayout>
</RelativeLayout>
diff --git a/preference/res/values-sw360dp/config.xml b/preference/res/values-sw360dp/config.xml
new file mode 100644
index 0000000..18345c8
--- /dev/null
+++ b/preference/res/values-sw360dp/config.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.
+-->
+<resources>
+ <bool name="config_materialPreferenceIconSpaceReserved">true</bool>
+</resources>
diff --git a/preference/res/values-sw360dp/dimens.xml b/preference/res/values-sw360dp/dimens.xml
new file mode 100644
index 0000000..e507017
--- /dev/null
+++ b/preference/res/values-sw360dp/dimens.xml
@@ -0,0 +1,19 @@
+<?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>
+ <!-- The width of the padding for PreferenceCategory's title/summary -->
+ <dimen name="preference_category_padding_start">56dp</dimen>
+</resources>
diff --git a/preference/res/values-v21/styles.xml b/preference/res/values-v21/styles.xml
new file mode 100644
index 0000000..878b32c
--- /dev/null
+++ b/preference/res/values-v21/styles.xml
@@ -0,0 +1,20 @@
+<?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>
+ <dimen name="preference_no_icon_padding_start">72dp</dimen>
+</resources>
+
diff --git a/preference/res/values/config.xml b/preference/res/values/config.xml
new file mode 100644
index 0000000..b81606e
--- /dev/null
+++ b/preference/res/values/config.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.
+-->
+<resources>
+ <bool name="config_materialPreferenceIconSpaceReserved">false</bool>
+</resources>
diff --git a/preference/res/values/dimens.xml b/preference/res/values/dimens.xml
index 4816e36..41a825b 100644
--- a/preference/res/values/dimens.xml
+++ b/preference/res/values/dimens.xml
@@ -10,4 +10,6 @@
<dimen name="preference_seekbar_padding_end">22dp</dimen>
<!-- The width of the TextView indicating the current value of the SeekBarPreference -->
<dimen name="preference_seekbar_value_width">36dp</dimen>
+ <!-- The width of the padding for PreferenceCategory's title/summary -->
+ <dimen name="preference_category_padding_start">0dp</dimen>
</resources>
\ No newline at end of file
diff --git a/preference/res/values/styles.xml b/preference/res/values/styles.xml
index be1797b..ce223ee 100644
--- a/preference/res/values/styles.xml
+++ b/preference/res/values/styles.xml
@@ -84,6 +84,10 @@
<style name="Preference.Material">
<item name="android:layout">@layout/preference_material</item>
+ <item name="allowDividerAbove">false</item>
+ <item name="allowDividerBelow">true</item>
+ <item name="singleLineTitle">false</item>
+ <item name="iconSpaceReserved">@bool/config_materialPreferenceIconSpaceReserved</item>
</style>
<style name="Preference.Information.Material">
@@ -94,40 +98,69 @@
<style name="Preference.Category.Material">
<item name="android:layout">@layout/preference_category_material</item>
+ <item name="allowDividerAbove">true</item>
+ <item name="allowDividerBelow">true</item>
+ <item name="iconSpaceReserved">@bool/config_materialPreferenceIconSpaceReserved</item>
</style>
<style name="Preference.CheckBoxPreference.Material">
<item name="android:layout">@layout/preference_material</item>
+ <item name="allowDividerAbove">false</item>
+ <item name="allowDividerBelow">true</item>
+ <item name="iconSpaceReserved">@bool/config_materialPreferenceIconSpaceReserved</item>
</style>
<style name="Preference.SwitchPreferenceCompat.Material">
<item name="android:layout">@layout/preference_material</item>
+ <item name="allowDividerAbove">false</item>
+ <item name="allowDividerBelow">true</item>
+ <item name="iconSpaceReserved">@bool/config_materialPreferenceIconSpaceReserved</item>
</style>
<style name="Preference.SwitchPreference.Material">
<item name="android:layout">@layout/preference_material</item>
+ <item name="allowDividerAbove">false</item>
+ <item name="allowDividerBelow">true</item>
+ <item name="singleLineTitle">false</item>
+ <item name="iconSpaceReserved">@bool/config_materialPreferenceIconSpaceReserved</item>
</style>
<style name="Preference.SeekBarPreference.Material">
<item name="android:layout">@layout/preference_widget_seekbar_material</item>
<item name="adjustable">true</item>
<item name="showSeekBarValue">true</item>
+ <item name="allowDividerAbove">false</item>
+ <item name="allowDividerBelow">true</item>
+ <item name="iconSpaceReserved">@bool/config_materialPreferenceIconSpaceReserved</item>
</style>
<style name="Preference.PreferenceScreen.Material">
<item name="android:layout">@layout/preference_material</item>
+ <item name="allowDividerAbove">false</item>
+ <item name="allowDividerBelow">true</item>
+ <item name="iconSpaceReserved">@bool/config_materialPreferenceIconSpaceReserved</item>
</style>
<style name="Preference.DialogPreference.Material">
<item name="android:layout">@layout/preference_material</item>
+ <item name="allowDividerAbove">false</item>
+ <item name="allowDividerBelow">true</item>
+ <item name="iconSpaceReserved">@bool/config_materialPreferenceIconSpaceReserved</item>
</style>
<style name="Preference.DialogPreference.EditTextPreference.Material">
<item name="android:layout">@layout/preference_material</item>
+ <item name="allowDividerAbove">false</item>
+ <item name="allowDividerBelow">true</item>
+ <item name="singleLineTitle">false</item>
+ <item name="iconSpaceReserved">@bool/config_materialPreferenceIconSpaceReserved</item>
</style>
<style name="Preference.DropDown.Material">
<item name="android:layout">@layout/preference_dropdown_material</item>
+ <item name="allowDividerAbove">false</item>
+ <item name="allowDividerBelow">true</item>
+ <item name="iconSpaceReserved">@bool/config_materialPreferenceIconSpaceReserved</item>
</style>
<style name="Preference_TextAppearanceMaterialBody2">
@@ -146,6 +179,7 @@
<style name="PreferenceFragment.Material">
<item name="android:divider">@drawable/preference_list_divider_material</item>
+ <item name="allowDividerAfterLastItem">false</item>
</style>
<style name="PreferenceFragmentList.Material">
diff --git a/preference/res/values/themes.xml b/preference/res/values/themes.xml
index 1f8f158..598c24f 100644
--- a/preference/res/values/themes.xml
+++ b/preference/res/values/themes.xml
@@ -51,5 +51,6 @@
<item name="editTextPreferenceStyle">@style/Preference.DialogPreference.EditTextPreference.Material</item>
<item name="dropdownPreferenceStyle">@style/Preference.DropDown.Material</item>
<item name="preferenceFragmentListStyle">@style/PreferenceFragmentList.Material</item>
+ <item name="android:scrollbars">vertical</item>
</style>
</resources>
diff --git a/preference/src/main/java/androidx/preference/AndroidResources.java b/preference/src/main/java/androidx/preference/AndroidResources.java
index 00b14f4..ec12596 100644
--- a/preference/src/main/java/androidx/preference/AndroidResources.java
+++ b/preference/src/main/java/androidx/preference/AndroidResources.java
@@ -34,4 +34,6 @@
public static final int ANDROID_R_EDITTEXT_PREFERENCE_STYLE
= android.R.attr.editTextPreferenceStyle;
+ private AndroidResources() {
+ }
}
diff --git a/preference/src/main/java/androidx/preference/CollapsiblePreferenceGroupController.java b/preference/src/main/java/androidx/preference/CollapsiblePreferenceGroupController.java
index bcc71ce..3e1d792 100644
--- a/preference/src/main/java/androidx/preference/CollapsiblePreferenceGroupController.java
+++ b/preference/src/main/java/androidx/preference/CollapsiblePreferenceGroupController.java
@@ -133,7 +133,8 @@
private ExpandButton createExpandButton(final PreferenceGroup group,
List<Preference> collapsedPreferences) {
- final ExpandButton preference = new ExpandButton(mContext, collapsedPreferences);
+ final ExpandButton preference = new ExpandButton(mContext, collapsedPreferences,
+ group.getId());
preference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
@@ -150,10 +151,16 @@
* {@link PreferenceGroup}.
*/
static class ExpandButton extends Preference {
- ExpandButton(Context context, List<Preference> collapsedPreferences) {
+ private long mId;
+
+ ExpandButton(Context context, List<Preference> collapsedPreferences, long parentId) {
super(context);
initLayout();
setSummary(collapsedPreferences);
+ // Since IDs are unique, using the parentId as a reference ensures that this expand
+ // button will have a unique ID and hence transitions will be correctly animated by
+ // RecyclerView when there are multiple ExpandButtons.
+ mId = parentId + 1000000;
}
private void initLayout() {
@@ -201,6 +208,11 @@
super.onBindViewHolder(holder);
holder.setDividerAllowedAbove(false);
}
+
+ @Override
+ public long getId() {
+ return mId;
+ }
}
}
\ No newline at end of file
diff --git a/print/src/main/java/androidx/print/PrintHelper.java b/print/src/main/java/androidx/print/PrintHelper.java
index 2630940..5cee6d3 100644
--- a/print/src/main/java/androidx/print/PrintHelper.java
+++ b/print/src/main/java/androidx/print/PrintHelper.java
@@ -252,7 +252,6 @@
return;
}
- final int fittingMode = mScaleMode; // grab the fitting mode at time of call
PrintManager printManager =
(PrintManager) mContext.getSystemService(Context.PRINT_SERVICE);
PrintAttributes.MediaSize mediaSize;
@@ -267,42 +266,57 @@
.build();
printManager.print(jobName,
- new PrintDocumentAdapter() {
- private PrintAttributes mAttributes;
+ new PrintBitmapAdapter(jobName, mScaleMode, bitmap, callback), attr);
+ }
- @Override
- public void onLayout(PrintAttributes oldPrintAttributes,
- PrintAttributes newPrintAttributes,
- CancellationSignal cancellationSignal,
- LayoutResultCallback layoutResultCallback,
- Bundle bundle) {
+ @RequiresApi(19)
+ private class PrintBitmapAdapter extends PrintDocumentAdapter {
+ private final String mJobName;
+ private final int mFittingMode;
+ private final Bitmap mBitmap;
+ private final OnPrintFinishCallback mCallback;
+ private PrintAttributes mAttributes;
- mAttributes = newPrintAttributes;
+ PrintBitmapAdapter(String jobName, int fittingMode, Bitmap bitmap,
+ OnPrintFinishCallback callback) {
+ mJobName = jobName;
+ mFittingMode = fittingMode;
+ mBitmap = bitmap;
+ mCallback = callback;
+ }
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
- .setPageCount(1)
- .build();
- boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
- layoutResultCallback.onLayoutFinished(info, changed);
- }
+ @Override
+ public void onLayout(PrintAttributes oldPrintAttributes,
+ PrintAttributes newPrintAttributes,
+ CancellationSignal cancellationSignal,
+ LayoutResultCallback layoutResultCallback,
+ Bundle bundle) {
- @Override
- public void onWrite(PageRange[] pageRanges,
- ParcelFileDescriptor fileDescriptor,
- CancellationSignal cancellationSignal,
- WriteResultCallback writeResultCallback) {
- writeBitmap(mAttributes, fittingMode, bitmap, fileDescriptor,
- cancellationSignal, writeResultCallback);
- }
+ mAttributes = newPrintAttributes;
- @Override
- public void onFinish() {
- if (callback != null) {
- callback.onFinish();
- }
- }
- }, attr);
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(mJobName)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
+ .setPageCount(1)
+ .build();
+ boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
+ layoutResultCallback.onLayoutFinished(info, changed);
+ }
+
+ @Override
+ public void onWrite(PageRange[] pageRanges,
+ ParcelFileDescriptor fileDescriptor,
+ CancellationSignal cancellationSignal,
+ WriteResultCallback writeResultCallback) {
+ writeBitmap(mAttributes, mFittingMode, mBitmap, fileDescriptor,
+ cancellationSignal, writeResultCallback);
+ }
+
+ @Override
+ public void onFinish() {
+ if (mCallback != null) {
+ mCallback.onFinish();
+ }
+ }
}
/**
@@ -336,148 +350,8 @@
return;
}
- final int fittingMode = mScaleMode;
-
- PrintDocumentAdapter printDocumentAdapter = new PrintDocumentAdapter() {
- private PrintAttributes mAttributes;
- AsyncTask<Uri, Boolean, Bitmap> mLoadBitmap;
- Bitmap mBitmap = null;
-
- @Override
- public void onLayout(final PrintAttributes oldPrintAttributes,
- final PrintAttributes newPrintAttributes,
- final CancellationSignal cancellationSignal,
- final LayoutResultCallback layoutResultCallback,
- Bundle bundle) {
-
- synchronized (this) {
- mAttributes = newPrintAttributes;
- }
-
- if (cancellationSignal.isCanceled()) {
- layoutResultCallback.onLayoutCancelled();
- return;
- }
- // we finished the load
- if (mBitmap != null) {
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
- .setPageCount(1)
- .build();
- boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
- layoutResultCallback.onLayoutFinished(info, changed);
- return;
- }
-
- mLoadBitmap = new AsyncTask<Uri, Boolean, Bitmap>() {
- @Override
- protected void onPreExecute() {
- // First register for cancellation requests.
- cancellationSignal.setOnCancelListener(
- new CancellationSignal.OnCancelListener() {
- @Override
- public void onCancel() { // on different thread
- cancelLoad();
- cancel(false);
- }
- });
- }
-
- @Override
- protected Bitmap doInBackground(Uri... uris) {
- try {
- return loadConstrainedBitmap(imageFile);
- } catch (FileNotFoundException e) {
- /* ignore */
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- super.onPostExecute(bitmap);
-
- // If orientation was not set by the caller, try to fit the bitmap on
- // the current paper by potentially rotating the bitmap by 90 degrees.
- if (bitmap != null
- && (!PRINT_ACTIVITY_RESPECTS_ORIENTATION || mOrientation == 0)) {
- PrintAttributes.MediaSize mediaSize;
-
- synchronized (this) {
- mediaSize = mAttributes.getMediaSize();
- }
-
- if (mediaSize != null) {
- if (mediaSize.isPortrait() != isPortrait(bitmap)) {
- Matrix rotation = new Matrix();
-
- rotation.postRotate(90);
- bitmap = Bitmap.createBitmap(bitmap, 0, 0,
- bitmap.getWidth(), bitmap.getHeight(), rotation,
- true);
- }
- }
- }
-
- mBitmap = bitmap;
- if (bitmap != null) {
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
- .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
- .setPageCount(1)
- .build();
-
- boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
-
- layoutResultCallback.onLayoutFinished(info, changed);
-
- } else {
- layoutResultCallback.onLayoutFailed(null);
- }
- mLoadBitmap = null;
- }
-
- @Override
- protected void onCancelled(Bitmap result) {
- // Task was cancelled, report that.
- layoutResultCallback.onLayoutCancelled();
- mLoadBitmap = null;
- }
- }.execute();
- }
-
- private void cancelLoad() {
- synchronized (mLock) { // prevent race with set null below
- if (mDecodeOptions != null) {
- mDecodeOptions.requestCancelDecode();
- mDecodeOptions = null;
- }
- }
- }
-
- @Override
- public void onFinish() {
- super.onFinish();
- cancelLoad();
- if (mLoadBitmap != null) {
- mLoadBitmap.cancel(true);
- }
- if (callback != null) {
- callback.onFinish();
- }
- if (mBitmap != null) {
- mBitmap.recycle();
- mBitmap = null;
- }
- }
-
- @Override
- public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor fileDescriptor,
- CancellationSignal cancellationSignal,
- WriteResultCallback writeResultCallback) {
- writeBitmap(mAttributes, fittingMode, mBitmap, fileDescriptor,
- cancellationSignal, writeResultCallback);
- }
- };
+ PrintDocumentAdapter printDocumentAdapter = new PrintUriAdapter(jobName, imageFile,
+ callback, mScaleMode);
PrintManager printManager =
(PrintManager) mContext.getSystemService(Context.PRINT_SERVICE);
@@ -494,6 +368,161 @@
printManager.print(jobName, printDocumentAdapter, attr);
}
+ @RequiresApi(19)
+ private class PrintUriAdapter extends PrintDocumentAdapter {
+ private final String mJobName;
+ private final Uri mImageFile;
+ private final OnPrintFinishCallback mCallback;
+ private final int mFittingMode;
+ private PrintAttributes mAttributes;
+ AsyncTask<Uri, Boolean, Bitmap> mLoadBitmap;
+ Bitmap mBitmap;
+
+ PrintUriAdapter(String jobName, Uri imageFile, OnPrintFinishCallback callback,
+ int fittingMode) {
+ mJobName = jobName;
+ mImageFile = imageFile;
+ mCallback = callback;
+ mFittingMode = fittingMode;
+ mBitmap = null;
+ }
+
+ @Override
+ public void onLayout(final PrintAttributes oldPrintAttributes,
+ final PrintAttributes newPrintAttributes,
+ final CancellationSignal cancellationSignal,
+ final LayoutResultCallback layoutResultCallback,
+ Bundle bundle) {
+
+ synchronized (this) {
+ mAttributes = newPrintAttributes;
+ }
+
+ if (cancellationSignal.isCanceled()) {
+ layoutResultCallback.onLayoutCancelled();
+ return;
+ }
+ // we finished the load
+ if (mBitmap != null) {
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(mJobName)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
+ .setPageCount(1)
+ .build();
+ boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
+ layoutResultCallback.onLayoutFinished(info, changed);
+ return;
+ }
+
+ mLoadBitmap = new AsyncTask<Uri, Boolean, Bitmap>() {
+ @Override
+ protected void onPreExecute() {
+ // First register for cancellation requests.
+ cancellationSignal.setOnCancelListener(
+ new CancellationSignal.OnCancelListener() {
+ @Override
+ public void onCancel() { // on different thread
+ cancelLoad();
+ cancel(false);
+ }
+ });
+ }
+
+ @Override
+ protected Bitmap doInBackground(Uri... uris) {
+ try {
+ return loadConstrainedBitmap(mImageFile);
+ } catch (FileNotFoundException e) {
+ /* ignore */
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ super.onPostExecute(bitmap);
+
+ // If orientation was not set by the caller, try to fit the bitmap on
+ // the current paper by potentially rotating the bitmap by 90 degrees.
+ if (bitmap != null
+ && (!PRINT_ACTIVITY_RESPECTS_ORIENTATION || mOrientation == 0)) {
+ PrintAttributes.MediaSize mediaSize;
+
+ synchronized (this) {
+ mediaSize = mAttributes.getMediaSize();
+ }
+
+ if (mediaSize != null) {
+ if (mediaSize.isPortrait() != isPortrait(bitmap)) {
+ Matrix rotation = new Matrix();
+
+ rotation.postRotate(90);
+ bitmap = Bitmap.createBitmap(bitmap, 0, 0,
+ bitmap.getWidth(), bitmap.getHeight(), rotation,
+ true);
+ }
+ }
+ }
+
+ mBitmap = bitmap;
+ if (bitmap != null) {
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(mJobName)
+ .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
+ .setPageCount(1)
+ .build();
+
+ boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
+
+ layoutResultCallback.onLayoutFinished(info, changed);
+
+ } else {
+ layoutResultCallback.onLayoutFailed(null);
+ }
+ mLoadBitmap = null;
+ }
+
+ @Override
+ protected void onCancelled(Bitmap result) {
+ // Task was cancelled, report that.
+ layoutResultCallback.onLayoutCancelled();
+ mLoadBitmap = null;
+ }
+ }.execute();
+ }
+
+ private void cancelLoad() {
+ synchronized (mLock) { // prevent race with set null below
+ if (mDecodeOptions != null) {
+ mDecodeOptions.requestCancelDecode();
+ mDecodeOptions = null;
+ }
+ }
+ }
+
+ @Override
+ public void onFinish() {
+ super.onFinish();
+ cancelLoad();
+ if (mLoadBitmap != null) {
+ mLoadBitmap.cancel(true);
+ }
+ if (mCallback != null) {
+ mCallback.onFinish();
+ }
+ if (mBitmap != null) {
+ mBitmap.recycle();
+ mBitmap = null;
+ }
+ }
+
+ @Override
+ public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor fileDescriptor,
+ CancellationSignal cancellationSignal,
+ WriteResultCallback writeResultCallback) {
+ writeBitmap(mAttributes, mFittingMode, mBitmap, fileDescriptor,
+ cancellationSignal, writeResultCallback);
+ }
+ }
+
/**
* Check if the supplied bitmap should best be printed on a portrait orientation paper.
*
diff --git a/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventBridge.java b/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventBridge.java
index 6089ec6..e117944 100644
--- a/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventBridge.java
+++ b/recyclerview-selection/src/main/java/androidx/recyclerview/selection/EventBridge.java
@@ -139,4 +139,7 @@
mAdapter.notifyItemChanged(position, SelectionTracker.SELECTION_CHANGED_MARKER);
}
}
+
+ private EventBridge() {
+ }
}
diff --git a/room/common/api/current.txt b/room/common/api/current.txt
index fd32902..250e4ec 100644
--- a/room/common/api/current.txt
+++ b/room/common/api/current.txt
@@ -77,7 +77,7 @@
}
public class RoomWarnings {
- ctor public RoomWarnings();
+ ctor public deprecated RoomWarnings();
field public static final java.lang.String CANNOT_CREATE_VERIFICATION_DATABASE = "ROOM_CANNOT_CREATE_VERIFICATION_DATABASE";
field public static final java.lang.String CURSOR_MISMATCH = "ROOM_CURSOR_MISMATCH";
field public static final java.lang.String DEFAULT_CONSTRUCTOR = "ROOM_DEFAULT_CONSTRUCTOR";
diff --git a/room/common/src/main/java/androidx/room/ForeignKey.java b/room/common/src/main/java/androidx/room/ForeignKey.java
index 847941a..54b9252 100644
--- a/room/common/src/main/java/androidx/room/ForeignKey.java
+++ b/room/common/src/main/java/androidx/room/ForeignKey.java
@@ -13,11 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package androidx.room;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import androidx.annotation.IntDef;
+import java.lang.annotation.Retention;
+
/**
* Declares a foreign key on another {@link Entity}.
* <p>
@@ -161,6 +164,7 @@
* {@link #onUpdate()}.
*/
@IntDef({NO_ACTION, RESTRICT, SET_NULL, SET_DEFAULT, CASCADE})
+ @Retention(SOURCE)
@interface Action {
}
}
diff --git a/room/common/src/main/java/androidx/room/RoomMasterTable.java b/room/common/src/main/java/androidx/room/RoomMasterTable.java
index 6c8c855..c1ab82a 100644
--- a/room/common/src/main/java/androidx/room/RoomMasterTable.java
+++ b/room/common/src/main/java/androidx/room/RoomMasterTable.java
@@ -52,4 +52,7 @@
+ COLUMN_ID + "," + COLUMN_IDENTITY_HASH + ")"
+ " VALUES(" + DEFAULT_ID + ", \"" + hash + "\")";
}
+
+ private RoomMasterTable() {
+ }
}
diff --git a/room/common/src/main/java/androidx/room/RoomWarnings.java b/room/common/src/main/java/androidx/room/RoomWarnings.java
index a4122bd..5f5d7f1 100644
--- a/room/common/src/main/java/androidx/room/RoomWarnings.java
+++ b/room/common/src/main/java/androidx/room/RoomWarnings.java
@@ -133,4 +133,10 @@
*/
public static final String RELATION_QUERY_WITHOUT_TRANSACTION =
"ROOM_RELATION_QUERY_WITHOUT_TRANSACTION";
+
+ /** @deprecated This type should not be instantiated as it contains only static methods. */
+ @Deprecated
+ @SuppressWarnings("PrivateConstructorForUtilityClass")
+ public RoomWarnings() {
+ }
}
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/InvalidationTrackerTrojan.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/InvalidationTrackerTrojan.java
index 2e52952..13a5469 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/InvalidationTrackerTrojan.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/InvalidationTrackerTrojan.java
@@ -23,4 +23,7 @@
public static int countObservers(InvalidationTracker tracker) {
return tracker.mObserverMap.size();
}
+
+ private InvalidationTrackerTrojan() {
+ }
}
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/TestUtil.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/TestUtil.java
index b3d5e24..69ac752 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/TestUtil.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/TestUtil.java
@@ -106,4 +106,7 @@
coordinates.lng = Math.random();
return coordinates;
}
+
+ private TestUtil() {
+ }
}
diff --git a/room/migration/src/main/java/androidx/room/migration/bundle/BundleUtil.java b/room/migration/src/main/java/androidx/room/migration/bundle/BundleUtil.java
index 8eb4d10..86888ba 100644
--- a/room/migration/src/main/java/androidx/room/migration/bundle/BundleUtil.java
+++ b/room/migration/src/main/java/androidx/room/migration/bundle/BundleUtil.java
@@ -33,4 +33,7 @@
static String replaceTableName(String contents, String tableName) {
return contents.replace(TABLE_NAME_PLACEHOLDER, tableName);
}
+
+ private BundleUtil() {
+ }
}
diff --git a/room/migration/src/main/java/androidx/room/migration/bundle/SchemaEqualityUtil.java b/room/migration/src/main/java/androidx/room/migration/bundle/SchemaEqualityUtil.java
index 799c3d8..910958d 100644
--- a/room/migration/src/main/java/androidx/room/migration/bundle/SchemaEqualityUtil.java
+++ b/room/migration/src/main/java/androidx/room/migration/bundle/SchemaEqualityUtil.java
@@ -87,4 +87,7 @@
}
return item1.isSchemaEqual(item2);
}
+
+ private SchemaEqualityUtil() {
+ }
}
diff --git a/room/runtime/api/current.txt b/room/runtime/api/current.txt
index cfd667d..3f317d5 100644
--- a/room/runtime/api/current.txt
+++ b/room/runtime/api/current.txt
@@ -25,7 +25,7 @@
}
public class Room {
- ctor public Room();
+ ctor public deprecated Room();
method public static <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T> databaseBuilder(android.content.Context, java.lang.Class<T>, java.lang.String);
method public static <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T> inMemoryDatabaseBuilder(android.content.Context, java.lang.Class<T>);
field public static final java.lang.String MASTER_TABLE_NAME = "room_master_table";
diff --git a/room/runtime/src/main/java/androidx/room/Room.java b/room/runtime/src/main/java/androidx/room/Room.java
index 15107fc..2e4dedc 100644
--- a/room/runtime/src/main/java/androidx/room/Room.java
+++ b/room/runtime/src/main/java/androidx/room/Room.java
@@ -100,4 +100,10 @@
+ klass.getCanonicalName());
}
}
+
+ /** @deprecated This type should not be instantiated as it contains only static methods. */
+ @Deprecated
+ @SuppressWarnings("PrivateConstructorForUtilityClass")
+ public Room() {
+ }
}
diff --git a/room/runtime/src/main/java/androidx/room/util/StringUtil.java b/room/runtime/src/main/java/androidx/room/util/StringUtil.java
index 8988490..b865138 100644
--- a/room/runtime/src/main/java/androidx/room/util/StringUtil.java
+++ b/room/runtime/src/main/java/androidx/room/util/StringUtil.java
@@ -112,4 +112,7 @@
}
return sb.toString();
}
+
+ private StringUtil() {
+ }
}
diff --git a/room/rxjava2/api/current.txt b/room/rxjava2/api/current.txt
index be690d9..355c117 100644
--- a/room/rxjava2/api/current.txt
+++ b/room/rxjava2/api/current.txt
@@ -5,7 +5,7 @@
}
public class RxRoom {
- ctor public RxRoom();
+ ctor public deprecated RxRoom();
method public static io.reactivex.Flowable<java.lang.Object> createFlowable(androidx.room.RoomDatabase, java.lang.String...);
field public static final java.lang.Object NOTHING;
}
diff --git a/room/rxjava2/src/main/java/androidx/room/RxRoom.java b/room/rxjava2/src/main/java/androidx/room/RxRoom.java
index d2ba8bf..89af1f3 100644
--- a/room/rxjava2/src/main/java/androidx/room/RxRoom.java
+++ b/room/rxjava2/src/main/java/androidx/room/RxRoom.java
@@ -16,7 +16,6 @@
package androidx.room;
-import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.arch.core.executor.ArchTaskExecutor;
@@ -29,13 +28,14 @@
import io.reactivex.Flowable;
import io.reactivex.FlowableEmitter;
import io.reactivex.FlowableOnSubscribe;
+import io.reactivex.Maybe;
+import io.reactivex.MaybeSource;
import io.reactivex.Scheduler;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.disposables.Disposables;
import io.reactivex.functions.Action;
import io.reactivex.functions.Function;
-import io.reactivex.functions.Predicate;
/**
* Helper class to add RxJava2 support to Room.
@@ -103,22 +103,12 @@
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static <T> Flowable<T> createFlowable(final RoomDatabase database,
final String[] tableNames, final Callable<T> callable) {
+ final Maybe<T> maybe = Maybe.fromCallable(callable);
return createFlowable(database, tableNames).observeOn(sAppToolkitIOScheduler)
- .map(new Function<Object, Optional<T>>() {
+ .flatMapMaybe(new Function<Object, MaybeSource<T>>() {
@Override
- public Optional<T> apply(@NonNull Object o) throws Exception {
- T data = callable.call();
- return new Optional<>(data);
- }
- }).filter(new Predicate<Optional<T>>() {
- @Override
- public boolean test(@NonNull Optional<T> optional) throws Exception {
- return optional.mValue != null;
- }
- }).map(new Function<Optional<T>, T>() {
- @Override
- public T apply(@NonNull Optional<T> optional) throws Exception {
- return optional.mValue;
+ public MaybeSource<T> apply(Object o) throws Exception {
+ return maybe;
}
});
}
@@ -177,12 +167,9 @@
}
}
- static class Optional<T> {
- @Nullable
- final T mValue;
-
- Optional(@Nullable T value) {
- this.mValue = value;
- }
+ /** @deprecated This type should not be instantiated as it contains only static methods. */
+ @Deprecated
+ @SuppressWarnings("PrivateConstructorForUtilityClass")
+ public RxRoom() {
}
}
diff --git a/samples/SupportCarDemos/src/main/AndroidManifest.xml b/samples/SupportCarDemos/src/main/AndroidManifest.xml
index 97ad4ed..00e4ebb 100644
--- a/samples/SupportCarDemos/src/main/AndroidManifest.xml
+++ b/samples/SupportCarDemos/src/main/AndroidManifest.xml
@@ -70,9 +70,42 @@
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
<meta-data android:name="android.support.PARENT_ACTIVITY"
- android:value=".SupportCarDemoActivity" />
+ android:value=".SupportCarDemoActivity" />
</activity>
+ <activity android:name=".AlphaJumpActivity"
+ android:label="Alpha Jump"
+ android:parentActivityName=".SupportCarDemoActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.SAMPLE_CODE" />
+ </intent-filter>
+ <meta-data android:name="android.support.PARENT_ACTIVITY"
+ android:value=".SupportCarDemoActivity" />
+ </activity>
+
+ <activity android:name=".DrawerDemoActivity"
+ android:label="@string/drawer_demo_activity_title"
+ android:theme="@style/CarDrawerTheme"
+ android:parentActivityName=".SupportCarDemoActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.SAMPLE_CODE" />
+ </intent-filter>
+ <meta-data android:name="android.support.PARENT_ACTIVITY"
+ android:value=".SupportCarDemoActivity" />
+ </activity>
+
+ <activity android:name=".SubheaderListItemActivity"
+ android:label="SubheaderListItem"
+ android:parentActivityName=".SupportCarDemoActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.SAMPLE_CODE" />
+ </intent-filter>
+ <meta-data android:name="android.support.PARENT_ACTIVITY"
+ android:value=".SupportCarDemoActivity" />
+ </activity>
</application>
</manifest>
diff --git a/samples/SupportCarDemos/src/main/java/com/example/androidx/car/AlphaJumpActivity.java b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/AlphaJumpActivity.java
new file mode 100644
index 0000000..7f491a9
--- /dev/null
+++ b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/AlphaJumpActivity.java
@@ -0,0 +1,153 @@
+/*
+ * 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 com.example.androidx.car;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import androidx.car.widget.AlphaJumpBucketer;
+import androidx.car.widget.IAlphaJumpAdapter;
+import androidx.car.widget.PagedListView;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * An activity with a long list of cheeses, initially in a random order but you can use alpha jump
+ * to quickly jump to your favourite cheese.
+ */
+public class AlphaJumpActivity extends Activity {
+ private static final String TAG = "AlphaJumpActivity";
+
+ private static final String[] CHEESES = {
+ "Pourly", "Macconais", "Bonchester", "Olivet Cendre", "Fruit Cream Cheese",
+ "Metton (Cancoillotte)", "Lyonnais", "Crema Agria", "Nantais",
+ "Brusselae Kaas (Fromage de Bruxelles)", "Rouleau De Beaulieu", "Flor de Guia",
+ "Poivre d'Ane", "Tomme des Chouans", "Whitestone Farmhouse", "Queso de Murcia",
+ "Saint-Marcellin", "Pave d'Affinois", "Quatre-Vents", "Galette du Paludier", "Pyramide",
+ "Capricorn Goat", "Feta", "Queso del Montsec", "Telemea", "Cooleney",
+ "Buchette d'Anjou", "Banon", "Bosworth", "Bergader", "Mothais a la Feuille",
+ "Mascarpone Torta", "Richelieu", "Guerbigny", "Taupiniere", "Anneau du Vic-Bilh",
+ "Tupi", "Queso Fresco", "Timboon Brie", "Neufchatel", "Blue Castello",
+ "Brebis du Puyfaucon", "Gratte-Paille", "Palet de Babligny", "Caciotta", "Rigotte",
+ "Caciocavallo", "Bleu Des Causses", "Civray", "Bath Cheese", "Farmer", "Cachaille",
+ "Ricotta", "Caravane", "Selles sur Cher", "Chaource", "Cottage Cheese (Australian)",
+ "Pelardon des Corbieres", "Cold Pack", "Queso Fresco (Adobera)", "Bleu de Gex",
+ "Provel", "Torta del Casar", "Golden Cross", "Mascarpone", "Fougerus",
+ "Dessertnyj Belyj", "Fresh Ricotta", "Gris de Lille", "Breakfast Cheese", "Venaco",
+ "Pant ys Gawn", "Mascarpone (Australian)", "Sharpam", "Humboldt Fog",
+ "Evansdale Farmhouse Brie", "Kernhem", "Mozzarella Rolls", "Dolcelatte",
+ "Briquette de Brebis", "Niolo", "Selva", "Dunbarra", "King Island Cape Wickham Brie",
+ "Carre de l'Est", "Broccio", "Castelo Branco", "Finn", "Panela", "Basket Cheese",
+ "Woodside Cabecou", "Truffe", "Flower Marie", "Mozzarella Fresh, in water",
+ "Jubilee Blue", "Coeur de Camembert au Calvados", "Caprice des Dieux", "Caboc",
+ "Crottin du Chavignol", "Cabecou", "Cottage Cheese", "Cashel Blue", "Patefine Fort",
+ "Mahoe Aged Gouda", "Cornish Pepper", "Greuilh", "Ricotta (Australian)", "Grand Vatel",
+ "Prince-Jean", "Coulommiers", "Scamorza", "Romans Part Dieu", "Quark", "Frinault",
+ "Chabichou du Poitou", "Le Lacandou", "Maredsous", "Fin-de-Siecle", "Button (Innes)",
+ "Washed Rind Cheese (Australian)", "Daralagjazsky", "Margotin", "Pithtviers au Foin",
+ "Cathelain", "Yarra Valley Pyramid", "Sirene", "Emlett", "Explorateur", "Bandal",
+ "Lingot Saint Bousquet d'Orb", "Gorgonzola", "Bresse Bleu", "Beer Cheese",
+ "Brinza (Burduf Brinza)", "Bakers", "Little Rydings", "Bryndza"
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_paged_list_view);
+
+ PagedListView pagedListView = findViewById(R.id.paged_list_view);
+ pagedListView.setAdapter(new CheeseAdapter());
+
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ /**
+ * Adapter that populates a number of items for demo purposes.
+ */
+ public static class CheeseAdapter extends RecyclerView.Adapter<CheeseAdapter.ViewHolder>
+ implements IAlphaJumpAdapter {
+ private String[] mCheeses;
+ private boolean mIsSorted;
+
+ CheeseAdapter() {
+ // Start out not being sorted.
+ mCheeses = CHEESES;
+ mIsSorted = false;
+ }
+
+ @Override
+ public CheeseAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+ View view = inflater.inflate(R.layout.alpha_jump_list_item, parent, false);
+ return new CheeseAdapter.ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(CheeseAdapter.ViewHolder holder, int position) {
+ holder.mTextView.setText(mCheeses[position]);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mCheeses.length;
+ }
+
+ @Override
+ public Collection<Bucket> getAlphaJumpBuckets() {
+ if (!mIsSorted) {
+ Log.i(TAG, "Sorting...");
+ // We'll sort the first time we need to populate the buckets.
+ mCheeses = mCheeses.clone();
+ Arrays.sort(mCheeses);
+ mIsSorted = true;
+ notifyDataSetChanged();
+ }
+
+ AlphaJumpBucketer bucketer = new AlphaJumpBucketer();
+ return bucketer.createBuckets(mCheeses);
+ }
+
+ @Override
+ public void onAlphaJumpEnter() {
+ Log.i(TAG, "onAlphaJumpEnter");
+ }
+
+ @Override
+ public void onAlphaJumpLeave(Bucket bucket) {
+ Log.i(TAG, "onAlphaJumpLeave: " + bucket.getLabel());
+ }
+
+ /**
+ * ViewHolder for CheeseAdapter.
+ */
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+ private final TextView mTextView;
+
+ public ViewHolder(View itemView) {
+ super(itemView);
+ mTextView = itemView.findViewById(R.id.text);
+ }
+ }
+ }
+}
diff --git a/samples/SupportCarDemos/src/main/java/com/example/androidx/car/DrawerDemoActivity.java b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/DrawerDemoActivity.java
new file mode 100644
index 0000000..ff858d4
--- /dev/null
+++ b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/DrawerDemoActivity.java
@@ -0,0 +1,94 @@
+/*
+ * 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 com.example.androidx.car;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.car.drawer.CarDrawerActivity;
+import androidx.car.drawer.CarDrawerAdapter;
+import androidx.car.drawer.DrawerItemViewHolder;
+
+/**
+ * A demo activity that will inflate a layout with a drawer.
+ */
+public class DrawerDemoActivity extends CarDrawerActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setMainContent(R.layout.drawer_activity);
+ getDrawerController().setRootAdapter(new DrawerRootAdapter(/* context= */ this));
+ }
+
+ private void openSubDrawerLevel() {
+ getDrawerController().pushAdapter(new SubItemAdapter(/* context= */ this));
+ }
+
+ /**
+ * The root drawer view that will delegate clicks to the a sub-drawer.
+ */
+ private class DrawerRootAdapter extends CarDrawerAdapter {
+ private static final int NUM_OF_ITEMS = 10;
+
+ DrawerRootAdapter(Context context) {
+ super(context, /* showDisabledListOnEmpty= */ true);
+ setTitle(getString(R.string.drawer_demo_activity_title));
+ }
+
+ @Override
+ protected void populateViewHolder(DrawerItemViewHolder holder, int position) {
+ holder.getTitle().setText(getString(R.string.drawer_demo_root_item_label, position));
+ }
+
+ @Override
+ protected int getActualItemCount() {
+ return NUM_OF_ITEMS;
+ }
+
+ @Override
+ public void onItemClick(int position) {
+ openSubDrawerLevel();
+ }
+ }
+
+ /**
+ * A sub-level of the drawer that is opened when any of the items in the root drawer view have
+ * been clicked.
+ */
+ private class SubItemAdapter extends CarDrawerAdapter {
+ private static final int NUM_OF_ITEMS = 3;
+
+ SubItemAdapter(Context context) {
+ super(context, /* showDisabledListOnEmpty= */ true);
+ setTitle(getString(R.string.drawer_demo_activity_title));
+ }
+
+ @Override
+ protected void populateViewHolder(DrawerItemViewHolder holder, int position) {
+ holder.getTitle().setText(getString(R.string.drawer_demo_subitem_label, position));
+ }
+
+ @Override
+ protected int getActualItemCount() {
+ return NUM_OF_ITEMS;
+ }
+
+ @Override
+ public void onItemClick(int position) {}
+
+ }
+}
diff --git a/samples/SupportCarDemos/src/main/java/com/example/androidx/car/SubheaderListItemActivity.java b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/SubheaderListItemActivity.java
new file mode 100644
index 0000000..3d5054d
--- /dev/null
+++ b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/SubheaderListItemActivity.java
@@ -0,0 +1,168 @@
+/*
+ * 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 com.example.androidx.car;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.car.widget.ListItem;
+import androidx.car.widget.ListItemAdapter;
+import androidx.car.widget.ListItemProvider;
+import androidx.car.widget.PagedListView;
+import androidx.car.widget.SubheaderListItem;
+import androidx.car.widget.TextListItem;
+
+/**
+ * Demo activity for {@link SubheaderListItem}.
+ */
+public class SubheaderListItemActivity extends Activity {
+
+ PagedListView mPagedListView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_paged_list_view);
+
+ mPagedListView = findViewById(R.id.paged_list_view);
+
+ ListItemAdapter adapter = new ListItemAdapter(this,
+ new SampleProvider(this), ListItemAdapter.BackgroundStyle.NONE);
+ mPagedListView.setAdapter(adapter);
+ mPagedListView.setMaxPages(PagedListView.UNLIMITED_PAGES);
+ mPagedListView.setDividerVisibilityManager(adapter);
+ }
+
+ private static class SampleProvider extends ListItemProvider {
+
+ private Context mContext;
+ private List<ListItem> mItems;
+
+ private ListItemProvider.ListProvider mListProvider;
+
+ SampleProvider(Context context) {
+ mContext = context;
+ mItems = new ArrayList<>();
+
+ SubheaderListItem subheaderItem;
+ TextListItem item;
+
+ subheaderItem = new SubheaderListItem(mContext,
+ "subheader matching items without start margin");
+ subheaderItem.setTextStartMarginType(SubheaderListItem.TEXT_START_MARGIN_TYPE_NONE);
+ subheaderItem.setHideDivider(true);
+ mItems.add(subheaderItem);
+
+ item = new TextListItem(mContext);
+ item.setTitle("item");
+ mItems.add(item);
+
+ item = new TextListItem(mContext);
+ item.setTitle("item");
+ mItems.add(item);
+
+ item = new TextListItem(mContext);
+ item.setTitle("item - hides following divider");
+ item.setHideDivider(true);
+ mItems.add(item);
+
+ // ========================
+
+ item = new TextListItem(mContext);
+ item.setPrimaryActionEmptyIcon();
+ item.setTitle("Header");
+ item.setBody("header text with more words");
+ item.setHideDivider(true);
+ mItems.add(item);
+
+ subheaderItem = new SubheaderListItem(mContext,
+ "subheader matching items with no icon");
+ subheaderItem.setTextStartMarginType(
+ SubheaderListItem.TEXT_START_MARGIN_TYPE_SMALL);
+ subheaderItem.setHideDivider(true);
+ mItems.add(subheaderItem);
+
+ item = new TextListItem(mContext);
+ item.setPrimaryActionEmptyIcon();
+ item.setTitle("item");
+ mItems.add(item);
+
+ item = new TextListItem(mContext);
+ item.setPrimaryActionEmptyIcon();
+ item.setTitle("item");
+ mItems.add(item);
+
+ item = new TextListItem(mContext);
+ item.setPrimaryActionEmptyIcon();
+ item.setTitle("item - hides following divider");
+ item.setHideDivider(true);
+ mItems.add(item);
+
+ // ========================
+
+ subheaderItem = new SubheaderListItem(mContext,
+ "subheader matching items with small icons");
+ subheaderItem.setTextStartMarginType(
+ SubheaderListItem.TEXT_START_MARGIN_TYPE_SMALL);
+ mItems.add(subheaderItem);
+
+ item = new TextListItem(mContext);
+ item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+ item.setTitle("item");
+ mItems.add(item);
+
+ item = new TextListItem(mContext);
+ item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
+ item.setTitle("item");
+ mItems.add(item);
+
+ // ========================
+
+ subheaderItem = new SubheaderListItem(mContext,
+ "subheader matching items with large icons");
+ subheaderItem.setTextStartMarginType(
+ SubheaderListItem.TEXT_START_MARGIN_TYPE_LARGE);
+ mItems.add(subheaderItem);
+
+ item = new TextListItem(mContext);
+ item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true);
+ item.setTitle("item");
+ mItems.add(item);
+
+ item = new TextListItem(mContext);
+ item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true);
+ item.setTitle("item");
+ mItems.add(item);
+
+ mListProvider = new ListItemProvider.ListProvider(mItems);
+ }
+
+ @Override
+ public ListItem get(int position) {
+ return mListProvider.get(position);
+ }
+
+ @Override
+ public int size() {
+ return mListProvider.size();
+ }
+ }
+}
diff --git a/samples/SupportCarDemos/src/main/java/com/example/androidx/car/TextListItemActivity.java b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/TextListItemActivity.java
index a0972e9..d2106a2 100644
--- a/samples/SupportCarDemos/src/main/java/com/example/androidx/car/TextListItemActivity.java
+++ b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/TextListItemActivity.java
@@ -55,7 +55,7 @@
SampleProvider provider = new SampleProvider(this);
ListItemAdapter adapter = new ListItemAdapter(this, provider,
- ListItemAdapter.BackgroundStyle.NONE);
+ ListItemAdapter.BackgroundStyle.SOLID);
final boolean[] hideDivider = {true};
// Demonstrate how to update list item post construction.
diff --git a/samples/SupportCarDemos/src/main/res/layout/alpha_jump_list_item.xml b/samples/SupportCarDemos/src/main/res/layout/alpha_jump_list_item.xml
new file mode 100644
index 0000000..f82247d
--- /dev/null
+++ b/samples/SupportCarDemos/src/main/res/layout/alpha_jump_list_item.xml
@@ -0,0 +1,30 @@
+<?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.
+ -->
+<androidx.car.widget.ColumnCardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="0dp"
+ android:layout_height="52dp"
+ app:cardBackgroundColor="@color/car_card">
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textAppearance="@style/TextAppearance.Car.Body1"/>
+</androidx.car.widget.ColumnCardView>
\ No newline at end of file
diff --git a/samples/SupportCarDemos/src/main/res/layout/drawer_activity.xml b/samples/SupportCarDemos/src/main/res/layout/drawer_activity.xml
new file mode 100644
index 0000000..791a615
--- /dev/null
+++ b/samples/SupportCarDemos/src/main/res/layout/drawer_activity.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:background="?android:attr/colorPrimary"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ <androidx.car.widget.PagedListView
+ android:id="@+id/list"
+ android:layout_gravity="center"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:gutter="both"
+ app:listContentTopOffset="@dimen/car_app_bar_height"
+ app:scrollBarEnabled="true"
+ app:scrollBarTopMargin="@dimen/car_app_bar_height"
+ app:showPagedListViewDivider="false" />
+</FrameLayout>
diff --git a/samples/SupportCarDemos/src/main/res/values/strings.xml b/samples/SupportCarDemos/src/main/res/values/strings.xml
index 2e278e2..9a6fc1a 100644
--- a/samples/SupportCarDemos/src/main/res/values/strings.xml
+++ b/samples/SupportCarDemos/src/main/res/values/strings.xml
@@ -49,5 +49,10 @@
line 28\n
line 29\n
</string>
+
+ <!-- String for the drawer demo -->
+ <string name="drawer_demo_activity_title">Drawer Demo</string>
+ <string name="drawer_demo_root_item_label">Item %1$d</string>
+ <string name="drawer_demo_subitem_label">SubItem %1$d</string>
</resources>
diff --git a/samples/SupportCarDemos/src/main/res/values/themes.xml b/samples/SupportCarDemos/src/main/res/values/themes.xml
index ab7f58b..2b50c23 100644
--- a/samples/SupportCarDemos/src/main/res/values/themes.xml
+++ b/samples/SupportCarDemos/src/main/res/values/themes.xml
@@ -18,4 +18,11 @@
<!-- The main theme for all activities within the Car Demo. -->
<style name="CarTheme" parent="Theme.Car.Light.NoActionBar">
</style>
+
+ <!-- A theme for activities within the Car Demo that have a drawer. -->
+ <style name="CarDrawerTheme" parent="Theme.Car.Light.NoActionBar.Drawer">
+ <item name="android:colorPrimary">@color/car_grey_200</item>
+ <item name="android:colorPrimaryDark">@color/car_grey_400</item>
+ <item name="drawerClosedHeaderColor">@color/car_title2_dark</item>
+ </style>
</resources>
diff --git a/samples/SupportSliceDemos/build.gradle b/samples/SupportSliceDemos/build.gradle
index 53b1bec..4d518ff 100644
--- a/samples/SupportSliceDemos/build.gradle
+++ b/samples/SupportSliceDemos/build.gradle
@@ -43,5 +43,5 @@
}
supportTestApp {
- minSdkVersion = 26
+ minSdkVersion = 19
}
diff --git a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
index 8f94bb9..08547fb 100644
--- a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
+++ b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
@@ -146,7 +146,7 @@
IconCompat.createWithResource(getContext(), R.drawable.weather_1),
"Weather is happening!");
return new ListBuilder(getContext(), sliceUri, INFINITY)
- .addGrid(gb -> gb
+ .addGridRow(gb -> gb
.setPrimaryAction(primaryAction)
.addCell(cb -> cb
.addImage(IconCompat.createWithResource(getContext(),
@@ -182,11 +182,17 @@
}
private Slice createGallery(Uri sliceUri) {
+ SliceAction primaryAction = new SliceAction(
+ getBroadcastIntent(ACTION_TOAST, "open photo album"),
+ IconCompat.createWithResource(getContext(), R.drawable.slices_1),
+ LARGE_IMAGE,
+ "Open photo album");
return new ListBuilder(getContext(), sliceUri, INFINITY)
.setColor(0xff4285F4)
.addRow(b -> b
.setTitle("Family trip to Hawaii")
- .setSubtitle("Sep 30, 2017 - Oct 2, 2017"))
+ .setSubtitle("Sep 30, 2017 - Oct 2, 2017")
+ .setPrimaryAction(primaryAction))
.addAction(new SliceAction(
getBroadcastIntent(ACTION_TOAST, "cast photo album"),
IconCompat.createWithResource(getContext(), R.drawable.ic_cast),
@@ -195,7 +201,7 @@
getBroadcastIntent(ACTION_TOAST, "share photo album"),
IconCompat.createWithResource(getContext(), R.drawable.ic_share),
"Share photo album"))
- .addGrid(b -> b
+ .addGridRow(b -> b
.addCell(cb -> cb
.addImage(IconCompat.createWithResource(getContext(),
R.drawable.slices_1),
@@ -224,7 +230,7 @@
.addImage(IconCompat.createWithResource(getContext(),
R.drawable.slices_4),
LARGE_IMAGE))
- .addSeeMoreAction(getBroadcastIntent(ACTION_TOAST, "see your gallery"))
+ .setSeeMoreAction(getBroadcastIntent(ACTION_TOAST, "see your gallery"))
.setContentDescription("Images from your trip to Hawaii"))
.build();
}
@@ -239,9 +245,9 @@
ICON_IMAGE);
cb.setContentIntent(pi);
cb.addTitleText("All cats");
- gb.addSeeMoreCell(cb);
+ gb.setSeeMoreCell(cb);
} else {
- gb.addSeeMoreAction(pi);
+ gb.setSeeMoreAction(pi);
}
gb.addCell(new GridRowBuilder.CellBuilder(gb)
.addImage(IconCompat.createWithResource(getContext(), R.drawable.cat_1),
@@ -319,7 +325,7 @@
.setColor(0xff3949ab)
.setHeader(b -> b
.setTitle("Mady Pitza")
- .setSummarySubtitle("Called " + lastCalledString)
+ .setSummary("Called " + lastCalledString)
.setPrimaryAction(primaryAction))
.addRow(b -> b
.setTitleItem(
@@ -398,13 +404,13 @@
.addAction(new SliceAction(getBroadcastIntent(ACTION_TOAST, "contact host"),
IconCompat.createWithResource(getContext(), R.drawable.ic_text),
"Contact host"))
- .addGrid(b -> b
+ .addGridRow(b -> b
.addCell(cb -> cb
.addImage(IconCompat.createWithResource(getContext(),
R.drawable.reservation),
LARGE_IMAGE)
.setContentDescription("Image of your reservation in Seattle")))
- .addGrid(b -> b
+ .addGridRow(b -> b
.addCell(cb -> cb
.addTitleText("Check In")
.addText("12:00 PM, Feb 1"))
@@ -430,7 +436,7 @@
.setHeader(b -> b
.setTitle("Get ride")
.setSubtitle(headerSubtitle)
- .setSummarySubtitle("Ride to work in 12 min | Ride home in 1 hour 45 min")
+ .setSummary("Ride to work in 12 min | Ride home in 1 hour 45 min")
.setPrimaryAction(primaryAction))
.addRow(b -> b
.setTitle("Work")
@@ -479,7 +485,8 @@
private Slice createWifiSlice(Uri sliceUri) {
// Get wifi state
- WifiManager wifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+ WifiManager wifiManager = (WifiManager) getContext()
+ .getApplicationContext().getSystemService(Context.WIFI_SERVICE);
int wifiState = wifiManager.getWifiState();
boolean wifiEnabled = false;
String state;
@@ -545,39 +552,49 @@
// Add see more intent
if (TEST_CUSTOM_SEE_MORE) {
- lb.addSeeMoreRow(rb -> rb
+ lb.setSeeMoreRow(rb -> rb
.setTitle("See all available networks")
.addEndItem(
IconCompat.createWithResource(getContext(), R.drawable.ic_right_caret),
SMALL_IMAGE)
.setPrimaryAction(primaryAction));
} else {
- lb.addSeeMoreAction(primaryAction.getAction());
+ lb.setSeeMoreAction(primaryAction.getAction());
}
return lb.build();
}
private Slice createStarRatingInputRange(Uri sliceUri) {
+ IconCompat icon = IconCompat.createWithResource(getContext(), R.drawable.ic_star_on);
+ SliceAction primaryAction =
+ new SliceAction(getBroadcastIntent(ACTION_TOAST, "open star rating"), icon, "Rate");
return new ListBuilder(getContext(), sliceUri, INFINITY)
.setColor(0xffff4081)
.addInputRange(c -> c
.setTitle("Star rating")
- .setThumb(
- IconCompat.createWithResource(getContext(), R.drawable.ic_star_on))
- .setAction(getBroadcastIntent(ACTION_TOAST_RANGE_VALUE, null))
+ .setSubtitle("Pick a rating from 0 to 5")
+ .setThumb(icon)
+ .setInputAction(getBroadcastIntent(ACTION_TOAST_RANGE_VALUE, null))
.setMax(5)
.setValue(3)
+ .setPrimaryAction(primaryAction)
.setContentDescription("Slider for star ratings"))
.build();
}
private Slice createDownloadProgressRange(Uri sliceUri) {
+ IconCompat icon = IconCompat.createWithResource(getContext(), R.drawable.ic_star_on);
+ SliceAction primaryAction =
+ new SliceAction(
+ getBroadcastIntent(ACTION_TOAST, "open download"), icon, "Download");
return new ListBuilder(getContext(), sliceUri, INFINITY)
.setColor(0xffff4081)
.addRange(c -> c
.setTitle("Download progress")
+ .setSubtitle("Download is happening")
.setMax(100)
- .setValue(75))
+ .setValue(75)
+ .setPrimaryAction(primaryAction))
.build();
}
@@ -657,7 +674,7 @@
updating || TextUtils.isEmpty(title))
.setSubtitle(subtitle,
updating || TextUtils.isEmpty(subtitle)))
- .addGrid(gb -> gb
+ .addGridRow(gb -> gb
.addCell(cb -> cb
.addImage(IconCompat.createWithResource(getContext(),
R.drawable.ic_home),
diff --git a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
index 174705e..673eea1 100644
--- a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
+++ b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
@@ -35,13 +35,14 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.SearchView;
import android.widget.SimpleCursorAdapter;
+import android.widget.Toast;
import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.lifecycle.LiveData;
@@ -52,14 +53,13 @@
import androidx.slice.widget.SliceView;
import java.util.ArrayList;
-import java.util.Comparator;
+import java.util.Collections;
import java.util.List;
/**
* Example use of SliceView. Uses a search bar to select/auto-complete a slice uri which is
* then displayed in the selected mode with SliceView.
*/
-@RequiresApi(api = 28)
public class SliceBrowser extends AppCompatActivity implements SliceView.OnSliceActionListener {
private static final String TAG = "SlicePresenter";
@@ -185,9 +185,9 @@
private void authAllSlices() {
List<ApplicationInfo> packages = getPackageManager().getInstalledApplications(0);
- packages.forEach(info -> {
+ for (ApplicationInfo info : packages) {
grantPackage(info.packageName);
- });
+ }
}
private void grantPackage(String packageName) {
@@ -256,19 +256,16 @@
final MatrixCursor c = new MatrixCursor(new String[]{BaseColumns._ID, "uri"});
ArrayMap<String, Integer> ranking = new ArrayMap<>();
ArrayList<String> suggestions = new ArrayList();
- mSliceUris.forEach(uri -> {
+ for (Uri uri : mSliceUris) {
+
String uriString = uri.toString();
if (uriString.contains(query)) {
ranking.put(uriString, uriString.indexOf(query));
suggestions.add(uriString);
}
- });
- suggestions.sort(new Comparator<String>() {
- @Override
- public int compare(String o1, String o2) {
- return Integer.compare(ranking.get(o1), ranking.get(o2));
- }
- });
+ }
+ Collections.sort(suggestions, (o1, o2) ->
+ Integer.compare(ranking.get(o1), ranking.get(o2)));
for (int i = 0; i < suggestions.size(); i++) {
c.addRow(new Object[]{i, suggestions.get(i)});
}
@@ -287,10 +284,24 @@
R.layout.slice_view, mContainer, false)
: new SliceView(getApplicationContext());
v.setOnSliceActionListener(this);
+ v.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Toast.makeText(getApplicationContext(),
+ "Custom listener clicked", Toast.LENGTH_SHORT).show();
+ }
+ });
if (mSliceLiveData != null) {
mSliceLiveData.removeObservers(this);
}
v.setScrollable(SCROLLING_ENABLED);
+ v.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ Toast.makeText(getApplicationContext(), "LONGPRESS !!", Toast.LENGTH_SHORT).show();
+ return true;
+ }
+ });
return v;
}
}
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_camera.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_camera.xml
index 74b6bf9..bd3436b 100644
--- a/samples/SupportSliceDemos/src/main/res/drawable/ic_camera.xml
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_camera.xml
@@ -24,8 +24,8 @@
android:pathData="M 12 8.8 C 13.7673111995 8.8 15.2 10.2326888005 15.2 12 C 15.2 13.7673111995 13.7673111995 15.2 12 15.2 C 10.2326888005 15.2 8.8 13.7673111995 8.8 12 C 8.8 10.2326888005 10.2326888005 8.8 12 8.8 Z" />
<path
android:fillColor="#000000"
- android:pathData="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1 .9 2 2 2h16c1.1 0 2-.9
-2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5
+ android:pathData="M9 2L7.17 4H4c-1.1 0-2 0.9-2 2v12c0 1.1 0.9 2 2 2h16c1.1 0 2-0.9
+2-2V6c0-1.1-0.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5
5-2.24 5-5 5z" />
<path
android:pathData="M0 0h24v24H0z" />
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_car.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_car.xml
index 6bab660..0f37425 100644
--- a/samples/SupportSliceDemos/src/main/res/drawable/ic_car.xml
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_car.xml
@@ -21,11 +21,11 @@
<path
android:fillColor="#000000"
- android:pathData="M18.92 6.01C18.72 5.42 18.16 5 17.5 5h-11c-.66 0-1.21 .42 -1.42 1.01L3 12v8c0
-.55 .45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55 .45 1 1 1h1c.55 0 1-.45
-1-1v-8l-2.08-5.99zM6.5 16c-.83 0-1.5-.67-1.5-1.5S5.67 13 6.5 13s1.5 .67 1.5
-1.5S7.33 16 6.5 16zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5 .67 1.5
-1.5-.67 1.5-1.5 1.5zM5 11l1.5-4.5h11L19 11H5z" />
+ android:pathData="M18.92 6.01C18.72 5.42 18.16 5 17.5 5h-11c-0.66 0-1.21 0.42 -1.42 1.01L3 12v8c0
+0.55 0.45 1 1 1h1c0.55 0 1-0.45 1-1v-1h12v1c0 0.55 0.45 1 1 1h1c0.55 0 1-0.45
+1-1v-8l-2.08-5.99zM6.5 16c-0.83 0-1.5-0.67-1.5-1.5S5.67 13 6.5 13s1.5 0.67 1.5
+1.5S7.33 16 6.5 16zm11 0c-0.83 0-1.5-0.67-1.5-1.5s0.67-1.5 1.5-1.5 1.5 0.67 1.5
+1.5-0.67 1.5-1.5 1.5zM5 11l1.5-4.5h11L19 11H5z" />
<path
android:pathData="M0 0h24v24H0z" />
</vector>
\ No newline at end of file
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_work.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_work.xml
index 6806402..9e28e15 100644
--- a/samples/SupportSliceDemos/src/main/res/drawable/ic_work.xml
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_work.xml
@@ -23,6 +23,6 @@
android:pathData="M0 0h24v24H0z" />
<path
android:fillColor="#000000"
- android:pathData="M20 6h-4V4c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H4c-1.11 0-1.99 .89 -1.99 2L2
-19c0 1.11 .89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-6 0h-4V4h4v2z" />
+ android:pathData="M20 6h-4V4c0-1.11-0.89-2-2-2h-4c-1.11 0-2 0.89-2 2v2H4c-1.11 0-1.99 0.89 -1.99 2L2
+19c0 1.11 0.89 2 2 2h16c1.11 0 2-0.89 2-2V8c0-1.11-0.89-2-2-2zm-6 0h-4V4h4v2z" />
</vector>
\ No newline at end of file
diff --git a/samples/SupportSliceDemos/src/main/res/layout/activity_layout.xml b/samples/SupportSliceDemos/src/main/res/layout/activity_layout.xml
index feb2119..b1acf00 100644
--- a/samples/SupportSliceDemos/src/main/res/layout/activity_layout.xml
+++ b/samples/SupportSliceDemos/src/main/res/layout/activity_layout.xml
@@ -42,7 +42,7 @@
android:background="?android:attr/selectableItemBackground"
android:contentInsetStart="0dp"
android:contentInsetStartWithNavigation="0dp"
- android:theme="?android:attr/actionBarTheme">
+ android:theme="?attr/actionBarTheme">
<SearchView
android:id="@+id/search_view"
android:layout_width="match_parent"
diff --git a/samples/SupportSliceDemos/src/main/res/values/styles.xml b/samples/SupportSliceDemos/src/main/res/values/styles.xml
index 38d7298..dc139b6 100644
--- a/samples/SupportSliceDemos/src/main/res/values/styles.xml
+++ b/samples/SupportSliceDemos/src/main/res/values/styles.xml
@@ -18,7 +18,7 @@
<resources>
<!-- Base application theme. -->
- <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <style name="AppTheme" parent="Theme.AppCompat.Light">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
diff --git a/settings.gradle b/settings.gradle
index 581897d..65ef3f4 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -44,6 +44,7 @@
includeProject(":contentpaging", "content")
includeProject(":coordinatorlayout", "coordinatorlayout")
includeProject(":core", "compat")
+includeProject(":core-ktx", "core/ktx")
includeProject(":cursoradapter", "cursoradapter")
includeProject(":customview", "customview")
includeProject(":documentfile", "documentfile")
@@ -58,6 +59,7 @@
includeProject(":heifwriter", "heifwriter")
includeProject(":interpolator", "interpolator")
includeProject(":jetifier-core", "jetifier/jetifier/core")
+includeProject(":jetifier-processor", "jetifier/jetifier/processor")
includeProject(":jetifier-gradle-plugin", "jetifier/jetifier/gradle-plugin")
includeProject(":jetifier-standalone", "jetifier/jetifier/standalone")
includeProject(":jetifier-preprocessor", "jetifier/jetifier/preprocessor")
diff --git a/slices/builders/api/current.txt b/slices/builders/api/current.txt
index 3ff195a..4538b46 100644
--- a/slices/builders/api/current.txt
+++ b/slices/builders/api/current.txt
@@ -3,10 +3,10 @@
public deprecated class GridBuilder extends androidx.slice.builders.TemplateSliceBuilder {
ctor public GridBuilder(androidx.slice.builders.ListBuilder);
method public androidx.slice.builders.GridBuilder addCell(androidx.slice.builders.GridBuilder.CellBuilder);
- method public androidx.slice.builders.GridBuilder addCell(java.util.function.Consumer<androidx.slice.builders.GridBuilder.CellBuilder>);
+ method public androidx.slice.builders.GridBuilder addCell(androidx.core.util.Consumer<androidx.slice.builders.GridBuilder.CellBuilder>);
method public androidx.slice.builders.GridBuilder addSeeMoreAction(android.app.PendingIntent);
method public androidx.slice.builders.GridBuilder addSeeMoreCell(androidx.slice.builders.GridBuilder.CellBuilder);
- method public androidx.slice.builders.GridBuilder addSeeMoreCell(java.util.function.Consumer<androidx.slice.builders.GridBuilder.CellBuilder>);
+ method public androidx.slice.builders.GridBuilder addSeeMoreCell(androidx.core.util.Consumer<androidx.slice.builders.GridBuilder.CellBuilder>);
method public androidx.slice.builders.GridBuilder setContentDescription(java.lang.CharSequence);
method public androidx.slice.builders.GridBuilder setPrimaryAction(androidx.slice.builders.SliceAction);
field public static final deprecated int ICON_IMAGE = 0; // 0x0
@@ -36,12 +36,15 @@
public class GridRowBuilder extends androidx.slice.builders.TemplateSliceBuilder {
ctor public GridRowBuilder(androidx.slice.builders.ListBuilder);
method public androidx.slice.builders.GridRowBuilder addCell(androidx.slice.builders.GridRowBuilder.CellBuilder);
- method public androidx.slice.builders.GridRowBuilder addCell(java.util.function.Consumer<androidx.slice.builders.GridRowBuilder.CellBuilder>);
- method public androidx.slice.builders.GridRowBuilder addSeeMoreAction(android.app.PendingIntent);
- method public androidx.slice.builders.GridRowBuilder addSeeMoreCell(androidx.slice.builders.GridRowBuilder.CellBuilder);
- method public androidx.slice.builders.GridRowBuilder addSeeMoreCell(java.util.function.Consumer<androidx.slice.builders.GridRowBuilder.CellBuilder>);
+ method public androidx.slice.builders.GridRowBuilder addCell(androidx.core.util.Consumer<androidx.slice.builders.GridRowBuilder.CellBuilder>);
+ method public deprecated androidx.slice.builders.GridRowBuilder addSeeMoreAction(android.app.PendingIntent);
+ method public deprecated androidx.slice.builders.GridRowBuilder addSeeMoreCell(androidx.slice.builders.GridRowBuilder.CellBuilder);
+ method public deprecated androidx.slice.builders.GridRowBuilder addSeeMoreCell(androidx.core.util.Consumer<androidx.slice.builders.GridRowBuilder.CellBuilder>);
method public androidx.slice.builders.GridRowBuilder setContentDescription(java.lang.CharSequence);
method public androidx.slice.builders.GridRowBuilder setPrimaryAction(androidx.slice.builders.SliceAction);
+ method public androidx.slice.builders.GridRowBuilder setSeeMoreAction(android.app.PendingIntent);
+ method public androidx.slice.builders.GridRowBuilder setSeeMoreCell(androidx.slice.builders.GridRowBuilder.CellBuilder);
+ method public androidx.slice.builders.GridRowBuilder setSeeMoreCell(androidx.core.util.Consumer<androidx.slice.builders.GridRowBuilder.CellBuilder>);
}
public static final class GridRowBuilder.CellBuilder extends androidx.slice.builders.TemplateSliceBuilder {
@@ -62,22 +65,25 @@
ctor public ListBuilder(android.content.Context, android.net.Uri, long);
method public androidx.slice.builders.ListBuilder addAction(androidx.slice.builders.SliceAction);
method public deprecated androidx.slice.builders.ListBuilder addGrid(androidx.slice.builders.GridBuilder);
- method public deprecated androidx.slice.builders.ListBuilder addGrid(java.util.function.Consumer<androidx.slice.builders.GridBuilder>);
+ method public deprecated androidx.slice.builders.ListBuilder addGrid(androidx.core.util.Consumer<androidx.slice.builders.GridBuilder>);
method public androidx.slice.builders.ListBuilder addGridRow(androidx.slice.builders.GridRowBuilder);
- method public androidx.slice.builders.ListBuilder addGridRow(java.util.function.Consumer<androidx.slice.builders.GridRowBuilder>);
+ method public androidx.slice.builders.ListBuilder addGridRow(androidx.core.util.Consumer<androidx.slice.builders.GridRowBuilder>);
method public androidx.slice.builders.ListBuilder addInputRange(androidx.slice.builders.ListBuilder.InputRangeBuilder);
- method public androidx.slice.builders.ListBuilder addInputRange(java.util.function.Consumer<androidx.slice.builders.ListBuilder.InputRangeBuilder>);
+ method public androidx.slice.builders.ListBuilder addInputRange(androidx.core.util.Consumer<androidx.slice.builders.ListBuilder.InputRangeBuilder>);
method public androidx.slice.builders.ListBuilder addRange(androidx.slice.builders.ListBuilder.RangeBuilder);
- method public androidx.slice.builders.ListBuilder addRange(java.util.function.Consumer<androidx.slice.builders.ListBuilder.RangeBuilder>);
+ method public androidx.slice.builders.ListBuilder addRange(androidx.core.util.Consumer<androidx.slice.builders.ListBuilder.RangeBuilder>);
method public androidx.slice.builders.ListBuilder addRow(androidx.slice.builders.ListBuilder.RowBuilder);
- method public androidx.slice.builders.ListBuilder addRow(java.util.function.Consumer<androidx.slice.builders.ListBuilder.RowBuilder>);
- method public androidx.slice.builders.ListBuilder addSeeMoreAction(android.app.PendingIntent);
- method public androidx.slice.builders.ListBuilder addSeeMoreRow(androidx.slice.builders.ListBuilder.RowBuilder);
- method public androidx.slice.builders.ListBuilder addSeeMoreRow(java.util.function.Consumer<androidx.slice.builders.ListBuilder.RowBuilder>);
+ method public androidx.slice.builders.ListBuilder addRow(androidx.core.util.Consumer<androidx.slice.builders.ListBuilder.RowBuilder>);
+ method public deprecated androidx.slice.builders.ListBuilder addSeeMoreAction(android.app.PendingIntent);
+ method public deprecated androidx.slice.builders.ListBuilder addSeeMoreRow(androidx.slice.builders.ListBuilder.RowBuilder);
+ method public deprecated androidx.slice.builders.ListBuilder addSeeMoreRow(androidx.core.util.Consumer<androidx.slice.builders.ListBuilder.RowBuilder>);
method public androidx.slice.builders.ListBuilder setColor(int);
method public androidx.slice.builders.ListBuilder setHeader(androidx.slice.builders.ListBuilder.HeaderBuilder);
- method public androidx.slice.builders.ListBuilder setHeader(java.util.function.Consumer<androidx.slice.builders.ListBuilder.HeaderBuilder>);
+ method public androidx.slice.builders.ListBuilder setHeader(androidx.core.util.Consumer<androidx.slice.builders.ListBuilder.HeaderBuilder>);
method public androidx.slice.builders.ListBuilder setKeywords(java.util.List<java.lang.String>);
+ method public androidx.slice.builders.ListBuilder setSeeMoreAction(android.app.PendingIntent);
+ method public androidx.slice.builders.ListBuilder setSeeMoreRow(androidx.slice.builders.ListBuilder.RowBuilder);
+ method public androidx.slice.builders.ListBuilder setSeeMoreRow(androidx.core.util.Consumer<androidx.slice.builders.ListBuilder.RowBuilder>);
field public static final int ICON_IMAGE = 0; // 0x0
field public static final long INFINITY = -1L; // 0xffffffffffffffffL
field public static final int LARGE_IMAGE = 2; // 0x2
@@ -91,17 +97,22 @@
method public androidx.slice.builders.ListBuilder.HeaderBuilder setPrimaryAction(androidx.slice.builders.SliceAction);
method public androidx.slice.builders.ListBuilder.HeaderBuilder setSubtitle(java.lang.CharSequence);
method public androidx.slice.builders.ListBuilder.HeaderBuilder setSubtitle(java.lang.CharSequence, boolean);
- method public androidx.slice.builders.ListBuilder.HeaderBuilder setSummarySubtitle(java.lang.CharSequence);
- method public androidx.slice.builders.ListBuilder.HeaderBuilder setSummarySubtitle(java.lang.CharSequence, boolean);
+ method public androidx.slice.builders.ListBuilder.HeaderBuilder setSummary(java.lang.CharSequence);
+ method public androidx.slice.builders.ListBuilder.HeaderBuilder setSummary(java.lang.CharSequence, boolean);
+ method public deprecated androidx.slice.builders.ListBuilder.HeaderBuilder setSummarySubtitle(java.lang.CharSequence);
+ method public deprecated androidx.slice.builders.ListBuilder.HeaderBuilder setSummarySubtitle(java.lang.CharSequence, boolean);
method public androidx.slice.builders.ListBuilder.HeaderBuilder setTitle(java.lang.CharSequence);
method public androidx.slice.builders.ListBuilder.HeaderBuilder setTitle(java.lang.CharSequence, boolean);
}
public static class ListBuilder.InputRangeBuilder extends androidx.slice.builders.TemplateSliceBuilder {
ctor public ListBuilder.InputRangeBuilder(androidx.slice.builders.ListBuilder);
- method public androidx.slice.builders.ListBuilder.InputRangeBuilder setAction(android.app.PendingIntent);
+ method public deprecated androidx.slice.builders.ListBuilder.InputRangeBuilder setAction(android.app.PendingIntent);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setContentDescription(java.lang.CharSequence);
+ method public androidx.slice.builders.ListBuilder.InputRangeBuilder setInputAction(android.app.PendingIntent);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setMax(int);
+ method public androidx.slice.builders.ListBuilder.InputRangeBuilder setPrimaryAction(androidx.slice.builders.SliceAction);
+ method public androidx.slice.builders.ListBuilder.InputRangeBuilder setSubtitle(java.lang.CharSequence);
method public deprecated androidx.slice.builders.ListBuilder.InputRangeBuilder setThumb(android.graphics.drawable.Icon);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setThumb(androidx.core.graphics.drawable.IconCompat);
method public androidx.slice.builders.ListBuilder.InputRangeBuilder setTitle(java.lang.CharSequence);
@@ -112,6 +123,8 @@
ctor public ListBuilder.RangeBuilder(androidx.slice.builders.ListBuilder);
method public androidx.slice.builders.ListBuilder.RangeBuilder setContentDescription(java.lang.CharSequence);
method public androidx.slice.builders.ListBuilder.RangeBuilder setMax(int);
+ method public androidx.slice.builders.ListBuilder.RangeBuilder setPrimaryAction(androidx.slice.builders.SliceAction);
+ method public androidx.slice.builders.ListBuilder.RangeBuilder setSubtitle(java.lang.CharSequence);
method public androidx.slice.builders.ListBuilder.RangeBuilder setTitle(java.lang.CharSequence);
method public androidx.slice.builders.ListBuilder.RangeBuilder setValue(int);
}
diff --git a/slices/builders/build.gradle b/slices/builders/build.gradle
index 1bf4c29..907a0b4 100644
--- a/slices/builders/build.gradle
+++ b/slices/builders/build.gradle
@@ -35,5 +35,5 @@
mavenGroup = LibraryGroups.SLICE
inceptionYear = "2017"
description = "A set of builders to create templates using SliceProvider APIs"
- minSdkVersion = 24
+ minSdkVersion = 19
}
diff --git a/slices/builders/src/main/java/androidx/slice/builders/GridBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/GridBuilder.java
index bd6dbf4..7c672d7 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/GridBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/GridBuilder.java
@@ -21,17 +21,15 @@
import android.app.PendingIntent;
import android.graphics.drawable.Icon;
import android.net.Uri;
-import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
+import androidx.core.util.Consumer;
import androidx.slice.builders.impl.TemplateBuilderImpl;
-import java.util.function.Consumer;
-
/**
* Builder to construct a row of slice content in a grid format.
@@ -94,7 +92,6 @@
/**
* Add a cell to the grid builder.
*/
- @RequiresApi(Build.VERSION_CODES.N)
@NonNull
public GridBuilder addCell(@NonNull Consumer<CellBuilder> c) {
CellBuilder b = new CellBuilder(this);
@@ -123,7 +120,7 @@
throw new IllegalStateException("Trying to add see more cell when one has "
+ "already been added");
}
- mImpl.addSeeMoreCell((TemplateBuilderImpl) builder.mImpl);
+ mImpl.setSeeMoreCell((TemplateBuilderImpl) builder.mImpl);
mHasSeeMore = true;
return this;
}
@@ -143,7 +140,6 @@
* a row or action has been previously added.
* </p>
*/
- @RequiresApi(Build.VERSION_CODES.N)
@NonNull
public GridBuilder addSeeMoreCell(@NonNull Consumer<CellBuilder> c) {
CellBuilder b = new CellBuilder(this);
@@ -166,7 +162,7 @@
throw new IllegalStateException("Trying to add see more action when one has "
+ "already been added");
}
- mImpl.addSeeMoreAction(intent);
+ mImpl.setSeeMoreAction(intent);
mHasSeeMore = true;
return this;
}
@@ -301,6 +297,7 @@
*/
@NonNull
@Deprecated
+ @RequiresApi(23)
public CellBuilder addLargeImage(@NonNull Icon image) {
return addImage(image, ListBuilder.LARGE_IMAGE, false /* isLoading */);
}
@@ -317,6 +314,7 @@
*/
@NonNull
@Deprecated
+ @RequiresApi(23)
public CellBuilder addLargeImage(@Nullable Icon image, boolean isLoading) {
return addImage(image, ListBuilder.LARGE_IMAGE, isLoading);
}
@@ -329,6 +327,7 @@
*/
@NonNull
@Deprecated
+ @RequiresApi(23)
public CellBuilder addImage(@NonNull Icon image) {
return addImage(image, ListBuilder.SMALL_IMAGE, false /* isLoading */);
}
@@ -338,6 +337,7 @@
*/
@NonNull
@Deprecated
+ @RequiresApi(23)
public CellBuilder addImage(@Nullable Icon image, boolean isLoading) {
return addImage(image, ListBuilder.SMALL_IMAGE, isLoading);
}
@@ -347,6 +347,7 @@
*/
@NonNull
@Deprecated
+ @RequiresApi(23)
public CellBuilder addImage(@NonNull Icon image, @ListBuilder.ImageMode int imageMode) {
return addImage(image, imageMode, false /* isLoading */);
}
@@ -356,6 +357,7 @@
*/
@NonNull
@Deprecated
+ @RequiresApi(23)
public CellBuilder addImage(@Nullable Icon image, @ListBuilder.ImageMode int imageMode,
boolean isLoading) {
mImpl.addImage(IconCompat.createFromIcon(image), imageMode, isLoading);
diff --git a/slices/builders/src/main/java/androidx/slice/builders/GridRowBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/GridRowBuilder.java
index 6213a4f..9e08bb9 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/GridRowBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/GridRowBuilder.java
@@ -20,24 +20,51 @@
import android.app.PendingIntent;
import android.net.Uri;
-import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
+import androidx.core.util.Consumer;
import androidx.slice.builders.impl.TemplateBuilderImpl;
-import java.util.function.Consumer;
-
/**
- * Builder to construct a row of slice content in a grid format.
+ * Builder to construct a grid row which may be added as a row to {@link ListBuilder}.
* <p>
- * A grid row is composed of cells, each cell can have a combination of text and images. For more
- * details see {@link CellBuilder}.
- * </p>
+ * A grid row supports cells of vertically laid out content in a single row. Each cell can
+ * contain a combination of text and images and is constructed using a {@link CellBuilder}.
+ * <p>
+ * A grid supports a couple of image types:
+ * <ul>
+ * <li>{@link ListBuilder#ICON_IMAGE} - icon images are expected to be tintable and are
+ * shown at a standard icon size.</li>
+ * <li>{@link ListBuilder#SMALL_IMAGE} - small images are not tinted and are shown at
+ * a small size.</li>
+ * <li>{@link ListBuilder#LARGE_IMAGE} - large images are not tinted and are shown as
+ * large as they can be, in a {@link android.widget.ImageView.ScaleType#CENTER_CROP}</li>
+ * </ul>
+ * <p>
+ * If the first row of your slice was created with {@link GridRowBuilder} then there are a couple
+ * of extra considerations you should take for when the slice is displayed in different modes,
+ * particularly:
+ * <ul>
+ * <li>{@link androidx.slice.widget.SliceView#MODE_SHORTCUT} - ensure you use
+ * {@link #setPrimaryAction(SliceAction)} on grid row builder to specify the shortcut action,
+ * icon, and label. This action will also be activated when the whole row is clicked, unless
+ * the individual cells in the row have actions set on them.</li>
+ * <li>{@link androidx.slice.widget.SliceView#MODE_SMALL} - in small format there might not
+ * be enough space to display a cell containing two lines of text and an image, in this case
+ * only one line of text will be shown with the image, favoring text added via
+ * {@link CellBuilder#addTitleText(CharSequence)} or the first text added if no title was
+ * added.</li>
+ * </ul>
+ * <p>
+ * If more cells are added to the grid row than can be displayed, the cells will be cut off. Using
+ * {@link #addSeeMoreAction(PendingIntent)} you can specify an action to take the user to see the
+ * rest of the content, this will take up space as a cell item in a row if added.
+ *
+ * @see ListBuilder#addGridRow(GridRowBuilder)
*/
public class GridRowBuilder extends TemplateSliceBuilder {
@@ -69,7 +96,6 @@
/**
* Add a cell to the grid builder.
*/
- @RequiresApi(Build.VERSION_CODES.N)
@NonNull
public GridRowBuilder addCell(@NonNull Consumer<CellBuilder> c) {
CellBuilder b = new CellBuilder(this);
@@ -93,12 +119,12 @@
* </p>
*/
@NonNull
- public GridRowBuilder addSeeMoreCell(@NonNull CellBuilder builder) {
+ public GridRowBuilder setSeeMoreCell(@NonNull CellBuilder builder) {
if (mHasSeeMore) {
throw new IllegalStateException("Trying to add see more cell when one has "
+ "already been added");
}
- mImpl.addSeeMoreCell((TemplateBuilderImpl) builder.mImpl);
+ mImpl.setSeeMoreCell((TemplateBuilderImpl) builder.mImpl);
mHasSeeMore = true;
return this;
}
@@ -118,8 +144,81 @@
* a row or action has been previously added.
* </p>
*/
- @RequiresApi(Build.VERSION_CODES.N)
@NonNull
+ public GridRowBuilder setSeeMoreCell(@NonNull Consumer<CellBuilder> c) {
+ CellBuilder b = new CellBuilder(this);
+ c.accept(b);
+ return setSeeMoreCell(b);
+ }
+
+ /**
+ * If all content in a slice cannot be shown, a "see more" affordance may be displayed where
+ * the content is cut off. The action added here should take the user to an activity to see
+ * all of the content, and will be invoked when the "see more" affordance is tapped.
+ * <p>
+ * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+ * a row or action has been previously added.
+ * </p>
+ */
+ @NonNull
+ public GridRowBuilder setSeeMoreAction(@NonNull PendingIntent intent) {
+ if (mHasSeeMore) {
+ throw new IllegalStateException("Trying to add see more action when one has "
+ + "already been added");
+ }
+ mImpl.setSeeMoreAction(intent);
+ mHasSeeMore = true;
+ return this;
+ }
+
+ /**
+ * If all content in a slice cannot be shown, the cell added here may be displayed where the
+ * content is cut off.
+ * <p>
+ * This method should only be used if you want to display a custom cell to indicate more
+ * content, consider using {@link #addSeeMoreAction(PendingIntent)} otherwise. If you do
+ * choose to specify a custom cell, the cell should have
+ * {@link CellBuilder#setContentIntent(PendingIntent)} specified to take the user to an
+ * activity to see all of the content.
+ * </p>
+ * <p>
+ * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+ * a row or action has been previously added.
+ * </p>
+ *
+ * @deprecated TO BE REMOVED
+ */
+ @NonNull
+ @Deprecated
+ public GridRowBuilder addSeeMoreCell(@NonNull CellBuilder builder) {
+ if (mHasSeeMore) {
+ throw new IllegalStateException("Trying to add see more cell when one has "
+ + "already been added");
+ }
+ mImpl.setSeeMoreCell((TemplateBuilderImpl) builder.mImpl);
+ mHasSeeMore = true;
+ return this;
+ }
+
+ /**
+ * If all content in a slice cannot be shown, the cell added here may be displayed where the
+ * content is cut off.
+ * <p>
+ * This method should only be used if you want to display a custom cell to indicate more
+ * content, consider using {@link #addSeeMoreAction(PendingIntent)} otherwise. If you do
+ * choose to specify a custom cell, the cell should have
+ * {@link CellBuilder#setContentIntent(PendingIntent)} specified to take the user to an
+ * activity to see all of the content.
+ * </p>
+ * <p>
+ * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+ * a row or action has been previously added.
+ * </p>
+ *
+ * @deprecated TO BE REMOVED
+ */
+ @NonNull
+ @Deprecated
public GridRowBuilder addSeeMoreCell(@NonNull Consumer<CellBuilder> c) {
CellBuilder b = new CellBuilder(this);
c.accept(b);
@@ -134,20 +233,30 @@
* Only one see more affordance can be added, this throws {@link IllegalStateException} if
* a row or action has been previously added.
* </p>
+ * @deprecated TO BE REMOVED
*/
+ @Deprecated
@NonNull
public GridRowBuilder addSeeMoreAction(@NonNull PendingIntent intent) {
if (mHasSeeMore) {
throw new IllegalStateException("Trying to add see more action when one has "
+ "already been added");
}
- mImpl.addSeeMoreAction(intent);
+ mImpl.setSeeMoreAction(intent);
mHasSeeMore = true;
return this;
}
/**
- * Sets the intent to send when the slice is activated.
+ * Sets the intent to send when the whole grid row is clicked.
+ * <p>
+ * If all the cells in the grid have specified a
+ * {@link CellBuilder#setPrimaryAction(SliceAction)} then the action set here on the
+ * {@link GridRowBuilder} may not ever be invoked.
+ * <p>
+ * If this grid row is the first row in {@link ListBuilder}, the action
+ * set here will be used to represent the slice when presented in
+ * {@link androidx.slice.widget.SliceView#MODE_SHORTCUT}.
*/
@NonNull
public GridRowBuilder setPrimaryAction(@NonNull SliceAction action) {
@@ -173,9 +282,11 @@
}
/**
- * Sub-builder to construct a cell to be displayed in a grid.
+ * Builder to construct a cell. A cell can be added as an item to GridRowBuilder via
+ * {@link GridRowBuilder#addCell(CellBuilder)}.
* <p>
- * Content added to a cell will be displayed in order vertically, for example the below code
+ * A cell supports up to two lines of text and one image. Content added to a cell will be
+ * displayed in the order that the content is added to it. For example, the below code
* would construct a cell with "First text", and image below it, and then "Second text" below
* the image.
*
@@ -185,9 +296,22 @@
* .addImage(middleIcon)
* .addText("Second text");
* </pre>
+ * <p>
+ * A cell supports a couple of image types:
+ * <ul>
+ * <li>{@link ListBuilder#ICON_IMAGE} - icon images are expected to be tintable and are
+ * shown at a standard icon size.</li>
+ * <li>{@link ListBuilder#SMALL_IMAGE} - small images are not tinted and are shown at
+ * a small size.</li>
+ * <li>{@link ListBuilder#LARGE_IMAGE} - large images are not tinted and are shown as
+ * large as they can be, in a {@link android.widget.ImageView.ScaleType#CENTER_CROP}</li>
+ * </ul>
*
- * A cell can have at most two text items and one image.
- * </p>
+ * @see GridRowBuilder#addCell(CellBuilder)
+ * @see ListBuilder#addGridRow(GridRowBuilder)
+ * @see ListBuilder#ICON_IMAGE
+ * @see ListBuilder#SMALL_IMAGE
+ * @see ListBuilder#ICON_IMAGE
*/
public static final class CellBuilder extends TemplateSliceBuilder {
private androidx.slice.builders.impl.GridRowBuilder.CellBuilder mImpl;
diff --git a/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java
index 29d43ac..d59aead 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/ListBuilder.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.graphics.drawable.Icon;
import android.net.Uri;
-import android.os.Build;
import androidx.annotation.ColorInt;
import androidx.annotation.IntDef;
@@ -32,33 +31,88 @@
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
+import androidx.core.util.Consumer;
import androidx.slice.SliceSpecs;
import androidx.slice.builders.impl.ListBuilderBasicImpl;
import androidx.slice.builders.impl.ListBuilderV1Impl;
import androidx.slice.builders.impl.TemplateBuilderImpl;
import androidx.slice.core.SliceHints;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
-import java.util.function.Consumer;
+
/**
- * Builder to construct slice content in a list format.
+ * A slice can be constructed with ListBuilder.
* <p>
- * Use this builder for showing rows of content which is composed of text, images, and actions. For
- * more details see {@link RowBuilder}.
- * </p>
+ * A slice is a piece of templated app content that can be presented outside of the app
+ * in a {@link androidx.slice.widget.SliceView}. To provide a slice you should implement a
+ * {@link androidx.slice.SliceProvider} and use ListBuilder to construct your slice when
+ * {@link androidx.slice.SliceProvider#onBindSlice(Uri)} is called.
* <p>
- * Slices can be displayed in different formats:
+ * ListBuilder allows you to construct a slice made up of rows of content. A list should
+ * have at least one row of content as well as a primary {@link SliceAction}. The row types that
+ * ListBuilder supports are:
* <ul>
- * <li>Shortcut - The slice is displayed as an icon with a text label.</li>
- * <li>Small - Only a single row of content is displayed in small format, to specify which
- * row to display in small format see {@link #setHeader(HeaderBuilder)}.</li>
- * <li>Large - As many rows of content are shown as possible. If the presenter of the slice
- * allows scrolling then all rows of content will be displayed in a scrollable view.</li>
+ * <li>{@link HeaderBuilder} - A list can have one header which appears at the top of the list.
+ * The header can support showing title, subtitle, and a row action. A header can also have
+ * a summary of the contents of the slice which can be shown when not all of the slice can be
+ * displayed.
+ * </li>
+ * <li>{@link RowBuilder} - A basic row supports title, subtitle, timestamps, images, row
+ * action, icon actions, and toggle actions.
+ * </li>
+ * <li>{@link GridRowBuilder} - A grid row supports cells of vertically laid out content
+ * displayed in a single row.
+ * </li>
+ * <li>{@link RangeBuilder} - A range row supports displaying a horizontal progress indicator.
+ * </li>
+ * <li>{@link InputRangeBuilder} - An input range row supports displaying a horizontal slider
+ * allowing slider input (e.g. brightness or volume slider).
+ * </li>
* </ul>
- * </p>
+ * <p>
+ * In addition to rows of content, ListBuilder can also have {@link SliceAction}s added to it. These
+ * actions may appear differently on your slice depending on how the
+ * {@link androidx.slice.widget.SliceView} is configured. Normally the actions would appear in
+ * the header.
+ * <p>
+ * To ensure your slice is presented correctly you should consider the configurations
+ * {@link androidx.slice.widget.SliceView} supports:
+ * <ul>
+ * <li>{@link androidx.slice.widget.SliceView#MODE_SHORTCUT} - The primary {@link SliceAction}
+ * of the slice is used your primary action should contain an image and title representative
+ * of your slice. If providing a tintable icon, use {@link #setColor(int)} to specify the color.
+ * If a header has been specified for the list, the primary action associated with it will be
+ * used, otherwise it will be the primary action associated with the first row of the list.
+ * </li>
+ * <li>{@link androidx.slice.widget.SliceView#MODE_SMALL} - Only a single row of content is
+ * displayed in small format. If a header has been specified it will be displayed. If no header
+ * was set, then the first row will be used and may appear differently depending on the row
+ * type and the configuration of {@link androidx.slice.widget.SliceView}.
+ * </li>
+ * <li>{@link androidx.slice.widget.SliceView#MODE_LARGE} - As many rows of content are shown
+ * as possible. If the presenter of the slice allows scrolling then all rows of content will
+ * be displayed in a scrollable view.
+ * </li>
+ * </ul>
+ * <p>
+ * Note that scrolling on SliceView can be disabled, in which case only the header and one or two
+ * rows of content may be shown for your slice. If your slice contains many rows of content to
+ * scroll through (e.g. list of wifi networks), consider using
+ * {@link #addSeeMoreAction(PendingIntent)} to provide a link to open the activity associated with
+ * the content.
*
+ * @see HeaderBuilder
* @see RowBuilder
+ * @see GridRowBuilder
+ * @see RangeBuilder
+ * @see InputRangeBuilder
+ * @see SliceAction
+ * @see androidx.slice.SliceProvider
+ * @see androidx.slice.SliceProvider#onBindSlice(Uri)
+ * @see androidx.slice.widget.SliceView
*/
public class ListBuilder extends TemplateSliceBuilder {
@@ -72,6 +126,7 @@
@IntDef({
LARGE_IMAGE, SMALL_IMAGE, ICON_IMAGE, UNKNOWN_IMAGE
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface ImageMode{}
/**
@@ -97,7 +152,8 @@
public static final long INFINITY = SliceHints.INFINITY;
/**
- * Create a builder which will construct a slice that will display rows of content.
+ * Create a builder which will construct a slice made up of rows of content.
+ *
* @param uri Uri to tag for this slice.
*
* @deprecated TO BE REMOVED; use {@link #ListBuilder(Context, Uri, long)}.
@@ -128,7 +184,7 @@
}
/**
- * Add a row to list builder.
+ * Add a row to the list builder.
*/
@NonNull
public ListBuilder addRow(@NonNull RowBuilder builder) {
@@ -137,9 +193,8 @@
}
/**
- * Add a row the list builder.
+ * Add a row to the list builder.
*/
- @RequiresApi(api = Build.VERSION_CODES.N)
@NonNull
public ListBuilder addRow(@NonNull Consumer<RowBuilder> c) {
RowBuilder b = new RowBuilder(this);
@@ -164,7 +219,6 @@
*
* @deprecated TO BE REMOVED; use {@link #addGridRow(GridRowBuilder)} instead
*/
- @RequiresApi(api = Build.VERSION_CODES.N)
@NonNull
@Deprecated
public ListBuilder addGrid(@NonNull Consumer<GridBuilder> c) {
@@ -185,7 +239,6 @@
/**
* Add a grid row to the list builder.
*/
- @RequiresApi(api = Build.VERSION_CODES.N)
@NonNull
public ListBuilder addGridRow(@NonNull Consumer<GridRowBuilder> c) {
GridRowBuilder b = new GridRowBuilder(this);
@@ -194,10 +247,20 @@
}
/**
- * Adds a header to this template.
+ * Sets a header for this list builder. A list can have only one header. Setting a header allows
+ * some flexibility in what's displayed in your slice when SliceView displays in
+ * {@link androidx.slice.widget.SliceView#MODE_SMALL} and
+ * {@link androidx.slice.widget.SliceView#MODE_SHORTCUT}.
* <p>
- * The header should contain a title that is representative of the content in this slice along
- * with an intent that links to the app activity associated with this content.
+ * In MODE_SMALL, the header row shown if one has been added. The header will also
+ * display the {@link HeaderBuilder#setSummary(CharSequence)} text if it has been
+ * specified, allowing a summary of otherwise hidden content to be shown.
+ * <p>
+ * In MODE_SHORTCUT, the primary action set using
+ * {@link HeaderBuilder#setPrimaryAction(SliceAction)} will be used for the shortcut
+ * representation.
+ *
+ * @see HeaderBuilder#setSummary(CharSequence)
*/
@NonNull
public ListBuilder setHeader(@NonNull HeaderBuilder builder) {
@@ -206,12 +269,21 @@
}
/**
- * Adds a header to this template.
+ * Sets a header for this list builder. A list can have only one header. Setting a header allows
+ * some flexibility in what's displayed in your slice when SliceView displays in
+ * {@link androidx.slice.widget.SliceView#MODE_SMALL} and
+ * {@link androidx.slice.widget.SliceView#MODE_SHORTCUT}.
* <p>
- * The header should contain a title that is representative of the content in this slice along
- * with an intent that links to the app activity associated with this content.
+ * In MODE_SMALL, the header row shown if one has been added. The header will also
+ * display the {@link HeaderBuilder#setSummary(CharSequence)} text if it has been
+ * specified, allowing a summary of otherwise hidden content to be shown.
+ * <p>
+ * In MODE_SHORTCUT, the primary action set using
+ * {@link HeaderBuilder#setPrimaryAction(SliceAction)} will be used for the shortcut
+ * representation.
+ *
+ * @see HeaderBuilder#setSummary(CharSequence)
*/
- @RequiresApi(api = Build.VERSION_CODES.N)
@NonNull
public ListBuilder setHeader(@NonNull Consumer<HeaderBuilder> c) {
HeaderBuilder b = new HeaderBuilder(this);
@@ -220,13 +292,17 @@
}
/**
- * Adds an action to this template. Actions added with this method are grouped together and
- * may be shown on the template in large or small formats. Generally these actions will be
- * displayed in the order they were added, however, if not all actions can be displayed then
- * actions with a higher priority may be shown first.
+ * Adds an action to this list builder.
+ * <p>
+ * Actions added with this method are grouped together on the list template. These actions may
+ * appear differently on the slice depending on how the {@link androidx.slice.widget.SliceView}
+ * is configured. Generally these actions will be displayed in the order they were added,
+ * however, if not all actions can be displayed then actions with a higher priority may be shown
+ * first.
*
* @see SliceAction
* @see SliceAction#setPriority(int)
+ * @see androidx.slice.widget.SliceView#setActions(List, int)
*/
@NonNull
public ListBuilder addAction(@NonNull SliceAction action) {
@@ -261,6 +337,78 @@
return this;
}
+
+ /**
+ * If all content in a slice cannot be shown, the row added here may be displayed where the
+ * content is cut off. This row should have an affordance to take the user to an activity to
+ * see all of the content.
+ * <p>
+ * This method should only be used if you want to display a customized row to indicate more
+ * content, consider using {@link #addSeeMoreAction(PendingIntent)} otherwise. If you do
+ * choose to specify a custom row, the row should have a content intent or action end item
+ * specified to take the user to an activity to see all of the content. The row added here
+ * will only appear when not all content can be displayed and it will not be styled any
+ * differently from row built by {@link RowBuilder} normally.
+ * </p>
+ * <p>
+ * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+ * a row or action has been previously added.
+ * </p>
+ */
+ @NonNull
+ public ListBuilder setSeeMoreRow(@NonNull RowBuilder builder) {
+ if (mHasSeeMore) {
+ throw new IllegalArgumentException("Trying to add see more row when one has "
+ + "already been added");
+ }
+ mImpl.setSeeMoreRow((TemplateBuilderImpl) builder.mImpl);
+ mHasSeeMore = true;
+ return this;
+ }
+
+ /**
+ * If all content in a slice cannot be shown, the row added here may be displayed where the
+ * content is cut off. This row should have an affordance to take the user to an activity to
+ * see all of the content.
+ * <p>
+ * This method should only be used if you want to display a customized row to indicate more
+ * content, consider using {@link #addSeeMoreAction(PendingIntent)} otherwise. If you do
+ * choose to specify a custom row, the row should have a content intent or action end item
+ * specified to take the user to an activity to see all of the content. The row added here
+ * will only appear when not all content can be displayed and it will not be styled any
+ * differently from row built by {@link RowBuilder} normally.
+ * </p>
+ * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+ * a row or action has been previously added.
+ * </p>
+ */
+ @NonNull
+ public ListBuilder setSeeMoreRow(@NonNull Consumer<RowBuilder> c) {
+ RowBuilder b = new RowBuilder(this);
+ c.accept(b);
+ return addSeeMoreRow(b);
+ }
+
+ /**
+ * If all content in a slice cannot be shown, a "see more" affordance may be displayed where
+ * the content is cut off. The action added here should take the user to an activity to see
+ * all of the content, and will be invoked when the "see more" affordance is tapped.
+ * <p>
+ * Only one see more affordance can be added, this throws {@link IllegalStateException} if
+ * a row or action has been previously added.
+ * </p>
+ */
+ @NonNull
+ public ListBuilder setSeeMoreAction(@NonNull PendingIntent intent) {
+ if (mHasSeeMore) {
+ throw new IllegalArgumentException("Trying to add see more action when one has "
+ + "already been added");
+ }
+ mImpl.setSeeMoreAction(intent);
+ mHasSeeMore = true;
+ return this;
+ }
+
/**
* If all content in a slice cannot be shown, the row added here may be displayed where the
* content is cut off. This row should have an affordance to take the user to an activity to
@@ -275,14 +423,17 @@
* Only one see more affordance can be added, this throws {@link IllegalStateException} if
* a row or action has been previously added.
* </p>
+ *
+ * @deprecated TO BE REMOVED
*/
@NonNull
+ @Deprecated
public ListBuilder addSeeMoreRow(@NonNull RowBuilder builder) {
if (mHasSeeMore) {
throw new IllegalArgumentException("Trying to add see more row when one has "
+ "already been added");
}
- mImpl.addSeeMoreRow((TemplateBuilderImpl) builder.mImpl);
+ mImpl.setSeeMoreRow((TemplateBuilderImpl) builder.mImpl);
mHasSeeMore = true;
return this;
}
@@ -300,13 +451,15 @@
* Only one see more affordance can be added, this throws {@link IllegalStateException} if
* a row or action has been previously added.
* </p>
+ *
+ * @deprecated TO BE REMOVED
*/
- @RequiresApi(api = Build.VERSION_CODES.N)
+ @Deprecated
@NonNull
public ListBuilder addSeeMoreRow(@NonNull Consumer<RowBuilder> c) {
RowBuilder b = new RowBuilder(this);
c.accept(b);
- return addSeeMoreRow(b);
+ return setSeeMoreRow(b);
}
/**
@@ -317,14 +470,17 @@
* Only one see more affordance can be added, this throws {@link IllegalStateException} if
* a row or action has been previously added.
* </p>
+ *
+ * @deprecated TO BE REMOVED
*/
+ @Deprecated
@NonNull
public ListBuilder addSeeMoreAction(@NonNull PendingIntent intent) {
if (mHasSeeMore) {
throw new IllegalArgumentException("Trying to add see more action when one has "
+ "already been added");
}
- mImpl.addSeeMoreAction(intent);
+ mImpl.setSeeMoreAction(intent);
mHasSeeMore = true;
return this;
}
@@ -363,7 +519,6 @@
/**
* Add an input range row to the list builder.
*/
- @RequiresApi(api = Build.VERSION_CODES.N)
@NonNull
public ListBuilder addInputRange(@NonNull Consumer<InputRangeBuilder> c) {
InputRangeBuilder inputRangeBuilder = new InputRangeBuilder(this);
@@ -383,7 +538,6 @@
/**
* Add a range row to the list builder.
*/
- @RequiresApi(api = Build.VERSION_CODES.N)
@NonNull
public ListBuilder addRange(@NonNull Consumer<RangeBuilder> c) {
RangeBuilder rangeBuilder = new RangeBuilder(this);
@@ -392,11 +546,24 @@
}
/**
- * Builder to construct a range row.
+ * Builder to construct a range row which can be added to a {@link ListBuilder}.
+ * <p>
+ * A range row supports displaying a horizontal progress indicator.
+ *
+ * @see ListBuilder#addRange(Consumer)
+ * @see ListBuilder#addRange(RangeBuilder)
*/
public static class RangeBuilder extends TemplateSliceBuilder {
private androidx.slice.builders.impl.ListBuilder.RangeBuilder mImpl;
+ /**
+ * Builder to construct a range row which can be added to a {@link ListBuilder}.
+ * <p>
+ * A range row supports displaying a horizontal progress indicator.
+ *
+ * @see ListBuilder#addRange(Consumer)
+ * @see ListBuilder#addRange(RangeBuilder)
+ */
public RangeBuilder(@NonNull ListBuilder parent) {
super(parent.mImpl.createRangeBuilder());
}
@@ -429,6 +596,28 @@
}
/**
+ * Set the subtitle.
+ */
+ @NonNull
+ public RangeBuilder setSubtitle(@NonNull CharSequence title) {
+ mImpl.setSubtitle(title);
+ return this;
+ }
+
+ /**
+ * Set the primary action for this row.
+ * <p>
+ * The action specified here will be sent when the whole row is clicked. If this
+ * is the first row in a {@link ListBuilder} this action will also be used to define
+ * the {@link androidx.slice.widget.SliceView#MODE_SHORTCUT} representation of the slice.
+ */
+ @NonNull
+ public RangeBuilder setPrimaryAction(@NonNull SliceAction action) {
+ mImpl.setPrimaryAction(action);
+ return this;
+ }
+
+ /**
* Sets the content description.
*/
@NonNull
@@ -445,10 +634,23 @@
/**
* Builder to construct a input range row.
+ * <p>
+ * An input range row supports displaying a horizontal slider allowing slider input.
+ *
+ * @see ListBuilder#addInputRange(Consumer)
+ * @see ListBuilder#addInputRange(InputRangeBuilder)
*/
public static class InputRangeBuilder extends TemplateSliceBuilder {
private androidx.slice.builders.impl.ListBuilder.InputRangeBuilder mImpl;
+ /**
+ * Builder to construct a input range row.
+ * <p>
+ * An input range row supports displaying a horizontal slider allowing slider input.
+ *
+ * @see ListBuilder#addInputRange(Consumer)
+ * @see ListBuilder#addInputRange(InputRangeBuilder)
+ */
public InputRangeBuilder(@NonNull ListBuilder parent) {
super(parent.mImpl.createInputRangeBuilder());
}
@@ -480,13 +682,33 @@
return this;
}
+ /**
+ * Set the subtitle.
+ */
+ @NonNull
+ public InputRangeBuilder setSubtitle(@NonNull CharSequence title) {
+ mImpl.setSubtitle(title);
+ return this;
+ }
+
+ /**
+ * Set the {@link PendingIntent} to send when the current value is updated.
+ *
+ * @deprecated TO BE REMOVED; use {@link InputRangeBuilder#setInputAction(PendingIntent)}
+ */
+ @Deprecated
+ @NonNull
+ public InputRangeBuilder setAction(@NonNull PendingIntent action) {
+ mImpl.setInputAction(action);
+ return this;
+ }
/**
* Set the {@link PendingIntent} to send when the current value is updated.
*/
@NonNull
- public InputRangeBuilder setAction(@NonNull PendingIntent action) {
- mImpl.setAction(action);
+ public InputRangeBuilder setInputAction(@NonNull PendingIntent action) {
+ mImpl.setInputAction(action);
return this;
}
@@ -494,6 +716,7 @@
* @deprecated TO BE REMOVED
*/
@NonNull
+ @RequiresApi(23)
public InputRangeBuilder setThumb(@NonNull Icon thumb) {
return setThumb(IconCompat.createFromIcon(thumb));
}
@@ -508,6 +731,21 @@
}
/**
+ * Set the primary action for this row.
+ * <p>
+ * The action specified here will be sent when the whole row is clicked, whereas
+ * the action specified via {@link #setAction(PendingIntent)} is used when the slider
+ * is interacted with. Additionally, if this is the first row in a {@link ListBuilder} this
+ * action will also be used to define the
+ * {@link androidx.slice.widget.SliceView#MODE_SHORTCUT} representation of the slice.
+ */
+ @NonNull
+ public InputRangeBuilder setPrimaryAction(@NonNull SliceAction action) {
+ mImpl.setPrimaryAction(action);
+ return this;
+ }
+
+ /**
* Sets the content description.
*/
@NonNull
@@ -523,24 +761,39 @@
}
/**
- * Sub-builder to construct a row of slice content.
+ * Builder to construct a row. A row can be added as an item to ListBuilder via
+ * {@link ListBuilder#addRow(RowBuilder)}.
* <p>
- * Row content can have:
+ * A row supports:
* <ul>
- * <li>Title item - This is only displayed if this is a list item in a large template, it
- * will not be shown if this template is being used for small format. The item appears at
- * the start of the template. There can only be one title item displayed, and it could be a
- * timestamp, image, or a tappable icon.</li>
- * <li>Title - Formatted as a title.</li>
- * <li>Subtitle - Appears below the title (if one exists) and is formatted as normal text.
+ * <li>Title item - The title item appears with at the start of the row. There can only
+ * be one title item added to a row, and it could be a timestamp, image, or a
+ * {@link SliceAction}.
* </li>
- * <li>End item - Appears at the end of the template, there can be multiple end items but
- * they are only shown if there's enough space. End items can be a timestamp, image, or a
- * tappable icon.</li>
+ * <li>Title - Single line of text formatted as a title, see
+ * {@link #setTitle(CharSequence)}.
+ * </li>
+ * <li>Subtitle - Single line of text below the title (if one exists) and is formatted as
+ * normal text, see {@link #setSubtitle(CharSequence)}.
+ * </li>
+ * <li>End item - End items appear at the end of the row. There can be multiple end items
+ * that show depending on available width. End items can be a timestamp, image, or a
+ * tappable icon.
+ * </li>
+ * <li>Primary action - The primary action for the row, this is the action that will be sent
+ * when the row is clicked. This is set via {@link #setPrimaryAction(SliceAction)}. If this
+ * is the only row in a slice, the slice action set here will be used to represent the
+ * slice in when slice view is displaying in
+ * {@link androidx.slice.widget.SliceView#MODE_SMALL}.
+ * </li>
* </ul>
- * </p>
+ * There are a couple of restrictions to how content can be added to a row:
+ * <ul>
+ * <li>End items cannot contain a mixture of {@link SliceAction}s and Icons.</li>
+ * <li>There can only be one timestamp added to the row.</li>
+ * </ul>
*
- * @see ListBuilder
+ * @see ListBuilder#addRow(RowBuilder)
*/
public static class RowBuilder extends TemplateSliceBuilder {
@@ -552,7 +805,7 @@
private boolean mHasTimestamp;
/**
- * Create a builder which will construct a slice displayed in a row format.
+ * Builder to construct a row.
* @param parent The builder constructing the parent slice.
*/
public RowBuilder(@NonNull ListBuilder parent) {
@@ -560,7 +813,7 @@
}
/**
- * Create a builder which will construct a slice displayed in a row format.
+ * Builder to construct a row.
* @param uri Uri to tag for this slice.
*/
public RowBuilder(@NonNull ListBuilder parent, @NonNull Uri uri) {
@@ -568,7 +821,7 @@
}
/**
- * Create a builder which will construct a slice displayed in a row format.
+ * Builder to construct a normal row.
* @param uri Uri to tag for this slice.
*/
public RowBuilder(@NonNull Context context, @NonNull Uri uri) {
@@ -598,6 +851,7 @@
*/
@Deprecated
@NonNull
+ @RequiresApi(23)
public RowBuilder setTitleItem(@NonNull Icon icon) {
return setTitleItem(icon, ICON_IMAGE);
}
@@ -607,6 +861,7 @@
*/
@Deprecated
@NonNull
+ @RequiresApi(23)
public RowBuilder setTitleItem(@Nullable Icon icon, boolean isLoading) {
return setTitleItem(icon, ICON_IMAGE, isLoading);
}
@@ -615,6 +870,7 @@
* @deprecated TO BE REMOVED.
*/
@Deprecated
+ @RequiresApi(23)
public RowBuilder setTitleItem(@NonNull Icon icon, @ImageMode int imageMode) {
mImpl.setTitleItem(IconCompat.createFromIcon(icon), imageMode, false /* isLoading */);
return this;
@@ -625,6 +881,7 @@
*/
@Deprecated
@NonNull
+ @RequiresApi(23)
public RowBuilder setTitleItem(@Nullable Icon icon, @ImageMode int imageMode,
boolean isLoading) {
mImpl.setTitleItem(IconCompat.createFromIcon(icon), imageMode,
@@ -636,7 +893,7 @@
* Sets the title item to be the provided icon. There can only be one title item, this
* will replace any other title items that may have been set.
*
- * @deprecated use ListBuilder{@link #setTitleItem(Icon, int)} instead.
+ * @deprecated TO BE REMOVED; use ListBuilder{@link #setTitleItem(Icon, int, boolean)}.
*/
@Deprecated
@NonNull
@@ -654,7 +911,7 @@
* @param isLoading indicates whether the app is doing work to load the added content in the
* background or not.
*
- * @deprecated use ListBuilder{@link #setTitleItem(Icon, int, boolean)} instead.
+ * @deprecated TO BE REMOVED; use ListBuilder{@link #setTitleItem(Icon, int, boolean)}.
*/
@Deprecated
@NonNull
@@ -727,7 +984,10 @@
}
/**
- * Sets the action to be invoked if the user taps on the main content of the template.
+ * The action specified here will be sent when the whole row is clicked.
+ * <p>
+ * If this is the first row in a {@link ListBuilder} this action will also be used to define
+ * the {@link androidx.slice.widget.SliceView#MODE_SHORTCUT} representation of the slice.
*/
@NonNull
public RowBuilder setPrimaryAction(@NonNull SliceAction action) {
@@ -736,7 +996,7 @@
}
/**
- * Sets the title text.
+ * Sets the title for the row builder.
*/
@NonNull
public RowBuilder setTitle(@NonNull CharSequence title) {
@@ -745,7 +1005,7 @@
}
/**
- * Sets the title text.
+ * Sets the title for the row builder.
* <p>
* Use this method to specify content that will appear in the template once it's been
* loaded.
@@ -760,7 +1020,7 @@
}
/**
- * Sets the subtitle text.
+ * Sets the subtitle for the row builder.
*/
@NonNull
public RowBuilder setSubtitle(@NonNull CharSequence subtitle) {
@@ -768,7 +1028,7 @@
}
/**
- * Sets the subtitle text.
+ * Sets the subtitle for the row builder.
* <p>
* Use this method to specify content that will appear in the template once it's been
* loaded.
@@ -783,7 +1043,7 @@
}
/**
- * Adds a timestamp to be displayed at the end of the row. Only one timestamp can be added,
+ * Adds a timestamp to the end items of the row builder. Only one timestamp can be added,
* if one is already added this will throw {@link IllegalArgumentException}.
*/
@NonNull
@@ -802,6 +1062,7 @@
*/
@Deprecated
@NonNull
+ @RequiresApi(23)
public RowBuilder addEndItem(@NonNull Icon icon) {
return addEndItem(icon, ICON_IMAGE, false /* isLoading */);
}
@@ -811,6 +1072,7 @@
*/
@Deprecated
@NonNull
+ @RequiresApi(23)
public RowBuilder addEndItem(@NonNull Icon icon, boolean isLoading) {
return addEndItem(icon, ICON_IMAGE, isLoading);
}
@@ -820,6 +1082,7 @@
*/
@Deprecated
@NonNull
+ @RequiresApi(23)
public RowBuilder addEndItem(@NonNull Icon icon, @ImageMode int imageMode) {
return addEndItem(icon, imageMode, false /* isLoading */);
}
@@ -829,6 +1092,7 @@
*/
@Deprecated
@NonNull
+ @RequiresApi(23)
public RowBuilder addEndItem(@Nullable Icon icon, @ImageMode int imageMode,
boolean isLoading) {
if (mHasEndActionOrToggle) {
@@ -842,7 +1106,7 @@
}
/**
- * Adds an icon to be displayed at the end of the row. A mixture of icons and actions
+ * Adds an icon to the end items of the row builder. A mixture of icons and actions
* is not permitted. If an action has already been added this will throw
* {@link IllegalArgumentException}.
*
@@ -874,7 +1138,7 @@
}
/**
- * Adds an icon to be displayed at the end of the row.
+ * Adds an icon to the end items of the row builder.
*
* @param icon the image to display.
* @param imageMode the mode that image should be displayed in.
@@ -889,7 +1153,7 @@
}
/**
- * Adds an icon to be displayed at the end of the row.
+ * Adds an icon to the end items of the row builder.
* <p>
* When set to true, the parameter {@code isLoading} indicates that the app is doing work
* to load this content in the background, in this case the template displays a placeholder
@@ -917,7 +1181,7 @@
}
/**
- * Adds an action to display at the end of the row. A mixture of icons and
+ * Adds an action to the end items of the row builder. A mixture of icons and
* actions is not permitted. If an icon has already been added, this will throw
* {@link IllegalArgumentException}.
*/
@@ -927,7 +1191,7 @@
}
/**
- * Adds an action to be displayed at the end of the row. A mixture of icons and
+ * Adds an action to the end items of the row builder. A mixture of icons and
* actions is not permitted. If an icon has already been added, this will throw
* {@link IllegalArgumentException}.
* <p>
@@ -980,23 +1244,41 @@
}
/**
- * Builder to construct a header. A header is displayed at the top of a list and can have
- * a title, subtitle, and an action.
+ * Builder to construct a header row.
+ * <p>
+ * A header provides some additional functionality compared to a {@link RowBuilder}. Like
+ * a row, a header has a title, subtitle, and primary action.
+ * <p>
+ * In addition to a row's title, subtitle, and primary action, a header also supports setting
+ * a summary description of the list contents using
+ * {@link HeaderBuilder#setSummary(CharSequence)}. This summary might be used when
+ * the rest of the list content is not shown (e.g. if SliceView presenting slice is
+ * configured to {@link androidx.slice.widget.SliceView#MODE_SMALL}.
+ * <p>
+ * The primary action specified by {@link HeaderBuilder#setPrimaryAction(SliceAction)} will
+ * be used as the PendingIntent sent when header is clicked. This action is also used when
+ * when SliceView displays in {@link androidx.slice.widget.SliceView#MODE_SHORTCUT}.
+ * <p>
+ * Unlike row builder, header builder does not support end items (e.g.
+ * {@link RowBuilder#addEndItem(SliceAction)}). The header may be used to display actions set
+ * on the list via {@link #addAction(SliceAction)}.
*
* @see ListBuilder#setHeader(HeaderBuilder)
+ * @see ListBuilder#addAction(SliceAction)
+ * @see SliceAction
*/
public static class HeaderBuilder extends TemplateSliceBuilder {
private androidx.slice.builders.impl.ListBuilder.HeaderBuilder mImpl;
/**
- * Create builder for header templates.
+ * Create builder for a header.
*/
public HeaderBuilder(@NonNull ListBuilder parent) {
super(parent.mImpl.createHeaderBuilder());
}
/**
- * Create builder for header templates.
+ * Create builder for a header.
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@@ -1005,7 +1287,7 @@
}
/**
- * Sets the title to be shown in this header.
+ * Sets the title for the header builder.
*/
@NonNull
public HeaderBuilder setTitle(@NonNull CharSequence title) {
@@ -1013,7 +1295,13 @@
}
/**
- * Sets the title to be shown in this header.
+ * Sets the title for the header builder.
+ * <p>
+ * Use this method to specify content that will appear in the template once it's been
+ * loaded.
+ * </p>
+ * @param isLoading indicates whether the app is doing work to load the added content in the
+ * background or not.
*/
@NonNull
public HeaderBuilder setTitle(@NonNull CharSequence title, boolean isLoading) {
@@ -1022,7 +1310,7 @@
}
/**
- * Sets the subtitle to be shown in this header.
+ * Sets the subtitle for the header builder.
*/
@NonNull
public HeaderBuilder setSubtitle(@NonNull CharSequence subtitle) {
@@ -1030,7 +1318,13 @@
}
/**
- * Sets the subtitle to be shown in this header.
+ * Sets the subtitle for the header builder.
+ * <p>
+ * Use this method to specify content that will appear in the template once it's been
+ * loaded.
+ * </p>
+ * @param isLoading indicates whether the app is doing work to load the added content in the
+ * background or not.
*/
@NonNull
public HeaderBuilder setSubtitle(@NonNull CharSequence subtitle, boolean isLoading) {
@@ -1042,26 +1336,61 @@
* Sets the summary subtitle to be shown in this header. If unset, the normal subtitle
* will be used. The summary is used when the parent template is presented in a
* small format.
+ * @deprecated TO BE REMOVED; use {@link #setSummary(CharSequence)}
*/
@NonNull
public HeaderBuilder setSummarySubtitle(@NonNull CharSequence summarySubtitle) {
- return setSummarySubtitle(summarySubtitle, false /* isLoading */);
+ return setSummary(summarySubtitle, false /* isLoading */);
}
/**
* Sets the summary subtitle to be shown in this header. If unset, the normal subtitle
* will be used. The summary is used when the parent template is presented in a
* small format.
+ * @deprecated TO BE REMOVED; use {@link #setSummary(CharSequence, boolean)}
*/
@NonNull
public HeaderBuilder setSummarySubtitle(@NonNull CharSequence summarySubtitle,
boolean isLoading) {
- mImpl.setSummarySubtitle(summarySubtitle, isLoading);
+ return setSummary(summarySubtitle, isLoading /* isLoading */);
+ }
+
+ /**
+ * Sets the summary for the header builder. A summary is optional.
+ * <p>
+ * The summary should be a description of the contents of the list. This summary might be
+ * used when the rest of the list content is not shown (e.g. if SliceView presenting slice
+ * is configured to {@link androidx.slice.widget.SliceView#MODE_SMALL}.
+ */
+ @NonNull
+ public HeaderBuilder setSummary(@NonNull CharSequence summary) {
+ return setSummary(summary, false /* isLoading */);
+ }
+
+ /**
+ * Sets the summary for the header builder. A summary is optional.
+ * <p>
+ * The summary should be a description of the contents of the list. This summary might be
+ * used when the rest of the list content is not shown (e.g. if SliceView presenting slice
+ * is configured to {@link androidx.slice.widget.SliceView#MODE_SMALL}.
+ * <p>
+ * Use this method to specify content that will appear in the template once it's been
+ * loaded.
+ * </p>
+ * @param isLoading indicates whether the app is doing work to load the added content in the
+ * background or not.
+ */
+ @NonNull
+ public HeaderBuilder setSummary(@NonNull CharSequence summary, boolean isLoading) {
+ mImpl.setSummary(summary, isLoading);
return this;
}
/**
- * Sets the action to invoke when the header is activated.
+ * Sets the action to send when the header is clicked.
+ * <p>
+ * Additionally, the action specified here is used when the slice associated with this
+ * header is displayed in {@link androidx.slice.widget.SliceView#MODE_SHORTCUT}.
*/
@NonNull
public HeaderBuilder setPrimaryAction(@NonNull SliceAction action) {
diff --git a/slices/builders/src/main/java/androidx/slice/builders/MessagingSliceBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/MessagingSliceBuilder.java
index c3f1f92..0459c24 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/MessagingSliceBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/MessagingSliceBuilder.java
@@ -28,6 +28,7 @@
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
+import androidx.core.util.Consumer;
import androidx.slice.SliceSpecs;
import androidx.slice.builders.impl.MessagingBasicImpl;
import androidx.slice.builders.impl.MessagingBuilder;
@@ -35,8 +36,6 @@
import androidx.slice.builders.impl.MessagingV1Impl;
import androidx.slice.builders.impl.TemplateBuilderImpl;
-import java.util.function.Consumer;
-
/**
* Builder to construct slice content in a messaging format.
* @hide
@@ -70,7 +69,6 @@
/**
* Add a subslice to this builder.
*/
- @RequiresApi(api = Build.VERSION_CODES.N)
public MessagingSliceBuilder add(Consumer<MessageBuilder> c) {
MessageBuilder b = new MessageBuilder(this);
c.accept(b);
@@ -115,6 +113,7 @@
/**
* Add the icon used to display contact in the messaging experience
*/
+ @RequiresApi(23)
public MessageBuilder addSource(Icon source) {
mImpl.addSource(source);
return this;
@@ -124,7 +123,9 @@
* Add the icon used to display contact in the messaging experience
*/
public MessageBuilder addSource(IconCompat source) {
- mImpl.addSource(source.toIcon());
+ if (Build.VERSION.SDK_INT >= 23) {
+ mImpl.addSource(source.toIcon());
+ }
return this;
}
diff --git a/slices/builders/src/main/java/androidx/slice/builders/SliceAction.java b/slices/builders/src/main/java/androidx/slice/builders/SliceAction.java
index e4e28f5..b98c723 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/SliceAction.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/SliceAction.java
@@ -25,6 +25,7 @@
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -41,6 +42,7 @@
* @deprecated TO BE REMOVED
*/
@Deprecated
+ @RequiresApi(23)
public SliceAction(@NonNull PendingIntent action, @NonNull Icon actionIcon,
@NonNull CharSequence actionTitle) {
this(action, actionIcon, ICON_IMAGE, actionTitle);
@@ -50,6 +52,7 @@
* @deprecated TO BE REMOVED
*/
@Deprecated
+ @RequiresApi(23)
public SliceAction(@NonNull PendingIntent action, @NonNull Icon actionIcon,
@ListBuilder.ImageMode int imageMode, @NonNull CharSequence actionTitle) {
this(action, IconCompat.createFromIcon(actionIcon), imageMode, actionTitle);
@@ -59,6 +62,7 @@
* @deprecated TO BE REMOVED
*/
@Deprecated
+ @RequiresApi(23)
public SliceAction(@NonNull PendingIntent action, @NonNull Icon actionIcon,
@NonNull CharSequence actionTitle, boolean isChecked) {
this(action, IconCompat.createFromIcon(actionIcon), actionTitle, isChecked);
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilder.java
index 9b59945..7c12311 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilder.java
@@ -56,7 +56,7 @@
* a row or action has been previously added.
* </p>
*/
- void addSeeMoreCell(TemplateBuilderImpl impl);
+ void setSeeMoreCell(TemplateBuilderImpl impl);
/**
* If all content in a slice cannot be shown, a "see more" affordance will be displayed where
@@ -67,7 +67,7 @@
* a row or action has been previously added.
* </p>
*/
- void addSeeMoreAction(PendingIntent intent);
+ void setSeeMoreAction(PendingIntent intent);
/**
* Sets the action to be invoked if the user taps on the main content of the template.
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderBasicImpl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderBasicImpl.java
index e1a0752..8051a10 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderBasicImpl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderBasicImpl.java
@@ -65,13 +65,13 @@
/**
*/
@Override
- public void addSeeMoreCell(TemplateBuilderImpl impl) {
+ public void setSeeMoreCell(TemplateBuilderImpl impl) {
}
/**
*/
@Override
- public void addSeeMoreAction(PendingIntent intent) {
+ public void setSeeMoreAction(PendingIntent intent) {
}
/**
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderListV1Impl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderListV1Impl.java
index 29158b4..714b570 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderListV1Impl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/GridRowBuilderListV1Impl.java
@@ -103,7 +103,7 @@
/**
*/
@Override
- public void addSeeMoreCell(@NonNull TemplateBuilderImpl builder) {
+ public void setSeeMoreCell(@NonNull TemplateBuilderImpl builder) {
builder.getBuilder().addHints(HINT_SEE_MORE);
getBuilder().addSubSlice(builder.build());
}
@@ -111,7 +111,7 @@
/**
*/
@Override
- public void addSeeMoreAction(PendingIntent intent) {
+ public void setSeeMoreAction(PendingIntent intent) {
getBuilder().addSubSlice(
new Slice.Builder(getBuilder())
.addHints(HINT_SEE_MORE)
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilder.java
index 0ebd32b..084b85a 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilder.java
@@ -77,7 +77,7 @@
* a row or action has been previously added.
* </p>
*/
- void addSeeMoreRow(TemplateBuilderImpl builder);
+ void setSeeMoreRow(TemplateBuilderImpl builder);
/**
* If all content in a slice cannot be shown, a "see more" affordance will be displayed where
* the content is cut off. The action added here should take the user to an activity to see
@@ -87,7 +87,7 @@
* a row or action has been previously added.
* </p>
*/
- void addSeeMoreAction(PendingIntent intent);
+ void setSeeMoreAction(PendingIntent intent);
/**
* Sets the color to tint items displayed by this template (e.g. icons).
@@ -156,6 +156,16 @@
void setTitle(@NonNull CharSequence title);
/**
+ * Set the subtitle.
+ */
+ void setSubtitle(@NonNull CharSequence title);
+
+ /**
+ * Set the primary action.
+ */
+ void setPrimaryAction(@NonNull SliceAction action);
+
+ /**
* Sets the content description.
*/
void setContentDescription(CharSequence description);
@@ -168,7 +178,7 @@
/**
* Set the {@link PendingIntent} to send when the value changes.
*/
- void setAction(@NonNull PendingIntent action);
+ void setInputAction(@NonNull PendingIntent action);
/**
* Set the {@link IconCompat} to be displayed as the thumb on the input range.
@@ -325,7 +335,7 @@
* will be used. The summary is used when the parent template is presented in a
* small format.
*/
- void setSummarySubtitle(CharSequence summarySubtitle, boolean isLoading);
+ void setSummary(CharSequence summarySubtitle, boolean isLoading);
/**
* Sets the action to invoke when the header is activated.
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderBasicImpl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderBasicImpl.java
index 204defa..94143e2 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderBasicImpl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderBasicImpl.java
@@ -17,7 +17,7 @@
package androidx.slice.builders.impl;
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
import android.app.PendingIntent;
import android.net.Uri;
@@ -85,13 +85,13 @@
/**
*/
@Override
- public void addSeeMoreRow(TemplateBuilderImpl builder) {
+ public void setSeeMoreRow(TemplateBuilderImpl builder) {
}
/**
*/
@Override
- public void addSeeMoreAction(PendingIntent intent) {
+ public void setSeeMoreAction(PendingIntent intent) {
}
/**
@@ -108,7 +108,7 @@
for (int i = 0; i < keywords.size(); i++) {
sb.addText(keywords.get(i), null);
}
- getBuilder().addSubSlice(sb.addHints(HINT_KEY_WORDS).build());
+ getBuilder().addSubSlice(sb.addHints(HINT_KEYWORDS).build());
}
/**
@@ -340,7 +340,7 @@
/**
*/
@Override
- public void setSummarySubtitle(CharSequence summarySubtitle, boolean isLoading) {
+ public void setSummary(CharSequence summarySubtitle, boolean isLoading) {
}
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderV1Impl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderV1Impl.java
index e85fbb1..f2ff884 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderV1Impl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/ListBuilderV1Impl.java
@@ -33,7 +33,7 @@
import static androidx.slice.builders.ListBuilder.ICON_IMAGE;
import static androidx.slice.builders.ListBuilder.INFINITY;
import static androidx.slice.builders.ListBuilder.LARGE_IMAGE;
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
import static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
import static androidx.slice.core.SliceHints.HINT_TTL;
import static androidx.slice.core.SliceHints.SUBTYPE_MAX;
@@ -136,7 +136,7 @@
/**
*/
@Override
- public void addSeeMoreRow(TemplateBuilderImpl builder) {
+ public void setSeeMoreRow(TemplateBuilderImpl builder) {
builder.getBuilder().addHints(HINT_SEE_MORE);
getBuilder().addSubSlice(builder.build());
}
@@ -144,7 +144,7 @@
/**
*/
@Override
- public void addSeeMoreAction(PendingIntent intent) {
+ public void setSeeMoreAction(PendingIntent intent) {
getBuilder().addSubSlice(
new Slice.Builder(getBuilder())
.addHints(HINT_SEE_MORE)
@@ -161,7 +161,9 @@
private int mMax = 100;
private int mValue = 0;
private CharSequence mTitle;
+ private CharSequence mSubtitle;
private CharSequence mContentDescr;
+ private SliceAction mPrimaryAction;
public RangeBuilderImpl(Slice.Builder sb) {
super(sb, null);
@@ -183,6 +185,16 @@
}
@Override
+ public void setSubtitle(@NonNull CharSequence title) {
+ mSubtitle = title;
+ }
+
+ @Override
+ public void setPrimaryAction(@NonNull SliceAction action) {
+ mPrimaryAction = action;
+ }
+
+ @Override
public void setContentDescription(@NonNull CharSequence description) {
mContentDescr = description;
}
@@ -192,9 +204,17 @@
if (mTitle != null) {
builder.addText(mTitle, null, HINT_TITLE);
}
+ if (mSubtitle != null) {
+ builder.addText(mSubtitle, null);
+ }
if (mContentDescr != null) {
builder.addText(mContentDescr, SUBTYPE_CONTENT_DESCRIPTION);
}
+ if (mPrimaryAction != null) {
+ Slice.Builder sb = new Slice.Builder(
+ getBuilder()).addHints(HINT_TITLE, HINT_SHORTCUT);
+ builder.addSubSlice(mPrimaryAction.buildSlice(sb), null /* subtype */);
+ }
builder.addHints(HINT_LIST_ITEM)
.addInt(mMax, SUBTYPE_MAX)
.addInt(mValue, SUBTYPE_VALUE);
@@ -214,7 +234,7 @@
}
@Override
- public void setAction(@NonNull PendingIntent action) {
+ public void setInputAction(@NonNull PendingIntent action) {
mAction = action;
}
@@ -253,7 +273,7 @@
for (int i = 0; i < keywords.size(); i++) {
sb.addText(keywords.get(i), null);
}
- getBuilder().addSubSlice(sb.addHints(HINT_KEY_WORDS).build());
+ getBuilder().addSubSlice(sb.addHints(HINT_KEYWORDS).build());
}
/**
@@ -603,7 +623,7 @@
/**
*/
@Override
- public void setSummarySubtitle(CharSequence summarySubtitle, boolean isLoading) {
+ public void setSummary(CharSequence summarySubtitle, boolean isLoading) {
mSummaryItem = new SliceItem(summarySubtitle, FORMAT_TEXT, null,
new String[] {HINT_SUMMARY});
if (isLoading) {
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBasicImpl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBasicImpl.java
index c999594..f53b9f5 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBasicImpl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBasicImpl.java
@@ -19,7 +19,9 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import android.graphics.drawable.Icon;
+import android.os.Build;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -44,8 +46,10 @@
@Override
public void apply(Slice.Builder builder) {
if (mLastMessage != null) {
- if (mLastMessage.mIcon != null) {
- builder.addIcon(IconCompat.createFromIcon(mLastMessage.mIcon), null);
+ if (Build.VERSION.SDK_INT >= 23) {
+ if (mLastMessage.mIcon != null) {
+ builder.addIcon(IconCompat.createFromIcon(mLastMessage.mIcon), null);
+ }
}
if (mLastMessage.mText != null) {
builder.addText(mLastMessage.mText, null);
@@ -75,6 +79,7 @@
public static final class MessageBuilder extends TemplateBuilderImpl
implements MessagingBuilder.MessageBuilder {
+ @RequiresApi(23)
private Icon mIcon;
private CharSequence mText;
private long mTimestamp;
@@ -94,6 +99,7 @@
/**
*/
@Override
+ @RequiresApi(23)
public void addSource(Icon source) {
mIcon = source;
}
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBuilder.java b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBuilder.java
index dd66616..a9fb0cc 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBuilder.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingBuilder.java
@@ -20,6 +20,7 @@
import android.graphics.drawable.Icon;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
/**
@@ -44,6 +45,7 @@
/**
* Add the icon used to display contact in the messaging experience
*/
+ @RequiresApi(23)
void addSource(Icon source);
/**
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingListV1Impl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingListV1Impl.java
index e47871a..99f0f34 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingListV1Impl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingListV1Impl.java
@@ -22,6 +22,7 @@
import android.graphics.drawable.Icon;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -85,6 +86,7 @@
/**
*/
@Override
+ @RequiresApi(23)
public void addSource(Icon source) {
mListBuilder.setTitleItem(IconCompat.createFromIcon(source), SMALL_IMAGE);
}
diff --git a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingV1Impl.java b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingV1Impl.java
index e601491..63214da 100644
--- a/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingV1Impl.java
+++ b/slices/builders/src/main/java/androidx/slice/builders/impl/MessagingV1Impl.java
@@ -20,6 +20,7 @@
import android.graphics.drawable.Icon;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -71,6 +72,7 @@
/**
*/
@Override
+ @RequiresApi(23)
public void addSource(Icon source) {
getBuilder().addIcon(IconCompat.createFromIcon(source),
android.app.slice.Slice.SUBTYPE_SOURCE);
diff --git a/slices/core/api/current.txt b/slices/core/api/current.txt
index acc5cbd..09a69fd 100644
--- a/slices/core/api/current.txt
+++ b/slices/core/api/current.txt
@@ -7,7 +7,6 @@
}
public class SliceConvert {
- ctor public SliceConvert();
method public static android.app.slice.Slice unwrap(androidx.slice.Slice);
method public static androidx.slice.Slice wrap(android.app.slice.Slice);
}
@@ -18,20 +17,33 @@
method public java.util.List<java.lang.String> getHints();
method public androidx.core.graphics.drawable.IconCompat getIcon();
method public int getInt();
+ method public long getLong();
method public androidx.slice.Slice getSlice();
method public java.lang.String getSubType();
method public java.lang.CharSequence getText();
- method public long getTimestamp();
+ method public deprecated long getTimestamp();
method public boolean hasHint(java.lang.String);
}
- public abstract class SliceProvider extends android.content.ContentProvider {
+ public abstract class SliceProvider extends android.content.ContentProvider implements androidx.core.app.CoreComponentFactory.CompatWrapped {
ctor public SliceProvider();
+ method public final int bulkInsert(android.net.Uri, android.content.ContentValues[]);
+ method public final android.net.Uri canonicalize(android.net.Uri);
+ method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
+ method public final java.lang.String getType(android.net.Uri);
+ method public java.lang.Object getWrapper();
+ method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
method public abstract androidx.slice.Slice onBindSlice(android.net.Uri);
+ method public final boolean onCreate();
method public abstract boolean onCreateSliceProvider();
+ method public java.util.Collection<android.net.Uri> onGetSliceDescendants(android.net.Uri);
method public android.net.Uri onMapIntentToUri(android.content.Intent);
method public void onSlicePinned(android.net.Uri);
method public void onSliceUnpinned(android.net.Uri);
+ method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+ method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
+ method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
+ method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
}
}
diff --git a/slices/core/build.gradle b/slices/core/build.gradle
index 7be7400..cb17394 100644
--- a/slices/core/build.gradle
+++ b/slices/core/build.gradle
@@ -39,5 +39,5 @@
mavenGroup = LibraryGroups.SLICE
inceptionYear = "2017"
description = "The slices core library provides utilities for the slices view and provider libraries"
- minSdkVersion = 24
+ minSdkVersion = 19
}
diff --git a/slices/core/src/main/java/androidx/slice/ArrayUtils.java b/slices/core/src/main/java/androidx/slice/ArrayUtils.java
index f485c22..f7921c5 100644
--- a/slices/core/src/main/java/androidx/slice/ArrayUtils.java
+++ b/slices/core/src/main/java/androidx/slice/ArrayUtils.java
@@ -72,4 +72,7 @@
}
return array;
}
+
+ private ArrayUtils() {
+ }
}
diff --git a/slices/core/src/main/java/androidx/slice/Slice.java b/slices/core/src/main/java/androidx/slice/Slice.java
index 84c7df7..d18ef23 100644
--- a/slices/core/src/main/java/androidx/slice/Slice.java
+++ b/slices/core/src/main/java/androidx/slice/Slice.java
@@ -31,14 +31,16 @@
import static android.app.slice.SliceItem.FORMAT_ACTION;
import static android.app.slice.SliceItem.FORMAT_IMAGE;
import static android.app.slice.SliceItem.FORMAT_INT;
+import static android.app.slice.SliceItem.FORMAT_LONG;
import static android.app.slice.SliceItem.FORMAT_REMOTE_INPUT;
import static android.app.slice.SliceItem.FORMAT_SLICE;
import static android.app.slice.SliceItem.FORMAT_TEXT;
import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
import static androidx.slice.SliceConvert.unwrap;
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
import static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
+import static androidx.slice.core.SliceHints.HINT_PERMISSION_REQUEST;
import static androidx.slice.core.SliceHints.HINT_TTL;
import android.app.PendingIntent;
@@ -57,6 +59,7 @@
import androidx.annotation.StringDef;
import androidx.core.graphics.drawable.IconCompat;
import androidx.core.os.BuildCompat;
+import androidx.core.util.Consumer;
import androidx.slice.compat.SliceProviderCompat;
import java.util.ArrayList;
@@ -84,9 +87,24 @@
* @hide
*/
@RestrictTo(Scope.LIBRARY)
- @StringDef({HINT_TITLE, HINT_LIST, HINT_LIST_ITEM, HINT_LARGE, HINT_ACTIONS, HINT_SELECTED,
- HINT_HORIZONTAL, HINT_NO_TINT, HINT_PARTIAL, HINT_SUMMARY, HINT_SEE_MORE,
- HINT_SHORTCUT, HINT_KEY_WORDS, HINT_TTL, HINT_LAST_UPDATED})
+ @StringDef({
+ HINT_TITLE,
+ HINT_LIST,
+ HINT_LIST_ITEM,
+ HINT_LARGE,
+ HINT_ACTIONS,
+ HINT_SELECTED,
+ HINT_HORIZONTAL,
+ HINT_NO_TINT,
+ HINT_PARTIAL,
+ HINT_SUMMARY,
+ HINT_SEE_MORE,
+ HINT_SHORTCUT,
+ HINT_KEYWORDS,
+ HINT_TTL,
+ HINT_LAST_UPDATED,
+ HINT_PERMISSION_REQUEST,
+ })
public @interface SliceHint{ }
private final SliceItem[] mItems;
@@ -269,6 +287,19 @@
}
/**
+ * Add an action to the slice being constructed
+ * @param subType Optional template-specific type information
+ * @see {@link SliceItem#getSubType()}
+ */
+ public Slice.Builder addAction(@NonNull Consumer<Uri> action,
+ @NonNull Slice s, @Nullable String subType) {
+ @SliceHint String[] hints = s != null
+ ? s.getHints().toArray(new String[s.getHints().size()]) : new String[0];
+ mItems.add(new SliceItem(action, s, FORMAT_ACTION, subType, hints));
+ return this;
+ }
+
+ /**
* Add text to the slice being constructed
* @param subType Optional template-specific type information
* @see {@link SliceItem#getSubType()}
@@ -361,6 +392,19 @@
* @param subType Optional template-specific type information
* @see {@link SliceItem#getSubType()}
*/
+ public Slice.Builder addLong(long time, @Nullable String subType,
+ @SliceHint String... hints) {
+ mItems.add(new SliceItem(time, FORMAT_LONG, subType, hints));
+ return this;
+ }
+
+ /**
+ * Add a timestamp to the slice being constructed
+ * @param subType Optional template-specific type information
+ * @see {@link SliceItem#getSubType()}
+ * @deprecated TO BE REMOVED
+ */
+ @Deprecated
public Slice.Builder addTimestamp(long time, @Nullable String subType,
@SliceHint String... hints) {
mItems.add(new SliceItem(time, FORMAT_TIMESTAMP, subType, hints));
@@ -396,35 +440,28 @@
}
/**
- * @hide
* @return A string representation of this slice.
*/
- @RestrictTo(Scope.LIBRARY)
@Override
public String toString() {
return toString("");
}
/**
+ * @return A string representation of this slice.
* @hide
*/
@RestrictTo(Scope.LIBRARY)
public String toString(String indent) {
StringBuilder sb = new StringBuilder();
+ sb.append(indent);
+ sb.append("slice: ");
+ sb.append("\n");
+ indent += " ";
for (int i = 0; i < mItems.length; i++) {
- sb.append(indent);
- if (FORMAT_SLICE.equals(mItems[i].getFormat())) {
- sb.append("slice:\n");
- sb.append(mItems[i].getSlice().toString(indent + " "));
- } else if (FORMAT_ACTION.equals(mItems[i].getFormat())) {
- sb.append("action:\n");
- sb.append(mItems[i].getSlice().toString(indent + " "));
- } else if (FORMAT_TEXT.equals(mItems[i].getFormat())) {
- sb.append("text: ");
- sb.append(mItems[i].getText());
- sb.append("\n");
- } else {
- sb.append(SliceItem.typeToString(mItems[i].getFormat()));
+ SliceItem item = mItems[i];
+ sb.append(item.toString(indent));
+ if (!FORMAT_SLICE.equals(item.getFormat())) {
sb.append("\n");
}
}
diff --git a/slices/core/src/main/java/androidx/slice/SliceConvert.java b/slices/core/src/main/java/androidx/slice/SliceConvert.java
index 1d89b8e..73ff368 100644
--- a/slices/core/src/main/java/androidx/slice/SliceConvert.java
+++ b/slices/core/src/main/java/androidx/slice/SliceConvert.java
@@ -144,4 +144,7 @@
}
return ret;
}
+
+ private SliceConvert() {
+ }
}
diff --git a/slices/core/src/main/java/androidx/slice/SliceItem.java b/slices/core/src/main/java/androidx/slice/SliceItem.java
index 067772e..004e51d 100644
--- a/slices/core/src/main/java/androidx/slice/SliceItem.java
+++ b/slices/core/src/main/java/androidx/slice/SliceItem.java
@@ -19,6 +19,7 @@
import static android.app.slice.SliceItem.FORMAT_ACTION;
import static android.app.slice.SliceItem.FORMAT_IMAGE;
import static android.app.slice.SliceItem.FORMAT_INT;
+import static android.app.slice.SliceItem.FORMAT_LONG;
import static android.app.slice.SliceItem.FORMAT_REMOTE_INPUT;
import static android.app.slice.SliceItem.FORMAT_SLICE;
import static android.app.slice.SliceItem.FORMAT_TEXT;
@@ -26,10 +27,12 @@
import android.app.PendingIntent;
import android.app.RemoteInput;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.text.TextUtils;
-import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
@@ -37,6 +40,8 @@
import androidx.annotation.RestrictTo.Scope;
import androidx.annotation.StringDef;
import androidx.core.graphics.drawable.IconCompat;
+import androidx.core.util.Consumer;
+import androidx.core.util.Pair;
import java.util.Arrays;
import java.util.List;
@@ -71,7 +76,7 @@
*/
@RestrictTo(Scope.LIBRARY)
@StringDef({FORMAT_SLICE, FORMAT_TEXT, FORMAT_IMAGE, FORMAT_ACTION, FORMAT_INT,
- FORMAT_TIMESTAMP, FORMAT_REMOTE_INPUT})
+ FORMAT_TIMESTAMP, FORMAT_REMOTE_INPUT, FORMAT_LONG})
public @interface SliceType {
}
@@ -111,7 +116,16 @@
@RestrictTo(Scope.LIBRARY)
public SliceItem(PendingIntent intent, Slice slice, String format, String subType,
@Slice.SliceHint String[] hints) {
- this(new Pair<>(intent, slice), format, subType, hints);
+ this(new Pair<Object, Slice>(intent, slice), format, subType, hints);
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY)
+ public SliceItem(Consumer<Uri> action, Slice slice, String format, String subType,
+ @Slice.SliceHint String[] hints) {
+ this(new Pair<Object, Slice>(action, slice), format, subType, hints);
}
/**
@@ -188,7 +202,20 @@
* SliceItem
*/
public PendingIntent getAction() {
- return ((Pair<PendingIntent, Slice>) mObj).first;
+ return (PendingIntent) ((Pair<Object, Slice>) mObj).first;
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public void fireAction(Context context, Intent i) throws PendingIntent.CanceledException {
+ Object action = ((Pair<Object, Slice>) mObj).first;
+ if (action instanceof PendingIntent) {
+ ((PendingIntent) action).send(context, 0, i, null, null);
+ } else {
+ ((Consumer<Uri>) action).accept(getSlice().getUri());
+ }
}
/**
@@ -215,15 +242,23 @@
*/
public Slice getSlice() {
if (FORMAT_ACTION.equals(getFormat())) {
- return ((Pair<PendingIntent, Slice>) mObj).second;
+ return ((Pair<Object, Slice>) mObj).second;
}
return (Slice) mObj;
}
/**
- * @return The timestamp held by this {@link android.app.slice.SliceItem#FORMAT_TIMESTAMP}
+ * @return The long held by this {@link android.app.slice.SliceItem#FORMAT_LONG}
* SliceItem
*/
+ public long getLong() {
+ return (Long) mObj;
+ }
+
+ /**
+ * @deprecated TO BE REMOVED
+ */
+ @Deprecated
public long getTimestamp() {
return (Long) mObj;
}
@@ -301,8 +336,8 @@
dest.putParcelable(OBJ, ((Slice) obj).toBundle());
break;
case FORMAT_ACTION:
- dest.putParcelable(OBJ, ((Pair<PendingIntent, Slice>) obj).first);
- dest.putBundle(OBJ_2, ((Pair<PendingIntent, Slice>) obj).second.toBundle());
+ dest.putParcelable(OBJ, (PendingIntent) ((Pair<Object, Slice>) obj).first);
+ dest.putBundle(OBJ_2, ((Pair<Object, Slice>) obj).second.toBundle());
break;
case FORMAT_TEXT:
dest.putCharSequence(OBJ, (CharSequence) obj);
@@ -328,7 +363,7 @@
return in.getCharSequence(OBJ);
case FORMAT_ACTION:
return new Pair<>(
- (PendingIntent) in.getParcelable(OBJ),
+ in.getParcelable(OBJ),
new Slice(in.getBundle(OBJ_2)));
case FORMAT_INT:
return in.getInt(OBJ);
@@ -363,31 +398,49 @@
}
/**
- * @hide
* @return A string representation of this slice item.
*/
- @RestrictTo(Scope.LIBRARY)
@Override
public String toString() {
return toString("");
}
- private String toString(String indent) {
+ /**
+ * @return A string representation of this slice item.
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY)
+ public String toString(String indent) {
StringBuilder sb = new StringBuilder();
- sb.append(indent);
- if (FORMAT_SLICE.equals(mFormat)) {
- sb.append("slice:\n");
- sb.append(getSlice().toString(indent + " "));
- } else if (FORMAT_ACTION.equals(mFormat)) {
- sb.append("action:\n");
- sb.append(getSlice().toString(indent + " "));
- } else if (FORMAT_TEXT.equals(mFormat)) {
- sb.append("text: ");
- sb.append(getText());
- sb.append("\n");
- } else {
- sb.append(SliceItem.typeToString(getFormat()));
- sb.append("\n");
+ if (!FORMAT_SLICE.equals(getFormat())) {
+ sb.append(indent);
+ sb.append(getFormat());
+ sb.append(": ");
+ }
+ switch (getFormat()) {
+ case FORMAT_SLICE:
+ sb.append(getSlice().toString(indent));
+ break;
+ case FORMAT_ACTION:
+ sb.append(getAction());
+ sb.append("\n");
+ sb.append(getSlice().toString(indent));
+ break;
+ case FORMAT_TEXT:
+ sb.append(getText());
+ break;
+ case FORMAT_IMAGE:
+ sb.append(getIcon());
+ break;
+ case FORMAT_INT:
+ sb.append(getInt());
+ break;
+ case FORMAT_TIMESTAMP:
+ sb.append(getTimestamp());
+ break;
+ default:
+ sb.append(SliceItem.typeToString(getFormat()));
+ break;
}
return sb.toString();
}
diff --git a/slices/core/src/main/java/androidx/slice/SliceProvider.java b/slices/core/src/main/java/androidx/slice/SliceProvider.java
index 51ffea6..7ec9232 100644
--- a/slices/core/src/main/java/androidx/slice/SliceProvider.java
+++ b/slices/core/src/main/java/androidx/slice/SliceProvider.java
@@ -15,22 +15,61 @@
*/
package androidx.slice;
+import static android.app.slice.Slice.HINT_SHORTCUT;
+import static android.app.slice.Slice.HINT_TITLE;
+import static android.app.slice.SliceProvider.SLICE_TYPE;
+
+import static androidx.slice.compat.SliceProviderCompat.EXTRA_BIND_URI;
+import static androidx.slice.compat.SliceProviderCompat.EXTRA_INTENT;
+import static androidx.slice.compat.SliceProviderCompat.EXTRA_PKG;
+import static androidx.slice.compat.SliceProviderCompat.EXTRA_PROVIDER_PKG;
+import static androidx.slice.compat.SliceProviderCompat.EXTRA_SLICE;
+import static androidx.slice.compat.SliceProviderCompat.EXTRA_SLICE_DESCENDANTS;
+import static androidx.slice.compat.SliceProviderCompat.METHOD_GET_DESCENDANTS;
+import static androidx.slice.compat.SliceProviderCompat.METHOD_GET_PINNED_SPECS;
+import static androidx.slice.compat.SliceProviderCompat.METHOD_MAP_INTENT;
+import static androidx.slice.compat.SliceProviderCompat.METHOD_MAP_ONLY_INTENT;
+import static androidx.slice.compat.SliceProviderCompat.METHOD_PIN;
+import static androidx.slice.compat.SliceProviderCompat.METHOD_SLICE;
+import static androidx.slice.compat.SliceProviderCompat.METHOD_UNPIN;
+import static androidx.slice.compat.SliceProviderCompat.addSpecs;
+import static androidx.slice.compat.SliceProviderCompat.getSpecs;
+import static androidx.slice.core.SliceHints.HINT_PERMISSION_REQUEST;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
+import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ProviderInfo;
+import android.content.pm.PackageManager;
import android.database.ContentObserver;
+import android.database.Cursor;
import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Process;
+import android.os.StrictMode;
+import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
+import androidx.core.app.CoreComponentFactory;
import androidx.core.os.BuildCompat;
-import androidx.slice.compat.ContentProviderWrapper;
-import androidx.slice.compat.SliceProviderCompat;
+import androidx.slice.compat.CompatPinnedList;
import androidx.slice.compat.SliceProviderWrapperContainer;
+import androidx.slice.core.R;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Set;
/**
@@ -72,20 +111,21 @@
*
* @see android.app.slice.Slice
*/
-public abstract class SliceProvider extends ContentProviderWrapper {
+public abstract class SliceProvider extends ContentProvider implements
+ CoreComponentFactory.CompatWrapped {
private static Set<SliceSpec> sSpecs;
- @Override
- public void attachInfo(Context context, ProviderInfo info) {
- ContentProvider impl;
- if (BuildCompat.isAtLeastP()) {
- impl = new SliceProviderWrapperContainer.SliceProviderWrapper(this);
- } else {
- impl = new SliceProviderCompat(this);
- }
- super.attachInfo(context, info, impl);
- }
+ private static final String TAG = "SliceProvider";
+
+ private static final String DATA_PREFIX = "slice_data_";
+ private static final long SLICE_BIND_ANR = 2000;
+
+ private static final boolean DEBUG = false;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private CompatPinnedList mPinnedList;
+
+ private String mCallback;
/**
* Implement this to initialize your slice provider on startup.
@@ -104,6 +144,228 @@
*/
public abstract boolean onCreateSliceProvider();
+ @Override
+ public Object getWrapper() {
+ if (BuildCompat.isAtLeastP()) {
+ return new SliceProviderWrapperContainer.SliceProviderWrapper(this);
+ }
+ return null;
+ }
+
+ @Override
+ public final boolean onCreate() {
+ mPinnedList = new CompatPinnedList(getContext(),
+ DATA_PREFIX + getClass().getName());
+ return onCreateSliceProvider();
+ }
+
+ @Override
+ public final String getType(Uri uri) {
+ if (DEBUG) Log.d(TAG, "getFormat " + uri);
+ return SLICE_TYPE;
+ }
+
+ @Override
+ public Bundle call(String method, String arg, Bundle extras) {
+ if (method.equals(METHOD_SLICE)) {
+ Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+ if (Binder.getCallingUid() != Process.myUid()) {
+ getContext().enforceUriPermission(uri, Binder.getCallingPid(),
+ Binder.getCallingUid(),
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+ "Slice binding requires write access to the uri");
+ }
+ Set<SliceSpec> specs = getSpecs(extras);
+
+ Slice s = handleBindSlice(uri, specs, getCallingPackage());
+ Bundle b = new Bundle();
+ b.putParcelable(EXTRA_SLICE, s.toBundle());
+ return b;
+ } else if (method.equals(METHOD_MAP_INTENT)) {
+ Intent intent = extras.getParcelable(EXTRA_INTENT);
+ Uri uri = onMapIntentToUri(intent);
+ Bundle b = new Bundle();
+ if (uri != null) {
+ Set<SliceSpec> specs = getSpecs(extras);
+ Slice s = handleBindSlice(uri, specs, getCallingPackage());
+ b.putParcelable(EXTRA_SLICE, s.toBundle());
+ } else {
+ b.putParcelable(EXTRA_SLICE, null);
+ }
+ return b;
+ } else if (method.equals(METHOD_MAP_ONLY_INTENT)) {
+ Intent intent = extras.getParcelable(EXTRA_INTENT);
+ Uri uri = onMapIntentToUri(intent);
+ Bundle b = new Bundle();
+ b.putParcelable(EXTRA_SLICE, uri);
+ return b;
+ } else if (method.equals(METHOD_PIN)) {
+ Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+ Set<SliceSpec> specs = getSpecs(extras);
+ String pkg = extras.getString(EXTRA_PKG);
+ if (mPinnedList.addPin(uri, pkg, specs)) {
+ handleSlicePinned(uri);
+ }
+ return null;
+ } else if (method.equals(METHOD_UNPIN)) {
+ Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+ String pkg = extras.getString(EXTRA_PKG);
+ if (mPinnedList.removePin(uri, pkg)) {
+ handleSliceUnpinned(uri);
+ }
+ return null;
+ } else if (method.equals(METHOD_GET_PINNED_SPECS)) {
+ Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+ Bundle b = new Bundle();
+ addSpecs(b, mPinnedList.getSpecs(uri));
+ return b;
+ } else if (method.equals(METHOD_GET_DESCENDANTS)) {
+ Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+ Bundle b = new Bundle();
+ b.putParcelableArrayList(EXTRA_SLICE_DESCENDANTS,
+ new ArrayList<>(handleGetDescendants(uri)));
+ return b;
+ }
+ return super.call(method, arg, extras);
+ }
+
+ private Collection<Uri> handleGetDescendants(Uri uri) {
+ mCallback = "onGetSliceDescendants";
+ mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
+ try {
+ return onGetSliceDescendants(uri);
+ } finally {
+ mHandler.removeCallbacks(mAnr);
+ }
+ }
+
+ private void handleSlicePinned(final Uri sliceUri) {
+ mCallback = "onSlicePinned";
+ mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
+ try {
+ onSlicePinned(sliceUri);
+ } finally {
+ mHandler.removeCallbacks(mAnr);
+ }
+ }
+
+ private void handleSliceUnpinned(final Uri sliceUri) {
+ mCallback = "onSliceUnpinned";
+ mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
+ try {
+ onSliceUnpinned(sliceUri);
+ } finally {
+ mHandler.removeCallbacks(mAnr);
+ }
+ }
+
+ private Slice handleBindSlice(final Uri sliceUri, final Set<SliceSpec> specs,
+ final String callingPkg) {
+ // This can be removed once Slice#bindSlice is removed and everyone is using
+ // SliceManager#bindSlice.
+ String pkg = callingPkg != null ? callingPkg
+ : getContext().getPackageManager().getNameForUid(Binder.getCallingUid());
+ if (Binder.getCallingUid() != Process.myUid()) {
+ try {
+ getContext().enforceUriPermission(sliceUri,
+ Binder.getCallingPid(), Binder.getCallingUid(),
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+ "Slice binding requires write access to Uri");
+ } catch (SecurityException e) {
+ return createPermissionSlice(getContext(), sliceUri, pkg);
+ }
+ }
+ return onBindSliceStrict(sliceUri, specs);
+ }
+
+ /**
+ * Generate a slice that contains a permission request.
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public static Slice createPermissionSlice(Context context, Uri sliceUri,
+ String callingPackage) {
+ Slice.Builder parent = new Slice.Builder(sliceUri);
+
+ Slice.Builder action = new Slice.Builder(parent)
+ .addHints(HINT_TITLE, HINT_SHORTCUT)
+ .addAction(createPermissionIntent(context, sliceUri, callingPackage),
+ new Slice.Builder(parent).build(), null);
+
+ parent.addSubSlice(new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
+ .addText(getPermissionString(context, callingPackage), null)
+ .addSubSlice(action.build())
+ .build());
+
+ return parent.addHints(HINT_PERMISSION_REQUEST).build();
+ }
+
+ /**
+ * Create a PendingIntent pointing at the permission dialog.
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public static PendingIntent createPermissionIntent(Context context, Uri sliceUri,
+ String callingPackage) {
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(context.getPackageName(),
+ "androidx.slice.compat.SlicePermissionActivity"));
+ intent.putExtra(EXTRA_BIND_URI, sliceUri);
+ intent.putExtra(EXTRA_PKG, callingPackage);
+ intent.putExtra(EXTRA_PROVIDER_PKG, context.getPackageName());
+ // Unique pending intent.
+ intent.setData(sliceUri.buildUpon().appendQueryParameter("package", callingPackage)
+ .build());
+
+ return PendingIntent.getActivity(context, 0, intent, 0);
+ }
+
+ /**
+ * Get string describing permission request.
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public static CharSequence getPermissionString(Context context, String callingPackage) {
+ PackageManager pm = context.getPackageManager();
+ try {
+ return context.getString(R.string.abc_slices_permission_request,
+ pm.getApplicationInfo(callingPackage, 0).loadLabel(pm),
+ context.getApplicationInfo().loadLabel(pm));
+ } catch (PackageManager.NameNotFoundException e) {
+ // This shouldn't be possible since the caller is verified.
+ throw new RuntimeException("Unknown calling app", e);
+ }
+ }
+
+ private Slice onBindSliceStrict(Uri sliceUri, Set<SliceSpec> specs) {
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
+ mCallback = "onBindSlice";
+ mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
+ try {
+ StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+ .detectAll()
+ .penaltyDeath()
+ .build());
+ SliceProvider.setSpecs(specs);
+ try {
+ return onBindSlice(sliceUri);
+ } finally {
+ SliceProvider.setSpecs(null);
+ mHandler.removeCallbacks(mAnr);
+ }
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+ }
+
+ private final Runnable mAnr = new Runnable() {
+ @Override
+ public void run() {
+ Process.sendSignal(Process.myPid(), Process.SIGNAL_QUIT);
+ Log.wtf(TAG, "Timed out while handling slice callback " + mCallback);
+ }
+ };
+
/**
* Implemented to create a slice.
* <p>
@@ -166,6 +428,75 @@
}
/**
+ * Obtains a list of slices that are descendants of the specified Uri.
+ * <p>
+ * Implementing this is optional for a SliceProvider, but does provide a good
+ * discovery mechanism for finding slice Uris.
+ *
+ * @param uri The uri to look for descendants under.
+ * @return All slices within the space.
+ * @see androidx.slice.SliceManager#getSliceDescendants(Uri)
+ */
+ public Collection<Uri> onGetSliceDescendants(Uri uri) {
+ return Collections.emptyList();
+ }
+
+ @Nullable
+ @Override
+ public final Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+ @Nullable String selection, @Nullable String[] selectionArgs,
+ @Nullable String sortOrder) {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ @RequiresApi(28)
+ public final Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+ @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ @RequiresApi(16)
+ public final Cursor query(@NonNull Uri uri, @Nullable String[] projection,
+ @Nullable String selection, @Nullable String[] selectionArgs,
+ @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public final Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public final int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
+ return 0;
+ }
+
+ @Override
+ public final int delete(@NonNull Uri uri, @Nullable String selection,
+ @Nullable String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public final int update(@NonNull Uri uri, @Nullable ContentValues values,
+ @Nullable String selection, @Nullable String[] selectionArgs) {
+ return 0;
+ }
+
+ @Nullable
+ @Override
+ @RequiresApi(19)
+ public final Uri canonicalize(@NonNull Uri url) {
+ return null;
+ }
+
+ /**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
diff --git a/slices/core/src/main/java/androidx/slice/SliceSpec.java b/slices/core/src/main/java/androidx/slice/SliceSpec.java
index 27a4dfb..9bbd2a9 100644
--- a/slices/core/src/main/java/androidx/slice/SliceSpec.java
+++ b/slices/core/src/main/java/androidx/slice/SliceSpec.java
@@ -88,7 +88,7 @@
@Override
public int hashCode() {
- return mType.hashCode() + Integer.hashCode(mRevision);
+ return mType.hashCode() + mRevision;
}
@Override
diff --git a/slices/core/src/main/java/androidx/slice/SliceSpecs.java b/slices/core/src/main/java/androidx/slice/SliceSpecs.java
index f91dec2..760d828 100644
--- a/slices/core/src/main/java/androidx/slice/SliceSpecs.java
+++ b/slices/core/src/main/java/androidx/slice/SliceSpecs.java
@@ -41,4 +41,7 @@
* a source of where the message came from.
*/
public static final SliceSpec MESSAGING = new SliceSpec("androidx.slice.MESSAGING", 1);
+
+ private SliceSpecs() {
+ }
}
diff --git a/slices/core/src/main/java/androidx/slice/compat/ContentProviderWrapper.java b/slices/core/src/main/java/androidx/slice/compat/ContentProviderWrapper.java
deleted file mode 100644
index 45c6ffc..0000000
--- a/slices/core/src/main/java/androidx/slice/compat/ContentProviderWrapper.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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.slice.compat;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.pm.ProviderInfo;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.RestrictTo.Scope;
-
-/**
- * @hide
- */
-// TODO: Remove as soon as we have better systems in place for this.
-@RestrictTo(Scope.LIBRARY)
-public class ContentProviderWrapper extends ContentProvider {
-
- private ContentProvider mImpl;
-
- /**
- * Triggers an attach with the object to wrap.
- */
- public void attachInfo(Context context, ProviderInfo info, ContentProvider impl) {
- mImpl = impl;
- super.attachInfo(context, info);
- mImpl.attachInfo(context, info);
- }
-
- @Override
- public final boolean onCreate() {
- return mImpl.onCreate();
- }
-
- @Nullable
- @Override
- public final Cursor query(@NonNull Uri uri, @Nullable String[] projection,
- @Nullable String selection, @Nullable String[] selectionArgs,
- @Nullable String sortOrder) {
- return mImpl.query(uri, projection, selection, selectionArgs, sortOrder);
- }
-
- @Nullable
- @Override
- @RequiresApi(28)
- public final Cursor query(@NonNull Uri uri, @Nullable String[] projection,
- @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) {
- return mImpl.query(uri, projection, queryArgs, cancellationSignal);
- }
-
- @Nullable
- @Override
- @RequiresApi(16)
- public final Cursor query(@NonNull Uri uri, @Nullable String[] projection,
- @Nullable String selection, @Nullable String[] selectionArgs,
- @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
- return mImpl.query(uri, projection, selection, selectionArgs, sortOrder,
- cancellationSignal);
- }
-
- @Nullable
- @Override
- public final String getType(@NonNull Uri uri) {
- return mImpl.getType(uri);
- }
-
- @Nullable
- @Override
- public final Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
- return mImpl.insert(uri, values);
- }
-
- @Override
- public final int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
- return mImpl.bulkInsert(uri, values);
- }
-
- @Override
- public final int delete(@NonNull Uri uri, @Nullable String selection,
- @Nullable String[] selectionArgs) {
- return mImpl.delete(uri, selection, selectionArgs);
- }
-
- @Override
- public final int update(@NonNull Uri uri, @Nullable ContentValues values,
- @Nullable String selection, @Nullable String[] selectionArgs) {
- return mImpl.update(uri, values, selection, selectionArgs);
- }
-
- @Nullable
- @Override
- public final Bundle call(@NonNull String method, @Nullable String arg,
- @Nullable Bundle extras) {
- return mImpl.call(method, arg, extras);
- }
-
- @Nullable
- @Override
- @RequiresApi(19)
- public final Uri canonicalize(@NonNull Uri url) {
- return mImpl.canonicalize(url);
- }
-}
diff --git a/slices/core/src/main/java/androidx/slice/compat/SlicePermissionActivity.java b/slices/core/src/main/java/androidx/slice/compat/SlicePermissionActivity.java
index d8882fa..f3270f8 100644
--- a/slices/core/src/main/java/androidx/slice/compat/SlicePermissionActivity.java
+++ b/slices/core/src/main/java/androidx/slice/compat/SlicePermissionActivity.java
@@ -17,7 +17,6 @@
package androidx.slice.compat;
import android.app.Activity;
-import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
@@ -26,10 +25,11 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
-import androidx.annotation.RestrictTo;
import android.util.Log;
import android.widget.TextView;
+import androidx.annotation.RestrictTo;
+import androidx.appcompat.app.AlertDialog;
import androidx.slice.core.R;
/**
diff --git a/slices/core/src/main/java/androidx/slice/compat/SliceProviderCompat.java b/slices/core/src/main/java/androidx/slice/compat/SliceProviderCompat.java
index 1e2c8f1..61bc65e 100644
--- a/slices/core/src/main/java/androidx/slice/compat/SliceProviderCompat.java
+++ b/slices/core/src/main/java/androidx/slice/compat/SliceProviderCompat.java
@@ -15,45 +15,33 @@
*/
package androidx.slice.compat;
-import static android.app.slice.Slice.HINT_LIST_ITEM;
import static android.app.slice.SliceProvider.SLICE_TYPE;
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.ContentProvider;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
-import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.database.Cursor;
import android.net.Uri;
-import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.Handler;
-import android.os.Looper;
import android.os.Parcelable;
-import android.os.Process;
import android.os.RemoteException;
-import android.os.StrictMode;
-import android.os.StrictMode.ThreadPolicy;
-import android.util.ArraySet;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
+import androidx.collection.ArraySet;
import androidx.core.util.Preconditions;
import androidx.slice.Slice;
-import androidx.slice.SliceProvider;
import androidx.slice.SliceSpec;
-import androidx.slice.core.R;
import androidx.slice.core.SliceHints;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -61,9 +49,8 @@
* @hide
*/
@RestrictTo(Scope.LIBRARY)
-public class SliceProviderCompat extends ContentProvider {
-
- private static final String TAG = "SliceProvider";
+public class SliceProviderCompat {
+ private static final String TAG = "SliceProviderCompat";
public static final String EXTRA_BIND_URI = "slice_uri";
public static final String METHOD_SLICE = "bind_slice";
@@ -72,6 +59,7 @@
public static final String METHOD_UNPIN = "unpin_slice";
public static final String METHOD_GET_PINNED_SPECS = "get_specs";
public static final String METHOD_MAP_ONLY_INTENT = "map_only";
+ public static final String METHOD_GET_DESCENDANTS = "get_descendants";
public static final String EXTRA_INTENT = "slice_intent";
public static final String EXTRA_SLICE = "slice";
@@ -79,238 +67,7 @@
public static final String EXTRA_SUPPORTED_SPECS_REVS = "revs";
public static final String EXTRA_PKG = "pkg";
public static final String EXTRA_PROVIDER_PKG = "provider_pkg";
- private static final String DATA_PREFIX = "slice_data_";
-
- private static final long SLICE_BIND_ANR = 2000;
-
- private static final boolean DEBUG = false;
- private final Handler mHandler = new Handler(Looper.getMainLooper());
- private SliceProvider mSliceProvider;
- private CompatPinnedList mPinnedList;
-
- private String mCallback;
-
- public SliceProviderCompat(SliceProvider provider) {
- mSliceProvider = provider;
- }
-
- @Override
- public boolean onCreate() {
- mPinnedList = new CompatPinnedList(getContext(),
- DATA_PREFIX + mSliceProvider.getClass().getName());
- return mSliceProvider.onCreateSliceProvider();
- }
-
- @Override
- public final int update(Uri uri, ContentValues values, String selection,
- String[] selectionArgs) {
- if (DEBUG) Log.d(TAG, "update " + uri);
- return 0;
- }
-
- @Override
- public final int delete(Uri uri, String selection, String[] selectionArgs) {
- if (DEBUG) Log.d(TAG, "delete " + uri);
- return 0;
- }
-
- @Override
- public final Cursor query(Uri uri, String[] projection, String selection,
- String[] selectionArgs, String sortOrder) {
- if (DEBUG) Log.d(TAG, "query " + uri);
- return null;
- }
-
- @Override
- public final Cursor query(Uri uri, String[] projection, String selection, String[]
- selectionArgs, String sortOrder, CancellationSignal cancellationSignal) {
- if (DEBUG) Log.d(TAG, "query " + uri);
- return null;
- }
-
- @Override
- public final Cursor query(Uri uri, String[] projection, Bundle queryArgs,
- CancellationSignal cancellationSignal) {
- if (DEBUG) Log.d(TAG, "query " + uri);
- return null;
- }
-
- @Override
- public final Uri insert(Uri uri, ContentValues values) {
- if (DEBUG) Log.d(TAG, "insert " + uri);
- return null;
- }
-
- @Override
- public final String getType(Uri uri) {
- if (DEBUG) Log.d(TAG, "getFormat " + uri);
- return SLICE_TYPE;
- }
-
- @Override
- public Bundle call(String method, String arg, Bundle extras) {
- if (method.equals(METHOD_SLICE)) {
- Uri uri = extras.getParcelable(EXTRA_BIND_URI);
- if (Binder.getCallingUid() != Process.myUid()) {
- getContext().enforceUriPermission(uri, Binder.getCallingPid(),
- Binder.getCallingUid(),
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
- "Slice binding requires the permission BIND_SLICE");
- }
- Set<SliceSpec> specs = getSpecs(extras);
-
- Slice s = handleBindSlice(uri, specs, getCallingPackage());
- Bundle b = new Bundle();
- b.putParcelable(EXTRA_SLICE, s.toBundle());
- return b;
- } else if (method.equals(METHOD_MAP_INTENT)) {
- Intent intent = extras.getParcelable(EXTRA_INTENT);
- Uri uri = mSliceProvider.onMapIntentToUri(intent);
- Bundle b = new Bundle();
- if (uri != null) {
- Set<SliceSpec> specs = getSpecs(extras);
- Slice s = handleBindSlice(uri, specs, getCallingPackage());
- b.putParcelable(EXTRA_SLICE, s.toBundle());
- } else {
- b.putParcelable(EXTRA_SLICE, null);
- }
- return b;
- } else if (method.equals(METHOD_MAP_INTENT)) {
- Intent intent = extras.getParcelable(EXTRA_INTENT);
- Uri uri = mSliceProvider.onMapIntentToUri(intent);
- Bundle b = new Bundle();
- b.putParcelable(EXTRA_SLICE, uri);
- return b;
- } else if (method.equals(METHOD_PIN)) {
- Uri uri = extras.getParcelable(EXTRA_BIND_URI);
- Set<SliceSpec> specs = getSpecs(extras);
- String pkg = extras.getString(EXTRA_PKG);
- if (mPinnedList.addPin(uri, pkg, specs)) {
- handleSlicePinned(uri);
- }
- return null;
- } else if (method.equals(METHOD_UNPIN)) {
- Uri uri = extras.getParcelable(EXTRA_BIND_URI);
- String pkg = extras.getString(EXTRA_PKG);
- if (mPinnedList.removePin(uri, pkg)) {
- handleSliceUnpinned(uri);
- }
- return null;
- } else if (method.equals(METHOD_GET_PINNED_SPECS)) {
- Uri uri = extras.getParcelable(EXTRA_BIND_URI);
- Bundle b = new Bundle();
- addSpecs(b, mPinnedList.getSpecs(uri));
- return b;
- }
- return super.call(method, arg, extras);
- }
-
- private void handleSlicePinned(final Uri sliceUri) {
- mCallback = "onSlicePinned";
- mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
- try {
- mSliceProvider.onSlicePinned(sliceUri);
- } finally {
- mHandler.removeCallbacks(mAnr);
- }
- }
-
- private void handleSliceUnpinned(final Uri sliceUri) {
- mCallback = "onSliceUnpinned";
- mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
- try {
- mSliceProvider.onSliceUnpinned(sliceUri);
- } finally {
- mHandler.removeCallbacks(mAnr);
- }
- }
-
- private Slice handleBindSlice(final Uri sliceUri, final Set<SliceSpec> specs,
- final String callingPkg) {
- // This can be removed once Slice#bindSlice is removed and everyone is using
- // SliceManager#bindSlice.
- String pkg = callingPkg != null ? callingPkg
- : getContext().getPackageManager().getNameForUid(Binder.getCallingUid());
- if (Binder.getCallingUid() != Process.myUid()) {
- try {
- getContext().enforceUriPermission(sliceUri,
- Binder.getCallingPid(), Binder.getCallingUid(),
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
- "Slice binding requires write access to Uri");
- } catch (SecurityException e) {
- return createPermissionSlice(getContext(), sliceUri, pkg);
- }
- }
- return onBindSliceStrict(sliceUri, specs);
- }
-
- /**
- * Generate a slice that contains a permission request.
- */
- public static Slice createPermissionSlice(Context context, Uri sliceUri,
- String callingPackage) {
- return new Slice.Builder(sliceUri)
- .addAction(createPermissionIntent(context, sliceUri, callingPackage),
- new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
- .addText(getPermissionString(context, callingPackage), null)
- .build(), null)
- .addHints(HINT_LIST_ITEM)
- .build();
- }
-
- /**
- * Create a PendingIntent pointing at the permission dialog.
- */
- public static PendingIntent createPermissionIntent(Context context, Uri sliceUri,
- String callingPackage) {
- Intent intent = new Intent();
- intent.setComponent(new ComponentName(context.getPackageName(),
- "androidx.slice.compat.SlicePermissionActivity"));
- intent.putExtra(EXTRA_BIND_URI, sliceUri);
- intent.putExtra(EXTRA_PKG, callingPackage);
- intent.putExtra(EXTRA_PROVIDER_PKG, context.getPackageName());
- // Unique pending intent.
- intent.setData(sliceUri.buildUpon().appendQueryParameter("package", callingPackage)
- .build());
-
- return PendingIntent.getActivity(context, 0, intent, 0);
- }
-
- /**
- * Get string describing permission request.
- */
- public static CharSequence getPermissionString(Context context, String callingPackage) {
- PackageManager pm = context.getPackageManager();
- try {
- return context.getString(R.string.abc_slices_permission_request,
- pm.getApplicationInfo(callingPackage, 0).loadLabel(pm),
- context.getApplicationInfo().loadLabel(pm));
- } catch (PackageManager.NameNotFoundException e) {
- // This shouldn't be possible since the caller is verified.
- throw new RuntimeException("Unknown calling app", e);
- }
- }
-
- private Slice onBindSliceStrict(Uri sliceUri, Set<SliceSpec> specs) {
- ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
- mCallback = "onBindSlice";
- mHandler.postDelayed(mAnr, SLICE_BIND_ANR);
- try {
- StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
- .detectAll()
- .penaltyDeath()
- .build());
- SliceProvider.setSpecs(specs);
- try {
- return mSliceProvider.onBindSlice(sliceUri);
- } finally {
- SliceProvider.setSpecs(null);
- mHandler.removeCallbacks(mAnr);
- }
- } finally {
- StrictMode.setThreadPolicy(oldPolicy);
- }
- }
+ public static final String EXTRA_SLICE_DESCENDANTS = "slice_descendants";
/**
* Compat version of {@link Slice#bindSlice}.
@@ -348,7 +105,10 @@
}
}
- private static void addSpecs(Bundle extras, Set<SliceSpec> supportedSpecs) {
+ /**
+ * Compat way to push specs through the call.
+ */
+ public static void addSpecs(Bundle extras, Set<SliceSpec> supportedSpecs) {
ArrayList<String> types = new ArrayList<>();
ArrayList<Integer> revs = new ArrayList<>();
for (SliceSpec spec : supportedSpecs) {
@@ -359,7 +119,10 @@
extras.putIntegerArrayList(EXTRA_SUPPORTED_SPECS_REVS, revs);
}
- private static Set<SliceSpec> getSpecs(Bundle extras) {
+ /**
+ * Compat way to push specs through the call.
+ */
+ public static Set<SliceSpec> getSpecs(Bundle extras) {
ArraySet<SliceSpec> specs = new ArraySet<>();
ArrayList<String> types = extras.getStringArrayList(EXTRA_SUPPORTED_SPECS);
ArrayList<Integer> revs = extras.getIntegerArrayList(EXTRA_SUPPORTED_SPECS_REVS);
@@ -540,11 +303,22 @@
}
}
- private final Runnable mAnr = new Runnable() {
- @Override
- public void run() {
- Process.sendSignal(Process.myPid(), Process.SIGNAL_QUIT);
- Log.wtf(TAG, "Timed out while handling slice callback " + mCallback);
+ /**
+ * Compat version of {@link android.app.slice.SliceManager#getSliceDescendants(Uri)}
+ */
+ public static @NonNull Collection<Uri> getSliceDescendants(Context context, @NonNull Uri uri) {
+ ContentResolver resolver = context.getContentResolver();
+ try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
+ Bundle extras = new Bundle();
+ extras.putParcelable(EXTRA_BIND_URI, uri);
+ final Bundle res = provider.call(METHOD_GET_DESCENDANTS, null, extras);
+ return res.getParcelableArrayList(EXTRA_SLICE_DESCENDANTS);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to get slice descendants", e);
}
- };
+ return Collections.emptyList();
+ }
+
+ private SliceProviderCompat() {
+ }
}
diff --git a/slices/core/src/main/java/androidx/slice/compat/SliceProviderWrapperContainer.java b/slices/core/src/main/java/androidx/slice/compat/SliceProviderWrapperContainer.java
index 65505f7..6691488 100644
--- a/slices/core/src/main/java/androidx/slice/compat/SliceProviderWrapperContainer.java
+++ b/slices/core/src/main/java/androidx/slice/compat/SliceProviderWrapperContainer.java
@@ -22,7 +22,9 @@
import android.app.slice.Slice;
import android.app.slice.SliceProvider;
import android.app.slice.SliceSpec;
+import android.content.Context;
import android.content.Intent;
+import android.content.pm.ProviderInfo;
import android.net.Uri;
import androidx.annotation.NonNull;
@@ -30,6 +32,7 @@
import androidx.collection.ArraySet;
import androidx.slice.SliceConvert;
+import java.util.Collection;
import java.util.List;
/**
@@ -50,8 +53,14 @@
}
@Override
+ public void attachInfo(Context context, ProviderInfo info) {
+ super.attachInfo(context, info);
+ mSliceProvider.attachInfo(context, info);
+ }
+
+ @Override
public boolean onCreate() {
- return mSliceProvider.onCreateSliceProvider();
+ return mSliceProvider.onCreate();
}
@Override
@@ -74,6 +83,11 @@
mSliceProvider.onSliceUnpinned(sliceUri);
}
+ @Override
+ public Collection<Uri> onGetSliceDescendants(Uri uri) {
+ return mSliceProvider.onGetSliceDescendants(uri);
+ }
+
/**
* Maps intents to uris.
*/
@@ -82,4 +96,7 @@
return mSliceProvider.onMapIntentToUri(intent);
}
}
+
+ private SliceProviderWrapperContainer() {
+ }
}
diff --git a/slices/core/src/main/java/androidx/slice/core/SliceActionImpl.java b/slices/core/src/main/java/androidx/slice/core/SliceActionImpl.java
index b5ca913..4fb81a6 100644
--- a/slices/core/src/main/java/androidx/slice/core/SliceActionImpl.java
+++ b/slices/core/src/main/java/androidx/slice/core/SliceActionImpl.java
@@ -27,7 +27,6 @@
import static android.app.slice.SliceItem.FORMAT_ACTION;
import static android.app.slice.SliceItem.FORMAT_IMAGE;
import static android.app.slice.SliceItem.FORMAT_INT;
-import static android.app.slice.SliceItem.FORMAT_SLICE;
import static android.app.slice.SliceItem.FORMAT_TEXT;
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
@@ -64,6 +63,7 @@
private boolean mIsChecked;
private int mPriority = -1;
private SliceItem mSliceItem;
+ private SliceItem mActionItem;
/**
* Construct a SliceAction representing a tappable icon.
@@ -147,40 +147,36 @@
@RestrictTo(LIBRARY)
public SliceActionImpl(SliceItem slice) {
mSliceItem = slice;
- if (slice.hasHint(HINT_SHORTCUT) && FORMAT_SLICE.equals(slice.getFormat())) {
- SliceItem actionItem = SliceQuery.find(slice, FORMAT_ACTION);
- if (actionItem == null) {
- // Can't have action slice without action
- return;
- }
- mAction = actionItem.getAction();
- SliceItem iconItem = SliceQuery.find(actionItem.getSlice(), FORMAT_IMAGE);
- if (iconItem != null) {
- mIcon = iconItem.getIcon();
- mImageMode = iconItem.hasHint(HINT_NO_TINT)
- ? iconItem.hasHint(HINT_LARGE) ? LARGE_IMAGE : SMALL_IMAGE
- : ICON_IMAGE;
- }
- SliceItem titleItem = SliceQuery.find(actionItem.getSlice(), FORMAT_TEXT, HINT_TITLE,
- null /* nonHints */);
- if (titleItem != null) {
- mTitle = titleItem.getText();
- }
- SliceItem cdItem = SliceQuery.findSubtype(actionItem.getSlice(), FORMAT_TEXT,
- SUBTYPE_CONTENT_DESCRIPTION);
- if (cdItem != null) {
- mContentDescription = cdItem.getText();
- }
- mIsToggle = SUBTYPE_TOGGLE.equals(actionItem.getSubType());
- if (mIsToggle) {
- mIsChecked = actionItem.hasHint(HINT_SELECTED);
- }
- SliceItem priority = SliceQuery.findSubtype(actionItem.getSlice(), FORMAT_INT,
- SUBTYPE_PRIORITY);
- mPriority = priority != null ? priority.getInt() : -1;
- } else if (FORMAT_ACTION.equals(slice.getFormat())) {
- mAction = slice.getAction();
+ SliceItem actionItem = SliceQuery.find(slice, FORMAT_ACTION);
+ if (actionItem == null) {
+ // Can't have action slice without action
+ return;
}
+ mActionItem = actionItem;
+ SliceItem iconItem = SliceQuery.find(actionItem.getSlice(), FORMAT_IMAGE);
+ if (iconItem != null) {
+ mIcon = iconItem.getIcon();
+ mImageMode = iconItem.hasHint(HINT_NO_TINT)
+ ? iconItem.hasHint(HINT_LARGE) ? LARGE_IMAGE : SMALL_IMAGE
+ : ICON_IMAGE;
+ }
+ SliceItem titleItem = SliceQuery.find(actionItem.getSlice(), FORMAT_TEXT, HINT_TITLE,
+ null /* nonHints */);
+ if (titleItem != null) {
+ mTitle = titleItem.getText();
+ }
+ SliceItem cdItem = SliceQuery.findSubtype(actionItem.getSlice(), FORMAT_TEXT,
+ SUBTYPE_CONTENT_DESCRIPTION);
+ if (cdItem != null) {
+ mContentDescription = cdItem.getText();
+ }
+ mIsToggle = SUBTYPE_TOGGLE.equals(actionItem.getSubType());
+ if (mIsToggle) {
+ mIsChecked = actionItem.hasHint(HINT_SELECTED);
+ }
+ SliceItem priority = SliceQuery.findSubtype(actionItem.getSlice(), FORMAT_INT,
+ SUBTYPE_PRIORITY);
+ mPriority = priority != null ? priority.getInt() : -1;
}
/**
@@ -218,7 +214,15 @@
@NonNull
@Override
public PendingIntent getAction() {
- return mAction;
+ return mAction != null ? mAction : mActionItem.getAction();
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ public SliceItem getActionItem() {
+ return mActionItem;
}
/**
diff --git a/slices/core/src/main/java/androidx/slice/core/SliceHints.java b/slices/core/src/main/java/androidx/slice/core/SliceHints.java
index acbdfc6..f546b6c 100644
--- a/slices/core/src/main/java/androidx/slice/core/SliceHints.java
+++ b/slices/core/src/main/java/androidx/slice/core/SliceHints.java
@@ -18,9 +18,13 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import androidx.annotation.IntDef;
import androidx.annotation.RestrictTo;
+import java.lang.annotation.Retention;
+
/**
* Temporary class to contain hint constants for slices to be used.
* @hide
@@ -60,7 +64,7 @@
* A hint to indicate that the contents of this subslice represent a list of keywords
* related to the parent slice.
*/
- public static final String HINT_KEY_WORDS = "key_words";
+ public static final String HINT_KEYWORDS = "keywords";
/**
* Hint indicating an item representing a time-to-live for the content.
@@ -78,9 +82,12 @@
*/
public static final String SUBTYPE_MILLIS = "millis";
+ public static final String HINT_PERMISSION_REQUEST = "permission_request";
+
@IntDef({
LARGE_IMAGE, SMALL_IMAGE, ICON_IMAGE, UNKNOWN_IMAGE
})
+ @Retention(SOURCE)
public @interface ImageMode{}
/**
@@ -104,4 +111,7 @@
* Constant representing infinity.
*/
public static final long INFINITY = -1;
+
+ private SliceHints() {
+ }
}
diff --git a/slices/core/src/main/java/androidx/slice/core/SliceQuery.java b/slices/core/src/main/java/androidx/slice/core/SliceQuery.java
index f123df0..626a1dc 100644
--- a/slices/core/src/main/java/androidx/slice/core/SliceQuery.java
+++ b/slices/core/src/main/java/androidx/slice/core/SliceQuery.java
@@ -19,22 +19,16 @@
import static android.app.slice.SliceItem.FORMAT_ACTION;
import static android.app.slice.SliceItem.FORMAT_SLICE;
-import androidx.annotation.RestrictTo;
import android.text.TextUtils;
-import java.util.ArrayDeque;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Queue;
-import java.util.Spliterators;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-
+import androidx.annotation.RestrictTo;
import androidx.slice.Slice;
import androidx.slice.SliceItem;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
/**
* Utilities for finding content within a Slice.
* @hide
@@ -98,12 +92,12 @@
*/
private static boolean contains(SliceItem container, final SliceItem item) {
if (container == null || item == null) return false;
- return stream(container).filter(new Predicate<SliceItem>() {
+ return findFirst(filter(stream(container), new Filter<SliceItem>() {
@Override
- public boolean test(SliceItem s) {
+ public boolean filter(SliceItem s) {
return s == item;
}
- }).findAny().isPresent();
+ }), null) != null;
}
/**
@@ -129,26 +123,26 @@
*/
public static List<SliceItem> findAll(Slice s, final String format, final String[] hints,
final String[] nonHints) {
- return stream(s).filter(new Predicate<SliceItem>() {
+ return collect(filter(stream(s), new Filter<SliceItem>() {
@Override
- public boolean test(SliceItem item) {
+ public boolean filter(SliceItem item) {
return checkFormat(item, format)
&& (hasHints(item, hints) && !hasAnyHints(item, nonHints));
}
- }).collect(Collectors.<SliceItem>toList());
+ }));
}
/**
*/
public static List<SliceItem> findAll(SliceItem s, final String format, final String[] hints,
final String[] nonHints) {
- return stream(s).filter(new Predicate<SliceItem>() {
+ return collect(filter(stream(s), new Filter<SliceItem>() {
@Override
- public boolean test(SliceItem item) {
+ public boolean filter(SliceItem item) {
return checkFormat(item, format)
&& (hasHints(item, hints) && !hasAnyHints(item, nonHints));
}
- }).collect(Collectors.<SliceItem>toList());
+ }));
}
/**
@@ -179,48 +173,48 @@
*/
public static SliceItem find(Slice s, final String format, final String[] hints,
final String[] nonHints) {
- return stream(s).filter(new Predicate<SliceItem>() {
+ return findFirst(filter(stream(s), new Filter<SliceItem>() {
@Override
- public boolean test(SliceItem item) {
+ public boolean filter(SliceItem item) {
return checkFormat(item, format)
&& (hasHints(item, hints) && !hasAnyHints(item, nonHints));
}
- }).findFirst().orElse(null);
+ }), null);
}
/**
*/
public static SliceItem findSubtype(Slice s, final String format, final String subtype) {
- return stream(s).filter(new Predicate<SliceItem>() {
+ return findFirst(filter(stream(s), new Filter<SliceItem>() {
@Override
- public boolean test(SliceItem item) {
+ public boolean filter(SliceItem item) {
return checkFormat(item, format) && checkSubtype(item, subtype);
}
- }).findFirst().orElse(null);
+ }), null);
}
/**
*/
public static SliceItem findSubtype(SliceItem s, final String format, final String subtype) {
- return stream(s).filter(new Predicate<SliceItem>() {
+ return findFirst(filter(stream(s), new Filter<SliceItem>() {
@Override
- public boolean test(SliceItem item) {
+ public boolean filter(SliceItem item) {
return checkFormat(item, format) && checkSubtype(item, subtype);
}
- }).findFirst().orElse(null);
+ }), null);
}
/**
*/
public static SliceItem find(SliceItem s, final String format, final String[] hints,
final String[] nonHints) {
- return stream(s).filter(new Predicate<SliceItem>() {
+ return findFirst(filter(stream(s), new Filter<SliceItem>() {
@Override
- public boolean test(SliceItem item) {
+ public boolean filter(SliceItem item) {
return checkFormat(item, format)
&& (hasHints(item, hints) && !hasAnyHints(item, nonHints));
}
- }).findFirst().orElse(null);
+ }), null);
}
private static boolean checkFormat(SliceItem item, String format) {
@@ -233,24 +227,24 @@
/**
*/
- public static Stream<SliceItem> stream(SliceItem slice) {
- Queue<SliceItem> items = new ArrayDeque<>();
+ public static Iterator<SliceItem> stream(SliceItem slice) {
+ ArrayList<SliceItem> items = new ArrayList<>();
items.add(slice);
return getSliceItemStream(items);
}
/**
*/
- public static Stream<SliceItem> stream(Slice slice) {
- Queue<SliceItem> items = new ArrayDeque<>();
+ public static Iterator<SliceItem> stream(Slice slice) {
+ ArrayList<SliceItem> items = new ArrayList<>();
items.addAll(slice.getItems());
return getSliceItemStream(items);
}
/**
*/
- private static Stream<SliceItem> getSliceItemStream(final Queue<SliceItem> items) {
- Iterator<SliceItem> iterator = new Iterator<SliceItem>() {
+ private static Iterator<SliceItem> getSliceItemStream(final ArrayList<SliceItem> items) {
+ return new Iterator<SliceItem>() {
@Override
public boolean hasNext() {
return items.size() != 0;
@@ -258,7 +252,7 @@
@Override
public SliceItem next() {
- SliceItem item = items.poll();
+ SliceItem item = items.remove(0);
if (FORMAT_SLICE.equals(item.getFormat())
|| FORMAT_ACTION.equals(item.getFormat())) {
items.addAll(item.getSlice().getItems());
@@ -266,6 +260,54 @@
return item;
}
};
- return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
+ }
+
+ private static <T> List<T> collect(Iterator<T> iter) {
+ List<T> list = new ArrayList<>();
+ while (iter.hasNext()) list.add(iter.next());
+ return list;
+ }
+
+ private static <T> Iterator<T> filter(final Iterator<T> input, final Filter<T> f) {
+ return new Iterator<T>() {
+ T mNext = findNext();
+
+ private T findNext() {
+ while (input.hasNext()) {
+ T i = input.next();
+ if (f.filter(i)) {
+ return i;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return mNext != null;
+ }
+
+ @Override
+ public T next() {
+ T ret = mNext;
+ mNext = findNext();
+ return ret;
+ }
+ };
+ }
+
+ private static <T> T findFirst(Iterator<T> filter, T def) {
+ while (filter.hasNext()) {
+ T r = filter.next();
+ if (r != null) return r;
+ }
+ return def;
+ }
+
+ private interface Filter<T> {
+ boolean filter(T input);
+ }
+
+ private SliceQuery() {
}
}
diff --git a/slices/core/src/main/res/values-as/strings.xml b/slices/core/src/main/res/values-as/strings.xml
new file mode 100644
index 0000000..95722b9
--- /dev/null
+++ b/slices/core/src/main/res/values-as/strings.xml
@@ -0,0 +1,27 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="abc_slices_permission_request" msgid="3604847235923472451">"<xliff:g id="APP_0">%1$s</xliff:g>এ <xliff:g id="APP_2">%2$s</xliff:g>ৰ অংশ দেখুওৱাব খুজিছে"</string>
+ <string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g>ক <xliff:g id="APP_2">%2$s</xliff:g>ৰ অংশ দেখুওৱাবলৈ অনুমতি দিবনে?"</string>
+ <string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- ই <xliff:g id="APP">%1$s</xliff:g>ৰ তথ্য পঢ়িব পাৰে"</string>
+ <string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- ই <xliff:g id="APP">%1$s</xliff:g>ৰ ভিতৰত কাৰ্য কৰিব পাৰে"</string>
+ <string name="abc_slice_permission_checkbox" msgid="5696872682700058611">"<xliff:g id="APP">%1$s</xliff:g>ক যিকোনো এপৰ অংশ দেখুওৱাবলৈ অনুমতি দিয়ক"</string>
+ <string name="abc_slice_permission_allow" msgid="5024599872061409708">"অনুমতি দিয়ক"</string>
+ <string name="abc_slice_permission_deny" msgid="3819478292430407705">"অস্বীকাৰ কৰক"</string>
+</resources>
diff --git a/slices/core/src/main/res/values-be/strings.xml b/slices/core/src/main/res/values-be/strings.xml
index c9fc9a8..5fadc3f 100644
--- a/slices/core/src/main/res/values-be/strings.xml
+++ b/slices/core/src/main/res/values-be/strings.xml
@@ -17,11 +17,11 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="abc_slices_permission_request" msgid="3604847235923472451">"Праграма <xliff:g id="APP_0">%1$s</xliff:g> запытвае дазвол на паказ зрэзаў праграмы <xliff:g id="APP_2">%2$s</xliff:g>"</string>
- <string name="abc_slice_permission_title" msgid="4175332421259324948">"Дазволіць праграме <xliff:g id="APP_0">%1$s</xliff:g> паказваць зрэзы праграмы <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
+ <string name="abc_slices_permission_request" msgid="3604847235923472451">"Праграма <xliff:g id="APP_0">%1$s</xliff:g> запытвае дазвол на паказ фрагментаў праграмы <xliff:g id="APP_2">%2$s</xliff:g>"</string>
+ <string name="abc_slice_permission_title" msgid="4175332421259324948">"Дазволіць праграме <xliff:g id="APP_0">%1$s</xliff:g> паказваць фрагменты праграмы <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Можа счытваць інфармацыю з праграмы <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Можа выконваць дзеянні ў праграме <xliff:g id="APP">%1$s</xliff:g>"</string>
- <string name="abc_slice_permission_checkbox" msgid="5696872682700058611">"Дазволіць праграме <xliff:g id="APP">%1$s</xliff:g> паказваць зрэзы іншых праграм"</string>
+ <string name="abc_slice_permission_checkbox" msgid="5696872682700058611">"Дазволіць праграме <xliff:g id="APP">%1$s</xliff:g> паказваць фрагменты іншых праграм"</string>
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Дазволіць"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Адмовіць"</string>
</resources>
diff --git a/slices/core/src/main/res/values-ja/strings.xml b/slices/core/src/main/res/values-ja/strings.xml
index a04df0b..315326f 100644
--- a/slices/core/src/main/res/values-ja/strings.xml
+++ b/slices/core/src/main/res/values-ja/strings.xml
@@ -19,7 +19,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slices_permission_request" msgid="3604847235923472451">"<xliff:g id="APP_0">%1$s</xliff:g> が <xliff:g id="APP_2">%2$s</xliff:g> のスライスの表示をリクエストしています"</string>
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_2">%2$s</xliff:g> のスライスの表示を <xliff:g id="APP_0">%1$s</xliff:g> に許可しますか?"</string>
- <string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- <xliff:g id="APP">%1$s</xliff:g> からの情報を読み取ることがあります"</string>
+ <string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- <xliff:g id="APP">%1$s</xliff:g> からの情報を読み取ることができます"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- <xliff:g id="APP">%1$s</xliff:g> 内部で操作することがあります"</string>
<string name="abc_slice_permission_checkbox" msgid="5696872682700058611">"すべてのアプリのスライスを表示することを <xliff:g id="APP">%1$s</xliff:g> に許可する"</string>
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"許可"</string>
diff --git a/slices/core/src/main/res/values-or/strings.xml b/slices/core/src/main/res/values-or/strings.xml
new file mode 100644
index 0000000..f6b9443
--- /dev/null
+++ b/slices/core/src/main/res/values-or/strings.xml
@@ -0,0 +1,27 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="abc_slices_permission_request" msgid="3604847235923472451">"<xliff:g id="APP_0">%1$s</xliff:g>, <xliff:g id="APP_2">%2$s</xliff:g> ସ୍ଲାଇସ୍କୁ ଦେଖାଇବା ପାଇଁ ଚାହେଁ"</string>
+ <string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_2">%2$s</xliff:g> ସ୍ଲାଇସ୍କୁ ଦେଖାଇବା ପାଇଁ <xliff:g id="APP_0">%1$s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string>
+ <string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- ଏହା <xliff:g id="APP">%1$s</xliff:g>ରୁ ସୂଚନାକୁ ପଢ଼ିପାରିବ"</string>
+ <string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- ଏହା <xliff:g id="APP">%1$s</xliff:g> ଭିତରେ କାମ କରିପାରିବ"</string>
+ <string name="abc_slice_permission_checkbox" msgid="5696872682700058611">"ଯେକୌଣସି ଆପ୍ରେ ସ୍ଲାଇସ୍କୁ ଦେଖାଇବା ପାଇଁ <xliff:g id="APP">%1$s</xliff:g>କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+ <string name="abc_slice_permission_allow" msgid="5024599872061409708">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+ <string name="abc_slice_permission_deny" msgid="3819478292430407705">"ଅସ୍ଵୀକାର କରନ୍ତୁ"</string>
+</resources>
diff --git a/slices/view/api/current.txt b/slices/view/api/current.txt
index 0936bbf..65d5faa 100644
--- a/slices/view/api/current.txt
+++ b/slices/view/api/current.txt
@@ -3,11 +3,15 @@
public abstract class SliceManager {
method public abstract androidx.slice.Slice bindSlice(android.net.Uri);
method public abstract androidx.slice.Slice bindSlice(android.content.Intent);
+ method public abstract int checkSlicePermission(android.net.Uri, int, int);
method public static androidx.slice.SliceManager getInstance(android.content.Context);
+ method public abstract java.util.Collection<android.net.Uri> getSliceDescendants(android.net.Uri);
+ method public abstract void grantSlicePermission(java.lang.String, android.net.Uri);
method public abstract android.net.Uri mapIntentToUri(android.content.Intent);
method public abstract void pinSlice(android.net.Uri);
method public abstract void registerSliceCallback(android.net.Uri, androidx.slice.SliceManager.SliceCallback);
method public abstract void registerSliceCallback(android.net.Uri, java.util.concurrent.Executor, androidx.slice.SliceManager.SliceCallback);
+ method public abstract void revokeSlicePermission(java.lang.String, android.net.Uri);
method public abstract void unpinSlice(android.net.Uri);
method public abstract void unregisterSliceCallback(android.net.Uri, androidx.slice.SliceManager.SliceCallback);
}
@@ -19,10 +23,16 @@
public class SliceMetadata {
method public static androidx.slice.SliceMetadata from(android.content.Context, androidx.slice.Slice);
method public long getExpiry();
+ method public int getHeaderType();
method public long getLastUpdatedTime();
method public int getLoadingState();
+ method public androidx.slice.core.SliceAction getPrimaryAction();
+ method public androidx.core.util.Pair<java.lang.Integer, java.lang.Integer> getRange();
method public java.util.List<androidx.slice.SliceItem> getSliceActions();
method public java.util.List<java.lang.String> getSliceKeywords();
+ method public java.util.List<androidx.slice.core.SliceAction> getToggles();
+ method public boolean hasLargeMode();
+ method public boolean isPermissionSlice();
field public static final int LOADED_ALL = 2; // 0x2
field public static final int LOADED_NONE = 0; // 0x0
field public static final int LOADED_PARTIAL = 1; // 0x1
@@ -32,8 +42,8 @@
method public static deprecated int getLoadingState(androidx.slice.Slice);
method public static deprecated java.util.List<androidx.slice.SliceItem> getSliceActions(androidx.slice.Slice);
method public static deprecated java.util.List<java.lang.String> getSliceKeywords(androidx.slice.Slice);
- method public static androidx.slice.Slice parseSlice(java.io.InputStream, java.lang.String) throws java.io.IOException;
- method public static void serializeSlice(androidx.slice.Slice, android.content.Context, java.io.OutputStream, java.lang.String, androidx.slice.SliceUtils.SerializeOptions) throws java.io.IOException;
+ method public static androidx.slice.Slice parseSlice(android.content.Context, java.io.InputStream, java.lang.String, androidx.slice.SliceUtils.SliceActionListener) throws java.io.IOException, androidx.slice.SliceUtils.SliceParseException;
+ method public static void serializeSlice(androidx.slice.Slice, android.content.Context, java.io.OutputStream, java.lang.String, androidx.slice.SliceUtils.SerializeOptions) throws java.io.IOException, java.lang.IllegalArgumentException;
field public static final deprecated int LOADING_ALL = 0; // 0x0
field public static final deprecated int LOADING_COMPLETE = 2; // 0x2
field public static final deprecated int LOADING_PARTIAL = 1; // 0x1
@@ -43,11 +53,20 @@
ctor public SliceUtils.SerializeOptions();
method public androidx.slice.SliceUtils.SerializeOptions setActionMode(int);
method public androidx.slice.SliceUtils.SerializeOptions setImageMode(int);
- field public static final int MODE_DISABLE = 2; // 0x2
+ method public androidx.slice.SliceUtils.SerializeOptions setMaxImageHeight(int);
+ method public androidx.slice.SliceUtils.SerializeOptions setMaxImageWidth(int);
+ field public static final int MODE_CONVERT = 2; // 0x2
field public static final int MODE_REMOVE = 1; // 0x1
field public static final int MODE_THROW = 0; // 0x0
}
+ public static abstract interface SliceUtils.SliceActionListener {
+ method public abstract void onSliceAction(android.net.Uri);
+ }
+
+ public static class SliceUtils.SliceParseException extends java.lang.Exception {
+ }
+
}
package androidx.slice.widget {
@@ -66,7 +85,10 @@
field public static final int ROW_TYPE_GRID = 1; // 0x1
field public static final int ROW_TYPE_LIST = 0; // 0x0
field public static final int ROW_TYPE_MESSAGING = 2; // 0x2
+ field public static final int ROW_TYPE_PROGRESS = 5; // 0x5
field public static final int ROW_TYPE_SHORTCUT = -1; // 0xffffffff
+ field public static final int ROW_TYPE_SLIDER = 4; // 0x4
+ field public static final int ROW_TYPE_TOGGLE = 3; // 0x3
field public static final int STATE_OFF = 0; // 0x0
field public static final int STATE_ON = 1; // 0x1
field public int actionCount;
@@ -80,12 +102,11 @@
}
public final class SliceLiveData {
- ctor public SliceLiveData();
method public static androidx.lifecycle.LiveData<androidx.slice.Slice> fromIntent(android.content.Context, android.content.Intent);
method public static androidx.lifecycle.LiveData<androidx.slice.Slice> fromUri(android.content.Context, android.net.Uri);
}
- public class SliceView extends android.view.ViewGroup implements androidx.lifecycle.Observer {
+ public class SliceView extends android.view.ViewGroup implements androidx.lifecycle.Observer android.view.View.OnClickListener {
ctor public SliceView(android.content.Context);
ctor public SliceView(android.content.Context, android.util.AttributeSet);
ctor public SliceView(android.content.Context, android.util.AttributeSet, int);
@@ -93,6 +114,7 @@
method public int getMode();
method public java.util.List<androidx.slice.SliceItem> getSliceActions();
method public void onChanged(androidx.slice.Slice);
+ method public void onClick(android.view.View);
method public void setMode(int);
method public void setOnSliceActionListener(androidx.slice.widget.SliceView.OnSliceActionListener);
method public void setScrollable(boolean);
diff --git a/slices/view/build.gradle b/slices/view/build.gradle
index 8653955..71de3a7 100644
--- a/slices/view/build.gradle
+++ b/slices/view/build.gradle
@@ -44,5 +44,5 @@
mavenGroup = LibraryGroups.SLICE
inceptionYear = "2017"
description = "A library that handles rendering of slice content into supported templates"
- minSdkVersion = 24
+ minSdkVersion = 19
}
diff --git a/slices/view/src/androidTest/AndroidManifest.xml b/slices/view/src/androidTest/AndroidManifest.xml
index 3bb50d5..78f3ad8 100644
--- a/slices/view/src/androidTest/AndroidManifest.xml
+++ b/slices/view/src/androidTest/AndroidManifest.xml
@@ -20,10 +20,15 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
- <application>
+ <application
+ android:label="Slice Render Test">
<provider android:name="androidx.slice.SliceManagerTest$TestSliceProvider"
android:authorities="androidx.slice.view.test"
- android:exported="true"/>
+ android:exported="true">
+ <intent-filter>
+ <action android:name="androidx.slice.action.TEST" />
+ </intent-filter>
+ </provider>
<activity android:name="androidx.slice.render.SliceRenderActivity"
android:theme="@style/AppTheme.NoActionBar">
diff --git a/slices/view/src/androidTest/java/androidx/slice/SliceManagerTest.java b/slices/view/src/androidTest/java/androidx/slice/SliceManagerTest.java
index 2e5ed54..5564f72 100644
--- a/slices/view/src/androidTest/java/androidx/slice/SliceManagerTest.java
+++ b/slices/view/src/androidTest/java/androidx/slice/SliceManagerTest.java
@@ -30,21 +30,23 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
-import androidx.annotation.NonNull;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+
+import androidx.annotation.NonNull;
import androidx.core.os.BuildCompat;
+import androidx.slice.render.SliceRenderActivity;
+import androidx.slice.widget.SliceLiveData;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.concurrent.Executor;
-import androidx.slice.render.SliceRenderActivity;
-import androidx.slice.widget.SliceLiveData;
-
@RunWith(AndroidJUnit4.class)
@SmallTest
public class SliceManagerTest {
@@ -126,12 +128,45 @@
}
@Test
+ public void testMapIntentToUriStatic() {
+ Uri expected = Uri.parse("content://androidx.slice.view.test/render");
+
+ Uri uri = mManager.mapIntentToUri(new Intent(mContext, SliceRenderActivity.class));
+
+ assertEquals(expected, uri);
+ }
+
+ @Test
public void testMapIntentToUri() {
Uri expected = Uri.parse("content://androidx.slice.view.test/render");
- Slice s = new Slice.Builder(expected).build();
- when(mSliceProvider.onBindSlice(eq(expected))).thenReturn(s);
- Uri uri = mManager.mapIntentToUri(new Intent(mContext, SliceRenderActivity.class));
+ Intent intent = new Intent("androidx.slice.action.TEST")
+ .setPackage(mContext.getPackageName());
+
+ when(mSliceProvider.onMapIntentToUri(eq(intent))).thenReturn(expected);
+ Uri uri = mManager.mapIntentToUri(intent);
+
assertEquals(expected, uri);
+ verify(mSliceProvider).onMapIntentToUri(eq(intent));
+ }
+
+ @Test
+ public void testGetDescendants() {
+ Uri uri = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(mContext.getPackageName())
+ .build();
+ Collection<Uri> collection = Arrays.asList(
+ uri,
+ uri.buildUpon().appendPath("1").build(),
+ uri.buildUpon().appendPath("2").build()
+ );
+ when(mSliceProvider.onGetSliceDescendants(any(Uri.class)))
+ .thenReturn(collection);
+
+ Collection<Uri> allUris = mManager.getSliceDescendants(uri);
+
+ assertEquals(allUris, collection);
+ verify(mSliceProvider).onGetSliceDescendants(eq(uri));
}
public static class TestSliceProvider extends SliceProvider {
@@ -176,5 +211,13 @@
sSliceProviderReceiver.onSliceUnpinned(sliceUri);
}
}
+
+ @Override
+ public Collection<Uri> onGetSliceDescendants(Uri uri) {
+ if (sSliceProviderReceiver != null) {
+ return sSliceProviderReceiver.onGetSliceDescendants(uri);
+ }
+ return super.onGetSliceDescendants(uri);
+ }
}
}
diff --git a/slices/view/src/androidTest/java/androidx/slice/SliceMetadataTest.java b/slices/view/src/androidTest/java/androidx/slice/SliceMetadataTest.java
index c416b30..ac3d214 100644
--- a/slices/view/src/androidTest/java/androidx/slice/SliceMetadataTest.java
+++ b/slices/view/src/androidTest/java/androidx/slice/SliceMetadataTest.java
@@ -22,7 +22,8 @@
import static androidx.slice.SliceMetadata.LOADED_ALL;
import static androidx.slice.SliceMetadata.LOADED_NONE;
import static androidx.slice.SliceMetadata.LOADED_PARTIAL;
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.builders.ListBuilder.ICON_IMAGE;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
@@ -30,17 +31,33 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertNull;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.core.util.Pair;
+import androidx.slice.builders.GridRowBuilder;
+import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.SliceAction;
+import androidx.slice.core.SliceActionImpl;
import androidx.slice.core.SliceHints;
+import androidx.slice.render.SliceRenderActivity;
+import androidx.slice.widget.EventInfo;
+import androidx.slice.widget.SliceLiveData;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -53,11 +70,421 @@
private final Context mContext = InstrumentationRegistry.getContext();
+ @Before
+ public void setup() {
+ SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
+ }
+
+ @Test
+ public void testGetSliceActionsNull() {
+ Uri uri = Uri.parse("content://pkg/slice");
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ lb.addRow(new ListBuilder.RowBuilder(lb).setTitle("Text"));
+
+ SliceMetadata sliceMetadata = SliceMetadata.from(mContext, lb.build());
+ assertNull(sliceMetadata.getSliceActions());
+ }
+
+ @Test
+ public void testGetSliceActions() {
+ Uri uri = Uri.parse("content://pkg/slice");
+ PendingIntent pi = getIntent("");
+ Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
+ new Canvas(b).drawColor(0xffff0000);
+ IconCompat icon = IconCompat.createFromIcon(Icon.createWithBitmap(b));
+
+ SliceAction action1 = new SliceAction(pi, icon, "action1");
+ SliceAction action2 = new SliceAction(pi, icon, "action2");
+ SliceAction action3 = new SliceAction(pi, icon, "action3");
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ lb.addRow(new ListBuilder.RowBuilder(lb).setTitle("Text"))
+ .addAction(action1)
+ .addAction(action2)
+ .addAction(action3);
+
+ ArrayList<SliceAction> expectedActions = new ArrayList<>();
+ expectedActions.add(action1);
+ expectedActions.add(action2);
+ expectedActions.add(action3);
+
+ SliceMetadata sliceMetadata = SliceMetadata.from(mContext, lb.build());
+ List<SliceItem> actions = sliceMetadata.getSliceActions();
+
+ for (int i = 0; i < expectedActions.size(); i++) {
+ assertEquivalent(expectedActions.get(i), new SliceActionImpl(actions.get(i)));
+ }
+ }
+
+ @Test
+ public void testGetPrimaryActionForGrid() {
+ Uri uri = Uri.parse("content://pkg/slice");
+ PendingIntent pi = getIntent("");
+ Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
+ new Canvas(b).drawColor(0xffff0000);
+ IconCompat icon = IconCompat.createFromIcon(Icon.createWithBitmap(b));
+
+ SliceAction primaryAction = new SliceAction(pi, icon, "action");
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ GridRowBuilder grb = new GridRowBuilder(lb);
+ grb.setPrimaryAction(primaryAction);
+ grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+ grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+ grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+ lb.addGridRow(grb);
+
+ Slice gridSlice = lb.build();
+ SliceMetadata gridInfo = SliceMetadata.from(mContext, gridSlice);
+ assertEquivalent(primaryAction, gridInfo.getPrimaryAction());
+ }
+
+ @Test
+ public void testGetPrimaryActionForRow() {
+ Uri uri = Uri.parse("content://pkg/slice");
+ PendingIntent pi = getIntent("");
+ Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
+ new Canvas(b).drawColor(0xffff0000);
+ IconCompat icon = IconCompat.createFromIcon(Icon.createWithBitmap(b));
+
+ SliceAction primaryAction = new SliceAction(pi, icon, "action");
+ SliceAction endAction = new SliceAction(pi, "toogle action", false);
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ ListBuilder.RowBuilder rb = new ListBuilder.RowBuilder(lb)
+ .setTitle("a title")
+ .addEndItem(endAction)
+ .setPrimaryAction(primaryAction);
+ lb.addRow(rb);
+
+ Slice rowSlice = lb.build();
+ SliceMetadata rowInfo = SliceMetadata.from(mContext, rowSlice);
+ assertEquivalent(primaryAction, rowInfo.getPrimaryAction());
+ }
+
+ @Test
+ public void testGetPrimaryActionForHeader() {
+ Uri uri = Uri.parse("content://pkg/slice");
+ PendingIntent pi = getIntent("");
+ Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
+ new Canvas(b).drawColor(0xffff0000);
+ IconCompat icon = IconCompat.createFromIcon(Icon.createWithBitmap(b));
+
+ SliceAction primaryAction = new SliceAction(pi, icon, "action");
+ SliceAction sliceAction = new SliceAction(pi, "another action", true);
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ lb.addAction(sliceAction);
+ ListBuilder.HeaderBuilder hb = new ListBuilder.HeaderBuilder(lb);
+ hb.setTitle("header title");
+ hb.setPrimaryAction(primaryAction);
+ lb.setHeader(hb);
+
+ Slice headerSlice = lb.build();
+ SliceMetadata headerInfo = SliceMetadata.from(mContext, headerSlice);
+ assertEquivalent(primaryAction, headerInfo.getPrimaryAction());
+ }
+
+ @Test
+ public void testGetPrimaryActionNull() {
+ Uri uri = Uri.parse("content://pkg/slice");
+ PendingIntent pi = getIntent("");
+ Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
+ new Canvas(b).drawColor(0xffff0000);
+ IconCompat icon = IconCompat.createFromIcon(Icon.createWithBitmap(b));
+
+ SliceAction endAction1 = new SliceAction(pi, icon, "action");
+ SliceAction endAction2 = new SliceAction(pi, "toogle action", false);
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ ListBuilder.RowBuilder rb = new ListBuilder.RowBuilder(lb)
+ .setTitle("a title")
+ .addEndItem(endAction1)
+ .addEndItem(endAction2);
+ lb.addRow(rb);
+
+ Slice rowSlice = lb.build();
+ SliceMetadata rowInfo = SliceMetadata.from(mContext, rowSlice);
+ assertNull(rowInfo.getPrimaryAction());
+ }
+
+ @Test
+ public void testGetHeaderTypeGrid() {
+ Uri uri = Uri.parse("content://pkg/slice");
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ GridRowBuilder grb = new GridRowBuilder(lb);
+ grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+ grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+ grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+ lb.addGridRow(grb);
+
+ Slice gridSlice = lb.build();
+ SliceMetadata gridInfo = SliceMetadata.from(mContext, gridSlice);
+ assertEquals(EventInfo.ROW_TYPE_GRID, gridInfo.getHeaderType());
+ }
+
+ @Test
+ public void testGetHeaderTypeRow() {
+ Uri uri = Uri.parse("content://pkg/slice");
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ lb.addRow(new ListBuilder.RowBuilder(lb).setTitle("a title"));
+
+ Slice rowSlice = lb.build();
+ SliceMetadata rowInfo = SliceMetadata.from(mContext, rowSlice);
+ assertEquals(EventInfo.ROW_TYPE_LIST, rowInfo.getHeaderType());
+ }
+
+ @Test
+ public void testGetHeaderTypeToggle() {
+ Uri uri = Uri.parse("content://pkg/slice");
+ PendingIntent pi = getIntent("");
+
+ SliceAction toggleAction = new SliceAction(pi, "toggle", false /* isChecked */);
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ lb.addRow(new ListBuilder.RowBuilder(lb)
+ .setTitle("another title")
+ .addEndItem(toggleAction));
+
+ Slice toggleSlice = lb.build();
+ SliceMetadata toggleInfo = SliceMetadata.from(mContext, toggleSlice);
+ assertEquals(EventInfo.ROW_TYPE_TOGGLE, toggleInfo.getHeaderType());
+ }
+
+ @Test
+ public void testGetHeaderTypeSlider() {
+ Uri uri = Uri.parse("content://pkg/slice");
+ PendingIntent pi = getIntent("");
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ lb.addInputRange(new ListBuilder.InputRangeBuilder(lb)
+ .setTitle("another title")
+ .setValue(5)
+ .setMax(10)
+ .setAction(pi));
+
+ Slice sliderSlice = lb.build();
+ SliceMetadata sliderInfo = SliceMetadata.from(mContext, sliderSlice);
+ assertEquals(EventInfo.ROW_TYPE_SLIDER, sliderInfo.getHeaderType());
+ }
+
+ @Test
+ public void testGetHeaderTypeProgress() {
+ Uri uri = Uri.parse("content://pkg/slice");
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ lb.addRange(new ListBuilder.RangeBuilder(lb)
+ .setTitle("another title")
+ .setValue(5)
+ .setMax(10));
+
+ Slice sliderSlice = lb.build();
+ SliceMetadata progressInfo = SliceMetadata.from(mContext, sliderSlice);
+ assertEquals(EventInfo.ROW_TYPE_PROGRESS, progressInfo.getHeaderType());
+ }
+
+ @Test
+ public void testGetHeaderTypeHeader() {
+ Uri uri = Uri.parse("content://pkg/slice");
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ ListBuilder.HeaderBuilder hb = new ListBuilder.HeaderBuilder(lb);
+ hb.setTitle("header title");
+ lb.setHeader(hb);
+
+ Slice headerSlice = lb.build();
+ SliceMetadata headerInfo = SliceMetadata.from(mContext, headerSlice);
+ assertEquals(EventInfo.ROW_TYPE_LIST, headerInfo.getHeaderType());
+ }
+
+ @Test
+ public void testHasLargeModeFullGrid() {
+ // If a grid is "full" with two text and an image there is a different small / large state.
+ Uri uri = Uri.parse("content://pkg/slice");
+ Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
+ new Canvas(b).drawColor(0xffff0000);
+ Icon icon = Icon.createWithBitmap(b);
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ GridRowBuilder grb = new GridRowBuilder(lb);
+ grb.addCell(new GridRowBuilder.CellBuilder(grb)
+ .addText("some text")
+ .addText("more text")
+ .addImage(IconCompat.createFromIcon(icon), ICON_IMAGE));
+ grb.addCell(new GridRowBuilder.CellBuilder(grb)
+ .addText("some text")
+ .addText("more text")
+ .addImage(IconCompat.createFromIcon(icon), ICON_IMAGE));
+ grb.addCell(new GridRowBuilder.CellBuilder(grb)
+ .addText("some text")
+ .addText("more text")
+ .addImage(IconCompat.createFromIcon(icon), ICON_IMAGE));
+ lb.addGridRow(grb);
+
+ Slice gridSlice = lb.build();
+ SliceMetadata gridInfo = SliceMetadata.from(mContext, gridSlice);
+ assertTrue(gridInfo.hasLargeMode());
+ }
+
+ @Test
+ public void testHasLargeModeGrid() {
+ // If a grid is not "full" either no image or an image and one text item, small / large
+ // is the same.
+ Uri uri = Uri.parse("content://pkg/slice");
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ GridRowBuilder grb = new GridRowBuilder(lb);
+ grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+ grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+ grb.addCell(new GridRowBuilder.CellBuilder(grb).addText("some text"));
+ lb.addGridRow(grb);
+
+ Slice gridSlice = lb.build();
+ SliceMetadata gridInfo = SliceMetadata.from(mContext, gridSlice);
+ assertTrue(!gridInfo.hasLargeMode());
+ }
+
+ @Test
+ public void testGetLargeModeMultipleRows() {
+ Uri uri = Uri.parse("content://pkg/slice");
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ lb.addRow(new ListBuilder.RowBuilder(lb).setTitle("a title"));
+ lb.addRow(new ListBuilder.RowBuilder(lb).setTitle("another row another title"));
+
+ Slice rowSlice = lb.build();
+ SliceMetadata rowInfo = SliceMetadata.from(mContext, rowSlice);
+ assertTrue(rowInfo.hasLargeMode());
+ }
+
+ @Test
+ public void testGetLargeModeSingleRow() {
+ Uri uri = Uri.parse("content://pkg/slice");
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ lb.addRow(new ListBuilder.RowBuilder(lb).setTitle("a title"));
+
+ Slice rowSlice = lb.build();
+ SliceMetadata rowInfo = SliceMetadata.from(mContext, rowSlice);
+ assertTrue(!rowInfo.hasLargeMode());
+ }
+
+ @Test
+ public void testGetTogglesNone() {
+ Uri uri = Uri.parse("content://pkg/slice");
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ lb.addRow(new ListBuilder.RowBuilder(lb).setTitle("a title"));
+
+ Slice rowSlice = lb.build();
+ SliceMetadata rowInfo = SliceMetadata.from(mContext, rowSlice);
+ assertTrue(rowInfo.getToggles().isEmpty());
+ }
+
+ @Test
+ public void testGetTogglesSingle() {
+ Uri uri = Uri.parse("content://pkg/slice");
+ PendingIntent pi = getIntent("");
+
+ SliceAction toggleAction = new SliceAction(pi, "toggle", false /* isChecked */);
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ lb.addRow(new ListBuilder.RowBuilder(lb)
+ .setTitle("another title")
+ .addEndItem(toggleAction));
+
+ Slice toggleSlice = lb.build();
+ SliceMetadata toggleInfo = SliceMetadata.from(mContext, toggleSlice);
+ List<androidx.slice.core.SliceAction> actualToggles = toggleInfo.getToggles();
+ assertEquals(1, actualToggles.size());
+ assertEquivalent(toggleAction, actualToggles.get(0));
+ }
+
+ @Test
+ public void testGetTogglesMultiple() {
+ Uri uri = Uri.parse("content://pkg/slice");
+ PendingIntent pi = getIntent("");
+ Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
+ new Canvas(b).drawColor(0xffff0000);
+ IconCompat icon = IconCompat.createFromIcon(Icon.createWithBitmap(b));
+
+ SliceAction toggleAction = new SliceAction(pi, icon, "toggle", false /* isChecked */);
+ SliceAction toggleAction2 = new SliceAction(pi, icon, "toggle2", true /* isChecked */);
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ lb.addRow(new ListBuilder.RowBuilder(lb)
+ .setTitle("another title")
+ .addEndItem(toggleAction)
+ .addEndItem(toggleAction2));
+
+ Slice toggleSlice = lb.build();
+ SliceMetadata toggleInfo = SliceMetadata.from(mContext, toggleSlice);
+ List<androidx.slice.core.SliceAction> actualToggles = toggleInfo.getToggles();
+
+ List<SliceAction> expectedToggles = new ArrayList<>();
+ expectedToggles.add(toggleAction);
+ expectedToggles.add(toggleAction2);
+
+ for (int i = 0; i < expectedToggles.size(); i++) {
+ assertEquivalent(expectedToggles.get(i), actualToggles.get(i));
+ }
+ }
+
+ @Test
+ public void testGetRangeNull() {
+ Uri uri = Uri.parse("content://pkg/slice");
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ lb.addRow(new ListBuilder.RowBuilder(lb).setTitle("a title"));
+
+ Slice rowSlice = lb.build();
+ SliceMetadata rowInfo = SliceMetadata.from(mContext, rowSlice);
+ assertNull(rowInfo.getRange());
+ }
+
+ @Test
+ public void testGetRangeForSlider() {
+ Uri uri = Uri.parse("content://pkg/slice");
+ PendingIntent pi = getIntent("");
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ lb.addInputRange(new ListBuilder.InputRangeBuilder(lb)
+ .setTitle("another title")
+ .setValue(5)
+ .setMax(10)
+ .setAction(pi));
+
+ Slice sliderSlice = lb.build();
+ SliceMetadata sliderInfo = SliceMetadata.from(mContext, sliderSlice);
+ Pair<Integer, Integer> values = sliderInfo.getRange();
+ assertEquals(5, (int) values.first);
+ assertEquals(10, (int) values.second);
+ }
+
+ @Test
+ public void testGetRangeForProgress() {
+ Uri uri = Uri.parse("content://pkg/slice");
+
+ ListBuilder lb = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
+ lb.addRange(new ListBuilder.RangeBuilder(lb)
+ .setTitle("another title")
+ .setValue(5)
+ .setMax(10));
+
+ Slice sliderSlice = lb.build();
+ SliceMetadata progressInfo = SliceMetadata.from(mContext, sliderSlice);
+ Pair<Integer, Integer> values = progressInfo.getRange();
+ assertEquals(5, (int) values.first);
+ assertEquals(10, (int) values.second);
+
+ }
+
@Test
public void testKeywords() {
Uri uri = Uri.parse("content://pkg/slice");
Slice keywordSlice = new Slice.Builder(uri)
- .addHints(HINT_KEY_WORDS)
+ .addHints(HINT_KEYWORDS)
.addText("keyword1", null)
.addText("keyword2", null)
.addText("keyword3", null).build();
@@ -67,8 +494,8 @@
.addSubSlice(keywordSlice)
.build();
- SliceMetadata sliceInfo1 = SliceMetadata.from(mContext, slice);
- List<String> sliceKeywords = sliceInfo1.getSliceKeywords();
+ SliceMetadata SliceMetadata1 = SliceMetadata.from(mContext, slice);
+ List<String> sliceKeywords = SliceMetadata1.getSliceKeywords();
String[] expectedList = new String[] {"keyword1", "keyword2", "keyword3"};
assertArrayEquals(expectedList, sliceKeywords.toArray());
@@ -77,12 +504,12 @@
.addText("Some text", null, HINT_TITLE)
.addText("Some other text", null).build();
- SliceMetadata sliceInfo2 = SliceMetadata.from(mContext, slice2);
- List<String> slice2Keywords = sliceInfo2.getSliceKeywords();
+ SliceMetadata SliceMetadata2 = SliceMetadata.from(mContext, slice2);
+ List<String> slice2Keywords = SliceMetadata2.getSliceKeywords();
assertNull(slice2Keywords);
// Make sure empty list if specified to have no keywords
- Slice noKeywordSlice = new Slice.Builder(uri).addHints(HINT_KEY_WORDS).build();
+ Slice noKeywordSlice = new Slice.Builder(uri).addHints(HINT_KEYWORDS).build();
Slice slice3 = new Slice.Builder(uri)
.addText("Some text", null, HINT_TITLE)
.addSubSlice(noKeywordSlice)
@@ -97,18 +524,18 @@
public void testGetLoadingState() {
Uri uri = Uri.parse("content://pkg/slice");
Slice s1 = new Slice.Builder(uri).build();
- SliceMetadata sliceInfo1 = SliceMetadata.from(mContext, s1);
- int actualState1 = sliceInfo1.getLoadingState();
+ SliceMetadata SliceMetadata1 = SliceMetadata.from(mContext, s1);
+ int actualState1 = SliceMetadata1.getLoadingState();
assertEquals(LOADED_NONE, actualState1);
Slice s2 = new Slice.Builder(uri).addText(null, null, HINT_PARTIAL).build();
- SliceMetadata sliceInfo2 = SliceMetadata.from(mContext, s2);
- int actualState2 = sliceInfo2.getLoadingState();
+ SliceMetadata SliceMetadata2 = SliceMetadata.from(mContext, s2);
+ int actualState2 = SliceMetadata2.getLoadingState();
assertEquals(LOADED_PARTIAL, actualState2);
Slice s3 = new Slice.Builder(uri).addText("Text", null).build();
- SliceMetadata sliceInfo3 = SliceMetadata.from(mContext, s3);
- int actualState3 = sliceInfo3.getLoadingState();
+ SliceMetadata SliceMetadata3 = SliceMetadata.from(mContext, s3);
+ int actualState3 = SliceMetadata3.getLoadingState();
assertEquals(LOADED_ALL, actualState3);
}
@@ -160,4 +587,32 @@
long retrievedLastUpdated2 = si2.getLastUpdatedTime();
assertEquals(0, retrievedLastUpdated2);
}
+
+ @Test
+ public void testIsPermissionSlice() {
+ Uri uri = Uri.parse("content://pkg/slice");
+ Slice permissionSlice =
+ SliceProvider.createPermissionSlice(mContext, uri, mContext.getPackageName());
+
+ SliceMetadata metadata = SliceMetadata.from(mContext, permissionSlice);
+ assertEquals(true, metadata.isPermissionSlice());
+ }
+
+ private PendingIntent getIntent(String action) {
+ Intent intent = new Intent(action);
+ intent.setClassName(mContext.getPackageName(), SliceRenderActivity.class.getName());
+ return PendingIntent.getActivity(mContext, 0, intent, 0);
+ }
+
+ private void assertEquivalent(androidx.slice.core.SliceAction desired,
+ androidx.slice.core.SliceAction actual) {
+ assertEquals(desired.getTitle(), actual.getTitle());
+ assertEquals(desired.getContentDescription(), actual.getContentDescription());
+ assertEquals(desired.isToggle(), actual.isToggle());
+ assertEquals(desired.isDefaultToggle(), actual.isDefaultToggle());
+ assertEquals(desired.isChecked(), actual.isChecked());
+ assertEquals(desired.getPriority(), actual.getPriority());
+ assertEquals(desired.getIcon() == null, actual.getIcon() == null);
+ assertEquals(desired.getImageMode(), actual.getImageMode());
+ }
}
diff --git a/slices/view/src/androidTest/java/androidx/slice/SliceXmlTest.java b/slices/view/src/androidTest/java/androidx/slice/SliceXmlTest.java
index 298d498..d1a647c 100644
--- a/slices/view/src/androidTest/java/androidx/slice/SliceXmlTest.java
+++ b/slices/view/src/androidTest/java/androidx/slice/SliceXmlTest.java
@@ -24,6 +24,11 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.PendingIntent;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -33,6 +38,8 @@
import android.support.test.runner.AndroidJUnit4;
import androidx.core.graphics.drawable.IconCompat;
+import androidx.slice.core.SliceQuery;
+import androidx.slice.view.R;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -51,7 +58,7 @@
public void testThrowForAction() throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Slice s = new Slice.Builder(Uri.parse("content://pkg/slice"))
- .addAction(null, null, null)
+ .addAction((PendingIntent) null, null, null)
.build();
SliceUtils.serializeSlice(s, mContext, outputStream, "UTF-8", new SliceUtils
.SerializeOptions());
@@ -81,7 +88,7 @@
public void testNoThrowForAction() throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Slice s = new Slice.Builder(Uri.parse("content://pkg/slice"))
- .addAction(null, null, null)
+ .addAction((PendingIntent) null, null, null)
.build();
SliceUtils.serializeSlice(s, mContext, outputStream, "UTF-8", new SliceUtils
.SerializeOptions().setActionMode(SliceUtils.SerializeOptions.MODE_REMOVE));
@@ -108,7 +115,7 @@
}
@Test
- public void testSerialization() throws IOException {
+ public void testSerialization() throws Exception {
Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888);
new Canvas(b).drawColor(0xffff0000);
// Create a slice containing all the types in a hierarchy.
@@ -118,24 +125,32 @@
.build())
.addIcon(IconCompat.createWithBitmap(b), null)
.addText("Some text", null)
- .addAction(null, new Slice.Builder(Uri.parse("content://pkg/slice/sub"))
+ .addAction((PendingIntent) null,
+ new Slice.Builder(Uri.parse("content://pkg/slice/action"))
.addText("Action text", null)
.build(), null)
.addInt(0xff00ff00, "subtype")
+ .addIcon(IconCompat.createWithResource(mContext, R.drawable.abc_slice_see_more_bg),
+ null)
.addHints("Hint 1", "Hint 2")
.build();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
SliceUtils.serializeSlice(before, mContext, outputStream, "UTF-8",
new SliceUtils.SerializeOptions()
- .setImageMode(SliceUtils.SerializeOptions.MODE_DISABLE)
- .setActionMode(SliceUtils.SerializeOptions.MODE_DISABLE));
+ .setImageMode(SliceUtils.SerializeOptions.MODE_CONVERT)
+ .setActionMode(SliceUtils.SerializeOptions.MODE_CONVERT));
byte[] bytes = outputStream.toByteArray();
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
- Slice after = SliceUtils.parseSlice(inputStream, "UTF-8");
+ SliceUtils.SliceActionListener listener = mock(SliceUtils.SliceActionListener.class);
+ Slice after = SliceUtils.parseSlice(mContext, inputStream, "UTF-8", listener);
assertEquivalent(before, after);
+
+ SliceItem action = SliceQuery.find(after, FORMAT_ACTION);
+ action.fireAction(null, null);
+ verify(listener).onSliceAction(eq(Uri.parse("content://pkg/slice/action")));
}
private void assertEquivalent(Slice desired, Slice actual) {
diff --git a/slices/view/src/androidTest/java/androidx/slice/render/SliceCreator.java b/slices/view/src/androidTest/java/androidx/slice/render/SliceCreator.java
index cf524fa..0100d84 100644
--- a/slices/view/src/androidTest/java/androidx/slice/render/SliceCreator.java
+++ b/slices/view/src/androidTest/java/androidx/slice/render/SliceCreator.java
@@ -36,6 +36,7 @@
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
+import androidx.slice.SliceProvider;
import androidx.slice.builders.GridRowBuilder;
import androidx.slice.builders.ListBuilder;
import androidx.slice.builders.MessagingSliceBuilder;
@@ -58,7 +59,7 @@
public static final String[] URI_PATHS = {"message", "wifi", "wifi2", "note", "ride",
"ride-ttl", "toggle", "toggle2", "contact", "gallery", "subscription", "subscription2",
- "weather", "reservation"};
+ "weather", "reservation", "inputrange", "range", "permission"};
private final Context mContext;
@@ -112,6 +113,12 @@
return createWeather(sliceUri);
case "/reservation":
return createReservationSlice(sliceUri);
+ case "/inputrange":
+ return createStarRatingInputRange(sliceUri);
+ case "/range":
+ return createDownloadProgressRange(sliceUri);
+ case "/permission":
+ return createPermissionSlice(sliceUri);
}
throw new IllegalArgumentException("Unknown uri " + sliceUri);
}
@@ -156,7 +163,7 @@
ListBuilder b = new ListBuilder(getContext(), sliceUri, INFINITY);
GridRowBuilder gb = new GridRowBuilder(b);
PendingIntent pi = getBroadcastIntent(ACTION_TOAST, "see more of your gallery");
- gb.addSeeMoreAction(pi);
+ gb.setSeeMoreAction(pi);
gb.addCell(new GridRowBuilder.CellBuilder(gb)
.addImage(IconCompat.createWithResource(getContext(), R.drawable.slices_1),
LARGE_IMAGE))
@@ -191,9 +198,9 @@
ICON_IMAGE);
cb.setContentIntent(pi);
cb.addText("All cats");
- gb.addSeeMoreCell(cb);
+ gb.setSeeMoreCell(cb);
} else {
- gb.addSeeMoreAction(pi);
+ gb.setSeeMoreAction(pi);
}
gb.addCell(new GridRowBuilder.CellBuilder(gb)
.addImage(IconCompat.createWithResource(getContext(), R.drawable.cat_1),
@@ -417,7 +424,8 @@
.setTitle("Network" + networkName);
boolean locked = i % 3 == 0;
if (locked) {
- rb.addEndItem(IconCompat.createWithResource(getContext(), R.drawable.ic_lock));
+ rb.addEndItem(IconCompat.createWithResource(getContext(), R.drawable.ic_lock),
+ ICON_IMAGE);
}
String message = locked ? "Open wifi password dialog" : "Connect to " + networkName;
rb.setPrimaryAction(new SliceAction(getBroadcastIntent(ACTION_TOAST, message), icon,
@@ -425,13 +433,14 @@
lb.addRow(rb);
}
if (customSeeMore) {
- lb.addSeeMoreRow(new ListBuilder.RowBuilder(lb)
+ lb.setSeeMoreRow(new ListBuilder.RowBuilder(lb)
.setTitle("See all available networks")
.addEndItem(
- IconCompat.createWithResource(getContext(), R.drawable.ic_right_caret))
+ IconCompat.createWithResource(getContext(), R.drawable.ic_right_caret),
+ ICON_IMAGE)
.setPrimaryAction(primaryAction));
} else {
- lb.addSeeMoreAction(primaryAction.getAction());
+ lb.setSeeMoreAction(primaryAction.getAction());
}
return lb.build();
}
@@ -466,6 +475,47 @@
.build();
}
+ private Slice createStarRatingInputRange(Uri sliceUri) {
+ IconCompat icon = IconCompat.createWithResource(getContext(), R.drawable.ic_star_on);
+ SliceAction primaryAction =
+ new SliceAction(getBroadcastIntent(ACTION_TOAST, "open star rating"), icon,
+ "Rate");
+ ListBuilder lb = new ListBuilder(getContext(), sliceUri, INFINITY);
+ return lb.setColor(0xffff4081)
+ .addInputRange(new ListBuilder.InputRangeBuilder(lb)
+ .setTitle("Star rating")
+ .setSubtitle("Pick a rating from 0 to 5")
+ .setThumb(icon)
+ .setInputAction(getBroadcastIntent(ACTION_TOAST, "range changed"))
+ .setMax(5)
+ .setValue(3)
+ .setPrimaryAction(primaryAction)
+ .setContentDescription("Slider for star ratings"))
+ .build();
+ }
+
+ private Slice createDownloadProgressRange(Uri sliceUri) {
+ IconCompat icon = IconCompat.createWithResource(getContext(), R.drawable.ic_star_on);
+ SliceAction primaryAction =
+ new SliceAction(
+ getBroadcastIntent(ACTION_TOAST, "open download"), icon,
+ "Download");
+ ListBuilder lb = new ListBuilder(getContext(), sliceUri, INFINITY);
+ return lb.setColor(0xffff4081)
+ .addRange(new ListBuilder.RangeBuilder(lb)
+ .setTitle("Download progress")
+ .setSubtitle("Download is happening")
+ .setMax(100)
+ .setValue(75)
+ .setPrimaryAction(primaryAction))
+ .build();
+ }
+
+ private Slice createPermissionSlice(Uri uri) {
+ return SliceProvider.createPermissionSlice(getContext(), uri,
+ getContext().getPackageName());
+ }
+
private PendingIntent getIntent(String action) {
Intent intent = new Intent(action);
intent.setClassName(getContext().getPackageName(), SliceRenderActivity.class.getName());
diff --git a/slices/view/src/androidTest/java/androidx/slice/render/SliceRenderer.java b/slices/view/src/androidTest/java/androidx/slice/render/SliceRenderer.java
index 5625c34..0734edf 100644
--- a/slices/view/src/androidTest/java/androidx/slice/render/SliceRenderer.java
+++ b/slices/view/src/androidTest/java/androidx/slice/render/SliceRenderer.java
@@ -22,7 +22,7 @@
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.os.AsyncTask;
+import android.net.Uri;
import android.os.Handler;
import android.util.Log;
import android.util.TypedValue;
@@ -30,24 +30,33 @@
import android.view.View;
import android.view.ViewGroup;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.util.concurrent.CountDownLatch;
-
import androidx.recyclerview.widget.RecyclerView;
import androidx.slice.Slice;
import androidx.slice.SliceProvider;
+import androidx.slice.SliceUtils;
import androidx.slice.view.test.R;
import androidx.slice.widget.SliceLiveData;
import androidx.slice.widget.SliceView;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
public class SliceRenderer {
private static final String TAG = "SliceRenderer";
public static final String SCREENSHOT_DIR = "slice-screenshots";
+
+ private static final int MAX_CONCURRENT = 5;
+
private static File sScreenshotDirectory;
+ private final Object mRenderLock = new Object();
+
private final Activity mContext;
private final View mLayout;
private final SliceView mSV1;
@@ -121,20 +130,46 @@
private void doRender() {
- File output = getScreenshotDirectory();
+ final File output = getScreenshotDirectory();
if (!output.exists()) {
output.mkdir();
}
- mDoneLatch = new CountDownLatch(SliceCreator.URI_PATHS.length);
- for (String slice : SliceCreator.URI_PATHS) {
- doRender(slice, new File(output, String.format("%s.png", slice)),
- true /* scrollable */);
+ mDoneLatch = new CountDownLatch(SliceCreator.URI_PATHS.length * 2 + 2);
+
+ ExecutorService executor = Executors.newFixedThreadPool(5);
+ for (final String slice : SliceCreator.URI_PATHS) {
+ final Slice s = mSliceCreator.onBindSlice(SliceCreator.getUri(slice, mContext));
+
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ doRender(slice, s, new File(output, String.format("%s.png", slice)),
+ true /* scrollable */);
+ }
+ });
+ final Slice serialized = serAndUnSer(s);
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ doRender(slice + "-ser", serialized, new File(output, String.format(
+ "%s-serialized.png", slice)), true /* scrollable */);
+ }
+ });
if (slice.equals("wifi") || slice.equals("wifi2")) {
// Test scrolling
- doRender(slice, new File(output, String.format("%s-no-scroll.png", slice)),
- false /* scrollable */);
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ doRender(slice + "-ns", s, new File(output, String.format(
+ "%s-no-scroll.png", slice)), false /* scrollable */);
+ }
+ });
}
}
+ try {
+ mDoneLatch.await();
+ } catch (InterruptedException e) {
+ }
Log.d(TAG, "Wrote render to " + output.getAbsolutePath());
mContext.runOnUiThread(new Runnable() {
@Override
@@ -142,71 +177,83 @@
((ViewGroup) mParent.getParent()).removeView(mParent);
}
});
+ }
+
+ private Slice serAndUnSer(Slice s) {
try {
- mDoneLatch.await();
- } catch (InterruptedException e) {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ SliceUtils.serializeSlice(s, mContext, outputStream, "UTF-8",
+ new SliceUtils.SerializeOptions()
+ .setImageMode(SliceUtils.SerializeOptions.MODE_CONVERT)
+ .setActionMode(SliceUtils.SerializeOptions.MODE_CONVERT));
+
+ byte[] bytes = outputStream.toByteArray();
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
+ return SliceUtils.parseSlice(mContext, inputStream, "UTF-8",
+ new SliceUtils.SliceActionListener() {
+ @Override
+ public void onSliceAction(Uri actionUri) {
+ }
+ });
+ } catch (Exception e) {
+ throw new RuntimeException(e);
}
}
- private void doRender(final String slice, final File file, final boolean scrollable) {
+ private void doRender(final String slice, final Slice s, final File file,
+ final boolean scrollable) {
Log.d(TAG, "Rendering " + slice + " to " + file.getAbsolutePath());
- final Slice s = mSliceCreator.onBindSlice(SliceCreator.getUri(slice, mContext));
-
- final CountDownLatch l = new CountDownLatch(1);
- mContext.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mSV1.setSlice(s);
- mSV2.setSlice(s);
- mSV3.setSlice(s);
- mSV3.setScrollable(scrollable);
- mSV1.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ try {
+ final CountDownLatch l = new CountDownLatch(1);
+ final Bitmap[] b = new Bitmap[1];
+ synchronized (mRenderLock) {
+ mContext.runOnUiThread(new Runnable() {
@Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- mSV1.removeOnLayoutChangeListener(this);
- mSV1.postDelayed(new Runnable() {
+ public void run() {
+ mSV1.setSlice(s);
+ mSV2.setSlice(s);
+ mSV3.setSlice(s);
+ mSV3.setScrollable(scrollable);
+ mSV1.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
- public void run() {
- Log.d(TAG, "Drawing " + slice);
- Bitmap b = Bitmap.createBitmap(mLayout.getMeasuredWidth(),
- mLayout.getMeasuredHeight(),
- Bitmap.Config.ARGB_8888);
+ public void onLayoutChange(View v, int left, int top, int right,
+ int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ mSV1.removeOnLayoutChangeListener(this);
+ mSV1.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ Log.d(TAG, "Drawing " + slice);
+ b[0] = Bitmap.createBitmap(mLayout.getMeasuredWidth(),
+ mLayout.getMeasuredHeight(),
+ Bitmap.Config.ARGB_8888);
- mLayout.draw(new Canvas(b));
- try {
- doCompress(slice, b, new FileOutputStream(file));
- } catch (FileNotFoundException e) {
- throw new RuntimeException(e);
- }
- l.countDown();
+ mLayout.draw(new Canvas(b[0]));
+ l.countDown();
+ }
+ }, 10);
}
- }, 10);
+ });
}
});
+ l.await();
}
- });
- try {
- l.await();
- } catch (InterruptedException e) {
+ doCompress(slice, b[0], new FileOutputStream(file));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
}
}
private void doCompress(final String slice, final Bitmap b, final FileOutputStream s) {
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- Log.d(TAG, "Compressing " + slice);
- if (!b.compress(Bitmap.CompressFormat.PNG, 100, s)) {
- throw new RuntimeException("Unable to compress");
- }
+ Log.d(TAG, "Compressing " + slice);
+ if (!b.compress(Bitmap.CompressFormat.PNG, 100, s)) {
+ throw new RuntimeException("Unable to compress");
+ }
- b.recycle();
- Log.d(TAG, "Done " + slice);
- mDoneLatch.countDown();
- }
- });
+ b.recycle();
+ mDoneLatch.countDown();
+ Log.d(TAG, "Done " + slice);
}
public void renderAll(final Runnable runnable) {
diff --git a/slices/view/src/main/java/androidx/slice/SliceManager.java b/slices/view/src/main/java/androidx/slice/SliceManager.java
index a97acd0..63c56e8 100644
--- a/slices/view/src/main/java/androidx/slice/SliceManager.java
+++ b/slices/view/src/main/java/androidx/slice/SliceManager.java
@@ -18,13 +18,16 @@
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.core.content.PermissionChecker;
import androidx.core.os.BuildCompat;
+import java.util.Collection;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -164,6 +167,62 @@
public abstract @Nullable Uri mapIntentToUri(@NonNull Intent intent);
/**
+ * Determine whether a particular process and user ID has been granted
+ * permission to access a specific slice URI.
+ *
+ * @param uri The uri that is being checked.
+ * @param pid The process ID being checked against. Must be > 0.
+ * @param uid The user ID being checked against. A uid of 0 is the root
+ * user, which will pass every permission check.
+ *
+ * @return {@link PackageManager#PERMISSION_GRANTED} if the given
+ * pid/uid is allowed to access that uri, or
+ * {@link PackageManager#PERMISSION_DENIED} if it is not.
+ *
+ * @see #grantSlicePermission(String, Uri)
+ */
+ @PermissionChecker.PermissionResult
+ public abstract int checkSlicePermission(@NonNull Uri uri, int pid, int uid);
+
+ /**
+ * Grant permission to access a specific slice Uri to another package.
+ *
+ * @param toPackage The package you would like to allow to access the Uri.
+ * @param uri The Uri you would like to grant access to.
+ *
+ * @see #revokeSlicePermission
+ */
+ public abstract void grantSlicePermission(@NonNull String toPackage, @NonNull Uri uri);
+
+ /**
+ * Remove permissions to access a particular content provider Uri
+ * that were previously added with {@link #grantSlicePermission} for a specific target
+ * package. The given Uri will match all previously granted Uris that are the same or a
+ * sub-path of the given Uri. That is, revoking "content://foo/target" will
+ * revoke both "content://foo/target" and "content://foo/target/sub", but not
+ * "content://foo". It will not remove any prefix grants that exist at a
+ * higher level.
+ *
+ * @param toPackage The package you would like to allow to access the Uri.
+ * @param uri The Uri you would like to revoke access to.
+ *
+ * @see #grantSlicePermission
+ */
+ public abstract void revokeSlicePermission(@NonNull String toPackage, @NonNull Uri uri);
+
+ /**
+ * Obtains a list of slices that are descendants of the specified Uri.
+ * <p>
+ * Not all slice providers will implement this functionality, in which case,
+ * an empty collection will be returned.
+ *
+ * @param uri The uri to look for descendants under.
+ * @return All slices within the space.
+ * @see SliceProvider#onGetSliceDescendants(Uri)
+ */
+ public abstract @NonNull Collection<Uri> getSliceDescendants(@NonNull Uri uri);
+
+ /**
* Class that listens to changes in {@link Slice}s.
*/
public interface SliceCallback {
diff --git a/slices/view/src/main/java/androidx/slice/SliceManagerBase.java b/slices/view/src/main/java/androidx/slice/SliceManagerBase.java
index e68223b..fb9fccd 100644
--- a/slices/view/src/main/java/androidx/slice/SliceManagerBase.java
+++ b/slices/view/src/main/java/androidx/slice/SliceManagerBase.java
@@ -19,9 +19,11 @@
import static androidx.slice.widget.SliceLiveData.SUPPORTED_SPECS;
import android.content.Context;
+import android.content.Intent;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.ArrayMap;
@@ -29,6 +31,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import androidx.core.content.PermissionChecker;
import java.util.concurrent.Executor;
@@ -68,6 +71,32 @@
if (impl != null) impl.stopListening();
}
+ @Override
+ @PermissionChecker.PermissionResult
+ public int checkSlicePermission(@NonNull Uri uri, int pid, int uid) {
+ // TODO: Switch off Uri permissions.
+ return mContext.checkUriPermission(uri, pid, uid,
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ }
+
+ @Override
+ public void grantSlicePermission(@NonNull String toPackage, @NonNull Uri uri) {
+ // TODO: Switch off Uri permissions.
+ mContext.grantUriPermission(toPackage, uri,
+ Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+ }
+
+ @Override
+ public void revokeSlicePermission(@NonNull String toPackage, @NonNull Uri uri) {
+ // TODO: Switch off Uri permissions.
+ if (Build.VERSION.SDK_INT >= 26) {
+ mContext.revokeUriPermission(toPackage, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ }
+ }
+
+
private SliceListenerImpl getListener(Uri uri, SliceCallback callback,
SliceListenerImpl listener) {
Pair<Uri, SliceCallback> key = new Pair<>(uri, callback);
diff --git a/slices/view/src/main/java/androidx/slice/SliceManagerCompat.java b/slices/view/src/main/java/androidx/slice/SliceManagerCompat.java
index 8abacd1..1badbb4 100644
--- a/slices/view/src/main/java/androidx/slice/SliceManagerCompat.java
+++ b/slices/view/src/main/java/androidx/slice/SliceManagerCompat.java
@@ -28,6 +28,7 @@
import androidx.slice.compat.SliceProviderCompat;
import androidx.slice.widget.SliceLiveData;
+import java.util.Collection;
import java.util.Set;
@@ -73,4 +74,9 @@
public Uri mapIntentToUri(@NonNull Intent intent) {
return SliceProviderCompat.mapIntentToUri(mContext, intent);
}
+
+ @Override
+ public Collection<Uri> getSliceDescendants(Uri uri) {
+ return SliceProviderCompat.getSliceDescendants(mContext, uri);
+ }
}
diff --git a/slices/view/src/main/java/androidx/slice/SliceManagerWrapper.java b/slices/view/src/main/java/androidx/slice/SliceManagerWrapper.java
index af072e6..e0c0342 100644
--- a/slices/view/src/main/java/androidx/slice/SliceManagerWrapper.java
+++ b/slices/view/src/main/java/androidx/slice/SliceManagerWrapper.java
@@ -30,6 +30,7 @@
import androidx.annotation.RestrictTo;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.Set;
@@ -71,22 +72,23 @@
@Nullable
@Override
public androidx.slice.Slice bindSlice(@NonNull Uri uri) {
- return SliceConvert.wrap(android.app.slice.Slice.bindSlice(
- mContext.getContentResolver(), uri, mSpecs));
+ return SliceConvert.wrap(mManager.bindSlice(uri, mSpecs));
}
@Nullable
@Override
public androidx.slice.Slice bindSlice(@NonNull Intent intent) {
- return SliceConvert.wrap(android.app.slice.Slice.bindSlice(
- mContext, intent, mSpecs));
+ return SliceConvert.wrap(mManager.bindSlice(intent, mSpecs));
+ }
+
+ @Override
+ public Collection<Uri> getSliceDescendants(Uri uri) {
+ return mManager.getSliceDescendants(uri);
}
@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/SliceMetadata.java b/slices/view/src/main/java/androidx/slice/SliceMetadata.java
index bf96854..d8258bf 100644
--- a/slices/view/src/main/java/androidx/slice/SliceMetadata.java
+++ b/slices/view/src/main/java/androidx/slice/SliceMetadata.java
@@ -17,15 +17,22 @@
package androidx.slice;
import static android.app.slice.Slice.HINT_ACTIONS;
+import static android.app.slice.Slice.HINT_HORIZONTAL;
import static android.app.slice.Slice.HINT_PARTIAL;
import static android.app.slice.Slice.HINT_SHORTCUT;
+import static android.app.slice.SliceItem.FORMAT_INT;
import static android.app.slice.SliceItem.FORMAT_SLICE;
import static android.app.slice.SliceItem.FORMAT_TEXT;
import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
import static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
+import static androidx.slice.core.SliceHints.HINT_PERMISSION_REQUEST;
import static androidx.slice.core.SliceHints.HINT_TTL;
+import static androidx.slice.core.SliceHints.SUBTYPE_MAX;
+import static androidx.slice.core.SliceHints.SUBTYPE_VALUE;
+import static androidx.slice.widget.EventInfo.ROW_TYPE_PROGRESS;
+import static androidx.slice.widget.EventInfo.ROW_TYPE_SLIDER;
import android.content.Context;
import android.text.TextUtils;
@@ -34,8 +41,18 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.core.util.Pair;
+import androidx.slice.core.SliceAction;
+import androidx.slice.core.SliceActionImpl;
import androidx.slice.core.SliceQuery;
+import androidx.slice.widget.EventInfo;
+import androidx.slice.widget.GridContent;
+import androidx.slice.widget.ListContent;
+import androidx.slice.widget.RowContent;
+import androidx.slice.widget.SliceView;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -51,6 +68,7 @@
@IntDef({
LOADED_NONE, LOADED_PARTIAL, LOADED_ALL
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface SliceLoadingState{}
/**
@@ -70,7 +88,11 @@
private Context mContext;
private long mExpiry;
private long mLastUpdated;
+ private ListContent mListContent;
+ private SliceItem mHeaderItem;
+ private SliceActionImpl mPrimaryAction;
private List<SliceItem> mSliceActions;
+ private @EventInfo.SliceRowType int mTemplateType;
/**
* Create a SliceMetadata object to provide access to some information around the slice and
@@ -104,6 +126,15 @@
mLastUpdated = updatedItem.getTimestamp();
}
mSliceActions = getSliceActions(mSlice);
+
+ mListContent = new ListContent(context, slice);
+ mHeaderItem = mListContent.getHeaderItem();
+ mTemplateType = mListContent.getHeaderTemplateType();
+
+ SliceItem action = mListContent.getPrimaryAction();
+ if (action != null) {
+ mPrimaryAction = new SliceActionImpl(action);
+ }
}
/**
@@ -115,12 +146,84 @@
}
/**
+ * @return the primary action for this slice, null if none specified.
+ */
+ @Nullable
+ public SliceAction getPrimaryAction() {
+ return mPrimaryAction;
+ }
+
+ /**
+ * @return the type of row that is used for the header of this slice, -1 if unknown.
+ */
+ public @EventInfo.SliceRowType int getHeaderType() {
+ return mTemplateType;
+ }
+
+ /**
+ * @return whether this slice has content to show when presented
+ * in {@link SliceView#MODE_LARGE}.
+ */
+ public boolean hasLargeMode() {
+ boolean isHeaderFullGrid = false;
+ if (mHeaderItem != null && mHeaderItem.hasHint(HINT_HORIZONTAL)) {
+ GridContent gc = new GridContent(mContext, mHeaderItem);
+ isHeaderFullGrid = gc.hasImage() && gc.getMaxCellLineCount() > 1;
+ }
+ return mListContent.getRowItems().size() > 1 || isHeaderFullGrid;
+ }
+
+ /**
+ * @return the toggles associated with the header of this slice.
+ */
+ public List<SliceAction> getToggles() {
+ List<SliceAction> toggles = new ArrayList<>();
+ // Is it the primary action?
+ if (mPrimaryAction != null && mPrimaryAction.isToggle()) {
+ toggles.add(mPrimaryAction);
+ } else if (mSliceActions != null && mSliceActions.size() > 0) {
+ for (int i = 0; i < mSliceActions.size(); i++) {
+ SliceAction action = new SliceActionImpl(mSliceActions.get(i));
+ if (action.isToggle()) {
+ toggles.add(action);
+ }
+ }
+ } else {
+ RowContent rc = new RowContent(mContext, mHeaderItem, true /* isHeader */);
+ toggles = rc.getToggleItems();
+ }
+ return toggles;
+ }
+
+ /**
+ * Gets the range information associated with a progress bar or input range associated with this
+ * slice, if it exists.
+ *
+ * @return a pair where the first item is the current value of the range and the second item is
+ * the maximum value of the range.
+ */
+ @Nullable
+ public Pair<Integer, Integer> getRange() {
+ if (mTemplateType == ROW_TYPE_SLIDER
+ || mTemplateType == ROW_TYPE_PROGRESS) {
+ RowContent rc = new RowContent(mContext, mHeaderItem, true /* isHeader */);
+ SliceItem range = rc.getRange();
+ SliceItem maxItem = SliceQuery.findSubtype(range, FORMAT_INT, SUBTYPE_MAX);
+ SliceItem currentItem = SliceQuery.findSubtype(range, FORMAT_INT, SUBTYPE_VALUE);
+ int max = maxItem != null ? maxItem.getInt() : -1;
+ int current = currentItem != null ? currentItem.getInt() : -1;
+ return new Pair<>(current, max);
+ }
+ return null;
+ }
+
+ /**
* @return the list of keywords associated with the provided slice, null if no keywords were
* specified or an empty list if the slice was specified to have no keywords.
*/
@Nullable
public List<String> getSliceKeywords() {
- SliceItem keywordGroup = SliceQuery.find(mSlice, FORMAT_SLICE, HINT_KEY_WORDS, null);
+ SliceItem keywordGroup = SliceQuery.find(mSlice, FORMAT_SLICE, HINT_KEYWORDS, null);
if (keywordGroup != null) {
List<SliceItem> itemList = SliceQuery.findAll(keywordGroup, FORMAT_TEXT);
if (itemList != null) {
@@ -180,6 +283,18 @@
}
/**
+ * To present a slice from another app, the app must grant uri permissions for the slice. If
+ * these permissions have not been granted and the app slice is requested then
+ * a permission request slice will be returned instead, allowing the user to grant permission.
+ * This method can be used to identify if a slice is a permission request.
+ *
+ * @return whether this slice represents a permission request.
+ */
+ public boolean isPermissionSlice() {
+ return mSlice.hasHint(HINT_PERMISSION_REQUEST);
+ }
+
+ /**
* @return the group of actions associated with the provided slice, if they exist.
* @hide
*/
diff --git a/slices/view/src/main/java/androidx/slice/SliceUtils.java b/slices/view/src/main/java/androidx/slice/SliceUtils.java
index 40d82bc..442b324 100644
--- a/slices/view/src/main/java/androidx/slice/SliceUtils.java
+++ b/slices/view/src/main/java/androidx/slice/SliceUtils.java
@@ -28,9 +28,12 @@
import static androidx.slice.SliceMetadata.LOADED_ALL;
import static androidx.slice.SliceMetadata.LOADED_NONE;
import static androidx.slice.SliceMetadata.LOADED_PARTIAL;
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.content.Context;
+import android.net.Uri;
import android.text.TextUtils;
import androidx.annotation.IntDef;
@@ -42,6 +45,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.List;
@@ -65,10 +69,11 @@
* @param output The output of the serialization.
* @param encoding The encoding to use for serialization.
* @param options Options defining how to handle non-serializable items.
+ * @throws IllegalArgumentException if the slice cannot be serialized using the given options.
*/
public static void serializeSlice(@NonNull Slice s, @NonNull Context context,
@NonNull OutputStream output, @NonNull String encoding,
- @NonNull SerializeOptions options) throws IOException {
+ @NonNull SerializeOptions options) throws IOException, IllegalArgumentException {
SliceXml.serializeSlice(s, context, output, encoding, options);
}
@@ -76,13 +81,18 @@
* Parse a slice that has been previously serialized.
* <p>
* Parses a slice that was serialized with {@link #serializeSlice}.
+ * <p>
+ * Note: Slices returned by this cannot be passed to {@link SliceConvert#unwrap(Slice)}.
*
* @param input The input stream to read from.
* @param encoding The encoding to read as.
+ * @param listener Listener used to handle actions when reconstructing the slice.
+ * @throws SliceParseException if the InputStream cannot be parsed.
*/
- public static @NonNull Slice parseSlice(@NonNull InputStream input, @NonNull String encoding)
- throws IOException {
- return SliceXml.parseSlice(input, encoding);
+ public static @NonNull Slice parseSlice(@NonNull Context context, @NonNull InputStream input,
+ @NonNull String encoding, @NonNull SliceActionListener listener)
+ throws IOException, SliceParseException {
+ return SliceXml.parseSlice(context, input, encoding, listener);
}
/**
@@ -101,17 +111,22 @@
/**
* Constant indicating that the SliceItem should be serialized as much as possible.
* <p>
- * For images this means it will be replaced with an empty image. For actions, the
- * action will be removed but the content of the action will be serialized.
+ * For images this means they will be attempted to be serialized. For actions, the
+ * action will be removed but the content of the action will be serialized. The action
+ * may be triggered later on a de-serialized slice by binding the slice again and activating
+ * a pending-intent at the same location as the serialized action.
*/
- public static final int MODE_DISABLE = 2;
+ public static final int MODE_CONVERT = 2;
- @IntDef({MODE_THROW, MODE_REMOVE, MODE_DISABLE})
+ @IntDef({MODE_THROW, MODE_REMOVE, MODE_CONVERT})
+ @Retention(SOURCE)
@interface FormatMode {
}
private int mActionMode = MODE_THROW;
private int mImageMode = MODE_THROW;
+ private int mMaxWidth = 1000;
+ private int mMaxHeight = 1000;
/**
* @hide
@@ -149,6 +164,22 @@
}
/**
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public int getMaxWidth() {
+ return mMaxWidth;
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public int getMaxHeight() {
+ return mMaxHeight;
+ }
+
+ /**
* Sets how {@link android.app.slice.SliceItem#FORMAT_ACTION} items should be handled.
*
* The default mode is {@link #MODE_THROW}.
@@ -169,6 +200,30 @@
mImageMode = mode;
return this;
}
+
+ /**
+ * Set the maximum width of an image to use when serializing.
+ * <p>
+ * Will only be used if the {@link #setImageMode(int)} is set to {@link #MODE_CONVERT}.
+ * Any images larger than the maximum size will be scaled down to fit within that size.
+ * The default value is 1000.
+ */
+ public SerializeOptions setMaxImageWidth(int width) {
+ mMaxWidth = width;
+ return this;
+ }
+
+ /**
+ * Set the maximum height of an image to use when serializing.
+ * <p>
+ * Will only be used if the {@link #setImageMode(int)} is set to {@link #MODE_CONVERT}.
+ * Any images larger than the maximum size will be scaled down to fit within that size.
+ * The default value is 1000.
+ */
+ public SerializeOptions setMaxImageHeight(int height) {
+ mMaxHeight = height;
+ return this;
+ }
}
/**
@@ -239,7 +294,7 @@
@Deprecated
@Nullable
public static List<String> getSliceKeywords(@NonNull Slice slice) {
- SliceItem keywordGroup = SliceQuery.find(slice, FORMAT_SLICE, HINT_KEY_WORDS, null);
+ SliceItem keywordGroup = SliceQuery.find(slice, FORMAT_SLICE, HINT_KEYWORDS, null);
if (keywordGroup != null) {
List<SliceItem> itemList = SliceQuery.findAll(keywordGroup, FORMAT_TEXT);
if (itemList != null) {
@@ -255,4 +310,39 @@
}
return null;
}
+
+ /**
+ * A listener used to receive events on slices parsed with
+ * {@link #parseSlice(Context, InputStream, String, SliceActionListener)}.
+ */
+ public interface SliceActionListener {
+ /**
+ * Called when an action is triggered on a slice parsed with
+ * {@link #parseSlice(Context, InputStream, String, SliceActionListener)}.
+ * @param actionUri The uri of the action selected.
+ */
+ void onSliceAction(Uri actionUri);
+ }
+
+ /**
+ * Exception thrown during
+ * {@link #parseSlice(Context, InputStream, String, SliceActionListener)}.
+ */
+ public static class SliceParseException extends Exception {
+ /**
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public SliceParseException(String s, Throwable e) {
+ super(s, e);
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public SliceParseException(String s) {
+ super(s);
+ }
+ }
}
diff --git a/slices/view/src/main/java/androidx/slice/SliceXml.java b/slices/view/src/main/java/androidx/slice/SliceXml.java
index e8747be..ba423ad 100644
--- a/slices/view/src/main/java/androidx/slice/SliceXml.java
+++ b/slices/view/src/main/java/androidx/slice/SliceXml.java
@@ -20,22 +20,31 @@
import static org.xmlpull.v1.XmlPullParser.TEXT;
import android.annotation.SuppressLint;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.text.Html;
import android.text.Spanned;
import android.text.TextUtils;
+import android.util.Base64;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
+import androidx.core.util.Consumer;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -50,14 +59,24 @@
private static final String NAMESPACE = null;
private static final String TAG_SLICE = "slice";
+ private static final String TAG_ACTION = "action";
private static final String TAG_ITEM = "item";
private static final String ATTR_URI = "uri";
private static final String ATTR_FORMAT = "format";
private static final String ATTR_SUBTYPE = "subtype";
private static final String ATTR_HINTS = "hints";
+ private static final String ATTR_ICON_TYPE = "iconType";
+ private static final String ATTR_ICON_PACKAGE = "pkg";
+ private static final String ATTR_ICON_RES_TYPE = "resType";
- public static Slice parseSlice(InputStream input, String encoding) throws IOException {
+ private static final String ICON_TYPE_RES = "res";
+ private static final String ICON_TYPE_URI = "uri";
+ private static final String ICON_TYPE_DEFAULT = "def";
+
+ public static Slice parseSlice(Context context, InputStream input,
+ String encoding, SliceUtils.SliceActionListener listener)
+ throws IOException, SliceUtils.SliceParseException {
try {
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
parser.setInput(input, encoding);
@@ -70,7 +89,7 @@
if (type != START_TAG) {
continue;
}
- s = parseSlice(parser);
+ s = parseSlice(context, parser, listener);
}
return s;
} catch (XmlPullParserException e) {
@@ -79,9 +98,10 @@
}
@SuppressLint("WrongConstant")
- private static Slice parseSlice(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- if (!TAG_SLICE.equals(parser.getName())) {
+ private static Slice parseSlice(Context context, XmlPullParser parser,
+ SliceUtils.SliceActionListener listener)
+ throws IOException, XmlPullParserException, SliceUtils.SliceParseException {
+ if (!TAG_SLICE.equals(parser.getName()) && !TAG_ACTION.equals(parser.getName())) {
throw new IOException("Unexpected tag " + parser.getName());
}
int outerDepth = parser.getDepth();
@@ -94,20 +114,24 @@
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == START_TAG && TAG_ITEM.equals(parser.getName())) {
- parseItem(b, parser);
+ parseItem(context, b, parser, listener);
}
}
return b.build();
}
@SuppressLint("WrongConstant")
- private static void parseItem(Slice.Builder b, XmlPullParser parser)
- throws IOException, XmlPullParserException {
+ private static void parseItem(Context context, Slice.Builder b,
+ XmlPullParser parser, final SliceUtils.SliceActionListener listener)
+ throws IOException, XmlPullParserException, SliceUtils.SliceParseException {
int type;
int outerDepth = parser.getDepth();
String format = parser.getAttributeValue(NAMESPACE, ATTR_FORMAT);
String subtype = parser.getAttributeValue(NAMESPACE, ATTR_SUBTYPE);
String hintStr = parser.getAttributeValue(NAMESPACE, ATTR_HINTS);
+ String iconType = parser.getAttributeValue(NAMESPACE, ATTR_ICON_TYPE);
+ String pkg = parser.getAttributeValue(NAMESPACE, ATTR_ICON_PACKAGE);
+ String resType = parser.getAttributeValue(NAMESPACE, ATTR_ICON_RES_TYPE);
String[] hints = hints(hintStr);
String v;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -118,16 +142,37 @@
// Nothing for now.
break;
case android.app.slice.SliceItem.FORMAT_IMAGE:
- v = parser.getText();
- if (!TextUtils.isEmpty(v)) {
- if (android.os.Build.VERSION.SDK_INT
- >= android.os.Build.VERSION_CODES.M) {
- String[] split = v.split(",");
- int w = Integer.parseInt(split[0]);
- int h = Integer.parseInt(split[1]);
- Bitmap image = Bitmap.createBitmap(w, h, Bitmap.Config.ALPHA_8);
+ switch (iconType) {
+ case ICON_TYPE_RES:
+ String resName = parser.getText();
+ try {
+ Resources r = context.getPackageManager()
+ .getResourcesForApplication(pkg);
+ int id = r.getIdentifier(resName, resType, pkg);
+ if (id != 0) {
+ b.addIcon(IconCompat.createWithResource(
+ context.createPackageContext(pkg, 0), id), subtype,
+ hints);
+ } else {
+ throw new SliceUtils.SliceParseException(
+ "Cannot find resource " + pkg + ":" + resType
+ + "/" + resName);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new SliceUtils.SliceParseException(
+ "Invalid icon package " + pkg, e);
+ }
+ break;
+ case ICON_TYPE_URI:
+ v = parser.getText();
+ b.addIcon(IconCompat.createWithContentUri(v), subtype, hints);
+ break;
+ default:
+ v = parser.getText();
+ byte[] data = Base64.decode(v, Base64.NO_WRAP);
+ Bitmap image = BitmapFactory.decodeByteArray(data, 0, data.length);
b.addIcon(IconCompat.createWithBitmap(image), subtype, hints);
- }
+ break;
}
break;
case android.app.slice.SliceItem.FORMAT_INT:
@@ -138,15 +183,22 @@
v = parser.getText();
b.addText(Html.fromHtml(v), subtype, hints);
break;
- case android.app.slice.SliceItem.FORMAT_TIMESTAMP:
+ case android.app.slice.SliceItem.FORMAT_LONG:
v = parser.getText();
- b.addTimestamp(Long.parseLong(v), subtype, hints);
+ b.addLong(Long.parseLong(v), subtype, hints);
break;
default:
throw new IllegalArgumentException("Unrecognized format " + format);
}
} else if (type == START_TAG && TAG_SLICE.equals(parser.getName())) {
- b.addSubSlice(parseSlice(parser), subtype);
+ b.addSubSlice(parseSlice(context, parser, listener), subtype);
+ } else if (type == START_TAG && TAG_ACTION.equals(parser.getName())) {
+ b.addAction(new Consumer<Uri>() {
+ @Override
+ public void accept(Uri uri) {
+ listener.onSliceAction(uri);
+ }
+ }, parseSlice(context, parser, listener), subtype);
}
}
}
@@ -162,7 +214,7 @@
serializer.setOutput(output, encoding);
serializer.startDocument(encoding, null);
- serialize(s, context, options, serializer);
+ serialize(s, context, options, serializer, false, null);
serializer.endDocument();
serializer.flush();
@@ -172,9 +224,12 @@
}
private static void serialize(Slice s, Context context, SliceUtils.SerializeOptions options,
- XmlSerializer serializer) throws IOException {
- serializer.startTag(NAMESPACE, TAG_SLICE);
+ XmlSerializer serializer, boolean isAction, String subType) throws IOException {
+ serializer.startTag(NAMESPACE, isAction ? TAG_ACTION : TAG_SLICE);
serializer.attribute(NAMESPACE, ATTR_URI, s.getUri().toString());
+ if (subType != null) {
+ serializer.attribute(NAMESPACE, ATTR_SUBTYPE, subType);
+ }
if (!s.getHints().isEmpty()) {
serializer.attribute(NAMESPACE, ATTR_HINTS, hintStr(s.getHints()));
}
@@ -182,7 +237,7 @@
serialize(item, context, options, serializer);
}
- serializer.endTag(NAMESPACE, TAG_SLICE);
+ serializer.endTag(NAMESPACE, isAction ? TAG_ACTION : TAG_SLICE);
}
private static void serialize(SliceItem item, Context context,
@@ -201,27 +256,45 @@
switch (format) {
case android.app.slice.SliceItem.FORMAT_ACTION:
- if (options.getActionMode() == SliceUtils.SerializeOptions.MODE_DISABLE) {
- serialize(item.getSlice(), context, options, serializer);
+ if (options.getActionMode() == SliceUtils.SerializeOptions.MODE_CONVERT) {
+ serialize(item.getSlice(), context, options, serializer, true,
+ item.getSubType());
+ } else if (options.getActionMode() == SliceUtils.SerializeOptions.MODE_THROW) {
+ throw new IllegalArgumentException("Slice contains an action " + item);
}
break;
case android.app.slice.SliceItem.FORMAT_REMOTE_INPUT:
// Nothing for now.
break;
case android.app.slice.SliceItem.FORMAT_IMAGE:
- if (options.getImageMode() == SliceUtils.SerializeOptions.MODE_DISABLE) {
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
- Drawable d = item.getIcon().loadDrawable(context);
- serializer.text(String.format("%d,%d",
- d.getIntrinsicWidth(), d.getIntrinsicHeight()));
+ if (options.getImageMode() == SliceUtils.SerializeOptions.MODE_CONVERT) {
+ IconCompat icon = item.getIcon();
+
+ switch (icon.getType()) {
+ case Icon.TYPE_RESOURCE:
+ serializeResIcon(serializer, icon, context);
+ break;
+ case Icon.TYPE_URI:
+ Uri uri = icon.getUri();
+ if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
+ serializeFileIcon(serializer, icon, context);
+ } else {
+ serializeIcon(serializer, icon, context, options);
+ }
+ break;
+ default:
+ serializeIcon(serializer, icon, context, options);
+ break;
}
+ } else if (options.getImageMode() == SliceUtils.SerializeOptions.MODE_THROW) {
+ throw new IllegalArgumentException("Slice contains an image " + item);
}
break;
case android.app.slice.SliceItem.FORMAT_INT:
serializer.text(String.valueOf(item.getInt()));
break;
case android.app.slice.SliceItem.FORMAT_SLICE:
- serialize(item.getSlice(), context, options, serializer);
+ serialize(item.getSlice(), context, options, serializer, false, item.getSubType());
break;
case android.app.slice.SliceItem.FORMAT_TEXT:
if (item.getText() instanceof Spanned) {
@@ -230,8 +303,8 @@
serializer.text(String.valueOf(item.getText()));
}
break;
- case android.app.slice.SliceItem.FORMAT_TIMESTAMP:
- serializer.text(String.valueOf(item.getTimestamp()));
+ case android.app.slice.SliceItem.FORMAT_LONG:
+ serializer.text(String.valueOf(item.getLong()));
break;
default:
throw new IllegalArgumentException("Unrecognized format " + format);
@@ -239,7 +312,57 @@
serializer.endTag(NAMESPACE, TAG_ITEM);
}
+ private static void serializeResIcon(XmlSerializer serializer, IconCompat icon, Context context)
+ throws IOException {
+ try {
+ Resources res = context.getPackageManager().getResourcesForApplication(
+ icon.getResPackage());
+ int id = icon.getResId();
+ serializer.attribute(NAMESPACE, ATTR_ICON_TYPE, ICON_TYPE_RES);
+ serializer.attribute(NAMESPACE, ATTR_ICON_PACKAGE, res.getResourcePackageName(id));
+ serializer.attribute(NAMESPACE, ATTR_ICON_RES_TYPE, res.getResourceTypeName(id));
+ serializer.text(res.getResourceEntryName(id));
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalArgumentException("Slice contains invalid icon", e);
+ }
+ }
+
+ private static void serializeFileIcon(XmlSerializer serializer, IconCompat icon,
+ Context context) throws IOException {
+ serializer.attribute(NAMESPACE, ATTR_ICON_TYPE, ICON_TYPE_URI);
+ serializer.text(icon.getUri().toString());
+ }
+
+ private static void serializeIcon(XmlSerializer serializer, IconCompat icon,
+ Context context, SliceUtils.SerializeOptions options) throws IOException {
+ Drawable d = icon.loadDrawable(context);
+ int width = d.getIntrinsicWidth();
+ int height = d.getIntrinsicHeight();
+ if (width > options.getMaxWidth()) {
+ height = (int) (options.getMaxWidth() * height / (double) width);
+ width = options.getMaxWidth();
+ }
+ if (height > options.getMaxHeight()) {
+ width = (int) (options.getMaxHeight() * width / (double) height);
+ height = options.getMaxHeight();
+ }
+ Bitmap b = Bitmap.createBitmap(width, height,
+ Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(b);
+ d.setBounds(0, 0, c.getWidth(), c.getHeight());
+ d.draw(c);
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ b.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
+ b.recycle();
+
+ serializer.attribute(NAMESPACE, ATTR_ICON_TYPE, ICON_TYPE_DEFAULT);
+ serializer.text(new String(Base64.encode(outputStream.toByteArray(), Base64.NO_WRAP)));
+ }
+
private static String hintStr(List<String> hints) {
return TextUtils.join(",", hints);
}
+
+ private SliceXml() {
+ }
}
diff --git a/slices/view/src/main/java/androidx/slice/widget/ActionRow.java b/slices/view/src/main/java/androidx/slice/widget/ActionRow.java
index 98ebfd2..55bbca1 100644
--- a/slices/view/src/main/java/androidx/slice/widget/ActionRow.java
+++ b/slices/view/src/main/java/androidx/slice/widget/ActionRow.java
@@ -22,13 +22,14 @@
import static androidx.slice.core.SliceHints.ICON_IMAGE;
-import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.app.RemoteInput;
import android.app.slice.Slice;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
+import android.os.Build;
+import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewParent;
@@ -39,6 +40,7 @@
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
import androidx.core.widget.ImageViewCompat;
@@ -55,6 +57,8 @@
public class ActionRow extends FrameLayout {
private static final int MAX_ACTIONS = 5;
+ private static final String TAG = "ActionRow";
+
private final int mSize;
private final int mIconPadding;
private final LinearLayout mActionsGroup;
@@ -118,22 +122,16 @@
}
final SliceItem input = SliceQuery.find(action, FORMAT_REMOTE_INPUT);
final SliceItem image = SliceQuery.find(action, FORMAT_IMAGE);
- if (input != null && image != null
- && input.getRemoteInput().getAllowFreeFormInput()) {
- boolean tint = !image.hasHint(HINT_NO_TINT);
- addAction(image.getIcon(), tint).setOnClickListener(
- new OnClickListener() {
- @Override
- public void onClick(View v) {
- handleRemoteInputClick(v, action.getAction(),
- input.getRemoteInput());
- }
- });
- createRemoteInputView(mColor, getContext());
+ if (input != null && image != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ handleSetRemoteInputActions(input, image, action);
+ } else {
+ Log.w(TAG, "Received RemoteInput on API <20 " + input);
+ }
} else if (action.hasHint(Slice.HINT_SHORTCUT)) {
final SliceActionImpl ac = new SliceActionImpl(action);
IconCompat iconItem = ac.getIcon();
- if (iconItem != null && ac.getAction() != null) {
+ if (iconItem != null && ac.getActionItem() != null) {
boolean tint = ac.getImageMode() == ICON_IMAGE;
addAction(iconItem, tint).setOnClickListener(
new OnClickListener() {
@@ -141,7 +139,7 @@
public void onClick(View v) {
try {
// TODO - should log events here
- ac.getAction().send();
+ ac.getActionItem().fireAction(null, null);
} catch (CanceledException e) {
e.printStackTrace();
}
@@ -157,6 +155,24 @@
mActionsGroup.addView(child, new LinearLayout.LayoutParams(mSize, mSize, 1));
}
+ @RequiresApi(21)
+ private void handleSetRemoteInputActions(final SliceItem input, SliceItem image,
+ final SliceItem action) {
+ if (input.getRemoteInput().getAllowFreeFormInput()) {
+ boolean tint = !image.hasHint(HINT_NO_TINT);
+ addAction(image.getIcon(), tint).setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ handleRemoteInputClick(v, action,
+ input.getRemoteInput());
+ }
+ });
+ createRemoteInputView(mColor, getContext());
+ }
+ }
+
+ @RequiresApi(21)
private void createRemoteInputView(int color, Context context) {
View riv = RemoteInputView.inflate(context, this);
riv.setVisibility(View.INVISIBLE);
@@ -164,7 +180,8 @@
riv.setBackgroundColor(color);
}
- private boolean handleRemoteInputClick(View view, PendingIntent pendingIntent,
+ @RequiresApi(21)
+ private boolean handleRemoteInputClick(View view, SliceItem action,
RemoteInput input) {
if (input == null) {
return false;
@@ -205,7 +222,7 @@
Math.max((w - cx) + cy, (w - cx) + (h - cy)));
riv.setRevealParameters(cx, cy, r);
- riv.setPendingIntent(pendingIntent);
+ riv.setAction(action);
riv.setRemoteInput(new RemoteInput[] {
input
}, input);
@@ -213,6 +230,7 @@
return true;
}
+ @RequiresApi(21)
private RemoteInputView findRemoteInputView(View v) {
if (v == null) {
return null;
diff --git a/slices/view/src/main/java/androidx/slice/widget/EventInfo.java b/slices/view/src/main/java/androidx/slice/widget/EventInfo.java
index 0b93381..9b3af24 100644
--- a/slices/view/src/main/java/androidx/slice/widget/EventInfo.java
+++ b/slices/view/src/main/java/androidx/slice/widget/EventInfo.java
@@ -19,6 +19,9 @@
import androidx.annotation.IntDef;
import androidx.annotation.RestrictTo;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Represents information associated with a logged event on {@link SliceView}.
*/
@@ -29,8 +32,15 @@
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
@IntDef({
- ROW_TYPE_SHORTCUT, ROW_TYPE_LIST, ROW_TYPE_GRID, ROW_TYPE_MESSAGING
+ ROW_TYPE_SHORTCUT,
+ ROW_TYPE_LIST,
+ ROW_TYPE_GRID,
+ ROW_TYPE_MESSAGING,
+ ROW_TYPE_TOGGLE,
+ ROW_TYPE_SLIDER,
+ ROW_TYPE_PROGRESS,
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface SliceRowType {}
/**
@@ -49,6 +59,18 @@
* Indicates the row is represented as a messaging template.
*/
public static final int ROW_TYPE_MESSAGING = 2;
+ /**
+ * Indicates the row represents a toggleable item.
+ */
+ public static final int ROW_TYPE_TOGGLE = 3;
+ /**
+ * Indicates the row represents an range input slider.
+ */
+ public static final int ROW_TYPE_SLIDER = 4;
+ /**
+ * Indicates the row represents a progress indicator.
+ */
+ public static final int ROW_TYPE_PROGRESS = 5;
/**
* @hide
@@ -58,6 +80,7 @@
ACTION_TYPE_TOGGLE, ACTION_TYPE_BUTTON, ACTION_TYPE_SLIDER, ACTION_TYPE_CONTENT,
ACTION_TYPE_SEE_MORE
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface SliceActionType{}
/**
@@ -91,6 +114,7 @@
@IntDef({
POSITION_START, POSITION_END, POSITION_CELL
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface SliceButtonPosition{}
/**
@@ -268,6 +292,12 @@
return "MESSAGING";
case ROW_TYPE_SHORTCUT:
return "SHORTCUT";
+ case ROW_TYPE_TOGGLE:
+ return "TOGGLE";
+ case ROW_TYPE_SLIDER:
+ return "SLIDER";
+ case ROW_TYPE_PROGRESS:
+ return "PROGRESS";
default:
return "unknown row type: " + type;
}
diff --git a/slices/view/src/main/java/androidx/slice/widget/GridContent.java b/slices/view/src/main/java/androidx/slice/widget/GridContent.java
index 14a05aa..69d5c44 100644
--- a/slices/view/src/main/java/androidx/slice/widget/GridContent.java
+++ b/slices/view/src/main/java/androidx/slice/widget/GridContent.java
@@ -30,7 +30,7 @@
import static android.app.slice.SliceItem.FORMAT_TEXT;
import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
import static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
import static androidx.slice.core.SliceHints.HINT_TTL;
import static androidx.slice.core.SliceHints.ICON_IMAGE;
@@ -198,7 +198,7 @@
for (int i = 0; i < items.size(); i++) {
SliceItem item = items.get(i);
boolean isNonCellContent = item.hasAnyHints(HINT_SHORTCUT, HINT_SEE_MORE,
- HINT_KEY_WORDS, HINT_TTL, HINT_LAST_UPDATED);
+ HINT_KEYWORDS, HINT_TTL, HINT_LAST_UPDATED);
if (SUBTYPE_CONTENT_DESCRIPTION.equals(item.getSubType())) {
mContentDescr = item;
} else if (item.hasHint(HINT_LIST_ITEM) && !isNonCellContent) {
@@ -337,7 +337,7 @@
private boolean isValidCellContent(SliceItem cellItem) {
final String format = cellItem.getFormat();
boolean isNonCellContent = SUBTYPE_CONTENT_DESCRIPTION.equals(cellItem.getSubType())
- || cellItem.hasAnyHints(HINT_KEY_WORDS, HINT_TTL, HINT_LAST_UPDATED);
+ || cellItem.hasAnyHints(HINT_KEYWORDS, HINT_TTL, HINT_LAST_UPDATED);
return !isNonCellContent
&& (FORMAT_TEXT.equals(format)
|| FORMAT_TIMESTAMP.equals(format)
diff --git a/slices/view/src/main/java/androidx/slice/widget/GridRowView.java b/slices/view/src/main/java/androidx/slice/widget/GridRowView.java
index dce37e0..a8afb7c 100644
--- a/slices/view/src/main/java/androidx/slice/widget/GridRowView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/GridRowView.java
@@ -17,7 +17,6 @@
package androidx.slice.widget;
import static android.app.slice.Slice.HINT_LARGE;
-import static android.app.slice.Slice.HINT_LIST_ITEM;
import static android.app.slice.Slice.HINT_NO_TINT;
import static android.app.slice.Slice.HINT_TITLE;
import static android.app.slice.SliceItem.FORMAT_ACTION;
@@ -133,16 +132,9 @@
}
}
- /**
- * This is called when GridView is presented in small format.
- */
@Override
public void setSlice(Slice slice) {
- resetView();
- mRowIndex = 0;
- SliceItem item = SliceQuery.find(slice, FORMAT_SLICE, HINT_LIST_ITEM, null);
- mGridContent = new GridContent(getContext(), item);
- populateViews(mGridContent);
+ // Nothing to do
}
/**
@@ -164,7 +156,7 @@
EventInfo.ROW_TYPE_GRID, mRowIndex);
Pair<SliceItem, EventInfo> tagItem = new Pair<>(gc.getContentIntent(), info);
mViewContainer.setTag(tagItem);
- makeClickable(mViewContainer);
+ makeClickable(mViewContainer, true);
}
CharSequence contentDescr = gc.getContentDescription();
if (contentDescr != null) {
@@ -222,7 +214,7 @@
info.setPosition(EventInfo.POSITION_CELL, index, total);
Pair<SliceItem, EventInfo> tagItem = new Pair<>(seeMoreItem, info);
seeMoreView.setTag(tagItem);
- makeClickable(seeMoreView);
+ makeClickable(seeMoreView, true);
}
/**
@@ -301,7 +293,7 @@
info.setPosition(EventInfo.POSITION_CELL, index, total);
Pair<SliceItem, EventInfo> tagItem = new Pair<>(contentIntentItem, info);
cellContainer.setTag(tagItem);
- makeClickable(cellContainer);
+ makeClickable(cellContainer, true);
}
}
}
@@ -347,10 +339,12 @@
return addedView != null;
}
- private void makeClickable(View layout) {
- layout.setOnClickListener(this);
- layout.setForeground(SliceViewUtil.getDrawable(getContext(),
- android.R.attr.selectableItemBackground));
+ private void makeClickable(View layout, boolean isClickable) {
+ layout.setOnClickListener(isClickable ? this : null);
+ layout.setBackground(isClickable
+ ? SliceViewUtil.getDrawable(getContext(), android.R.attr.selectableItemBackground)
+ : null);
+ layout.setClickable(isClickable);
}
@Override
@@ -360,7 +354,7 @@
final EventInfo info = tagItem.second;
if (actionItem != null && FORMAT_ACTION.equals(actionItem.getFormat())) {
try {
- actionItem.getAction().send();
+ actionItem.fireAction(null, null);
if (mObserver != null) {
mObserver.onSliceAction(info, actionItem);
}
@@ -373,5 +367,6 @@
@Override
public void resetView() {
mViewContainer.removeAllViews();
+ makeClickable(mViewContainer, false);
}
}
diff --git a/slices/view/src/main/java/androidx/slice/widget/LargeSliceAdapter.java b/slices/view/src/main/java/androidx/slice/widget/LargeSliceAdapter.java
index ffa64c4..9247b78 100644
--- a/slices/view/src/main/java/androidx/slice/widget/LargeSliceAdapter.java
+++ b/slices/view/src/main/java/androidx/slice/widget/LargeSliceAdapter.java
@@ -27,22 +27,23 @@
import android.app.slice.Slice;
import android.content.Context;
-import android.util.ArrayMap;
import android.util.AttributeSet;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import androidx.annotation.RestrictTo;
+import androidx.collection.ArrayMap;
import androidx.recyclerview.widget.RecyclerView;
import androidx.slice.SliceItem;
import androidx.slice.core.SliceQuery;
import androidx.slice.view.R;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
-import java.util.function.Consumer;
/**
* @hide
@@ -67,12 +68,25 @@
private List<SliceItem> mSliceActions;
private boolean mShowLastUpdated;
private long mLastUpdated;
+ private SliceView mParent;
+ private LargeTemplateView mTemplateView;
public LargeSliceAdapter(Context context) {
mContext = context;
setHasStableIds(true);
}
+ /**
+ * Sets the SliceView parent and the template parent.
+ */
+ public void setParents(SliceView parent, LargeTemplateView templateView) {
+ mParent = parent;
+ mTemplateView = templateView;
+ }
+
+ /**
+ * Sets the observer to pass down to child views.
+ */
public void setSliceObserver(SliceView.OnSliceActionListener observer) {
mSliceObserver = observer;
}
@@ -151,17 +165,7 @@
@Override
public void onBindViewHolder(SliceViewHolder holder, int position) {
SliceWrapper slice = mSlices.get(position);
- if (holder.mSliceView != null) {
- final boolean isHeader = position == HEADER_INDEX;
- holder.mSliceView.setTint(mColor);
- holder.mSliceView.setStyle(mAttrs);
- holder.mSliceView.setSliceItem(slice.mItem, isHeader, position, mSliceObserver);
- if (isHeader && holder.mSliceView instanceof RowView) {
- holder.mSliceView.setSliceActions(mSliceActions);
- holder.mSliceView.setLastUpdated(mLastUpdated);
- holder.mSliceView.setShowLastUpdated(mShowLastUpdated);
- }
- }
+ holder.bind(slice.mItem, position);
}
private void notifyHeaderChanged() {
@@ -184,7 +188,8 @@
null);
break;
}
- ((SliceChildView) v).setMode(MODE_LARGE);
+ int mode = mParent != null ? mParent.getMode() : MODE_LARGE;
+ ((SliceChildView) v).setMode(mode);
return v;
}
@@ -221,12 +226,53 @@
/**
* A {@link RecyclerView.ViewHolder} for presenting slices in {@link LargeSliceAdapter}.
*/
- public static class SliceViewHolder extends RecyclerView.ViewHolder {
- public final SliceChildView mSliceView;
+ public class SliceViewHolder extends RecyclerView.ViewHolder implements View.OnTouchListener,
+ View.OnClickListener {
+ public final SliceChildView mSliceChildView;
public SliceViewHolder(View itemView) {
super(itemView);
- mSliceView = itemView instanceof SliceChildView ? (SliceChildView) itemView : null;
+ mSliceChildView = itemView instanceof SliceChildView ? (SliceChildView) itemView : null;
+ }
+
+ void bind(SliceItem item, int position) {
+ if (mSliceChildView == null || item == null) {
+ return;
+ }
+ // Click listener used to pipe click events to parent
+ mSliceChildView.setOnClickListener(this);
+ // Touch listener used to pipe events to touch feedback drawable
+ mSliceChildView.setOnTouchListener(this);
+
+ final boolean isHeader = position == HEADER_INDEX;
+ mSliceChildView.setTint(mColor);
+ mSliceChildView.setStyle(mAttrs);
+ mSliceChildView.setSliceItem(item, isHeader, position, mSliceObserver);
+ if (isHeader && mSliceChildView instanceof RowView) {
+ mSliceChildView.setSliceActions(mSliceActions);
+ mSliceChildView.setLastUpdated(mLastUpdated);
+ mSliceChildView.setShowLastUpdated(mShowLastUpdated);
+ }
+ int[] info = new int[2];
+ info[0] = ListContent.getRowType(mContext, item, isHeader, mSliceActions);
+ info[1] = position;
+ mSliceChildView.setTag(info);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (mParent != null) {
+ mParent.setClickInfo((int[]) v.getTag());
+ mParent.performClick();
+ }
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (mTemplateView != null) {
+ mTemplateView.onForegroundActivated(event);
+ }
+ return false;
}
}
@@ -249,25 +295,24 @@
private String genString(SliceItem item) {
final StringBuilder builder = new StringBuilder();
- SliceQuery.stream(item).forEach(new Consumer<SliceItem>() {
- @Override
- public void accept(SliceItem i) {
- builder.append(i.getFormat());
- //i.removeHint(Slice.HINT_SELECTED);
- builder.append(i.getHints());
- switch (i.getFormat()) {
- case FORMAT_IMAGE:
- builder.append(i.getIcon());
- break;
- case FORMAT_TEXT:
- builder.append(i.getText());
- break;
- case FORMAT_INT:
- builder.append(i.getInt());
- break;
- }
+ Iterator<SliceItem> items = SliceQuery.stream(item);
+ while (items.hasNext()) {
+ SliceItem i = items.next();
+ builder.append(i.getFormat());
+ //i.removeHint(Slice.HINT_SELECTED);
+ builder.append(i.getHints());
+ switch (i.getFormat()) {
+ case FORMAT_IMAGE:
+ builder.append(i.getIcon());
+ break;
+ case FORMAT_TEXT:
+ builder.append(i.getText());
+ break;
+ case FORMAT_INT:
+ builder.append(i.getInt());
+ break;
}
- });
+ }
return builder.toString();
}
diff --git a/slices/view/src/main/java/androidx/slice/widget/LargeTemplateView.java b/slices/view/src/main/java/androidx/slice/widget/LargeTemplateView.java
index 36409e8..0c0aba8 100644
--- a/slices/view/src/main/java/androidx/slice/widget/LargeTemplateView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/LargeTemplateView.java
@@ -16,8 +16,14 @@
package androidx.slice.widget;
+import static android.app.slice.Slice.HINT_HORIZONTAL;
+
import android.content.Context;
+import android.os.Build;
import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
import androidx.annotation.RestrictTo;
import androidx.recyclerview.widget.LinearLayoutManager;
@@ -26,6 +32,7 @@
import androidx.slice.SliceItem;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -34,6 +41,8 @@
@RestrictTo(RestrictTo.Scope.LIBRARY)
public class LargeTemplateView extends SliceChildView {
+ private SliceView mParent;
+ private final View mForeground;
private final LargeSliceAdapter mAdapter;
private final RecyclerView mRecyclerView;
private Slice mSlice;
@@ -41,6 +50,7 @@
private ListContent mListContent;
private List<SliceItem> mDisplayedItems = new ArrayList<>();
private int mDisplayedItemsHeight = 0;
+ private int[] mLoc = new int[2];
public LargeTemplateView(Context context) {
super(context);
@@ -49,6 +59,23 @@
mAdapter = new LargeSliceAdapter(context);
mRecyclerView.setAdapter(mAdapter);
addView(mRecyclerView);
+
+ mForeground = new View(getContext());
+ mForeground.setBackground(SliceViewUtil.getDrawable(getContext(),
+ android.R.attr.selectableItemBackground));
+ addView(mForeground);
+
+ FrameLayout.LayoutParams lp = (LayoutParams) mForeground.getLayoutParams();
+ lp.width = LayoutParams.MATCH_PARENT;
+ lp.height = LayoutParams.MATCH_PARENT;
+ mForeground.setLayoutParams(lp);
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mParent = (SliceView) getParent();
+ mAdapter.setParents(mParent, this);
}
@Override
@@ -61,20 +88,62 @@
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
+ /**
+ * Called when the foreground view handling touch feedback should be activated.
+ * @param event the event to handle.
+ */
+ public void onForegroundActivated(MotionEvent event) {
+ if (mParent != null && !mParent.isSliceViewClickable()) {
+ // Only show highlight if clickable
+ mForeground.setPressed(false);
+ return;
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ mForeground.getLocationOnScreen(mLoc);
+ final int x = (int) (event.getRawX() - mLoc[0]);
+ final int y = (int) (event.getRawY() - mLoc[1]);
+ mForeground.getBackground().setHotspot(x, y);
+ }
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ mForeground.setPressed(true);
+ } else if (action == MotionEvent.ACTION_CANCEL
+ || action == MotionEvent.ACTION_UP
+ || action == MotionEvent.ACTION_MOVE) {
+ mForeground.setPressed(false);
+ }
+ }
+
+ @Override
+ public void setMode(int newMode) {
+ super.setMode(newMode);
+ updateDisplayedItems(getMeasuredHeight());
+ }
+
@Override
public int getActualHeight() {
return mDisplayedItemsHeight;
}
@Override
- public void setTint(int tint) {
- super.setTint(tint);
- populate();
+ public int getSmallHeight() {
+ if (mListContent == null || mListContent.getHeaderItem() == null) {
+ return 0;
+ }
+ SliceItem headerItem = mListContent.getHeaderItem();
+ if (headerItem.hasHint(HINT_HORIZONTAL)) {
+ GridContent gc = new GridContent(getContext(), headerItem);
+ return gc.getSmallHeight();
+ } else {
+ RowContent rc = new RowContent(getContext(), headerItem, mListContent.hasHeader());
+ return rc.getSmallHeight();
+ }
}
@Override
- public @SliceView.SliceMode int getMode() {
- return SliceView.MODE_LARGE;
+ public void setTint(int tint) {
+ super.setTint(tint);
+ populate();
}
@Override
@@ -148,7 +217,12 @@
mDisplayedItems = mListContent.getRowItems();
}
mDisplayedItemsHeight = ListContent.getListHeight(getContext(), mDisplayedItems);
- mAdapter.setSliceItems(mDisplayedItems, mTintColor);
+ if (getMode() == SliceView.MODE_LARGE) {
+ mAdapter.setSliceItems(mDisplayedItems, mTintColor);
+ } else if (getMode() == SliceView.MODE_SMALL) {
+ mAdapter.setSliceItems(
+ Collections.singletonList(mDisplayedItems.get(0)), mTintColor);
+ }
}
@Override
diff --git a/slices/view/src/main/java/androidx/slice/widget/ListContent.java b/slices/view/src/main/java/androidx/slice/widget/ListContent.java
index 955144d..b728a69 100644
--- a/slices/view/src/main/java/androidx/slice/widget/ListContent.java
+++ b/slices/view/src/main/java/androidx/slice/widget/ListContent.java
@@ -27,7 +27,7 @@
import static android.app.slice.SliceItem.FORMAT_SLICE;
import static android.app.slice.SliceItem.FORMAT_TEXT;
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
import static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
import static androidx.slice.core.SliceHints.HINT_TTL;
@@ -39,6 +39,8 @@
import androidx.slice.Slice;
import androidx.slice.SliceItem;
import androidx.slice.SliceMetadata;
+import androidx.slice.core.SliceAction;
+import androidx.slice.core.SliceActionImpl;
import androidx.slice.core.SliceQuery;
import java.util.ArrayList;
@@ -81,7 +83,7 @@
for (int i = 0; i < children.size(); i++) {
final SliceItem child = children.get(i);
final String format = child.getFormat();
- boolean isNonRowContent = child.hasAnyHints(HINT_ACTIONS, HINT_SEE_MORE, HINT_KEY_WORDS,
+ boolean isNonRowContent = child.hasAnyHints(HINT_ACTIONS, HINT_SEE_MORE, HINT_KEYWORDS,
HINT_TTL, HINT_LAST_UPDATED);
if (!isNonRowContent && (FORMAT_ACTION.equals(format) || FORMAT_SLICE.equals(format))) {
if (mHeaderItem == null && !child.hasHint(HINT_LIST_ITEM)) {
@@ -202,19 +204,78 @@
}
/**
+ * @return the type of template that the header represents.
+ */
+ public int getHeaderTemplateType() {
+ return getRowType(mContext, mHeaderItem, true, mSliceActions);
+ }
+
+ /**
+ * The type of template that the provided row item represents.
+ *
+ * @param context context used for this slice.
+ * @param rowItem the row item to determine the template type of.
+ * @param isHeader whether this row item is used as a header.
+ * @param actions the actions associated with this slice, only matter if this row is the header.
+ * @return the type of template the provided row item represents.
+ */
+ public static int getRowType(Context context, SliceItem rowItem, boolean isHeader,
+ List<SliceItem> actions) {
+ if (rowItem != null) {
+ if (rowItem.hasHint(HINT_HORIZONTAL)) {
+ return EventInfo.ROW_TYPE_GRID;
+ } else {
+ RowContent rc = new RowContent(context, rowItem, isHeader);
+ SliceItem actionItem = rc.getPrimaryAction();
+ SliceAction primaryAction = null;
+ if (actionItem != null) {
+ primaryAction = new SliceActionImpl(actionItem);
+ }
+ if (rc.getRange() != null) {
+ return FORMAT_ACTION.equals(rc.getRange().getFormat())
+ ? EventInfo.ROW_TYPE_SLIDER
+ : EventInfo.ROW_TYPE_PROGRESS;
+ } else if (primaryAction != null && primaryAction.isToggle()) {
+ return EventInfo.ROW_TYPE_TOGGLE;
+ } else if (isHeader && actions != null) {
+ for (int i = 0; i < actions.size(); i++) {
+ if (new SliceActionImpl(actions.get(i)).isToggle()) {
+ return EventInfo.ROW_TYPE_TOGGLE;
+ }
+ }
+ return EventInfo.ROW_TYPE_LIST;
+ } else {
+ return rc.getToggleItems().size() > 0
+ ? EventInfo.ROW_TYPE_TOGGLE
+ : EventInfo.ROW_TYPE_LIST;
+ }
+ }
+ }
+ return EventInfo.ROW_TYPE_LIST;
+ }
+
+ /**
* @return the primary action for this list; i.e. action on the header or first row.
*/
@Nullable
public SliceItem getPrimaryAction() {
- RowContent rc = new RowContent(mContext, mHeaderItem, false);
- return rc.getPrimaryAction();
+ if (mHeaderItem != null) {
+ if (mHeaderItem.hasHint(HINT_HORIZONTAL)) {
+ GridContent gc = new GridContent(mContext, mHeaderItem);
+ return gc.getContentIntent();
+ } else {
+ RowContent rc = new RowContent(mContext, mHeaderItem, false);
+ return rc.getPrimaryAction();
+ }
+ }
+ return null;
}
@Nullable
private static SliceItem findHeaderItem(@NonNull Slice slice) {
// See if header is specified
String[] nonHints = new String[] {HINT_LIST_ITEM, HINT_SHORTCUT, HINT_ACTIONS,
- HINT_KEY_WORDS, HINT_TTL, HINT_LAST_UPDATED};
+ HINT_KEYWORDS, HINT_TTL, HINT_LAST_UPDATED};
SliceItem header = SliceQuery.find(slice, FORMAT_SLICE, null, nonHints);
if (header != null && isValidHeader(header)) {
return header;
@@ -239,7 +300,7 @@
private static boolean isValidHeader(SliceItem sliceItem) {
if (FORMAT_SLICE.equals(sliceItem.getFormat()) && !sliceItem.hasAnyHints(HINT_LIST_ITEM,
- HINT_ACTIONS, HINT_KEY_WORDS)) {
+ HINT_ACTIONS, HINT_KEYWORDS)) {
// Minimum valid header is a slice with text
SliceItem item = SliceQuery.find(sliceItem, FORMAT_TEXT, (String) null, null);
return item != null;
diff --git a/slices/view/src/main/java/androidx/slice/widget/MessageView.java b/slices/view/src/main/java/androidx/slice/widget/MessageView.java
index 98d1a92..6035d0a 100644
--- a/slices/view/src/main/java/androidx/slice/widget/MessageView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/MessageView.java
@@ -24,18 +24,18 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
-import androidx.annotation.RestrictTo;
import android.text.SpannableStringBuilder;
import android.util.TypedValue;
import android.widget.ImageView;
import android.widget.TextView;
-import java.util.function.Consumer;
-
+import androidx.annotation.RestrictTo;
import androidx.slice.Slice;
import androidx.slice.SliceItem;
import androidx.slice.core.SliceQuery;
+import java.util.List;
+
/**
* @hide
*/
@@ -91,15 +91,13 @@
mIcon.setImageBitmap(SliceViewUtil.getCircularBitmap(iconBm));
}
final SpannableStringBuilder builder = new SpannableStringBuilder();
- SliceQuery.findAll(slice, FORMAT_TEXT).forEach(new Consumer<SliceItem>() {
- @Override
- public void accept(SliceItem text) {
- if (builder.length() != 0) {
- builder.append('\n');
- }
- builder.append(text.getText());
+ List<SliceItem> items = SliceQuery.findAll(slice, FORMAT_TEXT);
+ for (SliceItem text : items) {
+ if (builder.length() != 0) {
+ builder.append('\n');
}
- });
+ builder.append(text.getText());
+ }
mDetails.setText(builder.toString());
}
}
diff --git a/slices/view/src/main/java/androidx/slice/widget/RemoteInputView.java b/slices/view/src/main/java/androidx/slice/widget/RemoteInputView.java
index be0a7b7..d4ff299 100644
--- a/slices/view/src/main/java/androidx/slice/widget/RemoteInputView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/RemoteInputView.java
@@ -23,9 +23,8 @@
import android.content.Intent;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Bundle;
-import androidx.annotation.RestrictTo;
-import androidx.core.content.ContextCompat;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
@@ -48,6 +47,10 @@
import android.widget.TextView;
import android.widget.Toast;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.core.content.ContextCompat;
+import androidx.slice.SliceItem;
import androidx.slice.view.R;
/**
@@ -57,6 +60,7 @@
*/
// TODO this should be unified with SystemUI RemoteInputView (b/67527720)
@RestrictTo(RestrictTo.Scope.LIBRARY)
+@RequiresApi(21)
public class RemoteInputView extends LinearLayout implements View.OnClickListener, TextWatcher {
private static final String TAG = "RemoteInput";
@@ -69,7 +73,7 @@
private RemoteEditText mEditText;
private ImageButton mSendButton;
private ProgressBar mProgressBar;
- private PendingIntent mPendingIntent;
+ private SliceItem mAction;
private RemoteInput[] mRemoteInputs;
private RemoteInput mRemoteInput;
@@ -136,11 +140,11 @@
// but that's an edge case, and also because we can't always know which package will receive
// an intent, so we just reset for the creator.
//getContext().getSystemService(ShortcutManager.class).onApplicationActive(
- // mPendingIntent.getCreatorPackage(),
+ // mAction.getCreatorPackage(),
// getContext().getUserId());
try {
- mPendingIntent.send(getContext(), 0, fillInIntent);
+ mAction.fireAction(getContext(), fillInIntent);
reset();
} catch (PendingIntent.CanceledException e) {
Log.i(TAG, "Unable to send remote input result", e);
@@ -182,8 +186,8 @@
/**
* Set the pending intent for remote input.
*/
- public void setPendingIntent(PendingIntent pendingIntent) {
- mPendingIntent = pendingIntent;
+ public void setAction(SliceItem action) {
+ mAction = action;
}
/**
@@ -302,8 +306,8 @@
}
private void defocusIfNeeded(boolean animate) {
- if (mRemoteInputView != null || isTemporarilyDetached()) {
- if (isTemporarilyDetached()) {
+ if (mRemoteInputView != null || isTemporarilyDetachedCompat()) {
+ if (isTemporarilyDetachedCompat()) {
// We might get reattached but then the other one of HUN / expanded might steal
// our focus, so we'll need to save our text here.
}
@@ -318,6 +322,13 @@
}
}
+ private boolean isTemporarilyDetachedCompat() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ return isTemporarilyDetached();
+ }
+ return false;
+ }
+
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
diff --git a/slices/view/src/main/java/androidx/slice/widget/RowContent.java b/slices/view/src/main/java/androidx/slice/widget/RowContent.java
index 7565895..728715e 100644
--- a/slices/view/src/main/java/androidx/slice/widget/RowContent.java
+++ b/slices/view/src/main/java/androidx/slice/widget/RowContent.java
@@ -31,7 +31,7 @@
import static android.app.slice.SliceItem.FORMAT_TEXT;
import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
-import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
+import static androidx.slice.core.SliceHints.HINT_KEYWORDS;
import static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
import static androidx.slice.core.SliceHints.HINT_TTL;
import static androidx.slice.core.SliceHints.SUBTYPE_RANGE;
@@ -44,6 +44,8 @@
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.slice.SliceItem;
+import androidx.slice.core.SliceAction;
+import androidx.slice.core.SliceActionImpl;
import androidx.slice.core.SliceQuery;
import androidx.slice.view.R;
@@ -65,6 +67,7 @@
private SliceItem mSubtitleItem;
private SliceItem mSummaryItem;
private ArrayList<SliceItem> mEndItems = new ArrayList<>();
+ private ArrayList<SliceAction> mToggleItems = new ArrayList<>();
private SliceItem mRange;
private SliceItem mContentDescr;
private boolean mEndItemsContainAction;
@@ -72,11 +75,14 @@
private int mLineCount = 0;
private int mMaxHeight;
private int mMinHeight;
+ private int mMaxRangeHeight;
public RowContent(Context context, SliceItem rowSlice, boolean isHeader) {
populate(rowSlice, isHeader);
mMaxHeight = context.getResources().getDimensionPixelSize(R.dimen.abc_slice_row_max_height);
mMinHeight = context.getResources().getDimensionPixelSize(R.dimen.abc_slice_row_min_height);
+ mMaxRangeHeight = context.getResources().getDimensionPixelSize(
+ R.dimen.abc_slice_row_range_max_height);
}
/**
@@ -92,7 +98,7 @@
// Find primary action first (otherwise filtered out of valid row items)
String[] hints = new String[] {HINT_SHORTCUT, HINT_TITLE};
mPrimaryAction = SliceQuery.find(rowSlice, FORMAT_SLICE, hints,
- new String[] { HINT_ACTIONS, HINT_KEY_WORDS } /* nonHints */);
+ new String[] { HINT_ACTIONS, HINT_KEYWORDS} /* nonHints */);
if (mPrimaryAction == null && FORMAT_ACTION.equals(rowSlice.getFormat())
&& rowSlice.getSlice().getItems().size() == 1) {
@@ -149,32 +155,36 @@
if (hasText(mSubtitleItem)) {
mLineCount++;
}
- // Special rules for end items: only one timestamp, can't be mixture of icons / actions
+ // Special rules for end items: only one timestamp
boolean hasTimestamp = mStartItem != null
&& FORMAT_TIMESTAMP.equals(mStartItem.getFormat());
- String desiredFormat = null;
for (int i = 0; i < endItems.size(); i++) {
final SliceItem item = endItems.get(i);
- boolean isAction = FORMAT_SLICE.equals(item.getFormat())
- && item.hasHint(HINT_SHORTCUT);
+ boolean isAction = SliceQuery.find(item, FORMAT_ACTION) != null;
if (FORMAT_TIMESTAMP.equals(item.getFormat())) {
if (!hasTimestamp) {
hasTimestamp = true;
mEndItems.add(item);
}
- } else if (desiredFormat == null) {
- desiredFormat = item.getFormat();
- mEndItems.add(item);
- mEndItemsContainAction |= isAction;
- } else if (desiredFormat.equals(item.getFormat())) {
- mEndItems.add(item);
- mEndItemsContainAction |= isAction;
+ } else {
+ processContent(item, isAction);
}
}
}
return isValid();
}
+ private void processContent(@NonNull SliceItem item, boolean isAction) {
+ if (isAction) {
+ SliceAction ac = new SliceActionImpl(item);
+ if (ac.isToggle()) {
+ mToggleItems.add(ac);
+ }
+ }
+ mEndItems.add(item);
+ mEndItemsContainAction |= isAction;
+ }
+
/**
* @return the {@link SliceItem} used to populate this row.
*/
@@ -236,6 +246,13 @@
}
/**
+ * @return a list of toggles associated with this row.
+ */
+ public ArrayList<SliceAction> getToggleItems() {
+ return mToggleItems;
+ }
+
+ /**
* @return the content description to use for this row.
*/
@Nullable
@@ -261,16 +278,22 @@
* @return the height to display a row at when it is used as a small template.
*/
public int getSmallHeight() {
- return mMaxHeight;
+ return (getRange() != null && mLineCount > 1)
+ ? mMaxRangeHeight
+ : mMaxHeight;
}
/**
* @return the height the content in this template requires to be displayed.
*/
public int getActualHeight() {
- return isValid()
- ? (getLineCount() > 1 || mIsHeader) ? mMaxHeight : mMinHeight
- : 0;
+ if (!isValid()) {
+ return 0;
+ }
+ if (getRange() != null && mLineCount > 1) {
+ return mMaxRangeHeight;
+ }
+ return (getLineCount() > 1 || mIsHeader) ? mMaxHeight : mMinHeight;
}
private static boolean hasText(SliceItem textSlice) {
@@ -342,7 +365,7 @@
* @return whether this item is valid content to display in a row.
*/
private static boolean isValidRowContent(SliceItem slice, SliceItem item) {
- if (item.hasAnyHints(HINT_KEY_WORDS, HINT_TTL, HINT_LAST_UPDATED)) {
+ if (item.hasAnyHints(HINT_KEYWORDS, HINT_TTL, HINT_LAST_UPDATED)) {
return false;
}
if (FORMAT_SLICE.equals(item.getFormat()) && !item.hasHint(HINT_SHORTCUT)) {
diff --git a/slices/view/src/main/java/androidx/slice/widget/RowView.java b/slices/view/src/main/java/androidx/slice/widget/RowView.java
index 21264f5..782b5f4 100644
--- a/slices/view/src/main/java/androidx/slice/widget/RowView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/RowView.java
@@ -17,6 +17,7 @@
package androidx.slice.widget;
import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
+import static android.app.slice.Slice.HINT_LIST_ITEM;
import static android.app.slice.Slice.HINT_NO_TINT;
import static android.app.slice.Slice.HINT_PARTIAL;
import static android.app.slice.Slice.HINT_SHORTCUT;
@@ -34,11 +35,11 @@
import static androidx.slice.core.SliceHints.SUBTYPE_VALUE;
import static androidx.slice.widget.SliceView.MODE_SMALL;
-import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.StyleSpan;
@@ -59,6 +60,7 @@
import androidx.annotation.ColorInt;
import androidx.annotation.RestrictTo;
+import androidx.core.graphics.drawable.DrawableCompat;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
import androidx.slice.SliceItem;
@@ -83,6 +85,7 @@
// The number of items that fit on the right hand side of a small slice
private static final int MAX_END_ITEMS = 3;
+ private LinearLayout mRootView;
private LinearLayout mStartContainer;
private LinearLayout mContent;
private TextView mPrimaryText;
@@ -112,6 +115,7 @@
R.dimen.abc_slice_small_image_size);
mPadding = getContext().getResources().getDimensionPixelSize(R.dimen.abc_slice_padding);
inflate(context, R.layout.abc_slice_small_template, this);
+ mRootView = findViewById(R.id.row_view);
mStartContainer = (LinearLayout) findViewById(R.id.icon_frame);
mContent = (LinearLayout) findViewById(android.R.id.content);
@@ -168,6 +172,11 @@
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
+ @Override
+ public void setSlice(Slice slice) {
+ // Nothing to do
+ }
+
/**
* This is called when RowView is being used as a component in a large template.
*/
@@ -176,25 +185,12 @@
SliceView.OnSliceActionListener observer) {
setSliceActionListener(observer);
mRowIndex = index;
- mIsHeader = isHeader;
+ mIsHeader = !slice.hasHint(HINT_LIST_ITEM);
mHeaderActions = null;
mRowContent = new RowContent(getContext(), slice, mIsHeader);
populateViews();
}
- /**
- * This is called when RowView is being used as a small template.
- */
- @Override
- public void setSlice(Slice slice) {
- mRowIndex = 0;
- mIsHeader = true;
- mHeaderActions = null;
- ListContent lc = new ListContent(getContext(), slice);
- mRowContent = new RowContent(getContext(), lc.getHeaderItem(), true /* isHeader */);
- populateViews();
- }
-
private void populateViews() {
resetView();
if (mRowContent.isDefaultSeeMore()) {
@@ -231,59 +227,54 @@
: mRowContent.getSubtitleItem();
addSubtitle(subtitleItem);
- final SliceItem range = mRowContent.getRange();
- if (range != null) {
- addRange(range);
- return;
- }
-
SliceItem primaryAction = mRowContent.getPrimaryAction();
if (primaryAction != null) {
mRowAction = new SliceActionImpl(primaryAction);
if (mRowAction.isToggle()) {
// If primary action is a toggle, add it and we're done
addToggle(mRowAction, mTintColor, mEndContainer);
- setViewClickable(this, true);
+ setViewClickable(mRootView, true);
return;
}
}
+ final SliceItem range = mRowContent.getRange();
+ if (range != null) {
+ if (mRowAction != null) {
+ setViewClickable(mRootView, true);
+ }
+ addRange(range);
+ return;
+ }
+
// If we're here we can can show end items; check for top level actions first
List<SliceItem> endItems = mRowContent.getEndItems();
- String desiredFormat = FORMAT_ACTION;
if (mIsHeader && mHeaderActions != null && mHeaderActions.size() > 0) {
// Use these if we have them instead
endItems = mHeaderActions;
- } else if (!endItems.isEmpty()) {
- // Prefer to show actions as end items if possible; fall back to the first format type.
- SliceItem firstEndItem = endItems.get(0);
- desiredFormat = mRowContent.endItemsContainAction()
- ? FORMAT_ACTION : firstEndItem.getSlice().getItems().get(0).getFormat();
}
boolean hasRowAction = mRowAction != null;
if (endItems.isEmpty()) {
- if (hasRowAction) setViewClickable(this, true);
+ if (hasRowAction) setViewClickable(mRootView, true);
return;
}
// If we're here we might be able to show end items
int itemCount = 0;
boolean firstItemIsADefaultToggle = false;
+ boolean hasEndItemAction = false;
for (int i = 0; i < endItems.size(); i++) {
final SliceItem endItem = endItems.get(i);
- final String endFormat = endItem.hasHint(HINT_SHORTCUT)
- ? FORMAT_ACTION
- : endItem.getSlice().getItems().get(0).getFormat();
- // Only show one type of format at the end of the slice, use whatever is first
- if (itemCount < MAX_END_ITEMS
- && (desiredFormat.equals(endFormat)
- || FORMAT_TIMESTAMP.equals(endFormat))) {
+ if (itemCount < MAX_END_ITEMS) {
final EventInfo info = new EventInfo(getMode(),
EventInfo.ACTION_TYPE_BUTTON,
EventInfo.ROW_TYPE_LIST, mRowIndex);
info.setPosition(EventInfo.POSITION_END, i,
Math.min(endItems.size(), MAX_END_ITEMS));
if (addItem(endItem, mTintColor, false /* isStart */, mPadding, info)) {
+ if (FORMAT_ACTION.equals(endItem.getFormat())) {
+ hasEndItemAction = true;
+ }
itemCount++;
if (itemCount == 1) {
firstItemIsADefaultToggle = !mToggles.isEmpty()
@@ -293,7 +284,6 @@
}
}
- boolean hasEndItemAction = FORMAT_ACTION.contentEquals(desiredFormat);
// If there is a row action and the first end item is a default toggle, show the divider.
mDivider.setVisibility(hasRowAction && firstItemIsADefaultToggle
? View.VISIBLE : View.GONE);
@@ -301,17 +291,15 @@
if (itemCount > 0 && hasEndItemAction) {
setViewClickable(mContent, true);
} else {
- setViewClickable(this, true);
+ setViewClickable(mRootView, true);
}
- } else {
+ } else if (mRowContent.endItemsContainAction() && itemCount == 1) {
// If the only end item is an action, make the whole row clickable.
- if (mRowContent.endItemsContainAction() && itemCount == 1) {
- SliceItem unwrappedActionItem = endItems.get(0).getSlice().getItems().get(0);
- if (!SUBTYPE_TOGGLE.equals(unwrappedActionItem.getSubType())) {
- mRowAction = new SliceActionImpl(endItems.get(0));
- }
- setViewClickable(this, true);
+ SliceItem unwrappedActionItem = endItems.get(0).getSlice().getItems().get(0);
+ if (!SUBTYPE_TOGGLE.equals(unwrappedActionItem.getSubType())) {
+ mRowAction = new SliceActionImpl(endItems.get(0));
}
+ setViewClickable(mRootView, true);
}
}
@@ -358,19 +346,28 @@
progressBar.setProgress(progress.getInt());
}
progressBar.setVisibility(View.VISIBLE);
+ if (mTintColor != -1) {
+ Drawable drawable = DrawableCompat.wrap(progressBar.getProgressDrawable());
+ DrawableCompat.setTint(drawable, mTintColor);
+ mProgressBar.setProgressDrawable(drawable);
+ }
if (isSeekBar) {
SliceItem thumb = SliceQuery.find(range, FORMAT_IMAGE);
if (thumb != null) {
mSeekBar.setThumb(thumb.getIcon().loadDrawable(getContext()));
}
+ if (mTintColor != -1) {
+ Drawable drawable = DrawableCompat.wrap(mSeekBar.getThumb());
+ DrawableCompat.setTint(drawable, mTintColor);
+ mSeekBar.setThumb(drawable);
+ }
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
try {
- PendingIntent pi = range.getAction();
- Intent i = new Intent().putExtra(EXTRA_RANGE_VALUE, progress);
// TODO: sending this PendingIntent should be rate limited.
- pi.send(getContext(), 0, i, null, null);
+ range.fireAction(getContext(),
+ new Intent().putExtra(EXTRA_RANGE_VALUE, progress));
} catch (CanceledException e) { }
}
@@ -417,13 +414,12 @@
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
try {
- PendingIntent pi = actionContent.getAction();
Intent i = new Intent().putExtra(EXTRA_TOGGLE_STATE, isChecked);
- pi.send(getContext(), 0, i, null, null);
+ actionContent.getActionItem().fireAction(getContext(), i);
if (mObserver != null) {
final EventInfo info = new EventInfo(getMode(),
EventInfo.ACTION_TYPE_TOGGLE,
- EventInfo.ROW_TYPE_LIST, mRowIndex);
+ EventInfo.ROW_TYPE_TOGGLE, mRowIndex);
info.state = isChecked ? EventInfo.STATE_ON : EventInfo.STATE_OFF;
mObserver.onSliceAction(info, actionContent.getSliceItem());
}
@@ -504,7 +500,7 @@
@Override
public void onClick(View v) {
try {
- finalAction.getAction().send();
+ finalAction.getActionItem().fireAction(null, null);
if (mObserver != null) {
mObserver.onSliceAction(info, finalAction.getSliceItem());
}
@@ -531,7 +527,7 @@
EventInfo.ROW_TYPE_LIST, mRowIndex);
mObserver.onSliceAction(info, mRowContent.getSlice());
}
- mRowContent.getSlice().getAction().send();
+ mRowContent.getSlice().fireAction(null, null);
} catch (CanceledException e) {
Log.w(TAG, "PendingIntent for slice cannot be sent", e);
}
@@ -546,10 +542,10 @@
@Override
public void onClick(View view) {
- if (mRowAction != null && mRowAction.getAction() != null && !mRowAction.isToggle()) {
+ if (mRowAction != null && mRowAction.getActionItem() != null && !mRowAction.isToggle()) {
// Check for a row action
try {
- mRowAction.getAction().send();
+ mRowAction.getActionItem().fireAction(null, null);
if (mObserver != null) {
EventInfo info = new EventInfo(getMode(), EventInfo.ACTION_TYPE_CONTENT,
EventInfo.ROW_TYPE_LIST, mRowIndex);
@@ -566,14 +562,15 @@
private void setViewClickable(View layout, boolean isClickable) {
layout.setOnClickListener(isClickable ? this : null);
- layout.setBackground(isClickable ? SliceViewUtil.getDrawable(getContext(),
- android.R.attr.selectableItemBackground) : null);
+ layout.setBackground(isClickable
+ ? SliceViewUtil.getDrawable(getContext(), android.R.attr.selectableItemBackground)
+ : null);
layout.setClickable(isClickable);
}
@Override
public void resetView() {
- setViewClickable(this, false);
+ setViewClickable(mRootView, false);
setViewClickable(mContent, false);
mStartContainer.removeAllViews();
mEndContainer.removeAllViews();
diff --git a/slices/view/src/main/java/androidx/slice/widget/ShortcutView.java b/slices/view/src/main/java/androidx/slice/widget/ShortcutView.java
index b068c26..d200512 100644
--- a/slices/view/src/main/java/androidx/slice/widget/ShortcutView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/ShortcutView.java
@@ -114,7 +114,7 @@
if (!callOnClick()) {
try {
if (mActionItem != null) {
- mActionItem.getAction().send();
+ mActionItem.fireAction(null, null);
} else {
Intent intent = new Intent(Intent.ACTION_VIEW).setData(mUri);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
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 aa73ad5..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);
@@ -134,4 +132,7 @@
}
};
}
+
+ private SliceLiveData() {
+ }
}
diff --git a/slices/view/src/main/java/androidx/slice/widget/SliceView.java b/slices/view/src/main/java/androidx/slice/widget/SliceView.java
index 272bbdc..c9acef4 100644
--- a/slices/view/src/main/java/androidx/slice/widget/SliceView.java
+++ b/slices/view/src/main/java/androidx/slice/widget/SliceView.java
@@ -16,30 +16,38 @@
package androidx.slice.widget;
-import static android.app.slice.Slice.HINT_HORIZONTAL;
import static android.app.slice.Slice.SUBTYPE_COLOR;
import static android.app.slice.SliceItem.FORMAT_INT;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.ColorDrawable;
+import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.HapticFeedbackConstants;
+import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewGroup;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.lifecycle.Observer;
import androidx.slice.Slice;
import androidx.slice.SliceItem;
import androidx.slice.SliceMetadata;
+import androidx.slice.core.SliceActionImpl;
import androidx.slice.core.SliceHints;
import androidx.slice.core.SliceQuery;
import androidx.slice.view.R;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
@@ -73,7 +81,7 @@
* </pre>
* @see SliceLiveData
*/
-public class SliceView extends ViewGroup implements Observer<Slice> {
+public class SliceView extends ViewGroup implements Observer<Slice>, View.OnClickListener {
private static final String TAG = "SliceView";
@@ -98,6 +106,7 @@
@IntDef({
MODE_SMALL, MODE_LARGE, MODE_SHORTCUT
})
+ @Retention(RetentionPolicy.SOURCE)
public @interface SliceMode {}
/**
@@ -117,23 +126,33 @@
private int mMode = MODE_LARGE;
private Slice mCurrentSlice;
+ private ListContent mListContent;
private SliceChildView mCurrentView;
private List<SliceItem> mActions;
- private final ActionRow mActionRow;
+ private ActionRow mActionRow;
private boolean mShowActions = false;
private boolean mIsScrollable = true;
private boolean mShowLastUpdated = true;
- private final int mShortcutSize;
- private final int mMinLargeHeight;
- private final int mMaxLargeHeight;
- private final int mActionRowHeight;
+ private int mShortcutSize;
+ private int mMinLargeHeight;
+ private int mMaxLargeHeight;
+ private int mActionRowHeight;
private AttributeSet mAttrs;
private int mThemeTintColor = -1;
private OnSliceActionListener mSliceObserver;
+ private int mTouchSlopSquared;
+ private View.OnLongClickListener mLongClickListener;
+ private View.OnClickListener mOnClickListener;
+ private int mDownX;
+ private int mDownY;
+ private boolean mPressing;
+ private boolean mInLongpress;
+ private Handler mHandler;
+ int[] mClickInfo;
public SliceView(Context context) {
this(context, null);
@@ -144,11 +163,17 @@
}
public SliceView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, R.style.Widget_SliceView);
+ super(context, attrs, defStyleAttr);
+ init(context, attrs, defStyleAttr, R.style.Widget_SliceView);
}
+ @RequiresApi(21)
public SliceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ init(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
mAttrs = attrs;
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SliceView,
defStyleAttr, defStyleRes);
@@ -157,19 +182,126 @@
} finally {
a.recycle();
}
- // TODO: action row background should support light / dark / maybe presenter customization
- mActionRow = new ActionRow(getContext(), true);
- mActionRow.setBackground(new ColorDrawable(0xffeeeeee));
- mCurrentView = new LargeTemplateView(getContext());
- mCurrentView.setMode(getMode());
- addView(mCurrentView.getView(), getChildLp(mCurrentView.getView()));
- addView(mActionRow, getChildLp(mActionRow));
mShortcutSize = getContext().getResources()
.getDimensionPixelSize(R.dimen.abc_slice_shortcut_size);
mMinLargeHeight = getResources().getDimensionPixelSize(R.dimen.abc_slice_large_height);
mMaxLargeHeight = getResources().getDimensionPixelSize(R.dimen.abc_slice_max_large_height);
mActionRowHeight = getResources().getDimensionPixelSize(
R.dimen.abc_slice_action_row_height);
+
+ mCurrentView = new LargeTemplateView(getContext());
+ mCurrentView.setMode(getMode());
+ addView(mCurrentView, getChildLp(mCurrentView));
+
+ // TODO: action row background should support light / dark / maybe presenter customization
+ mActionRow = new ActionRow(getContext(), true);
+ mActionRow.setBackground(new ColorDrawable(0xffeeeeee));
+ addView(mActionRow, getChildLp(mActionRow));
+
+ final int slop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+ mTouchSlopSquared = slop * slop;
+ mHandler = new Handler();
+
+ super.setOnClickListener(this);
+ }
+
+ /**
+ * Indicates whether this view reacts to click events or not.
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public boolean isSliceViewClickable() {
+ return mOnClickListener != null
+ || (mListContent != null && mListContent.getPrimaryAction() != null);
+ }
+
+ /**
+ * Sets the event info for logging a click.
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public void setClickInfo(int[] info) {
+ mClickInfo = info;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (mListContent != null && mListContent.getPrimaryAction() != null) {
+ try {
+ SliceActionImpl sa = new SliceActionImpl(mListContent.getPrimaryAction());
+ sa.getAction().send();
+ if (mSliceObserver != null && mClickInfo != null && mClickInfo.length > 1) {
+ EventInfo eventInfo = new EventInfo(getMode(),
+ EventInfo.ACTION_TYPE_CONTENT, mClickInfo[0], mClickInfo[1]);
+ mSliceObserver.onSliceAction(eventInfo, mListContent.getPrimaryAction());
+ }
+ } catch (PendingIntent.CanceledException e) {
+ e.printStackTrace();
+ }
+ } else if (mOnClickListener != null) {
+ mOnClickListener.onClick(this);
+ }
+ }
+
+ @Override
+ public void setOnClickListener(View.OnClickListener listener) {
+ mOnClickListener = listener;
+ }
+
+ @Override
+ public void setOnLongClickListener(View.OnLongClickListener listener) {
+ super.setOnLongClickListener(listener);
+ mLongClickListener = listener;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ boolean ret = super.onInterceptTouchEvent(ev);
+ if (mLongClickListener != null) {
+ return handleTouchForLongpress(ev);
+ }
+ return ret;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ boolean ret = super.onTouchEvent(ev);
+ if (mLongClickListener != null) {
+ return handleTouchForLongpress(ev);
+ }
+ return ret;
+ }
+
+ private boolean handleTouchForLongpress(MotionEvent ev) {
+ int action = ev.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mHandler.removeCallbacks(mLongpressCheck);
+ mDownX = (int) ev.getRawX();
+ mDownY = (int) ev.getRawY();
+ mPressing = true;
+ mInLongpress = false;
+ mHandler.postDelayed(mLongpressCheck, ViewConfiguration.getLongPressTimeout());
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ final int deltaX = (int) ev.getRawX() - mDownX;
+ final int deltaY = (int) ev.getRawY() - mDownY;
+ int distance = (deltaX * deltaX) + (deltaY * deltaY);
+ if (distance > mTouchSlopSquared) {
+ mPressing = false;
+ mHandler.removeCallbacks(mLongpressCheck);
+ }
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ mPressing = false;
+ mInLongpress = false;
+ mHandler.removeCallbacks(mLongpressCheck);
+ break;
+ }
+ return mInLongpress;
}
private int getHeightForMode() {
@@ -348,18 +480,6 @@
}
/**
- * Sets whether this view should display when the slice was last updated.
- *
- * @param showLastUpdated whether the view should display when the slice was last updated.
- * @hide
- */
- @RestrictTo(RestrictTo.Scope.LIBRARY)
- public void setShowLastUpdated(boolean showLastUpdated) {
- mShowLastUpdated = showLastUpdated;
- mCurrentView.setShowLastUpdated(showLastUpdated);
- }
-
- /**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
@@ -401,53 +521,37 @@
return mShowActions;
}
- private SliceChildView createView(int mode, boolean isGrid) {
- switch (mode) {
- case MODE_SHORTCUT:
- return new ShortcutView(getContext());
- case MODE_SMALL:
- return isGrid ? new GridRowView(getContext()) : new RowView(getContext());
- }
- return new LargeTemplateView(getContext());
- }
-
private void reinflate() {
if (mCurrentSlice == null) {
mCurrentView.resetView();
return;
}
- ListContent lc = new ListContent(getContext(), mCurrentSlice);
- if (!lc.isValid()) {
+ mListContent = new ListContent(getContext(), mCurrentSlice);
+ if (!mListContent.isValid()) {
mCurrentView.resetView();
- mCurrentView.setVisibility(View.GONE);
return;
}
+
// TODO: Smarter mapping here from one state to the next.
int mode = getMode();
- boolean reuseView = mode == mCurrentView.getMode();
- SliceItem header = lc.getHeaderItem();
- boolean isSmallGrid = header != null && SliceQuery.hasHints(header, HINT_HORIZONTAL);
- if (reuseView && mode == MODE_SMALL) {
- reuseView = (mCurrentView instanceof GridRowView) == isSmallGrid;
- }
- if (!reuseView) {
+ boolean isCurrentViewShortcut = mCurrentView instanceof ShortcutView;
+ if (mode == MODE_SHORTCUT && !isCurrentViewShortcut) {
removeAllViews();
- mCurrentView = createView(mode, isSmallGrid);
- if (mSliceObserver != null) {
- mCurrentView.setSliceActionListener(mSliceObserver);
- }
- addView(mCurrentView.getView(), getChildLp(mCurrentView.getView()));
- addView(mActionRow, getChildLp(mActionRow));
- mCurrentView.setMode(mode);
+ mCurrentView = new ShortcutView(getContext());
+ addView(mCurrentView, getChildLp(mCurrentView));
+ } else if (mode != MODE_SHORTCUT && isCurrentViewShortcut) {
+ removeAllViews();
+ mCurrentView = new LargeTemplateView(getContext());
+ addView(mCurrentView, getChildLp(mCurrentView));
}
- // Scrolling
- if (mode == MODE_LARGE && (mCurrentView instanceof LargeTemplateView)) {
+ mCurrentView.setMode(mode);
+
+ mCurrentView.setSliceActionListener(mSliceObserver);
+ if (mCurrentView instanceof LargeTemplateView) {
((LargeTemplateView) mCurrentView).setScrollable(mIsScrollable);
}
- // Styles
mCurrentView.setStyle(mAttrs);
mCurrentView.setTint(getTintColor());
- mCurrentView.setVisibility(lc.isValid() ? View.VISIBLE : View.GONE);
// Check if the slice content is expired and show when it was last updated
SliceMetadata sliceMetadata = SliceMetadata.from(getContext(), mCurrentSlice);
@@ -455,7 +559,7 @@
long expiry = sliceMetadata.getExpiry();
long now = System.currentTimeMillis();
mCurrentView.setLastUpdated(lastUpdated);
- boolean expired = expiry != SliceHints.INFINITY && now > expiry;
+ boolean expired = expiry != 0 && expiry != SliceHints.INFINITY && now > expiry;
mCurrentView.setShowLastUpdated(mShowLastUpdated && expired);
// Set the slice
@@ -524,4 +628,15 @@
return "unknown mode: " + mode;
}
}
+
+ Runnable mLongpressCheck = new Runnable() {
+ @Override
+ public void run() {
+ if (mPressing && mLongClickListener != null) {
+ mInLongpress = true;
+ mLongClickListener.onLongClick(SliceView.this);
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ }
+ }
+ };
}
diff --git a/slices/view/src/main/java/androidx/slice/widget/SliceViewUtil.java b/slices/view/src/main/java/androidx/slice/widget/SliceViewUtil.java
index c8e3d74..78ac154 100644
--- a/slices/view/src/main/java/androidx/slice/widget/SliceViewUtil.java
+++ b/slices/view/src/main/java/androidx/slice/widget/SliceViewUtil.java
@@ -38,7 +38,6 @@
import androidx.annotation.AttrRes;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.IconCompat;
@@ -139,7 +138,6 @@
/**
*/
- @RequiresApi(23)
public static IconCompat createIconFromDrawable(Drawable d) {
if (d instanceof BitmapDrawable) {
return IconCompat.createWithBitmap(((BitmapDrawable) d).getBitmap());
@@ -200,4 +198,7 @@
return DateUtils.getRelativeTimeSpanString(time, Calendar.getInstance().getTimeInMillis(),
DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE);
}
+
+ private SliceViewUtil() {
+ }
}
diff --git a/slices/view/src/main/res/layout/abc_slice_small_template.xml b/slices/view/src/main/res/layout/abc_slice_small_template.xml
index 1327066..867de1f 100644
--- a/slices/view/src/main/res/layout/abc_slice_small_template.xml
+++ b/slices/view/src/main/res/layout/abc_slice_small_template.xml
@@ -16,11 +16,11 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/row_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:layout_gravity="center_vertical"
- android:background="?android:attr/activatedBackgroundIndicator"
android:orientation="horizontal"
android:clipToPadding="false">
@@ -91,7 +91,8 @@
android:background="?android:attr/listDivider"
android:visibility="gone"/>
- <LinearLayout android:id="@android:id/widget_frame"
+ <LinearLayout
+ android:id="@android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="8dp"
diff --git a/slices/view/src/main/res/values-af/strings.xml b/slices/view/src/main/res/values-af/strings.xml
index 812f811..cfe8138 100644
--- a/slices/view/src/main/res/values-af/strings.xml
+++ b/slices/view/src/main/res/values-af/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Opgedateer om <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min. gelede</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> min. gelede</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> jaar gelede</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> jaar gelede</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> dae gelede</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> dag gelede</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Kon nie koppel nie"</string>
</resources>
diff --git a/slices/view/src/main/res/values-am/strings.xml b/slices/view/src/main/res/values-am/strings.xml
index 86c2c4e..da542e1 100644
--- a/slices/view/src/main/res/values-am/strings.xml
+++ b/slices/view/src/main/res/values-am/strings.xml
@@ -20,4 +20,18 @@
<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>
+ <string name="abc_slice_updated" msgid="8155085405396453848">"የተዘመነው <xliff:g id="TIME">%1$s</xliff:g> ላይ"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> ደቂቃ በፊት</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ደቂቃ በፊት</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> ዓመት በፊት</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ዓመት በፊት</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> ቀናት በፊት</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ቀናት በፊት</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"መገናኘት አልተቻለም"</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 3acde5d..1e9ca94 100644
--- a/slices/view/src/main/res/values-ar/strings.xml
+++ b/slices/view/src/main/res/values-ar/strings.xml
@@ -20,4 +20,30 @@
<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>
+ <string name="abc_slice_updated" msgid="8155085405396453848">"وقت التحديث الأخير: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="zero">قبل <xliff:g id="ID_2">%d</xliff:g> دقيقة</item>
+ <item quantity="two">قبل دقيقتين (<xliff:g id="ID_2">%d</xliff:g>)</item>
+ <item quantity="few">قبل <xliff:g id="ID_2">%d</xliff:g> دقائق</item>
+ <item quantity="many">قبل <xliff:g id="ID_2">%d</xliff:g> دقيقة</item>
+ <item quantity="other">قبل <xliff:g id="ID_2">%d</xliff:g> دقيقة</item>
+ <item quantity="one">قبل <xliff:g id="ID_1">%d</xliff:g> دقيقة</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="zero">قبل <xliff:g id="ID_2">%d</xliff:g> سنة</item>
+ <item quantity="two">قبل سنتين (<xliff:g id="ID_2">%d</xliff:g>)</item>
+ <item quantity="few">قبل <xliff:g id="ID_2">%d</xliff:g> سنوات</item>
+ <item quantity="many">قبل <xliff:g id="ID_2">%d</xliff:g> سنة</item>
+ <item quantity="other">قبل <xliff:g id="ID_2">%d</xliff:g> سنة</item>
+ <item quantity="one">قبل <xliff:g id="ID_1">%d</xliff:g> سنة</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="zero">قبل <xliff:g id="ID_2">%d</xliff:g> يوم</item>
+ <item quantity="two">قبل يومين (<xliff:g id="ID_2">%d</xliff:g>)</item>
+ <item quantity="few">قبل <xliff:g id="ID_2">%d</xliff:g> أيام</item>
+ <item quantity="many">قبل <xliff:g id="ID_2">%d</xliff:g> يومًا</item>
+ <item quantity="other">قبل <xliff:g id="ID_2">%d</xliff:g> يوم</item>
+ <item quantity="one">قبل <xliff:g id="ID_1">%d</xliff:g> يوم</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"تعذّر الاتصال."</string>
</resources>
diff --git a/slices/view/src/main/res/values-as/strings.xml b/slices/view/src/main/res/values-as/strings.xml
new file mode 100644
index 0000000..77f9af6
--- /dev/null
+++ b/slices/view/src/main/res/values-as/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 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.
+ -->
+
+<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>
+ <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> আপডেট কৰা হৈছিল"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"সংযোগ কৰিব পৰা নগ\'ল"</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 8ff3908..837f08c 100644
--- a/slices/view/src/main/res/values-az/strings.xml
+++ b/slices/view/src/main/res/values-az/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> tarixində yenilənib"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> dəq əvvəl</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> dəq əvvəl</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> il əvvəl</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> il əvvəl</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> gün əvvəl</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> gün əvvəl</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Qoşulmaq mümkün olmadı"</string>
</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 22e42e7..8deb17c 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
@@ -20,4 +20,21 @@
<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>
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Ažurirano <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="one">pre <xliff:g id="ID_2">%d</xliff:g> min</item>
+ <item quantity="few">pre <xliff:g id="ID_2">%d</xliff:g> min</item>
+ <item quantity="other">pre <xliff:g id="ID_2">%d</xliff:g> min</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="one">pre <xliff:g id="ID_2">%d</xliff:g> god</item>
+ <item quantity="few">pre <xliff:g id="ID_2">%d</xliff:g> god</item>
+ <item quantity="other">pre <xliff:g id="ID_2">%d</xliff:g> god</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="one">pre <xliff:g id="ID_2">%d</xliff:g> dan</item>
+ <item quantity="few">pre <xliff:g id="ID_2">%d</xliff:g> dana</item>
+ <item quantity="other">pre <xliff:g id="ID_2">%d</xliff:g> dana</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Povezivanje nije uspelo"</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 6ea856b..f1ae045 100644
--- a/slices/view/src/main/res/values-be/strings.xml
+++ b/slices/view/src/main/res/values-be/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Абноўлена <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Не атрымалася падключыцца"</string>
</resources>
diff --git a/slices/view/src/main/res/values-bg/strings.xml b/slices/view/src/main/res/values-bg/strings.xml
index 882def9..b019a46 100644
--- a/slices/view/src/main/res/values-bg/strings.xml
+++ b/slices/view/src/main/res/values-bg/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Актуализирано <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other">Преди <xliff:g id="ID_2">%d</xliff:g> мин</item>
+ <item quantity="one">Преди <xliff:g id="ID_1">%d</xliff:g> мин</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other">Преди <xliff:g id="ID_2">%d</xliff:g> год</item>
+ <item quantity="one">Преди <xliff:g id="ID_1">%d</xliff:g> год</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other">Преди <xliff:g id="ID_2">%d</xliff:g> дни</item>
+ <item quantity="one">Преди <xliff:g id="ID_1">%d</xliff:g> ден</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Не можа да се установи връзка"</string>
</resources>
diff --git a/slices/view/src/main/res/values-bn/strings.xml b/slices/view/src/main/res/values-bn/strings.xml
index eb83395..3b8acdf 100644
--- a/slices/view/src/main/res/values-bn/strings.xml
+++ b/slices/view/src/main/res/values-bn/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> আপডেট করা হয়েছে"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"কানেক্ট করা যায়নি"</string>
</resources>
diff --git a/slices/view/src/main/res/values-bs/strings.xml b/slices/view/src/main/res/values-bs/strings.xml
index 693f885..cb84c18 100644
--- a/slices/view/src/main/res/values-bs/strings.xml
+++ b/slices/view/src/main/res/values-bs/strings.xml
@@ -20,8 +20,21 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Ažurirano <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="one">Prije <xliff:g id="ID_2">%d</xliff:g> min.</item>
+ <item quantity="few">Prije <xliff:g id="ID_2">%d</xliff:g> min.</item>
+ <item quantity="other">Prije <xliff:g id="ID_2">%d</xliff:g> min.</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="one">Prije <xliff:g id="ID_2">%d</xliff:g> god.</item>
+ <item quantity="few">Prije <xliff:g id="ID_2">%d</xliff:g> god.</item>
+ <item quantity="other">Prije <xliff:g id="ID_2">%d</xliff:g> god.</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="one">Prije <xliff:g id="ID_2">%d</xliff:g> dan</item>
+ <item quantity="few">Prije <xliff:g id="ID_2">%d</xliff:g> dana</item>
+ <item quantity="other">Prije <xliff:g id="ID_2">%d</xliff:g> dana</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Povezivanje nije uspjelo"</string>
</resources>
diff --git a/slices/view/src/main/res/values-ca/strings.xml b/slices/view/src/main/res/values-ca/strings.xml
index 4ac9c6d..52e1988 100644
--- a/slices/view/src/main/res/values-ca/strings.xml
+++ b/slices/view/src/main/res/values-ca/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"S\'ha actualitzat <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"No s\'ha pogut connectar"</string>
</resources>
diff --git a/slices/view/src/main/res/values-cs/strings.xml b/slices/view/src/main/res/values-cs/strings.xml
index 278810c..c4c1fce 100644
--- a/slices/view/src/main/res/values-cs/strings.xml
+++ b/slices/view/src/main/res/values-cs/strings.xml
@@ -20,4 +20,24 @@
<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>
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Aktualizováno <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="few">před <xliff:g id="ID_2">%d</xliff:g> min</item>
+ <item quantity="many">před <xliff:g id="ID_2">%d</xliff:g> min</item>
+ <item quantity="other">před <xliff:g id="ID_2">%d</xliff:g> min</item>
+ <item quantity="one">před <xliff:g id="ID_1">%d</xliff:g> min</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="few">před <xliff:g id="ID_2">%d</xliff:g> lety</item>
+ <item quantity="many">před <xliff:g id="ID_2">%d</xliff:g> roku</item>
+ <item quantity="other">před <xliff:g id="ID_2">%d</xliff:g> lety</item>
+ <item quantity="one">před <xliff:g id="ID_1">%d</xliff:g> rokem</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="few">před <xliff:g id="ID_2">%d</xliff:g> dny</item>
+ <item quantity="many">před <xliff:g id="ID_2">%d</xliff:g> dne</item>
+ <item quantity="other">před <xliff:g id="ID_2">%d</xliff:g> dny</item>
+ <item quantity="one">před <xliff:g id="ID_1">%d</xliff:g> dnem</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Nelze se připojit"</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 806a5fe..33d9c9d 100644
--- a/slices/view/src/main/res/values-da/strings.xml
+++ b/slices/view/src/main/res/values-da/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Opdateret <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Der kunne ikke oprettes forbindelse"</string>
</resources>
diff --git a/slices/view/src/main/res/values-de/strings.xml b/slices/view/src/main/res/values-de/strings.xml
index f6a1e32..96da7eb 100644
--- a/slices/view/src/main/res/values-de/strings.xml
+++ b/slices/view/src/main/res/values-de/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Aktualisiert: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Verbindung nicht möglich"</string>
</resources>
diff --git a/slices/view/src/main/res/values-el/strings.xml b/slices/view/src/main/res/values-el/strings.xml
index 19963f2..31092af 100644
--- a/slices/view/src/main/res/values-el/strings.xml
+++ b/slices/view/src/main/res/values-el/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Ενημερώθηκε <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> λεπ. πριν</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> λεπ. πριν</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> χρ. πριν</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> χρ. πριν</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ημ. πριν</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> ημ. πριν</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Αδυναμία σύνδεσης"</string>
</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 f222ba5..8fa5b45 100644
--- a/slices/view/src/main/res/values-en-rAU/strings.xml
+++ b/slices/view/src/main/res/values-en-rAU/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Updated <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min ago</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> min ago</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> yr ago</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> yr ago</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> days ago</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> day ago</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Couldn\'t connect"</string>
</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 f222ba5..8fa5b45 100644
--- a/slices/view/src/main/res/values-en-rCA/strings.xml
+++ b/slices/view/src/main/res/values-en-rCA/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Updated <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min ago</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> min ago</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> yr ago</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> yr ago</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> days ago</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> day ago</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Couldn\'t connect"</string>
</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 f222ba5..8fa5b45 100644
--- a/slices/view/src/main/res/values-en-rGB/strings.xml
+++ b/slices/view/src/main/res/values-en-rGB/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Updated <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min ago</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> min ago</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> yr ago</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> yr ago</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> days ago</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> day ago</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Couldn\'t connect"</string>
</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 f222ba5..8fa5b45 100644
--- a/slices/view/src/main/res/values-en-rIN/strings.xml
+++ b/slices/view/src/main/res/values-en-rIN/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Updated <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min ago</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> min ago</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> yr ago</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> yr ago</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> days ago</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> day ago</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Couldn\'t connect"</string>
</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 243dccb..b24c1e6 100644
--- a/slices/view/src/main/res/values-en-rXC/strings.xml
+++ b/slices/view/src/main/res/values-en-rXC/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Updated <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min ago</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> min ago</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> yr ago</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> yr ago</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> days ago</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> day ago</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Couldn\'t connect"</string>
</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 7fc5b56..124c40c 100644
--- a/slices/view/src/main/res/values-es-rUS/strings.xml
+++ b/slices/view/src/main/res/values-es-rUS/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Última actualización: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"No se pudo establecer conexión"</string>
</resources>
diff --git a/slices/view/src/main/res/values-es/strings.xml b/slices/view/src/main/res/values-es/strings.xml
index b81e23c..57685ba 100644
--- a/slices/view/src/main/res/values-es/strings.xml
+++ b/slices/view/src/main/res/values-es/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Última actualización: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"No se ha podido establecer la conexión"</string>
</resources>
diff --git a/slices/view/src/main/res/values-et/strings.xml b/slices/view/src/main/res/values-et/strings.xml
index 80bec54..4bd8a92 100644
--- a/slices/view/src/main/res/values-et/strings.xml
+++ b/slices/view/src/main/res/values-et/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Värskendatud kell <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Ühendamine ebaõnnestus"</string>
</resources>
diff --git a/slices/view/src/main/res/values-eu/strings.xml b/slices/view/src/main/res/values-eu/strings.xml
index 295f184..e689ada 100644
--- a/slices/view/src/main/res/values-eu/strings.xml
+++ b/slices/view/src/main/res/values-eu/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Azken eguneratzea: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Ezin izan da konektatu"</string>
</resources>
diff --git a/slices/view/src/main/res/values-fa/strings.xml b/slices/view/src/main/res/values-fa/strings.xml
index 3ef8f85..cb94e5d 100644
--- a/slices/view/src/main/res/values-fa/strings.xml
+++ b/slices/view/src/main/res/values-fa/strings.xml
@@ -20,4 +20,18 @@
<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>
+ <string name="abc_slice_updated" msgid="8155085405396453848">"زمان بهروزرسانی <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> دقیقه قبل</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> دقیقه قبل</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> سال قبل</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> سال قبل</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> روز قبل</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> روز قبل</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"متصل نشد"</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 0e37b9d..bc37611 100644
--- a/slices/view/src/main/res/values-fi/strings.xml
+++ b/slices/view/src/main/res/values-fi/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Päivitetty <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Ei yhteyttä"</string>
</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 cc10117..1563136 100644
--- a/slices/view/src/main/res/values-fr-rCA/strings.xml
+++ b/slices/view/src/main/res/values-fr-rCA/strings.xml
@@ -20,4 +20,9 @@
<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>
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Mise à jour : <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Impossible de se connecter"</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 b4f147b..6762544 100644
--- a/slices/view/src/main/res/values-fr/strings.xml
+++ b/slices/view/src/main/res/values-fr/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Dernière mise à jour : <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Impossible de se connecter"</string>
</resources>
diff --git a/slices/view/src/main/res/values-gl/strings.xml b/slices/view/src/main/res/values-gl/strings.xml
index f8686de..70b31bb 100644
--- a/slices/view/src/main/res/values-gl/strings.xml
+++ b/slices/view/src/main/res/values-gl/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Contido actualizado (<xliff:g id="TIME">%1$s</xliff:g>)"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other">Hai <xliff:g id="ID_2">%d</xliff:g> min</item>
+ <item quantity="one">Hai <xliff:g id="ID_1">%d</xliff:g> min</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other">Hai <xliff:g id="ID_2">%d</xliff:g> anos</item>
+ <item quantity="one">Hai <xliff:g id="ID_1">%d</xliff:g> ano</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other">Hai <xliff:g id="ID_2">%d</xliff:g> días</item>
+ <item quantity="one">Hai <xliff:g id="ID_1">%d</xliff:g> día</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Non se puido establecer conexión"</string>
</resources>
diff --git a/slices/view/src/main/res/values-gu/strings.xml b/slices/view/src/main/res/values-gu/strings.xml
index 4cf8b47..d6a7ee8 100644
--- a/slices/view/src/main/res/values-gu/strings.xml
+++ b/slices/view/src/main/res/values-gu/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> અપડેટ થયું"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"કનેક્ટ કરી શકાયું નથી"</string>
</resources>
diff --git a/slices/view/src/main/res/values-hi/strings.xml b/slices/view/src/main/res/values-hi/strings.xml
index 3688497..380025e 100644
--- a/slices/view/src/main/res/values-hi/strings.xml
+++ b/slices/view/src/main/res/values-hi/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> बजे अपडेट किया गया"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"कनेक्ट नहीं हो पाया"</string>
</resources>
diff --git a/slices/view/src/main/res/values-hr/strings.xml b/slices/view/src/main/res/values-hr/strings.xml
index 5ec52b8..f44b06c 100644
--- a/slices/view/src/main/res/values-hr/strings.xml
+++ b/slices/view/src/main/res/values-hr/strings.xml
@@ -20,8 +20,21 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Ažurirano <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="one">Prije <xliff:g id="ID_2">%d</xliff:g> min</item>
+ <item quantity="few">Prije <xliff:g id="ID_2">%d</xliff:g> min</item>
+ <item quantity="other">Prije <xliff:g id="ID_2">%d</xliff:g> min</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="one">Prije <xliff:g id="ID_2">%d</xliff:g> godinu</item>
+ <item quantity="few">Prije <xliff:g id="ID_2">%d</xliff:g> godine</item>
+ <item quantity="other">Prije <xliff:g id="ID_2">%d</xliff:g> godina</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="one">Prije <xliff:g id="ID_2">%d</xliff:g> dan</item>
+ <item quantity="few">Prije <xliff:g id="ID_2">%d</xliff:g> dana</item>
+ <item quantity="other">Prije <xliff:g id="ID_2">%d</xliff:g> dana</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Povezivanje nije moguće"</string>
</resources>
diff --git a/slices/view/src/main/res/values-hu/strings.xml b/slices/view/src/main/res/values-hu/strings.xml
index 957dd68..c56143b 100644
--- a/slices/view/src/main/res/values-hu/strings.xml
+++ b/slices/view/src/main/res/values-hu/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Frissítve: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Nem sikerült kapcsolódni"</string>
</resources>
diff --git a/slices/view/src/main/res/values-hy/strings.xml b/slices/view/src/main/res/values-hy/strings.xml
index 49e731b..bb6b812 100644
--- a/slices/view/src/main/res/values-hy/strings.xml
+++ b/slices/view/src/main/res/values-hy/strings.xml
@@ -20,4 +20,9 @@
<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>
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Թարմացվել է <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Չհաջողվեց միանալ"</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 7d46931..5641ebb 100644
--- a/slices/view/src/main/res/values-in/strings.xml
+++ b/slices/view/src/main/res/values-in/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Diupdate <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> menit lalu</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> menit lalu</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> tahun lalu</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> tahun lalu</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> hari lalu</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> hari lalu</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Tidak dapat terhubung"</string>
</resources>
diff --git a/slices/view/src/main/res/values-is/strings.xml b/slices/view/src/main/res/values-is/strings.xml
index 231dd20..c059c03 100644
--- a/slices/view/src/main/res/values-is/strings.xml
+++ b/slices/view/src/main/res/values-is/strings.xml
@@ -20,4 +20,9 @@
<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>
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Uppfært <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Tenging mistókst"</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 8f0a761..05410e8 100644
--- a/slices/view/src/main/res/values-it/strings.xml
+++ b/slices/view/src/main/res/values-it/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Aggiornamento: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Impossibile collegarsi"</string>
</resources>
diff --git a/slices/view/src/main/res/values-iw/strings.xml b/slices/view/src/main/res/values-iw/strings.xml
index c160611..5c55fe2 100644
--- a/slices/view/src/main/res/values-iw/strings.xml
+++ b/slices/view/src/main/res/values-iw/strings.xml
@@ -20,4 +20,9 @@
<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>
+ <string name="abc_slice_updated" msgid="8155085405396453848">"עודכן ב-<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"לא ניתן היה להתחבר"</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 a18751f..286949f 100644
--- a/slices/view/src/main/res/values-ja/strings.xml
+++ b/slices/view/src/main/res/values-ja/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"更新時刻: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"接続できませんでした"</string>
</resources>
diff --git a/slices/view/src/main/res/values-ka/strings.xml b/slices/view/src/main/res/values-ka/strings.xml
index 8701f5d..542c6cc 100644
--- a/slices/view/src/main/res/values-ka/strings.xml
+++ b/slices/view/src/main/res/values-ka/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"განახლების დრო: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"დაკავშირება ვერ მოხერხდა"</string>
</resources>
diff --git a/slices/view/src/main/res/values-kk/strings.xml b/slices/view/src/main/res/values-kk/strings.xml
index 1867ada..231a499 100644
--- a/slices/view/src/main/res/values-kk/strings.xml
+++ b/slices/view/src/main/res/values-kk/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Жаңартылған уақыты: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Байланыс орнатылмады"</string>
</resources>
diff --git a/slices/view/src/main/res/values-km/strings.xml b/slices/view/src/main/res/values-km/strings.xml
index 3f50a99..1e78acb 100644
--- a/slices/view/src/main/res/values-km/strings.xml
+++ b/slices/view/src/main/res/values-km/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"បានធ្វើបច្ចុប្បន្នភាពកាលពី <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> នាទីមុន</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> នាទីមុន</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ឆ្នាំមុន</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> ឆ្នាំមុន</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ថ្ងៃមុន</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> ថ្ងៃមុន</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"មិនអាចភ្ជាប់បានទេ"</string>
</resources>
diff --git a/slices/view/src/main/res/values-kn/strings.xml b/slices/view/src/main/res/values-kn/strings.xml
index e8224f3..ed7dcd6 100644
--- a/slices/view/src/main/res/values-kn/strings.xml
+++ b/slices/view/src/main/res/values-kn/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> ನಿಮಿಷದ ಹಿಂದೆ</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ನಿಮಿಷದ ಹಿಂದೆ</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> ವರ್ಷದ ಹಿಂದೆ</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ವರ್ಷದ ಹಿಂದೆ</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> ದಿನಗಳ ಹಿಂದೆ</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ದಿನಗಳ ಹಿಂದೆ</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ"</string>
</resources>
diff --git a/slices/view/src/main/res/values-ko/strings.xml b/slices/view/src/main/res/values-ko/strings.xml
index 8e7cb24..1ec58f0 100644
--- a/slices/view/src/main/res/values-ko/strings.xml
+++ b/slices/view/src/main/res/values-ko/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g>에 업데이트됨"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"연결할 수 없음"</string>
</resources>
diff --git a/slices/view/src/main/res/values-ky/strings.xml b/slices/view/src/main/res/values-ky/strings.xml
index 910ae6d..06244ab 100644
--- a/slices/view/src/main/res/values-ky/strings.xml
+++ b/slices/view/src/main/res/values-ky/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> жаңыртылды"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Туташпай койду"</string>
</resources>
diff --git a/slices/view/src/main/res/values-lo/strings.xml b/slices/view/src/main/res/values-lo/strings.xml
index d266a43..44111ea 100644
--- a/slices/view/src/main/res/values-lo/strings.xml
+++ b/slices/view/src/main/res/values-lo/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"ອັບເດດເມື່ອ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"ບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້"</string>
</resources>
diff --git a/slices/view/src/main/res/values-lt/strings.xml b/slices/view/src/main/res/values-lt/strings.xml
index cc4b804..e4094b4 100644
--- a/slices/view/src/main/res/values-lt/strings.xml
+++ b/slices/view/src/main/res/values-lt/strings.xml
@@ -20,4 +20,24 @@
<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>
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Atnaujinta <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="one">Prieš <xliff:g id="ID_2">%d</xliff:g> min.</item>
+ <item quantity="few">Prieš <xliff:g id="ID_2">%d</xliff:g> min.</item>
+ <item quantity="many">Prieš <xliff:g id="ID_2">%d</xliff:g> min.</item>
+ <item quantity="other">Prieš <xliff:g id="ID_2">%d</xliff:g> min.</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="one">Prieš <xliff:g id="ID_2">%d</xliff:g> m.</item>
+ <item quantity="few">Prieš <xliff:g id="ID_2">%d</xliff:g> m.</item>
+ <item quantity="many">Prieš <xliff:g id="ID_2">%d</xliff:g> m.</item>
+ <item quantity="other">Prieš <xliff:g id="ID_2">%d</xliff:g> m.</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="one">Prieš <xliff:g id="ID_2">%d</xliff:g> d.</item>
+ <item quantity="few">Prieš <xliff:g id="ID_2">%d</xliff:g> d.</item>
+ <item quantity="many">Prieš <xliff:g id="ID_2">%d</xliff:g> d.</item>
+ <item quantity="other">Prieš <xliff:g id="ID_2">%d</xliff:g> d.</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Prisijungti nepavyko"</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 042436d..45be57c 100644
--- a/slices/view/src/main/res/values-lv/strings.xml
+++ b/slices/view/src/main/res/values-lv/strings.xml
@@ -20,4 +20,21 @@
<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>
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Atjaunināts <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="zero">Pirms <xliff:g id="ID_2">%d</xliff:g> minūtēm</item>
+ <item quantity="one">Pirms <xliff:g id="ID_2">%d</xliff:g> minūtes</item>
+ <item quantity="other">Pirms <xliff:g id="ID_2">%d</xliff:g> minūtēm</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="zero">Pirms <xliff:g id="ID_2">%d</xliff:g> gadiem</item>
+ <item quantity="one">Pirms <xliff:g id="ID_2">%d</xliff:g> gada</item>
+ <item quantity="other">Pirms <xliff:g id="ID_2">%d</xliff:g> gadiem</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="zero">Pirms <xliff:g id="ID_2">%d</xliff:g> dienām</item>
+ <item quantity="one">Pirms <xliff:g id="ID_2">%d</xliff:g> dienas</item>
+ <item quantity="other">Pirms <xliff:g id="ID_2">%d</xliff:g> dienām</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Nevarēja izveidot savienojumu"</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 b3c5da4..ff88cd7 100644
--- a/slices/view/src/main/res/values-mk/strings.xml
+++ b/slices/view/src/main/res/values-mk/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Ажурирано <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="one">Пред <xliff:g id="ID_2">%d</xliff:g> мин.</item>
+ <item quantity="other">Пред <xliff:g id="ID_2">%d</xliff:g> мин.</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="one">Пред <xliff:g id="ID_2">%d</xliff:g> год.</item>
+ <item quantity="other">Пред <xliff:g id="ID_2">%d</xliff:g> год.</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="one">Пред <xliff:g id="ID_2">%d</xliff:g> ден</item>
+ <item quantity="other">Пред <xliff:g id="ID_2">%d</xliff:g> дена</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Не може да се поврзе"</string>
</resources>
diff --git a/slices/view/src/main/res/values-ml/strings.xml b/slices/view/src/main/res/values-ml/strings.xml
index e2dc0b1..b67ae80 100644
--- a/slices/view/src/main/res/values-ml/strings.xml
+++ b/slices/view/src/main/res/values-ml/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> അപ്ഡേറ്റ് ചെയ്തു"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> മിനിറ്റ് മുൻപ്</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> മിനിറ്റ് മുൻപ്</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> വർഷം മുൻപ്</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> വർഷം മുൻപ്</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ദിവസം മുൻപ്</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> ദിവസം മുൻപ്</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"കണക്റ്റ് ചെയ്യാനായില്ല"</string>
</resources>
diff --git a/slices/view/src/main/res/values-mn/strings.xml b/slices/view/src/main/res/values-mn/strings.xml
index 740f455..beaa1a3 100644
--- a/slices/view/src/main/res/values-mn/strings.xml
+++ b/slices/view/src/main/res/values-mn/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> шинэчилсэн"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Холбогдож чадсангүй"</string>
</resources>
diff --git a/slices/view/src/main/res/values-mr/strings.xml b/slices/view/src/main/res/values-mr/strings.xml
index ab06bea..693c821 100644
--- a/slices/view/src/main/res/values-mr/strings.xml
+++ b/slices/view/src/main/res/values-mr/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> ला अपडेट केले"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"कनेक्ट करता आले नाही"</string>
</resources>
diff --git a/slices/view/src/main/res/values-ms/strings.xml b/slices/view/src/main/res/values-ms/strings.xml
index 03bda10..eced3de 100644
--- a/slices/view/src/main/res/values-ms/strings.xml
+++ b/slices/view/src/main/res/values-ms/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Dikemas kini pada <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Tidak dapat menyambung"</string>
</resources>
diff --git a/slices/view/src/main/res/values-my/strings.xml b/slices/view/src/main/res/values-my/strings.xml
index 754d40e..4a3e3d4 100644
--- a/slices/view/src/main/res/values-my/strings.xml
+++ b/slices/view/src/main/res/values-my/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> က အပ်ဒိတ်လုပ်ထားသည်"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other">ပြီးခဲ့သော<xliff:g id="ID_2">%d</xliff:g>မိနစ်</item>
+ <item quantity="one">ပြီးခဲ့သော<xliff:g id="ID_1">%d</xliff:g>မိနစ်</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other">ပြီးခဲ့သော <xliff:g id="ID_2">%d</xliff:g>နှစ်</item>
+ <item quantity="one">ပြီးခဲ့သော <xliff:g id="ID_1">%d</xliff:g>နှစ်</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other">ပြီးခဲ့သော <xliff:g id="ID_2">%d</xliff:g> ရက်</item>
+ <item quantity="one">ပြီးခဲ့သော <xliff:g id="ID_1">%d</xliff:g> ရက်</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"ချိတ်ဆက်၍ မရပါ"</string>
</resources>
diff --git a/slices/view/src/main/res/values-nb/strings.xml b/slices/view/src/main/res/values-nb/strings.xml
index e977965..6576626 100644
--- a/slices/view/src/main/res/values-nb/strings.xml
+++ b/slices/view/src/main/res/values-nb/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Oppdatert <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Kunne ikke koble til"</string>
</resources>
diff --git a/slices/view/src/main/res/values-ne/strings.xml b/slices/view/src/main/res/values-ne/strings.xml
index 6a95860..beb8b31 100644
--- a/slices/view/src/main/res/values-ne/strings.xml
+++ b/slices/view/src/main/res/values-ne/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"अद्यावधिक गरिएको समय: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> मिनेटअघि</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> मिनेटअघि</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> वर्षअघि</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> वर्षअघि</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> दिनअघि</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> दिनअघि</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"जडान गर्न सकिएन"</string>
</resources>
diff --git a/slices/view/src/main/res/values-nl/strings.xml b/slices/view/src/main/res/values-nl/strings.xml
index ed28ef2..1ba58e9 100644
--- a/slices/view/src/main/res/values-nl/strings.xml
+++ b/slices/view/src/main/res/values-nl/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Geüpdatet: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Kan geen verbinding maken"</string>
</resources>
diff --git a/slices/view/src/main/res/values-or/strings.xml b/slices/view/src/main/res/values-or/strings.xml
new file mode 100644
index 0000000..1099e80
--- /dev/null
+++ b/slices/view/src/main/res/values-or/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 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.
+ -->
+
+<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>
+ <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g>ରେ ଅପଡେଟ୍ ହୋଇଥିଲା"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"କନେକ୍ଟ ହେଲାନାହିଁ"</string>
+</resources>
diff --git a/slices/view/src/main/res/values-pa/strings.xml b/slices/view/src/main/res/values-pa/strings.xml
index cd6db9b..8926d0f 100644
--- a/slices/view/src/main/res/values-pa/strings.xml
+++ b/slices/view/src/main/res/values-pa/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> ਅੱਪਡੇਟ ਕੀਤੀ ਗਈ"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
</resources>
diff --git a/slices/view/src/main/res/values-pl/strings.xml b/slices/view/src/main/res/values-pl/strings.xml
index 5fe2d2b..fa565f3 100644
--- a/slices/view/src/main/res/values-pl/strings.xml
+++ b/slices/view/src/main/res/values-pl/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Aktualizacja: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Nie udało się połączyć"</string>
</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 a163c4a..79cc7b7 100644
--- a/slices/view/src/main/res/values-pt-rBR/strings.xml
+++ b/slices/view/src/main/res/values-pt-rBR/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Atualizado às <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> min atrás</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min atrás</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> ano atrás</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> anos atrás</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> dia atrás</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> dias atrás</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Não foi possível conectar"</string>
</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 7d4c127..b81ec60 100644
--- a/slices/view/src/main/res/values-pt-rPT/strings.xml
+++ b/slices/view/src/main/res/values-pt-rPT/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Atualizado: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other">Há <xliff:g id="ID_2">%d</xliff:g> minutos.</item>
+ <item quantity="one">Há <xliff:g id="ID_1">%d</xliff:g> minuto.</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other">Há <xliff:g id="ID_2">%d</xliff:g> anos.</item>
+ <item quantity="one">Há <xliff:g id="ID_1">%d</xliff:g> ano.</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other">Há <xliff:g id="ID_2">%d</xliff:g> dias.</item>
+ <item quantity="one">Há <xliff:g id="ID_1">%d</xliff:g> dia.</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Não foi possível ligar."</string>
</resources>
diff --git a/slices/view/src/main/res/values-pt/strings.xml b/slices/view/src/main/res/values-pt/strings.xml
index a163c4a..79cc7b7 100644
--- a/slices/view/src/main/res/values-pt/strings.xml
+++ b/slices/view/src/main/res/values-pt/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Atualizado às <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> min atrás</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> min atrás</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> ano atrás</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> anos atrás</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> dia atrás</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> dias atrás</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Não foi possível conectar"</string>
</resources>
diff --git a/slices/view/src/main/res/values-ro/strings.xml b/slices/view/src/main/res/values-ro/strings.xml
index ed61fde..a3f9ff2 100644
--- a/slices/view/src/main/res/values-ro/strings.xml
+++ b/slices/view/src/main/res/values-ro/strings.xml
@@ -20,8 +20,21 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Actualizat la <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="few">Acum <xliff:g id="ID_2">%d</xliff:g> min.</item>
+ <item quantity="other">Acum <xliff:g id="ID_2">%d</xliff:g> de min.</item>
+ <item quantity="one">Acum <xliff:g id="ID_1">%d</xliff:g> min.</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="few">Acum <xliff:g id="ID_2">%d</xliff:g> ani</item>
+ <item quantity="other">Acum <xliff:g id="ID_2">%d</xliff:g> de ani</item>
+ <item quantity="one">Acum <xliff:g id="ID_1">%d</xliff:g> an</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="few">Acum <xliff:g id="ID_2">%d</xliff:g> zile</item>
+ <item quantity="other">Acum <xliff:g id="ID_2">%d</xliff:g> de zile</item>
+ <item quantity="one">Acum <xliff:g id="ID_1">%d</xliff:g> zi</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Nu s-a putut conecta"</string>
</resources>
diff --git a/slices/view/src/main/res/values-ru/strings.xml b/slices/view/src/main/res/values-ru/strings.xml
index e3e95f7..85953aa 100644
--- a/slices/view/src/main/res/values-ru/strings.xml
+++ b/slices/view/src/main/res/values-ru/strings.xml
@@ -20,4 +20,24 @@
<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>
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Обновлено <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> мин. назад</item>
+ <item quantity="few"><xliff:g id="ID_2">%d</xliff:g> мин. назад</item>
+ <item quantity="many"><xliff:g id="ID_2">%d</xliff:g> мин. назад</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> мин. назад</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> г. назад</item>
+ <item quantity="few"><xliff:g id="ID_2">%d</xliff:g> г. назад</item>
+ <item quantity="many"><xliff:g id="ID_2">%d</xliff:g> лет назад</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> г. назад</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> дн. назад</item>
+ <item quantity="few"><xliff:g id="ID_2">%d</xliff:g> дн. назад</item>
+ <item quantity="many"><xliff:g id="ID_2">%d</xliff:g> дн. назад</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> дн. назад</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Ошибка подключения"</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 58feb78..be85112 100644
--- a/slices/view/src/main/res/values-si/strings.xml
+++ b/slices/view/src/main/res/values-si/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> යාවත්කාලීන කරන ලදී"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"සම්බන්ධ වීමට නොහැකි විය"</string>
</resources>
diff --git a/slices/view/src/main/res/values-sk/strings.xml b/slices/view/src/main/res/values-sk/strings.xml
index fe77506..a153760 100644
--- a/slices/view/src/main/res/values-sk/strings.xml
+++ b/slices/view/src/main/res/values-sk/strings.xml
@@ -20,8 +20,24 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Aktualizované <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="few">Pred <xliff:g id="ID_2">%d</xliff:g> min</item>
+ <item quantity="many">Pred <xliff:g id="ID_2">%d</xliff:g> min</item>
+ <item quantity="other">Pred <xliff:g id="ID_2">%d</xliff:g> min</item>
+ <item quantity="one">Pred <xliff:g id="ID_1">%d</xliff:g> min</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="few">Pred <xliff:g id="ID_2">%d</xliff:g> rokmi</item>
+ <item quantity="many">Pred <xliff:g id="ID_2">%d</xliff:g> rokom</item>
+ <item quantity="other">Pred <xliff:g id="ID_2">%d</xliff:g> rokmi</item>
+ <item quantity="one">Pred <xliff:g id="ID_1">%d</xliff:g> rokom</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="few">Pred <xliff:g id="ID_2">%d</xliff:g> dňami</item>
+ <item quantity="many">Pred <xliff:g id="ID_2">%d</xliff:g> dňami</item>
+ <item quantity="other">Pred <xliff:g id="ID_2">%d</xliff:g> dňami</item>
+ <item quantity="one">Pred <xliff:g id="ID_1">%d</xliff:g> dňom</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Nepodarilo sa pripojiť"</string>
</resources>
diff --git a/slices/view/src/main/res/values-sl/strings.xml b/slices/view/src/main/res/values-sl/strings.xml
index 802caf1..0389500 100644
--- a/slices/view/src/main/res/values-sl/strings.xml
+++ b/slices/view/src/main/res/values-sl/strings.xml
@@ -20,8 +20,24 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Posodobljeno: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="one">pred <xliff:g id="ID_2">%d</xliff:g> minuto</item>
+ <item quantity="two">pred <xliff:g id="ID_2">%d</xliff:g> minutama</item>
+ <item quantity="few">pred <xliff:g id="ID_2">%d</xliff:g> minutami</item>
+ <item quantity="other">pred <xliff:g id="ID_2">%d</xliff:g> minutami</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="one">pred <xliff:g id="ID_2">%d</xliff:g> letom</item>
+ <item quantity="two">pred <xliff:g id="ID_2">%d</xliff:g> letoma</item>
+ <item quantity="few">pred <xliff:g id="ID_2">%d</xliff:g> leti</item>
+ <item quantity="other">pred <xliff:g id="ID_2">%d</xliff:g> leti</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="one">pred <xliff:g id="ID_2">%d</xliff:g> dnem</item>
+ <item quantity="two">pred <xliff:g id="ID_2">%d</xliff:g> dnevoma</item>
+ <item quantity="few">pred <xliff:g id="ID_2">%d</xliff:g> dnevi</item>
+ <item quantity="other">pred <xliff:g id="ID_2">%d</xliff:g> dnevi</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Povezava ni mogoča"</string>
</resources>
diff --git a/slices/view/src/main/res/values-sq/strings.xml b/slices/view/src/main/res/values-sq/strings.xml
index e1cdae8..0359c86 100644
--- a/slices/view/src/main/res/values-sq/strings.xml
+++ b/slices/view/src/main/res/values-sq/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Përditësuar <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> minuta më parë</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> minutë më parë</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> vite më parë</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> vit më parë</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ditë më parë</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> ditë më parë</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Nuk mund të lidhej"</string>
</resources>
diff --git a/slices/view/src/main/res/values-sr/strings.xml b/slices/view/src/main/res/values-sr/strings.xml
index 627ede6..718f3d7 100644
--- a/slices/view/src/main/res/values-sr/strings.xml
+++ b/slices/view/src/main/res/values-sr/strings.xml
@@ -20,4 +20,21 @@
<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>
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Ажурирано <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="one">пре <xliff:g id="ID_2">%d</xliff:g> мин</item>
+ <item quantity="few">пре <xliff:g id="ID_2">%d</xliff:g> мин</item>
+ <item quantity="other">пре <xliff:g id="ID_2">%d</xliff:g> мин</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="one">пре <xliff:g id="ID_2">%d</xliff:g> год</item>
+ <item quantity="few">пре <xliff:g id="ID_2">%d</xliff:g> год</item>
+ <item quantity="other">пре <xliff:g id="ID_2">%d</xliff:g> год</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="one">пре <xliff:g id="ID_2">%d</xliff:g> дан</item>
+ <item quantity="few">пре <xliff:g id="ID_2">%d</xliff:g> дана</item>
+ <item quantity="other">пре <xliff:g id="ID_2">%d</xliff:g> дана</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Повезивање није успело"</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 f8ed0c7..bc6947d 100644
--- a/slices/view/src/main/res/values-sv/strings.xml
+++ b/slices/view/src/main/res/values-sv/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Uppdaterades <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Det gick inte att ansluta"</string>
</resources>
diff --git a/slices/view/src/main/res/values-sw/strings.xml b/slices/view/src/main/res/values-sw/strings.xml
index b23ed5e..609c51f 100644
--- a/slices/view/src/main/res/values-sw/strings.xml
+++ b/slices/view/src/main/res/values-sw/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Ilisasishwa <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other">Dakika <xliff:g id="ID_2">%d</xliff:g> zilizopita</item>
+ <item quantity="one">Dakika <xliff:g id="ID_1">%d</xliff:g> iliyopita</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other">Miaka <xliff:g id="ID_2">%d</xliff:g> iliyopita</item>
+ <item quantity="one">Mwaka <xliff:g id="ID_1">%d</xliff:g> uliopita</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other">Siku <xliff:g id="ID_2">%d</xliff:g> zilizopita</item>
+ <item quantity="one">Siku <xliff:g id="ID_1">%d</xliff:g> iliyopita</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Imeshindwa kuunganisha"</string>
</resources>
diff --git a/slices/view/src/main/res/values-ta/strings.xml b/slices/view/src/main/res/values-ta/strings.xml
index 9f2be0e..5b36b79 100644
--- a/slices/view/src/main/res/values-ta/strings.xml
+++ b/slices/view/src/main/res/values-ta/strings.xml
@@ -22,6 +22,9 @@
<string name="abc_slice_show_more" msgid="1567717014004692768">"மேலும் காட்டு"</string>
<!-- no translation found for abc_slice_updated (8155085405396453848) -->
<skip />
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
<!-- 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 0eb1da7..82137b8 100644
--- a/slices/view/src/main/res/values-te/strings.xml
+++ b/slices/view/src/main/res/values-te/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"అప్డేట్ చేసిన సమయం <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> నిమి క్రితం</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> నిమి క్రితం</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> సం క్రితం</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> సం క్రితం</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> రోజుల క్రితం</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> రోజు క్రితం</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"కనెక్ట్ చేయడం సాధ్యపడలేదు"</string>
</resources>
diff --git a/slices/view/src/main/res/values-th/strings.xml b/slices/view/src/main/res/values-th/strings.xml
index 4cdd991..3ae19da 100644
--- a/slices/view/src/main/res/values-th/strings.xml
+++ b/slices/view/src/main/res/values-th/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"อัปเดตเมื่อ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> นาทีที่แล้ว</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> นาทีที่แล้ว</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> ปีที่แล้ว</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> ปีที่แล้ว</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> วันที่แล้ว</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> วันที่แล้ว</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"เชื่อมต่อไม่ได้"</string>
</resources>
diff --git a/slices/view/src/main/res/values-tl/strings.xml b/slices/view/src/main/res/values-tl/strings.xml
index 05917b0..d2d5526 100644
--- a/slices/view/src/main/res/values-tl/strings.xml
+++ b/slices/view/src/main/res/values-tl/strings.xml
@@ -20,4 +20,18 @@
<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>
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Na-update noong <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> minuto ang nakalipas</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> na minuto ang nakalipas</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> taon ang nakalipas</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> na taon ang nakalipas</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> araw ang nakalipas</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> na araw ang nakalipas</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Hindi makakonekta"</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 09a589e..84da5fb 100644
--- a/slices/view/src/main/res/values-tr/strings.xml
+++ b/slices/view/src/main/res/values-tr/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Güncellenme zamanı: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> dk. önce</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> dk. önce</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> yıl önce</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> yıl önce</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> gün önce</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> gün önce</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Bağlanılamadı"</string>
</resources>
diff --git a/slices/view/src/main/res/values-uk/strings.xml b/slices/view/src/main/res/values-uk/strings.xml
index 06c02bc..0706d80 100644
--- a/slices/view/src/main/res/values-uk/strings.xml
+++ b/slices/view/src/main/res/values-uk/strings.xml
@@ -20,8 +20,24 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Оновлено: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> хвилину тому</item>
+ <item quantity="few"><xliff:g id="ID_2">%d</xliff:g> хвилини тому</item>
+ <item quantity="many"><xliff:g id="ID_2">%d</xliff:g> хвилин тому</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> хвилини тому</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> рік тому</item>
+ <item quantity="few"><xliff:g id="ID_2">%d</xliff:g> роки тому</item>
+ <item quantity="many"><xliff:g id="ID_2">%d</xliff:g> років тому</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> року тому</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> день тому</item>
+ <item quantity="few"><xliff:g id="ID_2">%d</xliff:g> дні тому</item>
+ <item quantity="many"><xliff:g id="ID_2">%d</xliff:g> днів тому</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> дня тому</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Не вдалося під’єднатися"</string>
</resources>
diff --git a/slices/view/src/main/res/values-ur/strings.xml b/slices/view/src/main/res/values-ur/strings.xml
index f12d6b3..4f51700 100644
--- a/slices/view/src/main/res/values-ur/strings.xml
+++ b/slices/view/src/main/res/values-ur/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> اپ ڈیٹ کیا گیا"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"منسلک نہیں ہو سکا"</string>
</resources>
diff --git a/slices/view/src/main/res/values-uz/strings.xml b/slices/view/src/main/res/values-uz/strings.xml
index cbc9979..2250c91 100644
--- a/slices/view/src/main/res/values-uz/strings.xml
+++ b/slices/view/src/main/res/values-uz/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Yangilandi: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Ulanib bo‘lmadi"</string>
</resources>
diff --git a/slices/view/src/main/res/values-vi/strings.xml b/slices/view/src/main/res/values-vi/strings.xml
index 3890636..c9129be 100644
--- a/slices/view/src/main/res/values-vi/strings.xml
+++ b/slices/view/src/main/res/values-vi/strings.xml
@@ -20,8 +20,9 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Đã cập nhật lúc <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"Không thể kết nối"</string>
</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 ffb63a4..2ecb835 100644
--- a/slices/view/src/main/res/values-zh-rCN/strings.xml
+++ b/slices/view/src/main/res/values-zh-rCN/strings.xml
@@ -20,4 +20,9 @@
<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>
+ <string name="abc_slice_updated" msgid="8155085405396453848">"更新时间:<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <!-- no translation found for abc_slice_duration_min (6996334305156847955) -->
+ <!-- no translation found for abc_slice_duration_years (6212691832333991589) -->
+ <!-- no translation found for abc_slice_duration_days (6241698511167107334) -->
+ <string name="abc_slice_error" msgid="4188371422904147368">"无法连接"</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 565b565..d1ac276 100644
--- a/slices/view/src/main/res/values-zh-rHK/strings.xml
+++ b/slices/view/src/main/res/values-zh-rHK/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"更新時間:<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> 分鐘前</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> 分鐘前</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> 年前</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> 年前</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> 天前</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> 天前</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"無法連線"</string>
</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 565b565..d1ac276 100644
--- a/slices/view/src/main/res/values-zh-rTW/strings.xml
+++ b/slices/view/src/main/res/values-zh-rTW/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"更新時間:<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> 分鐘前</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> 分鐘前</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> 年前</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> 年前</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> 天前</item>
+ <item quantity="one"><xliff:g id="ID_1">%d</xliff:g> 天前</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"無法連線"</string>
</resources>
diff --git a/slices/view/src/main/res/values-zu/strings.xml b/slices/view/src/main/res/values-zu/strings.xml
index c5ebb10..966bb1b 100644
--- a/slices/view/src/main/res/values-zu/strings.xml
+++ b/slices/view/src/main/res/values-zu/strings.xml
@@ -20,8 +20,18 @@
<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 />
+ <string name="abc_slice_updated" msgid="8155085405396453848">"Kubuyekezwe ngo-<xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> iminithi eledlule</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> iminithi eledlule</item>
+ </plurals>
+ <plurals name="abc_slice_duration_years" formatted="false" msgid="6212691832333991589">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> unyaka owedlule</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> unyaka owedlule</item>
+ </plurals>
+ <plurals name="abc_slice_duration_days" formatted="false" msgid="6241698511167107334">
+ <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> izinsuku ezedlule</item>
+ <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> izinsuku ezedlule</item>
+ </plurals>
+ <string name="abc_slice_error" msgid="4188371422904147368">"Ayikwazanga ukuxhuma"</string>
</resources>
diff --git a/slices/view/src/main/res/values/attrs.xml b/slices/view/src/main/res/values/attrs.xml
index 5779d31..f3e621b 100644
--- a/slices/view/src/main/res/values/attrs.xml
+++ b/slices/view/src/main/res/values/attrs.xml
@@ -17,19 +17,30 @@
<resources>
<declare-styleable name="SliceView">
- <!-- Colors -->
+ <!-- Color to use for any title text within the slice. -->
<attr name="titleColor" format="color" />
+ <!-- Color to use for any other text within the slice. -->
<attr name="subtitleColor" format="color" />
+ <!-- Color to use for tinting elements within the slice. Normally an app
+ providing a slice is able to specify a color to use for tinting, specifying
+ a tint color here will override the app supplied color. -->
<attr name="tintColor" format="color" />
- <!-- Text sizes -->
+ <!-- Text size to use for title text in the header of the slice. -->
<attr name="headerTitleSize" format="dimension" />
+ <!-- Text size to use for subtitle text in the header of the slice. -->
<attr name="headerSubtitleSize" format="dimension"/>
+ <!-- Text size to use for title text in a non-header row of the slice. -->
<attr name="titleSize" format="dimension" />
+ <!-- Text size to use for subtitle text in a non-header row of the slice. -->
<attr name="subtitleSize" format="dimension" />
+ <!-- Text size to use for title text in a grid row of the slice. -->
<attr name="gridTitleSize" format="dimension" />
+ <!-- Text size to use for the subtitle text in a grid row of the slice. -->
<attr name="gridSubtitleSize" format="dimension" />
</declare-styleable>
+ <!-- To apply a style for all slices shown within an activity or app you
+ may set this in your app theme pointing to your custom SliceView style.-->
<attr name="sliceViewStyle" format="reference"/>
</resources>
\ No newline at end of file
diff --git a/slices/view/src/main/res/values/dimens.xml b/slices/view/src/main/res/values/dimens.xml
index bb90106..19e3327 100644
--- a/slices/view/src/main/res/values/dimens.xml
+++ b/slices/view/src/main/res/values/dimens.xml
@@ -42,6 +42,8 @@
<dimen name="abc_slice_row_max_height">60dp</dimen>
<!-- Min height of a row showing an input field that is active -->
<dimen name="abc_slice_row_active_input_height">120dp</dimen>
+ <!-- Max height of of a row view with a range element -->
+ <dimen name="abc_slice_row_range_max_height">86dp</dimen>
<!-- Grid view sizes-->
<!-- Height of a grid row displaying only text or only small images (but not both) -->
diff --git a/testutils/src/main/java/androidx/testutils/AppCompatActivityUtils.java b/testutils/src/main/java/androidx/testutils/AppCompatActivityUtils.java
index e36a2ec..104fb44 100644
--- a/testutils/src/main/java/androidx/testutils/AppCompatActivityUtils.java
+++ b/testutils/src/main/java/androidx/testutils/AppCompatActivityUtils.java
@@ -92,4 +92,7 @@
RecreatedAppCompatActivity.clearState();
return newActivity;
}
+
+ private AppCompatActivityUtils() {
+ }
}
diff --git a/testutils/src/main/java/androidx/testutils/FragmentActivityUtils.java b/testutils/src/main/java/androidx/testutils/FragmentActivityUtils.java
index 136cc31..43404f0 100644
--- a/testutils/src/main/java/androidx/testutils/FragmentActivityUtils.java
+++ b/testutils/src/main/java/androidx/testutils/FragmentActivityUtils.java
@@ -89,4 +89,7 @@
RecreatedActivity.clearState();
return newActivity;
}
+
+ private FragmentActivityUtils() {
+ }
}
diff --git a/transition/src/main/java/androidx/transition/AnimatorUtils.java b/transition/src/main/java/androidx/transition/AnimatorUtils.java
index 452bc66..6772a60 100644
--- a/transition/src/main/java/androidx/transition/AnimatorUtils.java
+++ b/transition/src/main/java/androidx/transition/AnimatorUtils.java
@@ -77,4 +77,7 @@
void onAnimationResume(Animator animation);
}
+
+ private AnimatorUtils() {
+ }
}
diff --git a/transition/src/main/java/androidx/transition/GhostViewUtils.java b/transition/src/main/java/androidx/transition/GhostViewUtils.java
index 447f728..9e460ca 100644
--- a/transition/src/main/java/androidx/transition/GhostViewUtils.java
+++ b/transition/src/main/java/androidx/transition/GhostViewUtils.java
@@ -38,4 +38,6 @@
}
}
+ private GhostViewUtils() {
+ }
}
diff --git a/transition/src/main/java/androidx/transition/ImageViewUtils.java b/transition/src/main/java/androidx/transition/ImageViewUtils.java
index a7c947c..9d73e9e 100644
--- a/transition/src/main/java/androidx/transition/ImageViewUtils.java
+++ b/transition/src/main/java/androidx/transition/ImageViewUtils.java
@@ -105,4 +105,6 @@
}
}
+ private ImageViewUtils() {
+ }
}
diff --git a/transition/src/main/java/androidx/transition/MatrixUtils.java b/transition/src/main/java/androidx/transition/MatrixUtils.java
index a04ebe2..9a13796 100644
--- a/transition/src/main/java/androidx/transition/MatrixUtils.java
+++ b/transition/src/main/java/androidx/transition/MatrixUtils.java
@@ -204,4 +204,6 @@
};
+ private MatrixUtils() {
+ }
}
diff --git a/transition/src/main/java/androidx/transition/ObjectAnimatorUtils.java b/transition/src/main/java/androidx/transition/ObjectAnimatorUtils.java
index a7fb97d..bd73b58 100644
--- a/transition/src/main/java/androidx/transition/ObjectAnimatorUtils.java
+++ b/transition/src/main/java/androidx/transition/ObjectAnimatorUtils.java
@@ -31,4 +31,6 @@
return ObjectAnimator.ofFloat(target, new PathProperty<>(property, path), 0f, 1f);
}
+ private ObjectAnimatorUtils() {
+ }
}
diff --git a/transition/src/main/java/androidx/transition/PropertyValuesHolderUtils.java b/transition/src/main/java/androidx/transition/PropertyValuesHolderUtils.java
index 2ea657c..42527c4 100644
--- a/transition/src/main/java/androidx/transition/PropertyValuesHolderUtils.java
+++ b/transition/src/main/java/androidx/transition/PropertyValuesHolderUtils.java
@@ -41,4 +41,6 @@
return PropertyValuesHolder.ofFloat(new PathProperty<>(property, path), 0f, 1f);
}
+ private PropertyValuesHolderUtils() {
+ }
}
diff --git a/transition/src/main/java/androidx/transition/Styleable.java b/transition/src/main/java/androidx/transition/Styleable.java
index cdfb9ec..40f3cca 100644
--- a/transition/src/main/java/androidx/transition/Styleable.java
+++ b/transition/src/main/java/androidx/transition/Styleable.java
@@ -175,4 +175,6 @@
int PATTERN_PATH_DATA = 0;
}
+ private Styleable() {
+ }
}
diff --git a/transition/src/main/java/androidx/transition/TransitionUtils.java b/transition/src/main/java/androidx/transition/TransitionUtils.java
index 7a1b7ec..39de677 100644
--- a/transition/src/main/java/androidx/transition/TransitionUtils.java
+++ b/transition/src/main/java/androidx/transition/TransitionUtils.java
@@ -130,4 +130,6 @@
}
+ private TransitionUtils() {
+ }
}
diff --git a/transition/src/main/java/androidx/transition/TranslationAnimationCreator.java b/transition/src/main/java/androidx/transition/TranslationAnimationCreator.java
index 004c1a4..c3e30e8 100644
--- a/transition/src/main/java/androidx/transition/TranslationAnimationCreator.java
+++ b/transition/src/main/java/androidx/transition/TranslationAnimationCreator.java
@@ -133,4 +133,6 @@
}
}
+ private TranslationAnimationCreator() {
+ }
}
diff --git a/transition/src/main/java/androidx/transition/ViewGroupUtils.java b/transition/src/main/java/androidx/transition/ViewGroupUtils.java
index 55ab3ff..dee0fa9 100644
--- a/transition/src/main/java/androidx/transition/ViewGroupUtils.java
+++ b/transition/src/main/java/androidx/transition/ViewGroupUtils.java
@@ -47,4 +47,6 @@
}
}
+ private ViewGroupUtils() {
+ }
}
diff --git a/transition/src/main/java/androidx/transition/ViewGroupUtilsApi14.java b/transition/src/main/java/androidx/transition/ViewGroupUtilsApi14.java
index 70af497..32d546a 100644
--- a/transition/src/main/java/androidx/transition/ViewGroupUtilsApi14.java
+++ b/transition/src/main/java/androidx/transition/ViewGroupUtilsApi14.java
@@ -126,4 +126,6 @@
}
}
+ private ViewGroupUtilsApi14() {
+ }
}
diff --git a/transition/src/main/java/androidx/transition/ViewGroupUtilsApi18.java b/transition/src/main/java/androidx/transition/ViewGroupUtilsApi18.java
index bd0938a..e4d4ffa 100644
--- a/transition/src/main/java/androidx/transition/ViewGroupUtilsApi18.java
+++ b/transition/src/main/java/androidx/transition/ViewGroupUtilsApi18.java
@@ -59,4 +59,6 @@
}
}
+ private ViewGroupUtilsApi18() {
+ }
}
diff --git a/transition/src/main/java/androidx/transition/ViewOverlayApi14.java b/transition/src/main/java/androidx/transition/ViewOverlayApi14.java
index 12ebf77..049e685 100644
--- a/transition/src/main/java/androidx/transition/ViewOverlayApi14.java
+++ b/transition/src/main/java/androidx/transition/ViewOverlayApi14.java
@@ -351,5 +351,6 @@
}
}
-
+ private ViewOverlayApi14() {
+ }
}
diff --git a/transition/src/main/java/androidx/transition/ViewUtils.java b/transition/src/main/java/androidx/transition/ViewUtils.java
index 89eaadd..d770ab6 100644
--- a/transition/src/main/java/androidx/transition/ViewUtils.java
+++ b/transition/src/main/java/androidx/transition/ViewUtils.java
@@ -222,4 +222,6 @@
}
}
+ private ViewUtils() {
+ }
}
diff --git a/tv-provider/api/current.txt b/tv-provider/api/current.txt
index ec21ef7..0c0a202 100644
--- a/tv-provider/api/current.txt
+++ b/tv-provider/api/current.txt
@@ -71,7 +71,7 @@
}
public class ChannelLogoUtils {
- ctor public ChannelLogoUtils();
+ ctor public deprecated ChannelLogoUtils();
method public static android.graphics.Bitmap loadChannelLogo(android.content.Context, long);
method public static boolean storeChannelLogo(android.content.Context, long, android.net.Uri);
method public static boolean storeChannelLogo(android.content.Context, long, android.graphics.Bitmap);
diff --git a/tv-provider/src/main/java/androidx/tvprovider/media/tv/ChannelLogoUtils.java b/tv-provider/src/main/java/androidx/tvprovider/media/tv/ChannelLogoUtils.java
index 85b2bce..5e1aa19 100644
--- a/tv-provider/src/main/java/androidx/tvprovider/media/tv/ChannelLogoUtils.java
+++ b/tv-provider/src/main/java/androidx/tvprovider/media/tv/ChannelLogoUtils.java
@@ -165,4 +165,10 @@
urlConnection.setReadTimeout(READ_TIMEOUT_MS_FOR_URLCONNECTION);
return urlConnection;
}
+
+ /** @deprecated This type should not be instantiated as it contains only static methods. */
+ @Deprecated
+ @SuppressWarnings("PrivateConstructorForUtilityClass")
+ public ChannelLogoUtils() {
+ }
}
diff --git a/tv-provider/src/main/java/androidx/tvprovider/media/tv/CollectionUtils.java b/tv-provider/src/main/java/androidx/tvprovider/media/tv/CollectionUtils.java
index 14fbc77..595e451 100644
--- a/tv-provider/src/main/java/androidx/tvprovider/media/tv/CollectionUtils.java
+++ b/tv-provider/src/main/java/androidx/tvprovider/media/tv/CollectionUtils.java
@@ -46,4 +46,7 @@
}
return result;
}
+
+ private CollectionUtils() {
+ }
}
diff --git a/tv-provider/src/main/java/androidx/tvprovider/media/tv/PreviewChannel.java b/tv-provider/src/main/java/androidx/tvprovider/media/tv/PreviewChannel.java
index 9bdc108..d976798 100644
--- a/tv-provider/src/main/java/androidx/tvprovider/media/tv/PreviewChannel.java
+++ b/tv-provider/src/main/java/androidx/tvprovider/media/tv/PreviewChannel.java
@@ -331,6 +331,9 @@
public static final int COL_INTERNAL_PROVIDER_FLAG2 = 9;
public static final int COL_INTERNAL_PROVIDER_FLAG3 = 10;
public static final int COL_INTERNAL_PROVIDER_FLAG4 = 11;
+
+ private Columns() {
+ }
}
/**
diff --git a/v7/appcompat/res/values-as/strings.xml b/v7/appcompat/res/values-as/strings.xml
index 1f90618..a3574d7 100644
--- a/v7/appcompat/res/values-as/strings.xml
+++ b/v7/appcompat/res/values-as/strings.xml
@@ -29,13 +29,19 @@
<string name="abc_searchview_description_voice" msgid="893419373245838918">"কণ্ঠধ্বনিৰ যোগেৰে সন্ধান কৰক"</string>
<string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"এটা এপ্ বাছনি কৰক"</string>
<string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"সকলো চাওক"</string>
- <!-- no translation found for abc_shareactionprovider_share_with_application (3300176832234831527) -->
- <skip />
+ <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>
- <!-- no translation found for abc_capital_on (3405795526292276155) -->
- <skip />
- <!-- no translation found for abc_capital_off (121134116657445385) -->
- <skip />
- <!-- no translation found for search_menu_title (146198913615257606) -->
- <skip />
+ <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">"শ্বিফ্ট+"</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-gu/strings.xml b/v7/appcompat/res/values-gu/strings.xml
index a8b0c48..390d59b 100644
--- a/v7/appcompat/res/values-gu/strings.xml
+++ b/v7/appcompat/res/values-gu/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_action_mode_done" msgid="4076576682505996667">"થઈ ગયું"</string>
<string name="abc_action_bar_home_description" msgid="4600421777120114993">"હોમ પર નેવિગેટ કરો"</string>
- <string name="abc_action_bar_up_description" msgid="1594238315039666878">"ઉપર નેવિગેટ કરો"</string>
+ <string name="abc_action_bar_up_description" msgid="1594238315039666878">"ઉપર નૅવિગેટ કરો"</string>
<string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"વધુ વિકલ્પો"</string>
<string name="abc_toolbar_collapse_description" msgid="1603543279005712093">"સંકુચિત કરો"</string>
<string name="abc_searchview_description_search" msgid="8264924765203268293">"શોધો"</string>
diff --git a/v7/appcompat/res/values-or/strings.xml b/v7/appcompat/res/values-or/strings.xml
index 4486724..86e4fb7 100644
--- a/v7/appcompat/res/values-or/strings.xml
+++ b/v7/appcompat/res/values-or/strings.xml
@@ -29,30 +29,19 @@
<string name="abc_searchview_description_voice" msgid="893419373245838918">"ଭଏସ୍ ସର୍ଚ୍ଚ"</string>
<string name="abc_activitychooserview_choose_application" msgid="2031811694353399454">"ଗୋଟିଏ ଆପ୍ ବାଛନ୍ତୁ"</string>
<string name="abc_activity_chooser_view_see_all" msgid="7468859129482906941">"ସବୁ ଦେଖନ୍ତୁ"</string>
- <!-- no translation found for abc_shareactionprovider_share_with_application (3300176832234831527) -->
- <skip />
+ <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_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_menu_meta_shortcut_label (7643535737296831317) -->
- <skip />
- <!-- 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 />
+ <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">"ଏଣ୍ଟର୍"</string>
+ <string name="abc_menu_delete_shortcut_label" msgid="8362206064229013510">"ଡିଲିଟ୍"</string>
</resources>
diff --git a/v7/appcompat/res/values/bools.xml b/v7/appcompat/res/values/bools.xml
index c38c0ee..793719b 100644
--- a/v7/appcompat/res/values/bools.xml
+++ b/v7/appcompat/res/values/bools.xml
@@ -18,8 +18,6 @@
<bool name="abc_action_bar_embed_tabs">true</bool>
- <bool name="abc_config_showMenuShortcutsWhenKeyboardPresent">false</bool>
-
<!-- Whether to allow vertically stacked button bars. This is disabled for
configurations with a small (e.g. less than 320dp) screen height. -->
<bool name="abc_allow_stacked_button_bar">false</bool>
diff --git a/v7/appcompat/src/androidTest/java/androidx/appcompat/app/DrawerLayoutDoubleTest.java b/v7/appcompat/src/androidTest/java/androidx/appcompat/app/DrawerLayoutDoubleTest.java
index 9b2decc..ad78cc0 100755
--- a/v7/appcompat/src/androidTest/java/androidx/appcompat/app/DrawerLayoutDoubleTest.java
+++ b/v7/appcompat/src/androidTest/java/androidx/appcompat/app/DrawerLayoutDoubleTest.java
@@ -97,7 +97,7 @@
}
@Test(expected=IllegalArgumentException.class)
- @SmallTest
+ @LargeTest
public void testCloseNonExistentDrawer() {
// Note that we're expecting the closeDrawer action to result in an exception being
// thrown since mContentView is not a drawer.
diff --git a/v7/appcompat/src/main/java/androidx/appcompat/app/ActionBarDrawerToggleHoneycomb.java b/v7/appcompat/src/main/java/androidx/appcompat/app/ActionBarDrawerToggleHoneycomb.java
index ac02d1b..f4102c3 100644
--- a/v7/appcompat/src/main/java/androidx/appcompat/app/ActionBarDrawerToggleHoneycomb.java
+++ b/v7/appcompat/src/main/java/androidx/appcompat/app/ActionBarDrawerToggleHoneycomb.java
@@ -136,4 +136,7 @@
}
}
}
+
+ private ActionBarDrawerToggleHoneycomb() {
+ }
}
diff --git a/v7/appcompat/src/main/java/androidx/appcompat/app/ResourcesFlusher.java b/v7/appcompat/src/main/java/androidx/appcompat/app/ResourcesFlusher.java
index 8e1d113..2cd12d2 100644
--- a/v7/appcompat/src/main/java/androidx/appcompat/app/ResourcesFlusher.java
+++ b/v7/appcompat/src/main/java/androidx/appcompat/app/ResourcesFlusher.java
@@ -205,4 +205,7 @@
}
return false;
}
+
+ private ResourcesFlusher() {
+ }
}
diff --git a/v7/appcompat/src/main/java/androidx/appcompat/view/menu/MenuBuilder.java b/v7/appcompat/src/main/java/androidx/appcompat/view/menu/MenuBuilder.java
index a1a20e5..2b5e653 100644
--- a/v7/appcompat/src/main/java/androidx/appcompat/view/menu/MenuBuilder.java
+++ b/v7/appcompat/src/main/java/androidx/appcompat/view/menu/MenuBuilder.java
@@ -35,14 +35,15 @@
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
+import android.view.ViewConfiguration;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
-import androidx.appcompat.R;
import androidx.core.content.ContextCompat;
import androidx.core.internal.view.SupportMenu;
import androidx.core.internal.view.SupportMenuItem;
import androidx.core.view.ActionProvider;
+import androidx.core.view.ViewConfigurationCompat;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -816,7 +817,8 @@
private void setShortcutsVisibleInner(boolean shortcutsVisible) {
mShortcutsVisible = shortcutsVisible
&& mResources.getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS
- && mResources.getBoolean(R.bool.abc_config_showMenuShortcutsWhenKeyboardPresent);
+ && ViewConfigurationCompat.shouldShowMenuShortcutsWhenKeyboardPresent(
+ ViewConfiguration.get(mContext), mContext);
}
/**
diff --git a/v7/appcompat/src/main/java/androidx/appcompat/widget/ActionBarBackgroundDrawable.java b/v7/appcompat/src/main/java/androidx/appcompat/widget/ActionBarBackgroundDrawable.java
index 175cdb3..010a30b 100644
--- a/v7/appcompat/src/main/java/androidx/appcompat/widget/ActionBarBackgroundDrawable.java
+++ b/v7/appcompat/src/main/java/androidx/appcompat/widget/ActionBarBackgroundDrawable.java
@@ -23,6 +23,7 @@
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
class ActionBarBackgroundDrawable extends Drawable {
@@ -62,6 +63,7 @@
}
@Override
+ @RequiresApi(21)
public void getOutline(@NonNull Outline outline) {
if (mContainer.mIsSplit) {
if (mContainer.mSplitBackground != null) {
diff --git a/v7/appcompat/src/main/java/androidx/appcompat/widget/AppCompatHintHelper.java b/v7/appcompat/src/main/java/androidx/appcompat/widget/AppCompatHintHelper.java
index ed28d99..bacff63 100644
--- a/v7/appcompat/src/main/java/androidx/appcompat/widget/AppCompatHintHelper.java
+++ b/v7/appcompat/src/main/java/androidx/appcompat/widget/AppCompatHintHelper.java
@@ -40,4 +40,6 @@
return ic;
}
+ private AppCompatHintHelper() {
+ }
}
diff --git a/v7/appcompat/src/main/java/androidx/appcompat/widget/ThemeUtils.java b/v7/appcompat/src/main/java/androidx/appcompat/widget/ThemeUtils.java
index a972ca6..ee00ded 100644
--- a/v7/appcompat/src/main/java/androidx/appcompat/widget/ThemeUtils.java
+++ b/v7/appcompat/src/main/java/androidx/appcompat/widget/ThemeUtils.java
@@ -111,4 +111,6 @@
return ColorUtils.setAlphaComponent(color, Math.round(originalAlpha * alpha));
}
+ private ThemeUtils() {
+ }
}
diff --git a/v7/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAutofillTest.java b/v7/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAutofillTest.java
new file mode 100644
index 0000000..3ccb11e
--- /dev/null
+++ b/v7/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewAutofillTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.recyclerview.widget;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.os.Build;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.core.view.ViewCompat;
+import androidx.recyclerview.test.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RecyclerViewAutofillTest {
+ static Context getContext() {
+ return InstrumentationRegistry.getTargetContext();
+ }
+
+ @Test
+ public void initializeWithAutofillDisabled() {
+ RecyclerView recyclerView = new RecyclerView(getContext());
+ int importance = ViewCompat.getImportantForAutofill(recyclerView);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ assertEquals(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS, importance);
+ } else {
+ assertEquals(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO, importance);
+ }
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ public void testXmlValues() {
+ ViewGroup parent = (ViewGroup) LayoutInflater.from(getContext())
+ .inflate(R.layout.autofill_rv, null);
+ assertEquals(View.IMPORTANT_FOR_AUTOFILL_YES,
+ parent.findViewById(R.id.autofill_yes).getImportantForAutofill());
+ assertEquals(View.IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS,
+ parent.findViewById(R.id.autofill_yesExcludeDescendants).getImportantForAutofill());
+ assertEquals(View.IMPORTANT_FOR_AUTOFILL_NO,
+ parent.findViewById(R.id.autofill_no).getImportantForAutofill());
+
+ // NOTE: RV overrides auto specifically
+ assertEquals(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS,
+ parent.findViewById(R.id.autofill_auto).getImportantForAutofill());
+ }
+}
diff --git a/v7/recyclerview/src/androidTest/res/layout/autofill_rv.xml b/v7/recyclerview/src/androidTest/res/layout/autofill_rv.xml
new file mode 100644
index 0000000..fb838f3
--- /dev/null
+++ b/v7/recyclerview/src/androidTest/res/layout/autofill_rv.xml
@@ -0,0 +1,42 @@
+<?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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/autofill_yes"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:importantForAutofill="yes"/>
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/autofill_yesExcludeDescendants"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:importantForAutofill="yesExcludeDescendants"/>
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/autofill_no"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:importantForAutofill="no"/>>
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/autofill_auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:importantForAutofill="auto"/>>
+</LinearLayout>
diff --git a/v7/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java b/v7/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
index 2d09cda..08196eb 100644
--- a/v7/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
+++ b/v7/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
@@ -21,6 +21,7 @@
import static androidx.core.view.ViewCompat.TYPE_NON_TOUCH;
import static androidx.core.view.ViewCompat.TYPE_TOUCH;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -625,6 +626,7 @@
mItemAnimator.setListener(mItemAnimatorListener);
initAdapterManager();
initChildrenHelper();
+ initAutofill();
// If not explicitly specified this view is important for accessibility.
if (ViewCompat.getImportantForAccessibility(this)
== ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
@@ -690,6 +692,20 @@
}
/**
+ * If not explicitly specified, this view and its children don't support autofill.
+ * <p>
+ * This is done because autofill's means of uniquely identifying views doesn't work out of the
+ * box with View recycling.
+ */
+ @SuppressLint("InlinedApi")
+ private void initAutofill() {
+ if (ViewCompat.getImportantForAutofill(this) == View.IMPORTANT_FOR_AUTOFILL_AUTO) {
+ ViewCompat.setImportantForAutofill(this,
+ View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
+ }
+ }
+
+ /**
* Returns the accessibility delegate compatibility implementation used by the RecyclerView.
* @return An instance of AccessibilityDelegateCompat used by RecyclerView
*/
diff --git a/v7/recyclerview/src/main/java/androidx/recyclerview/widget/ScrollbarHelper.java b/v7/recyclerview/src/main/java/androidx/recyclerview/widget/ScrollbarHelper.java
index 683f877..8cc24ae 100644
--- a/v7/recyclerview/src/main/java/androidx/recyclerview/widget/ScrollbarHelper.java
+++ b/v7/recyclerview/src/main/java/androidx/recyclerview/widget/ScrollbarHelper.java
@@ -95,4 +95,7 @@
// estimate a size for full list.
return (int) ((float) laidOutArea / laidOutRange * state.getItemCount());
}
+
+ private ScrollbarHelper() {
+ }
}
diff --git a/wear/res/layout/ws_single_page_nav_drawer_4_item.xml b/wear/res/layout/ws_single_page_nav_drawer_4_item.xml
index 7057ec8..3e8b5d4 100644
--- a/wear/res/layout/ws_single_page_nav_drawer_4_item.xml
+++ b/wear/res/layout/ws_single_page_nav_drawer_4_item.xml
@@ -46,7 +46,7 @@
<androidx.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_1"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@id/ws_nav_drawer_guide_start"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
@@ -54,7 +54,7 @@
<androidx.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_2"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:ignore="ContentDescription"
@@ -63,7 +63,7 @@
<androidx.wear.widget.CircledImageView
android:id="@+id/ws_nav_drawer_icon_3"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/ws_nav_drawer_guide_end"
tools:ignore="ContentDescription"
style="@style/WsSinglePageNavDrawerIconStyle" />
diff --git a/wear/res/values-as/strings.xml b/wear/res/values-as/strings.xml
new file mode 100644
index 0000000..2a51efa
--- /dev/null
+++ b/wear/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="ws_navigation_drawer_content_description" msgid="7216697245762194759">"নেভিগেশ্বন ড্ৰৱাৰ"</string>
+ <string name="ws_action_drawer_content_description" msgid="1837365417701148489">"কাৰ্য ড্ৰৱাৰ"</string>
+</resources>
diff --git a/wear/res/values-or/strings.xml b/wear/res/values-or/strings.xml
new file mode 100644
index 0000000..53e75b3
--- /dev/null
+++ b/wear/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="ws_navigation_drawer_content_description" msgid="7216697245762194759">"ନେଭିଗେଶନ୍ ପ୍ୟାନେଲ୍"</string>
+ <string name="ws_action_drawer_content_description" msgid="1837365417701148489">"କାର୍ଯ୍ୟକାରୀ ପ୍ୟାନେଲ୍"</string>
+</resources>
diff --git a/wear/src/main/java/androidx/wear/ambient/SharedLibraryVersion.java b/wear/src/main/java/androidx/wear/ambient/SharedLibraryVersion.java
index 228956a..2c7f14d 100644
--- a/wear/src/main/java/androidx/wear/ambient/SharedLibraryVersion.java
+++ b/wear/src/main/java/androidx/wear/ambient/SharedLibraryVersion.java
@@ -73,6 +73,9 @@
}
return WearableSharedLib.version();
}
+
+ private VersionHolder() {
+ }
}
// Lazy initialization holder class (see Effective Java item 71)
@@ -90,5 +93,8 @@
}
return true;
}
+
+ private PresenceHolder() {
+ }
}
}
diff --git a/webkit/src/main/java/androidx/webkit/WebViewCompat.java b/webkit/src/main/java/androidx/webkit/WebViewCompat.java
index 45def2e..bdd53c7 100644
--- a/webkit/src/main/java/androidx/webkit/WebViewCompat.java
+++ b/webkit/src/main/java/androidx/webkit/WebViewCompat.java
@@ -285,8 +285,13 @@
webviewPackageName = (String) webviewUpdateServiceClass.getMethod(
"getCurrentWebViewPackageName").invoke(null);
}
- } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException
- | NoSuchMethodException e) {
+ } catch (ClassNotFoundException e) {
+ return null;
+ } catch (IllegalAccessException e) {
+ return null;
+ } catch (InvocationTargetException e) {
+ return null;
+ } catch (NoSuchMethodException e) {
return null;
}
if (webviewPackageName == null) return null;
diff --git a/webkit/src/main/java/androidx/webkit/internal/WebViewGlueCommunicator.java b/webkit/src/main/java/androidx/webkit/internal/WebViewGlueCommunicator.java
index 5bb0666..33ac145 100644
--- a/webkit/src/main/java/androidx/webkit/internal/WebViewGlueCommunicator.java
+++ b/webkit/src/main/java/androidx/webkit/internal/WebViewGlueCommunicator.java
@@ -65,12 +65,17 @@
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.
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ } catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
+ // TODO(gsennton) if the above happens we should avoid throwing an exception! And probably
+ // declare that the list of features supported by the WebView APK is empty.
}
private static WebViewProviderFactoryBoundaryInterface createGlueProviderFactory() {
@@ -104,4 +109,7 @@
throw new RuntimeException(e);
}
}
+
+ private WebViewGlueCommunicator() {
+ }
}