[automerger] Merge changes from topic "paging-4-docs" into oc-mr1-support-27.0-dev am: 3927f914b3 am: d3f13b667b
Change-Id: I4e0b1c140f6f6bc7375e0e7aa43f9ca95b31b3ff
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 7b2174e..7dc4050 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -46,6 +46,8 @@
$(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/annotations/Android.mk b/annotations/Android.mk
index 41abfaa..684a77a 100644
--- a/annotations/Android.mk
+++ b/annotations/Android.mk
@@ -15,11 +15,9 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
LOCAL_MODULE := android-support-annotations
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under,src/main/java)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/app-toolkit/dependencies.gradle b/app-toolkit/dependencies.gradle
index b794c10..be0ce42 100644
--- a/app-toolkit/dependencies.gradle
+++ b/app-toolkit/dependencies.gradle
@@ -40,7 +40,8 @@
fragments : getSupportLib(':support-fragment', ffVersions.support_lib),
app_compat : getSupportLib(':appcompat-v7', ffVersions.support_lib),
design : getSupportLib(':design', ffVersions.support_lib),
- recyclerview : getSupportLib(':recyclerview-v7', ffVersions.support_lib)
+ recyclerview : getSupportLib(':recyclerview-v7', ffVersions.support_lib),
+ cardview : getSupportLib(':cardview-v7', ffVersions.support_lib)
]
ffLibs.support_exclude_config = {
diff --git a/app-toolkit/settings.gradle b/app-toolkit/settings.gradle
index aa0db94..703ee07 100644
--- a/app-toolkit/settings.gradle
+++ b/app-toolkit/settings.gradle
@@ -96,6 +96,18 @@
include ':room:integration-tests:kotlintestapp'
project(':room:integration-tests:kotlintestapp').projectDir = new File(supportRoot, "room/integration-tests/kotlintestapp")
+include ':slices-core'
+project(':slices-core').projectDir = new File(supportRoot, 'slices/core')
+
+include ':slices-view'
+project(':slices-view').projectDir = new File(supportRoot, 'slices/view')
+
+include ':slices-builders'
+project(':slices-builders').projectDir = new File(supportRoot, 'slices/builders')
+
+include ':support-slices-demos'
+project(':support-slices-demos').projectDir = new File(supportRoot, 'samples/SupportSliceDemos')
+
/////////////////////////////
//
// SupportLib
diff --git a/buildSrc/init.gradle b/buildSrc/init.gradle
index a0df393..31e40d8 100644
--- a/buildSrc/init.gradle
+++ b/buildSrc/init.gradle
@@ -65,7 +65,7 @@
ext.buildToolsVersion = '27.0.1'
final String fullSdkPath = getFullSdkPath();
if (file(fullSdkPath).exists()) {
- gradle.ext.currentSdk = 26
+ gradle.ext.currentSdk = 28
project.ext.androidJar =
files("${fullSdkPath}/platforms/android-${gradle.currentSdk}/android.jar")
project.ext.androidSrcJar =
diff --git a/buildSrc/src/main/java/android/support/LibraryGroups.java b/buildSrc/src/main/java/android/support/LibraryGroups.java
index feaefbc..19c0a92 100644
--- a/buildSrc/src/main/java/android/support/LibraryGroups.java
+++ b/buildSrc/src/main/java/android/support/LibraryGroups.java
@@ -27,4 +27,5 @@
public static final String ARCH_CORE = "android.arch.core";
public static final String PAGING = "android.arch.paging";
public static final String NAVIGATION = "android.arch.navigation";
+ public static final String SLICES = "androidx.app.slice";
}
diff --git a/buildSrc/src/main/java/android/support/LibraryVersions.java b/buildSrc/src/main/java/android/support/LibraryVersions.java
index 02adfc0..a96f630 100644
--- a/buildSrc/src/main/java/android/support/LibraryVersions.java
+++ b/buildSrc/src/main/java/android/support/LibraryVersions.java
@@ -23,7 +23,7 @@
/**
* Version code of the support library components.
*/
- public static final Version SUPPORT_LIBRARY = new Version("27.1.0-SNAPSHOT");
+ public static final Version SUPPORT_LIBRARY = new Version("28.0.0-SNAPSHOT");
/**
* Version code for flatfoot 1.0 projects (room, lifecycles)
diff --git a/car/Android.mk b/car/Android.mk
index fa20f26..18f08e4 100644
--- a/car/Android.mk
+++ b/car/Android.mk
@@ -27,8 +27,9 @@
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under,src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-annotations \
android-support-v4 \
android-support-v7-appcompat \
android-support-v7-cardview \
diff --git a/compat/Android.mk b/compat/Android.mk
index e16bba6..720a1eb 100644
--- a/compat/Android.mk
+++ b/compat/Android.mk
@@ -30,7 +30,7 @@
$(call all-java-files-under,src/main/java) \
$(call all-Iaidl-files-under,src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := \
+LOCAL_JAVA_LIBRARIES := \
android-support-annotations
LOCAL_STATIC_JAVA_LIBRARIES := \
apptoolkit-arch-core-common \
diff --git a/compat/src/main/java/android/support/v4/graphics/TypefaceCompat.java b/compat/src/main/java/android/support/v4/graphics/TypefaceCompat.java
index 3c55df6..734f183 100644
--- a/compat/src/main/java/android/support/v4/graphics/TypefaceCompat.java
+++ b/compat/src/main/java/android/support/v4/graphics/TypefaceCompat.java
@@ -35,7 +35,6 @@
import android.support.v4.provider.FontsContractCompat;
import android.support.v4.provider.FontsContractCompat.FontInfo;
import android.support.v4.util.LruCache;
-
/**
* Helper for accessing features in {@link Typeface}.
* @hide
@@ -46,7 +45,9 @@
private static final TypefaceCompatImpl sTypefaceCompatImpl;
static {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ sTypefaceCompatImpl = new TypefaceCompatApi28Impl();
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
sTypefaceCompatImpl = new TypefaceCompatApi26Impl();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
&& TypefaceCompatApi24Impl.isUsable()) {
diff --git a/compat/src/main/java/android/support/v4/graphics/TypefaceCompatApi26Impl.java b/compat/src/main/java/android/support/v4/graphics/TypefaceCompatApi26Impl.java
index 28ab3ed..4260c55 100644
--- a/compat/src/main/java/android/support/v4/graphics/TypefaceCompatApi26Impl.java
+++ b/compat/src/main/java/android/support/v4/graphics/TypefaceCompatApi26Impl.java
@@ -60,76 +60,69 @@
"createFromFamiliesWithDefault";
private static final String FREEZE_METHOD = "freeze";
private static final String ABORT_CREATION_METHOD = "abortCreation";
- private static final Class sFontFamily;
- private static final Constructor sFontFamilyCtor;
- private static final Method sAddFontFromAssetManager;
- private static final Method sAddFontFromBuffer;
- private static final Method sFreeze;
- private static final Method sAbortCreation;
- private static final Method sCreateFromFamiliesWithDefault;
private static final int RESOLVE_BY_FONT_TABLE = -1;
- static {
- Class fontFamilyClass;
+ protected final Class mFontFamily;
+ protected final Constructor mFontFamilyCtor;
+ protected final Method mAddFontFromAssetManager;
+ protected final Method mAddFontFromBuffer;
+ protected final Method mFreeze;
+ protected final Method mAbortCreation;
+ protected final Method mCreateFromFamiliesWithDefault;
+
+ public TypefaceCompatApi26Impl() {
+ Class fontFamily;
Constructor fontFamilyCtor;
- Method addFontMethod;
- Method addFromBufferMethod;
- Method freezeMethod;
- Method abortCreationMethod;
- Method createFromFamiliesWithDefaultMethod;
+ Method addFontFromAssetManager;
+ Method addFontFromBuffer;
+ Method freeze;
+ Method abortCreation;
+ Method createFromFamiliesWithDefault;
try {
- fontFamilyClass = Class.forName(FONT_FAMILY_CLASS);
- fontFamilyCtor = fontFamilyClass.getConstructor();
- addFontMethod = fontFamilyClass.getMethod(ADD_FONT_FROM_ASSET_MANAGER_METHOD,
- AssetManager.class, String.class, Integer.TYPE, Boolean.TYPE, Integer.TYPE,
- Integer.TYPE, Integer.TYPE, FontVariationAxis[].class);
- addFromBufferMethod = fontFamilyClass.getMethod(ADD_FONT_FROM_BUFFER_METHOD,
- ByteBuffer.class, Integer.TYPE, FontVariationAxis[].class, Integer.TYPE,
- Integer.TYPE);
- freezeMethod = fontFamilyClass.getMethod(FREEZE_METHOD);
- abortCreationMethod = fontFamilyClass.getMethod(ABORT_CREATION_METHOD);
- Object familyArray = Array.newInstance(fontFamilyClass, 1);
- createFromFamiliesWithDefaultMethod =
- Typeface.class.getDeclaredMethod(CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD,
- familyArray.getClass(), Integer.TYPE, Integer.TYPE);
- createFromFamiliesWithDefaultMethod.setAccessible(true);
+ fontFamily = obtainFontFamily();
+ fontFamilyCtor = obtainFontFamilyCtor(fontFamily);
+ addFontFromAssetManager = obtainAddFontFromAssetManagerMethod(fontFamily);
+ addFontFromBuffer = obtainAddFontFromBufferMethod(fontFamily);
+ freeze = obtainFreezeMethod(fontFamily);
+ abortCreation = obtainAbortCreationMethod(fontFamily);
+ createFromFamiliesWithDefault = obtainCreateFromFamiliesWithDefaultMethod(fontFamily);
} catch (ClassNotFoundException | NoSuchMethodException e) {
Log.e(TAG, "Unable to collect necessary methods for class " + e.getClass().getName(),
e);
- fontFamilyClass = null;
+ fontFamily = null;
fontFamilyCtor = null;
- addFontMethod = null;
- addFromBufferMethod = null;
- freezeMethod = null;
- abortCreationMethod = null;
- createFromFamiliesWithDefaultMethod = null;
+ addFontFromAssetManager = null;
+ addFontFromBuffer = null;
+ freeze = null;
+ abortCreation = null;
+ createFromFamiliesWithDefault = null;
}
- sFontFamilyCtor = fontFamilyCtor;
- sFontFamily = fontFamilyClass;
- sAddFontFromAssetManager = addFontMethod;
- sAddFontFromBuffer = addFromBufferMethod;
- sFreeze = freezeMethod;
- sAbortCreation = abortCreationMethod;
- sCreateFromFamiliesWithDefault = createFromFamiliesWithDefaultMethod;
+ mFontFamily = fontFamily;
+ mFontFamilyCtor = fontFamilyCtor;
+ mAddFontFromAssetManager = addFontFromAssetManager;
+ mAddFontFromBuffer = addFontFromBuffer;
+ mFreeze = freeze;
+ mAbortCreation = abortCreation;
+ mCreateFromFamiliesWithDefault = createFromFamiliesWithDefault;
}
/**
- * Returns true if API26 implementation is usable.
+ * Returns true if all the necessary methods were found.
*/
- private static boolean isFontFamilyPrivateAPIAvailable() {
- if (sAddFontFromAssetManager == null) {
+ private boolean isFontFamilyPrivateAPIAvailable() {
+ if (mAddFontFromAssetManager == null) {
Log.w(TAG, "Unable to collect necessary private methods. "
+ "Fallback to legacy implementation.");
}
- return sAddFontFromAssetManager != null;
+ return mAddFontFromAssetManager != null;
}
/**
* Create a new FontFamily instance
*/
- private static Object newFamily() {
+ private Object newFamily() {
try {
- return sFontFamilyCtor.newInstance();
+ return mFontFamilyCtor.newInstance();
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
throw new RuntimeException(e);
}
@@ -139,10 +132,10 @@
* Call FontFamily#addFontFromAssetManager(AssetManager mgr, String path, int cookie,
* boolean isAsset, int ttcIndex, int weight, int isItalic, FontVariationAxis[] axes)
*/
- private static boolean addFontFromAssetManager(Context context, Object family, String fileName,
+ private boolean addFontFromAssetManager(Context context, Object family, String fileName,
int ttcIndex, int weight, int style) {
try {
- final Boolean result = (Boolean) sAddFontFromAssetManager.invoke(family,
+ final Boolean result = (Boolean) mAddFontFromAssetManager.invoke(family,
context.getAssets(), fileName, 0 /* cookie */, false /* isAsset */, ttcIndex,
weight, style, null /* axes */);
return result.booleanValue();
@@ -155,10 +148,10 @@
* Call FontFamily#addFontFromBuffer(ByteBuffer font, int ttcIndex, FontVariationAxis[] axes,
* int weight, int italic)
*/
- private static boolean addFontFromBuffer(Object family, ByteBuffer buffer,
+ private boolean addFontFromBuffer(Object family, ByteBuffer buffer,
int ttcIndex, int weight, int style) {
try {
- final Boolean result = (Boolean) sAddFontFromBuffer.invoke(family,
+ final Boolean result = (Boolean) mAddFontFromBuffer.invoke(family,
buffer, ttcIndex, null /* axes */, weight, style);
return result.booleanValue();
} catch (IllegalAccessException | InvocationTargetException e) {
@@ -167,14 +160,14 @@
}
/**
- * Call static method Typeface#createFromFamiliesWithDefault(
+ * Call method Typeface#createFromFamiliesWithDefault(
* FontFamily[] families, int weight, int italic)
*/
- private static Typeface createFromFamiliesWithDefault(Object family) {
+ protected Typeface createFromFamiliesWithDefault(Object family) {
try {
- Object familyArray = Array.newInstance(sFontFamily, 1);
+ Object familyArray = Array.newInstance(mFontFamily, 1);
Array.set(familyArray, 0, family);
- return (Typeface) sCreateFromFamiliesWithDefault.invoke(null /* static method */,
+ return (Typeface) mCreateFromFamiliesWithDefault.invoke(null /* static method */,
familyArray, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
@@ -184,9 +177,9 @@
/**
* Call FontFamily#freeze()
*/
- private static boolean freeze(Object family) {
+ private boolean freeze(Object family) {
try {
- Boolean result = (Boolean) sFreeze.invoke(family);
+ Boolean result = (Boolean) mFreeze.invoke(family);
return result.booleanValue();
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
@@ -196,9 +189,9 @@
/**
* Call FontFamily#abortCreation()
*/
- private static void abortCreation(Object family) {
+ private void abortCreation(Object family) {
try {
- sAbortCreation.invoke(family);
+ mAbortCreation.invoke(family);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
@@ -301,4 +294,47 @@
}
return createFromFamiliesWithDefault(fontFamily);
}
+
+ // The following getters retrieve by reflection the Typeface methods, belonging to the
+ // framework code, which will be invoked. Since the definitions of these methods can change
+ // across different API versions, inheriting classes should override these getters in order to
+ // reflect the method definitions in the API versions they represent.
+ //===========================================================================================
+ protected Class obtainFontFamily() throws ClassNotFoundException {
+ return Class.forName(FONT_FAMILY_CLASS);
+ }
+
+ protected Constructor obtainFontFamilyCtor(Class fontFamily) throws NoSuchMethodException {
+ return fontFamily.getConstructor();
+ }
+
+ protected Method obtainAddFontFromAssetManagerMethod(Class fontFamily)
+ throws NoSuchMethodException {
+ return fontFamily.getMethod(ADD_FONT_FROM_ASSET_MANAGER_METHOD,
+ AssetManager.class, String.class, Integer.TYPE, Boolean.TYPE, Integer.TYPE,
+ Integer.TYPE, Integer.TYPE, FontVariationAxis[].class);
+ }
+
+ protected Method obtainAddFontFromBufferMethod(Class fontFamily) throws NoSuchMethodException {
+ return fontFamily.getMethod(ADD_FONT_FROM_BUFFER_METHOD,
+ ByteBuffer.class, Integer.TYPE, FontVariationAxis[].class, Integer.TYPE,
+ Integer.TYPE);
+ }
+
+ protected Method obtainFreezeMethod(Class fontFamily) throws NoSuchMethodException {
+ return fontFamily.getMethod(FREEZE_METHOD);
+ }
+
+ protected Method obtainAbortCreationMethod(Class fontFamily) throws NoSuchMethodException {
+ return fontFamily.getMethod(ABORT_CREATION_METHOD);
+ }
+
+ protected Method obtainCreateFromFamiliesWithDefaultMethod(Class fontFamily)
+ throws NoSuchMethodException {
+ Object familyArray = Array.newInstance(fontFamily, 1);
+ Method m = Typeface.class.getDeclaredMethod(CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD,
+ familyArray.getClass(), Integer.TYPE, Integer.TYPE);
+ m.setAccessible(true);
+ return m;
+ }
}
diff --git a/compat/src/main/java/android/support/v4/graphics/TypefaceCompatApi28Impl.java b/compat/src/main/java/android/support/v4/graphics/TypefaceCompatApi28Impl.java
new file mode 100644
index 0000000..baa2ce6
--- /dev/null
+++ b/compat/src/main/java/android/support/v4/graphics/TypefaceCompatApi28Impl.java
@@ -0,0 +1,68 @@
+/*
+ * 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 android.support.v4.graphics;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.graphics.Typeface;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Implementation of the Typeface compat methods for API 28 and above.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@RequiresApi(28)
+public class TypefaceCompatApi28Impl extends TypefaceCompatApi26Impl {
+ private static final String TAG = "TypefaceCompatApi28Impl";
+
+ private static final String CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD =
+ "createFromFamiliesWithDefault";
+ private static final int RESOLVE_BY_FONT_TABLE = -1;
+ private static final String DEFAULT_FAMILY = "sans-serif";
+
+ /**
+ * Call method Typeface#createFromFamiliesWithDefault(
+ * FontFamily[] families, String fallbackName, int weight, int italic)
+ */
+ @Override
+ protected Typeface createFromFamiliesWithDefault(Object family) {
+ try {
+ Object familyArray = Array.newInstance(mFontFamily, 1);
+ Array.set(familyArray, 0, family);
+ return (Typeface) mCreateFromFamiliesWithDefault.invoke(null /* static method */,
+ familyArray, DEFAULT_FAMILY, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected Method obtainCreateFromFamiliesWithDefaultMethod(Class fontFamily)
+ throws NoSuchMethodException {
+ Object familyArray = Array.newInstance(fontFamily, 1);
+ Method m = Typeface.class.getDeclaredMethod(CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD,
+ familyArray.getClass(), String.class, Integer.TYPE, Integer.TYPE);
+ m.setAccessible(true);
+ return m;
+ }
+}
diff --git a/content/Android.mk b/content/Android.mk
index 32caf53..1eca4f5 100644
--- a/content/Android.mk
+++ b/content/Android.mk
@@ -20,9 +20,10 @@
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-compat \
+LOCAL_JAVA_LIBRARIES := \
android-support-annotations
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-compat
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/core-ui/Android.mk b/core-ui/Android.mk
index 47846a9..1473381 100644
--- a/core-ui/Android.mk
+++ b/core-ui/Android.mk
@@ -28,11 +28,13 @@
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under,src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-compat \
- android-support-core-utils \
- android-support-annotations
+ android-support-core-utils
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
+LOCAL_EXPORT_PROGUARD_FLAG_FILES := proguard-rules.pro
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/core-utils/Android.mk b/core-utils/Android.mk
index 6dda862..751091b 100644
--- a/core-utils/Android.mk
+++ b/core-utils/Android.mk
@@ -31,9 +31,10 @@
$(call all-java-files-under,api21) \
$(call all-java-files-under,java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-compat \
+LOCAL_JAVA_LIBRARIES := \
android-support-annotations
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-compat
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/customtabs/Android.mk b/customtabs/Android.mk
index f9195e8..30132a1 100644
--- a/customtabs/Android.mk
+++ b/customtabs/Android.mk
@@ -31,8 +31,9 @@
$(call all-java-files-under,src/main/java) \
$(call all-Iaidl-files-under,src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-annotations \
android-support-compat
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/design/Android.mk b/design/Android.mk
index 4a51f77..f856de6 100644
--- a/design/Android.mk
+++ b/design/Android.mk
@@ -27,6 +27,7 @@
# in their makefiles to include the resources and their dependencies in their package.
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
+LOCAL_AAPT2_ONLY := true
LOCAL_MODULE := android-support-design
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := \
@@ -34,6 +35,8 @@
$(call all-java-files-under,lollipop) \
$(call all-java-files-under,src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-transition \
android-support-v7-appcompat \
@@ -42,10 +45,10 @@
android-support-media-compat \
android-support-core-utils \
android-support-core-ui \
- android-support-fragment \
- android-support-annotations
+ android-support-fragment
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := \
--no-version-vectors \
--add-javadoc-annotation doconly
+LOCAL_EXPORT_PROGUARD_FLAG_FILES := proguard-rules.pro
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/design/api/27.0.0-SNAPSHOT.ignore b/design/api/27.0.0-SNAPSHOT.ignore
new file mode 100644
index 0000000..533cc49
--- /dev/null
+++ b/design/api/27.0.0-SNAPSHOT.ignore
@@ -0,0 +1,5 @@
+197ce1d
+88bc57e
+9761c3e
+86b38bf
+c6abd5e
diff --git a/dynamic-animation/Android.mk b/dynamic-animation/Android.mk
index 11aa484..98e8f45 100644
--- a/dynamic-animation/Android.mk
+++ b/dynamic-animation/Android.mk
@@ -20,9 +20,10 @@
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under,src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-compat \
+LOCAL_JAVA_LIBRARIES := \
android-support-annotations
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-compat
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/emoji/appcompat/Android.mk b/emoji/appcompat/Android.mk
index f04d158..0ef8cb7 100644
--- a/emoji/appcompat/Android.mk
+++ b/emoji/appcompat/Android.mk
@@ -30,8 +30,9 @@
LOCAL_MODULE := android-support-emoji-appcompat
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java)
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-annotations \
android-support-v7-appcompat \
android-support-emoji \
android-support-compat
diff --git a/emoji/bundled/Android.mk b/emoji/bundled/Android.mk
index 29f0d58..c5c1ac9 100644
--- a/emoji/bundled/Android.mk
+++ b/emoji/bundled/Android.mk
@@ -29,8 +29,9 @@
LOCAL_MODULE := android-support-emoji-bundled
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java)
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-annotations \
android-support-emoji \
android-support-compat
LOCAL_JAR_EXCLUDE_FILES := none
diff --git a/emoji/core/Android.mk b/emoji/core/Android.mk
index 8b7a032..89426f0 100644
--- a/emoji/core/Android.mk
+++ b/emoji/core/Android.mk
@@ -31,8 +31,9 @@
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/src/main/res
LOCAL_STATIC_JAVA_LIBRARIES := \
noto-emoji-compat-java
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-annotations \
android-support-compat
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
diff --git a/exifinterface/Android.mk b/exifinterface/Android.mk
index 4e5bdc0..1571116 100644
--- a/exifinterface/Android.mk
+++ b/exifinterface/Android.mk
@@ -25,7 +25,7 @@
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := \
+LOCAL_JAVA_LIBRARIES := \
android-support-annotations
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
diff --git a/fragment/Android.mk b/fragment/Android.mk
index efac954..6513542 100644
--- a/fragment/Android.mk
+++ b/fragment/Android.mk
@@ -32,11 +32,12 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under, src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-compat \
android-support-core-ui \
- android-support-core-utils \
- android-support-annotations
+ android-support-core-utils
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/graphics/drawable/Android.mk b/graphics/drawable/Android.mk
index b1f0b38..6667dd9 100644
--- a/graphics/drawable/Android.mk
+++ b/graphics/drawable/Android.mk
@@ -26,9 +26,10 @@
LOCAL_SRC_FILES := $(call all-java-files-under, static/src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/static/res
LOCAL_MANIFEST_FILE := static/AndroidManifest.xml
-LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-compat \
+LOCAL_JAVA_LIBRARIES := \
android-support-annotations
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-compat
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
@@ -45,10 +46,11 @@
LOCAL_SRC_FILES := $(call all-java-files-under, animated/src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/animated/res
LOCAL_MANIFEST_FILE := animated/AndroidManifest.xml
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-compat \
android-support-vectordrawable \
- android-support-annotations \
android-support-core-ui
LOCAL_AAPT_FLAGS := --no-version-vectors --add-javadoc-annotation doconly
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
diff --git a/leanback/Android.mk b/leanback/Android.mk
index d98abf6..e39261f 100644
--- a/leanback/Android.mk
+++ b/leanback/Android.mk
@@ -25,6 +25,7 @@
# in their makefiles to include the resources and their dependencies in their package.
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
+LOCAL_AAPT2_ONLY := true
LOCAL_MODULE := android-support-v17-leanback
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := \
@@ -34,13 +35,14 @@
$(call all-java-files-under, api21) \
$(call all-java-files-under, src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-v7-recyclerview \
android-support-compat \
android-support-core-ui \
android-support-media-compat \
- android-support-fragment \
- android-support-annotations
+ android-support-fragment
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/leanback/res/values-mr/strings.xml b/leanback/res/values-mr/strings.xml
index 8629ef1..1fcb920 100644
--- a/leanback/res/values-mr/strings.xml
+++ b/leanback/res/values-mr/strings.xml
@@ -47,7 +47,7 @@
<string name="lb_playback_controls_high_quality_disable" msgid="8637371582779057866">"उच्च गुणवत्ता अक्षम करा"</string>
<string name="lb_playback_controls_closed_captioning_enable" msgid="2429655367176440226">"उपशीर्षके सक्षम करा"</string>
<string name="lb_playback_controls_closed_captioning_disable" msgid="6133362019475930048">"सबटायटल अक्षम करा"</string>
- <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"चित्र मोडमध्ये चित्र प्रविष्ट करा"</string>
+ <string name="lb_playback_controls_picture_in_picture" msgid="3040035547765350690">"चित्र मोडमध्ये चित्र एंटर करा"</string>
<string name="lb_playback_time_separator" msgid="3208380806582304911">"/"</string>
<string name="lb_playback_controls_shown" msgid="6382160135512023238">"मीडिया नियंत्रणे दर्शवली आहेत"</string>
<string name="lb_playback_controls_hidden" msgid="8940984081242033574">"मीडिया नियंत्रणे लपलेली आहेत, दर्शवण्यासाठी d-pad दाबा"</string>
diff --git a/media-compat/Android.mk b/media-compat/Android.mk
index 4a4cd02..8538752 100644
--- a/media-compat/Android.mk
+++ b/media-compat/Android.mk
@@ -36,9 +36,10 @@
$(call all-java-files-under,java) \
$(call all-Iaidl-files-under,java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-compat \
+LOCAL_JAVA_LIBRARIES := \
android-support-annotations
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-compat
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/preference-leanback/Android.mk b/preference-leanback/Android.mk
index 263d334..e2ad1de 100644
--- a/preference-leanback/Android.mk
+++ b/preference-leanback/Android.mk
@@ -29,20 +29,22 @@
# in their makefiles to include the resources in their package.
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
+LOCAL_AAPT2_ONLY := true
LOCAL_MODULE := android-support-v17-preference-leanback
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := \
$(call all-java-files-under,api21) \
$(call all-java-files-under,src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-v17-leanback \
android-support-v14-preference \
android-support-v7-preference \
android-support-v7-appcompat \
android-support-v7-recyclerview \
- android-support-v4 \
- android-support-annotations
+ android-support-v4
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/recommendation/Android.mk b/recommendation/Android.mk
index ea819e9..6249df7 100644
--- a/recommendation/Android.mk
+++ b/recommendation/Android.mk
@@ -27,9 +27,10 @@
LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-v4 \
+LOCAL_JAVA_LIBRARIES := \
android-support-annotations
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-v4
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/recyclerview-selection/Android.mk b/recyclerview-selection/Android.mk
index ed93fa2..511806f 100644
--- a/recyclerview-selection/Android.mk
+++ b/recyclerview-selection/Android.mk
@@ -20,10 +20,11 @@
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-v7-recyclerview \
- android-support-compat \
- android-support-annotations
+ android-support-compat
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/samples/SupportPreferenceDemos/src/main/res/xml/preferences.xml b/samples/SupportPreferenceDemos/src/main/res/xml/preferences.xml
index f469af2..4d1dca7 100644
--- a/samples/SupportPreferenceDemos/src/main/res/xml/preferences.xml
+++ b/samples/SupportPreferenceDemos/src/main/res/xml/preferences.xml
@@ -31,6 +31,12 @@
android:title="@string/title_stylish_preference"
android:summary="@string/summary_stylish_preference" />
+ <Preference
+ android:key="preference_with_icon"
+ android:title="Preference with icon"
+ android:summary="This preference has an icon"
+ android:icon="@android:drawable/ic_menu_camera" />
+
<PreferenceCategory
android:title="@string/inline_preferences">
@@ -39,6 +45,11 @@
android:title="@string/title_checkbox_preference"
android:summary="@string/summary_checkbox_preference" />
+ <SwitchPreference
+ android:key="switch_preference"
+ android:title="Switch preference"
+ android:summary="This is a switch" />
+
<DropDownPreference
android:key="dropdown_preference"
android:title="@string/title_dropdown_preference"
diff --git a/samples/SupportSliceDemos/OWNERS b/samples/SupportSliceDemos/OWNERS
new file mode 100644
index 0000000..921a517
--- /dev/null
+++ b/samples/SupportSliceDemos/OWNERS
@@ -0,0 +1,2 @@
+jmonk@google.com
+madym@google.com
diff --git a/samples/SupportSliceDemos/build.gradle b/samples/SupportSliceDemos/build.gradle
new file mode 100644
index 0000000..bc17b88
--- /dev/null
+++ b/samples/SupportSliceDemos/build.gradle
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'com.android.application'
+
+dependencies {
+ implementation project(':slices-view')
+ implementation project(':slices-builders')
+ implementation project(':lifecycle:extensions')
+ implementation libs.support.design, libs.support_exclude_config
+ implementation libs.support.app_compat, libs.support_exclude_config
+ implementation libs.support.cardview, libs.support_exclude_config
+ implementation project(':lifecycle:extensions')
+}
+
+android {
+ compileSdkVersion project.ext.currentSdk
+
+ defaultConfig {
+ applicationId "com.example.androidx.slice.demos"
+ minSdkVersion 26
+ targetSdkVersion project.ext.currentSdk
+ }
+
+ signingConfigs {
+ debug {
+ // Use a local debug keystore to avoid build server issues.
+ storeFile project.rootProject.init.debugKeystore
+ }
+ }
+
+ lintOptions {
+ abortOnError true
+ disable "SetTextI18n", "AppCompatResource", "WrongConstant", "AllowBackup",
+ "GoogleAppIndexingWarning", "AlwaysShowAction"
+ }
+ compileOptions {
+ targetCompatibility 1.8
+ sourceCompatibility 1.8
+ }
+}
diff --git a/samples/SupportSliceDemos/src/main/AndroidManifest.xml b/samples/SupportSliceDemos/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5285c34
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/AndroidManifest.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.example.androidx.slice.demos">
+
+ <uses-sdk tools:overrideLibrary="androidx.app.slice.view, androidx.app.slice.builders, androidx.app.slice.core" />
+
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.BIND_SLICE" />
+
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+ <activity
+ android:name=".SliceBrowser"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme.NoActionBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name="androidx.intent.SLICE_ACTION"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
+ <provider android:authorities="com.example.androidx.slice.demos"
+ android:name=".SampleSliceProvider"
+ android:grantUriPermissions="true">
+ <intent-filter>
+ <action android:name="androidx.intent.SLICE_ACTION"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </provider>
+
+ <receiver
+ android:name=".SliceBroadcastReceiver"
+ android:exported="true" >
+ <intent-filter>
+ <action android:name="com.example.androidx.slice.action.*"/>
+ </intent-filter>
+ </receiver>
+ </application>
+
+</manifest>
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
new file mode 100644
index 0000000..2f2a1c3
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
@@ -0,0 +1,203 @@
+/*
+ * 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.example.androidx.slice.demos;
+
+import android.app.PendingIntent;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.provider.Settings;
+import android.support.annotation.NonNull;
+import android.text.format.DateUtils;
+
+import androidx.app.slice.Slice;
+import androidx.app.slice.SliceProvider;
+import androidx.app.slice.builders.ListBuilder;
+import androidx.app.slice.builders.MessagingSliceBuilder;
+
+/**
+ * Examples of using slice template builders.
+ */
+public class SampleSliceProvider extends SliceProvider {
+
+ public static final String ACTION_WIFI_CHANGED =
+ "com.example.androidx.slice.action.WIFI_CHANGED";
+ public static final String ACTION_TOAST =
+ "com.example.androidx.slice.action.TOAST";
+ public static final String EXTRA_TOAST_MESSAGE = "com.example.androidx.extra.TOAST_MESSAGE";
+
+ public static final String[] URI_PATHS = { "message", "wifi", "note", "ride", "toggle"};
+
+ /**
+ * @return Uri with the provided path
+ */
+ public static Uri getUri(String path, Context context) {
+ return new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(context.getPackageName())
+ .appendPath(path)
+ .build();
+ }
+
+ @Override
+ public boolean onCreateSliceProvider() {
+ return true;
+ }
+
+ @NonNull
+ @Override
+ public Uri onMapIntentToUri(Intent intent) {
+ return getUri("wifi", getContext());
+ }
+
+ @Override
+ public Slice onBindSlice(Uri sliceUri) {
+ String path = sliceUri.getPath();
+ switch (path) {
+ case "/message":
+ return createMessagingSlice(sliceUri);
+ case "/wifi":
+ return createWifiSlice(sliceUri);
+ case "/note":
+ return createNoteSlice(sliceUri);
+ case "/ride":
+ return createRideSlice(sliceUri);
+ case "/toggle":
+ return createCustomToggleSlice(sliceUri);
+ }
+ throw new IllegalArgumentException("Unknown uri " + sliceUri);
+ }
+
+ private Slice createMessagingSlice(Uri sliceUri) {
+ // TODO: Remote input.
+ return new MessagingSliceBuilder(sliceUri)
+ .add(b -> b
+ .addText("yo home \uD83C\uDF55, I emailed you the info")
+ .addTimestamp(System.currentTimeMillis() - 20 * DateUtils.MINUTE_IN_MILLIS)
+ .addSource(Icon.createWithResource(getContext(), R.drawable.mady)))
+ .add(b -> b
+ .addText("just bought my tickets")
+ .addTimestamp(System.currentTimeMillis() - 10 * DateUtils.MINUTE_IN_MILLIS))
+ .add(b -> b
+ .addText("yay! can't wait for getContext() weekend!\n"
+ + "\uD83D\uDE00")
+ .addTimestamp(System.currentTimeMillis() - 5 * DateUtils.MINUTE_IN_MILLIS)
+ .addSource(Icon.createWithResource(getContext(), R.drawable.mady)))
+ .build();
+
+ }
+
+ private Slice createNoteSlice(Uri sliceUri) {
+ return new ListBuilder(sliceUri)
+ .setColor(0xfff4b400)
+ .add(b -> b
+ .setTitle("Create new note")
+ .setSubtitle("with this note taking app")
+ .addEndItem(getBroadcastIntent(ACTION_TOAST, "create note"),
+ Icon.createWithResource(getContext(), R.drawable.ic_create))
+ .addEndItem(getBroadcastIntent(ACTION_TOAST, "voice note"),
+ Icon.createWithResource(getContext(), R.drawable.ic_voice))
+ .addEndItem(getIntent("android.media.action.IMAGE_CAPTURE"),
+ Icon.createWithResource(getContext(), R.drawable.ic_camera)))
+ .build();
+ }
+
+ private Slice createRideSlice(Uri sliceUri) {
+ return new ListBuilder(sliceUri)
+ .setColor(0xff1b5e20)
+ .add(b -> b
+ .setContentIntent(getBroadcastIntent(ACTION_TOAST, "work"))
+ .setTitle("Work")
+ .setSubtitle("2 min")
+ .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_work)))
+ .add(b -> b
+ .setContentIntent(getBroadcastIntent(ACTION_TOAST, "home"))
+ .setTitle("Home")
+ .setSubtitle("2 hours 33 min via 101")
+ .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_home))
+ .setIsHeader(true))
+ .add(b -> b
+ .setContentIntent(getBroadcastIntent(ACTION_TOAST, "book ride"))
+ .setTitle("Book ride")
+ .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_car)))
+ .build();
+ }
+
+ private Slice createCustomToggleSlice(Uri sliceUri) {
+ return new ListBuilder(sliceUri)
+ .setColor(0xffff4081)
+ .add(b -> b
+ .setTitle("Custom toggle")
+ .setSubtitle("It can support two states")
+ .addToggle(getBroadcastIntent(ACTION_TOAST, "start toggled"),
+ Icon.createWithResource(getContext(), R.drawable.toggle_star),
+ true /* isChecked */))
+ .build();
+ }
+
+ private Slice createWifiSlice(Uri sliceUri) {
+ // Get wifi state
+ WifiManager wifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+ int wifiState = wifiManager.getWifiState();
+ boolean wifiEnabled = false;
+ String state;
+ switch (wifiState) {
+ case WifiManager.WIFI_STATE_DISABLED:
+ case WifiManager.WIFI_STATE_DISABLING:
+ state = "disconnected";
+ break;
+ case WifiManager.WIFI_STATE_ENABLED:
+ case WifiManager.WIFI_STATE_ENABLING:
+ state = wifiManager.getConnectionInfo().getSSID();
+ wifiEnabled = true;
+ break;
+ case WifiManager.WIFI_STATE_UNKNOWN:
+ default:
+ state = ""; // just don't show anything?
+ break;
+ }
+ boolean finalWifiEnabled = wifiEnabled;
+ return new ListBuilder(sliceUri)
+ .setColor(0xff4285f4)
+ .add(b -> b
+ .setTitle("Wi-fi")
+ .setTitleItem(Icon.createWithResource(getContext(), R.drawable.ic_wifi))
+ .setSubtitle(state)
+ .addToggle(getBroadcastIntent(ACTION_WIFI_CHANGED, null), finalWifiEnabled)
+ .setContentIntent(getIntent(Settings.ACTION_WIFI_SETTINGS)))
+ .build();
+ }
+
+ private PendingIntent getIntent(String action) {
+ Intent intent = new Intent(action);
+ PendingIntent pi = PendingIntent.getActivity(getContext(), 0, intent, 0);
+ return pi;
+ }
+
+ private PendingIntent getBroadcastIntent(String action, String message) {
+ Intent intent = new Intent(action);
+ if (message != null) {
+ intent.putExtra(EXTRA_TOAST_MESSAGE, message);
+ }
+ intent.setClass(getContext(), SliceBroadcastReceiver.class);
+ return PendingIntent.getBroadcast(getContext(), 0, intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ }
+}
diff --git a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBroadcastReceiver.java b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBroadcastReceiver.java
new file mode 100644
index 0000000..05f0820
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBroadcastReceiver.java
@@ -0,0 +1,57 @@
+/*
+ * 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.example.androidx.slice.demos;
+
+import static com.example.androidx.slice.demos.SampleSliceProvider.getUri;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.widget.Toast;
+
+import androidx.app.slice.core.SliceHints;
+
+/**
+ * Responds to actions performed on slices and notifies slices of updates in state changes.
+ */
+public class SliceBroadcastReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent i) {
+ String action = i.getAction();
+ switch (action) {
+ case SampleSliceProvider.ACTION_WIFI_CHANGED:
+ WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ boolean newState = i.getBooleanExtra(SliceHints.EXTRA_TOGGLE_STATE,
+ wm.isWifiEnabled());
+ wm.setWifiEnabled(newState);
+ // Wait a bit for wifi to update (TODO: is there a better way to do this?)
+ Handler h = new Handler();
+ h.postDelayed(() -> {
+ context.getContentResolver().notifyChange(getUri("wifi", context), null);
+ }, 1000);
+ break;
+ case SampleSliceProvider.ACTION_TOAST:
+ String message = i.getExtras().getString(SampleSliceProvider.EXTRA_TOAST_MESSAGE,
+ "no message");
+ Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
+ break;
+ }
+ }
+}
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
new file mode 100644
index 0000000..82804fd
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
@@ -0,0 +1,273 @@
+/*
+ * 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.example.androidx.slice.demos;
+
+import static com.example.androidx.slice.demos.SampleSliceProvider.URI_PATHS;
+import static com.example.androidx.slice.demos.SampleSliceProvider.getUri;
+
+import android.arch.lifecycle.LiveData;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.BaseColumns;
+import android.support.annotation.RequiresApi;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.SubMenu;
+import android.view.ViewGroup;
+import android.widget.CursorAdapter;
+import android.widget.SearchView;
+import android.widget.SimpleCursorAdapter;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+import androidx.app.slice.Slice;
+import androidx.app.slice.widget.SliceLiveData;
+import androidx.app.slice.widget.SliceView;
+
+/**
+ * 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 {
+
+ private static final String TAG = "SlicePresenter";
+
+ private static final String SLICE_METADATA_KEY = "android.metadata.SLICE_URI";
+ private static final boolean TEST_INTENT = false;
+
+ private ArrayList<Uri> mSliceUris = new ArrayList<Uri>();
+ private int mSelectedMode;
+ private ViewGroup mContainer;
+ private SearchView mSearchView;
+ private SimpleCursorAdapter mAdapter;
+ private SubMenu mTypeMenu;
+ private LiveData<Slice> mSliceLiveData;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_layout);
+
+ Toolbar toolbar = findViewById(R.id.search_toolbar);
+ setSupportActionBar(toolbar);
+
+ // Shows the slice
+ mContainer = findViewById(R.id.slice_preview);
+ mSearchView = findViewById(R.id.search_view);
+
+ final String[] from = new String[]{"uri"};
+ final int[] to = new int[]{android.R.id.text1};
+ mAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1,
+ null, from, to, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
+ mSearchView.setSuggestionsAdapter(mAdapter);
+ mSearchView.setIconifiedByDefault(false);
+ mSearchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
+ @Override
+ public boolean onSuggestionClick(int position) {
+ mSearchView.setQuery(((Cursor) mAdapter.getItem(position)).getString(1), true);
+ return true;
+ }
+
+ @Override
+ public boolean onSuggestionSelect(int position) {
+ mSearchView.setQuery(((Cursor) mAdapter.getItem(position)).getString(1), true);
+ return true;
+ }
+ });
+ mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ addSlice(Uri.parse(s));
+ mSearchView.clearFocus();
+ return false;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String s) {
+ populateAdapter(s);
+ return false;
+ }
+ });
+
+ mSelectedMode = (savedInstanceState != null)
+ ? savedInstanceState.getInt("SELECTED_MODE", SliceView.MODE_SHORTCUT)
+ : SliceView.MODE_SHORTCUT;
+ if (savedInstanceState != null) {
+ mSearchView.setQuery(savedInstanceState.getString("SELECTED_QUERY"), true);
+ }
+
+ // TODO: Listen for changes.
+ updateAvailableSlices();
+ if (TEST_INTENT) {
+ addSlice(new Intent("androidx.intent.SLICE_ACTION").setPackage(getPackageName()));
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ mTypeMenu = menu.addSubMenu("Type");
+ mTypeMenu.setIcon(R.drawable.ic_shortcut);
+ mTypeMenu.getItem().setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ mTypeMenu.add("Shortcut");
+ mTypeMenu.add("Small");
+ mTypeMenu.add("Large");
+ menu.add("Auth");
+ super.onCreateOptionsMenu(menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getTitle().toString()) {
+ case "Auth":
+ authAllSlices();
+ return true;
+ case "Shortcut":
+ mTypeMenu.setIcon(R.drawable.ic_shortcut);
+ mSelectedMode = SliceView.MODE_SHORTCUT;
+ updateSliceModes();
+ return true;
+ case "Small":
+ mTypeMenu.setIcon(R.drawable.ic_small);
+ mSelectedMode = SliceView.MODE_SMALL;
+ updateSliceModes();
+ return true;
+ case "Large":
+ mTypeMenu.setIcon(R.drawable.ic_large);
+ mSelectedMode = SliceView.MODE_LARGE;
+ updateSliceModes();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt("SELECTED_MODE", mSelectedMode);
+ outState.putString("SELECTED_QUERY", mSearchView.getQuery().toString());
+ }
+
+ private void authAllSlices() {
+ List<ApplicationInfo> packages = getPackageManager().getInstalledApplications(0);
+ packages.forEach(info -> {
+ for (int i = 0; i < URI_PATHS.length; i++) {
+ grantUriPermission(info.packageName, getUri(URI_PATHS[i], getApplicationContext()),
+ Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ }
+ });
+ }
+
+ private void updateAvailableSlices() {
+ mSliceUris.clear();
+ List<PackageInfo> packageInfos = getPackageManager()
+ .getInstalledPackages(PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
+ for (PackageInfo pi : packageInfos) {
+ ActivityInfo[] activityInfos = pi.activities;
+ if (activityInfos != null) {
+ for (ActivityInfo ai : activityInfos) {
+ if (ai.metaData != null) {
+ String sliceUri = ai.metaData.getString(SLICE_METADATA_KEY);
+ if (sliceUri != null) {
+ mSliceUris.add(Uri.parse(sliceUri));
+ }
+ }
+ }
+ }
+ }
+ for (int i = 0; i < URI_PATHS.length; i++) {
+ mSliceUris.add(getUri(URI_PATHS[i], getApplicationContext()));
+ }
+ populateAdapter(String.valueOf(mSearchView.getQuery()));
+ }
+
+ private void addSlice(Intent intent) {
+ SliceView v = new SliceView(getApplicationContext());
+ v.setTag(intent);
+ if (mSliceLiveData != null) {
+ mSliceLiveData.removeObservers(this);
+ }
+ mContainer.removeAllViews();
+ mContainer.addView(v);
+ mSliceLiveData = SliceLiveData.fromIntent(this, intent);
+ v.setMode(mSelectedMode);
+ mSliceLiveData.observe(this, v);
+ }
+
+ private void addSlice(Uri uri) {
+ if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+ SliceView v = new SliceView(getApplicationContext());
+ v.setTag(uri);
+ if (mSliceLiveData != null) {
+ mSliceLiveData.removeObservers(this);
+ }
+ mContainer.removeAllViews();
+ mContainer.addView(v);
+ mSliceLiveData = SliceLiveData.fromUri(this, uri);
+ v.setMode(mSelectedMode);
+ mSliceLiveData.observe(this, v);
+ } else {
+ Log.w(TAG, "Invalid uri, skipping slice: " + uri);
+ }
+ }
+
+ private void updateSliceModes() {
+ final int count = mContainer.getChildCount();
+ for (int i = 0; i < count; i++) {
+ ((SliceView) mContainer.getChildAt(i)).setMode(mSelectedMode);
+ }
+ }
+
+ private void populateAdapter(String query) {
+ final MatrixCursor c = new MatrixCursor(new String[]{BaseColumns._ID, "uri"});
+ ArrayMap<String, Integer> ranking = new ArrayMap<>();
+ ArrayList<String> suggestions = new ArrayList();
+ mSliceUris.forEach(uri -> {
+ 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));
+ }
+ });
+ for (int i = 0; i < suggestions.size(); i++) {
+ c.addRow(new Object[]{i, suggestions.get(i)});
+ }
+ mAdapter.changeCursor(c);
+ }
+}
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_camera.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_camera.xml
new file mode 100644
index 0000000..74b6bf9
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_camera.xml
@@ -0,0 +1,32 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#000000"
+ 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
+5-2.24 5-5 5z" />
+ <path
+ android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_car.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_car.xml
new file mode 100644
index 0000000..6bab660
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_car.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <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" />
+ <path
+ android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_create.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_create.xml
new file mode 100644
index 0000000..d1666a8
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_create.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+</vector>
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_home.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_home.xml
new file mode 100644
index 0000000..b278230
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_home.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#000000"
+ android:pathData="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" />
+ <path
+ android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_large.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_large.xml
new file mode 100644
index 0000000..79ac590
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_large.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48.0dp"
+ android:height="48.0dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M34.0,2.02L14.0,2.0c-2.21,0.0 -4.0,1.79 -4.0,4.0l0.0,36.0c0.0,2.21 1.79,4.0 4.0,4.0l20.0,0.0c2.21,0.0 4.0,-1.79 4.0,-4.0L38.0,6.0c0.0,-2.21 -1.79,-3.98 -4.0,-3.98zM34.0,38.0L14.0,38.0L14.0,10.0l20.0,0.0l0.0,28.0z"/>
+ <path
+ android:strokeColor="#FF000000"
+ android:strokeWidth="2"
+ android:pathData="M16,18 l16,0 l0,10 l-16,0z"/>
+</vector>
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_shortcut.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_shortcut.xml
new file mode 100644
index 0000000..bf9572a
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_shortcut.xml
@@ -0,0 +1,34 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="48.0dp"
+ android:viewportHeight="48.0"
+ android:viewportWidth="48.0"
+ android:width="48.0dp">
+ <path
+ android:fillColor="#e2e2e2"
+ android:pathData="M24.0,24.0m-19.0,0.0a19.0,19.0 0.0,1.0 1.0,38.0 0.0a19.0,19.0 0.0,1.0 1.0,-38.0 0.0"/>
+ <group
+ android:scaleX=".7"
+ android:scaleY=".7"
+ android:translateX="7.2"
+ android:translateY="7.2">
+
+ <path
+ android:fillColor="#ff000000"
+ android:pathData="M12.0,36.0c0.0,1.0 0.9,2.0 2.0,2.0l2.0,0.0l0.0,7.0c0.0,1.66 1.34,3.0 3.0,3.0s3.0,-1.34 3.0,-3.0l0.0,-7.0l4.0,0.0l0.0,7.0c0.0,1.66 1.34,3.0 3.0,3.0s3.0,-1.34 3.0,-3.0l0.0,-7.0l2.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L36.0,16.0L12.0,16.0l0.0,20.0zM7.0,16.0c-1.66,0.0 -3.0,1.34 -3.0,3.0l0.0,14.0c0.0,1.66 1.34,3.0 3.0,3.0s3.0,-1.34 3.0,-3.0L10.0,19.0c0.0,-1.66 -1.34,-3.0 -3.0,-3.0zm34.0,0.0c-1.66,0.0 -3.0,1.34 -3.0,3.0l0.0,14.0c0.0,1.66 1.34,3.0 3.0,3.0s3.0,-1.34 3.0,-3.0L44.0,19.0c0.0,-1.66 -1.34,-3.0 -3.0,-3.0zM31.06,4.32l2.61,-2.61c0.39,-0.3 0.39,-1.02 0.0,-1.41 -0.39,-0.39 -1.02,-0.39 -1.41,0.0L29.3,3.25C27.7,2.46 25.91,2.0 24.0,2.0c-1.92,0.0 -3.7,0.46 -5.33,1.26L15.0,0.29c-0.39,-0.39 -1.02,-0.39 -1.41,0.0 -0.3,0.39 -0.39,1.02 0.0,1.41l2.62,2.62C13.94,6.51 12.0,10.03 12.0,14.0l24.0,0.0c0.0,-3.98 -1.95,-7.5 -4.94,-9.68zM20.0,10.0l-2.0,0.0L18.0,8.0l2.0,0.0l0.0,2.0zm10.0,0.0l-2.0,0.0L28.0,8.0l2.0,0.0l0.0,2.0z"/>
+ </group>
+</vector>
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_small.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_small.xml
new file mode 100644
index 0000000..8fd43df
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_small.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="48.0dp"
+ android:height="48.0dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M34.0,2.02L14.0,2.0c-2.21,0.0 -4.0,1.79 -4.0,4.0l0.0,36.0c0.0,2.21 1.79,4.0 4.0,4.0l20.0,0.0c2.21,0.0 4.0,-1.79 4.0,-4.0L38.0,6.0c0.0,-2.21 -1.79,-3.98 -4.0,-3.98zM34.0,38.0L14.0,38.0L14.0,10.0l20.0,0.0l0.0,28.0z"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M16,18 l16,0 l0,4 l-16,0z"/>
+</vector>
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_star_off.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_star_off.xml
new file mode 100644
index 0000000..7f023b3
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_star_off.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
+</vector>
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_star_on.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_star_on.xml
new file mode 100644
index 0000000..f3ca086
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_star_on.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
+</vector>
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_voice.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_voice.xml
new file mode 100644
index 0000000..8f465bb
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_voice.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,15c1.66,0 2.99,-1.34 2.99,-3L15,6c0,-1.66 -1.34,-3 -3,-3S9,4.34 9,6v6c0,1.66 1.34,3 3,3zM17.3,12c0,3 -2.54,5.1 -5.3,5.1S6.7,15 6.7,12L5,12c0,3.42 2.72,6.23 6,6.72L11,22h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/>
+</vector>
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_wifi.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_wifi.xml
new file mode 100644
index 0000000..4fbfd60
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_wifi.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M3.53,10.95l8.46,10.54 0.01,0.01 0.01,-0.01 8.46,-10.54C20.04,10.62 16.81,8 12,8c-4.81,0 -8.04,2.62 -8.47,2.95z"/>
+</vector>
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/ic_work.xml b/samples/SupportSliceDemos/src/main/res/drawable/ic_work.xml
new file mode 100644
index 0000000..6806402
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/drawable/ic_work.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ 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" />
+</vector>
\ No newline at end of file
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/mady.jpg b/samples/SupportSliceDemos/src/main/res/drawable/mady.jpg
new file mode 100644
index 0000000..8b61f1b
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/drawable/mady.jpg
Binary files differ
diff --git a/samples/SupportSliceDemos/src/main/res/drawable/toggle_star.xml b/samples/SupportSliceDemos/src/main/res/drawable/toggle_star.xml
new file mode 100644
index 0000000..e964925
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/drawable/toggle_star.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/on"
+ android:state_checked="true"
+ android:drawable="@drawable/ic_star_on" />
+ <item
+ android:id="@+id/off"
+ android:drawable="@drawable/ic_star_off" />
+</selector>
\ No newline at end of file
diff --git a/samples/SupportSliceDemos/src/main/res/layout/activity_demo.xml b/samples/SupportSliceDemos/src/main/res/layout/activity_demo.xml
new file mode 100644
index 0000000..f557f40
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/layout/activity_demo.xml
@@ -0,0 +1,40 @@
+<?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"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="com.example.android.support.content.demos.ContentPagerDemoActivity">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="?attr/colorPrimary"
+ android:theme="@style/AppTheme.AppBarOverlay"
+ app:popupTheme="@style/AppTheme.PopupOverlay"/>
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+</LinearLayout>
diff --git a/samples/SupportSliceDemos/src/main/res/layout/activity_layout.xml b/samples/SupportSliceDemos/src/main/res/layout/activity_layout.xml
new file mode 100644
index 0000000..6a087a3
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/layout/activity_layout.xml
@@ -0,0 +1,75 @@
+<?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.
+-->
+
+<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="match_parent"
+ android:gravity="top"
+ android:orientation="vertical" >
+
+ <FrameLayout
+ android:id="@+id/search_bar_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="#f2f2f2">
+ <android.support.v7.widget.CardView
+ android:id="@+id/search_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ app:cardCornerRadius="2dp"
+ app:cardBackgroundColor="?android:attr/colorBackground"
+ app:cardElevation="2dp">
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/search_toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:contentInsetStart="0dp"
+ android:contentInsetStartWithNavigation="0dp"
+ android:theme="?android:attr/actionBarTheme">
+ <SearchView
+ android:id="@+id/search_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:iconifiedByDefault="false"
+ android:imeOptions="actionSearch|flagNoExtractUi"
+ android:queryHint="content://..."
+ android:searchIcon="@null"/>
+ </android.support.v7.widget.Toolbar>
+ </android.support.v7.widget.CardView>
+ </FrameLayout>
+
+
+ <ScrollView
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" >
+
+ <FrameLayout
+ android:id="@+id/slice_preview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:orientation="vertical"
+ android:background="?android:attr/colorBackground"
+ android:elevation="10dp" />
+ </ScrollView>
+
+</LinearLayout>
diff --git a/samples/SupportSliceDemos/src/main/res/mipmap-hdpi/ic_launcher.png b/samples/SupportSliceDemos/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/SupportSliceDemos/src/main/res/mipmap-hdpi/ic_launcher_round.png b/samples/SupportSliceDemos/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..9a078e3
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/mipmap-hdpi/ic_launcher_round.png
Binary files differ
diff --git a/samples/SupportSliceDemos/src/main/res/mipmap-mdpi/ic_launcher.png b/samples/SupportSliceDemos/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/SupportSliceDemos/src/main/res/mipmap-mdpi/ic_launcher_round.png b/samples/SupportSliceDemos/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..efc028a
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/mipmap-mdpi/ic_launcher_round.png
Binary files differ
diff --git a/samples/SupportSliceDemos/src/main/res/mipmap-xhdpi/ic_launcher.png b/samples/SupportSliceDemos/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/SupportSliceDemos/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/samples/SupportSliceDemos/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..3af2608
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Binary files differ
diff --git a/samples/SupportSliceDemos/src/main/res/mipmap-xxhdpi/ic_launcher.png b/samples/SupportSliceDemos/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/SupportSliceDemos/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/samples/SupportSliceDemos/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..9bec2e6
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/samples/SupportSliceDemos/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/samples/SupportSliceDemos/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/SupportSliceDemos/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/samples/SupportSliceDemos/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..34947cd
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/samples/SupportSliceDemos/src/main/res/values/colors.xml b/samples/SupportSliceDemos/src/main/res/values/colors.xml
new file mode 100644
index 0000000..86b4304
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/values/colors.xml
@@ -0,0 +1,22 @@
+<?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>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+</resources>
diff --git a/samples/SupportSliceDemos/src/main/res/values/strings.xml b/samples/SupportSliceDemos/src/main/res/values/strings.xml
new file mode 100644
index 0000000..89583bd
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/values/strings.xml
@@ -0,0 +1,18 @@
+<?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:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name">Slice Browser</string>
+</resources>
diff --git a/samples/SupportSliceDemos/src/main/res/values/styles.xml b/samples/SupportSliceDemos/src/main/res/values/styles.xml
new file mode 100644
index 0000000..afb6bad
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/values/styles.xml
@@ -0,0 +1,34 @@
+<?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>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ <item name="colorPrimary">@color/colorPrimary</item>
+ <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="colorAccent">@color/colorAccent</item>
+ </style>
+ <style name="AppTheme.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ </style>
+ <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
+ <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
+
+</resources>
diff --git a/slices/Android.mk b/slices/Android.mk
new file mode 100644
index 0000000..b622eed
--- /dev/null
+++ b/slices/Android.mk
@@ -0,0 +1,18 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+# Build all slices libraries
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/slices/OWNERS b/slices/OWNERS
new file mode 100644
index 0000000..921a517
--- /dev/null
+++ b/slices/OWNERS
@@ -0,0 +1,2 @@
+jmonk@google.com
+madym@google.com
diff --git a/slices/builders/Android.mk b/slices/builders/Android.mk
new file mode 100644
index 0000000..ca66cbe
--- /dev/null
+++ b/slices/builders/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+# LOCAL_STATIC_ANDROID_LIBRARIES := \
+# android-slices-core \
+# android-slices-builders
+#
+# in their makefiles to include the resources and their dependencies in their package.
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_MODULE := android-slices-builders
+LOCAL_MANIFEST_FILE := src/main/AndroidManifest.xml
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under,src/main/java) \
+ $(call all-Iaidl-files-under,src/main/java)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/src/main/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations \
+ android-slices-core
+LOCAL_JAR_EXCLUDE_FILES := none
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/slices/builders/api/current.txt b/slices/builders/api/current.txt
new file mode 100644
index 0000000..52f30c8
--- /dev/null
+++ b/slices/builders/api/current.txt
@@ -0,0 +1,47 @@
+package androidx.app.slice.builders {
+
+ public class ListBuilder extends androidx.app.slice.builders.TemplateSliceBuilder {
+ ctor public ListBuilder(android.net.Uri);
+ method public androidx.app.slice.builders.ListBuilder add(androidx.app.slice.builders.RowBuilder);
+ method public androidx.app.slice.builders.ListBuilder add(java.util.function.Consumer<androidx.app.slice.builders.RowBuilder>);
+ }
+
+ public class MessagingSliceBuilder extends androidx.app.slice.builders.TemplateSliceBuilder {
+ ctor public MessagingSliceBuilder(android.net.Uri);
+ method public androidx.app.slice.builders.MessagingSliceBuilder add(androidx.app.slice.builders.MessagingSliceBuilder.MessageBuilder);
+ method public androidx.app.slice.builders.MessagingSliceBuilder add(java.util.function.Consumer<androidx.app.slice.builders.MessagingSliceBuilder.MessageBuilder>);
+ field public static final int MAXIMUM_RETAINED_MESSAGES = 50; // 0x32
+ }
+
+ public static final class MessagingSliceBuilder.MessageBuilder extends androidx.app.slice.builders.TemplateSliceBuilder {
+ method public androidx.app.slice.builders.MessagingSliceBuilder.MessageBuilder addSource(android.graphics.drawable.Icon);
+ method public androidx.app.slice.builders.MessagingSliceBuilder.MessageBuilder addText(java.lang.CharSequence);
+ method public androidx.app.slice.builders.MessagingSliceBuilder.MessageBuilder addTimestamp(long);
+ method public void apply(androidx.app.slice.Slice.Builder);
+ }
+
+ public class RowBuilder extends androidx.app.slice.builders.TemplateSliceBuilder {
+ ctor public RowBuilder(androidx.app.slice.builders.ListBuilder);
+ ctor public RowBuilder(android.net.Uri);
+ method public androidx.app.slice.builders.RowBuilder addEndItem(long);
+ method public androidx.app.slice.builders.RowBuilder addEndItem(android.graphics.drawable.Icon);
+ method public androidx.app.slice.builders.RowBuilder addEndItem(android.app.PendingIntent, android.graphics.drawable.Icon);
+ method public androidx.app.slice.builders.RowBuilder addToggle(android.app.PendingIntent, boolean);
+ method public androidx.app.slice.builders.RowBuilder addToggle(android.app.PendingIntent, android.graphics.drawable.Icon, boolean);
+ method public void apply(androidx.app.slice.Slice.Builder);
+ method public androidx.app.slice.builders.RowBuilder setContentIntent(android.app.PendingIntent);
+ method public androidx.app.slice.builders.RowBuilder setIsHeader(boolean);
+ method public androidx.app.slice.builders.RowBuilder setSubtitle(java.lang.CharSequence);
+ method public androidx.app.slice.builders.RowBuilder setTitle(java.lang.CharSequence);
+ method public androidx.app.slice.builders.RowBuilder setTitleItem(long);
+ method public androidx.app.slice.builders.RowBuilder setTitleItem(android.graphics.drawable.Icon);
+ method public androidx.app.slice.builders.RowBuilder setTitleItem(android.app.PendingIntent, android.graphics.drawable.Icon);
+ }
+
+ public abstract class TemplateSliceBuilder {
+ ctor public TemplateSliceBuilder(android.net.Uri);
+ method public androidx.app.slice.Slice build();
+ }
+
+}
+
diff --git a/slices/builders/build.gradle b/slices/builders/build.gradle
new file mode 100644
index 0000000..60aca50
--- /dev/null
+++ b/slices/builders/build.gradle
@@ -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.
+ */
+
+import android.support.LibraryVersions
+import android.support.LibraryGroups
+
+plugins {
+ id("SupportAndroidLibraryPlugin")
+}
+
+dependencies {
+ compile project(path: ':slices-core')
+ implementation libs.support.annotations, libs.support_exclude_config
+}
+
+android {
+ defaultConfig {
+ minSdkVersion 19
+ }
+}
+
+supportLibrary {
+ name 'Slice builders'
+ publish true
+ mavenVersion = LibraryVersions.SUPPORT_LIBRARY
+ mavenGroup = LibraryGroups.SLICES
+ inceptionYear '2017'
+ description "A set of builders to create templates using SliceProvider APIs"
+ java8Library false
+}
diff --git a/slices/builders/lint-baseline.xml b/slices/builders/lint-baseline.xml
new file mode 100644
index 0000000..2cadde1
--- /dev/null
+++ b/slices/builders/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0">
+
+</issues>
diff --git a/slices/builders/src/main/AndroidManifest.xml b/slices/builders/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..75e31f1
--- /dev/null
+++ b/slices/builders/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="androidx.app.slice.builders">
+ <uses-sdk android:minSdkVersion="28"/>
+</manifest>
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/ListBuilder.java b/slices/builders/src/main/java/androidx/app/slice/builders/ListBuilder.java
new file mode 100644
index 0000000..cd644d5
--- /dev/null
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/ListBuilder.java
@@ -0,0 +1,90 @@
+/*
+ * 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 androidx.app.slice.builders;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.net.Uri;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+
+import java.util.function.Consumer;
+
+/**
+ * Builder to construct slice content.
+ * <p>
+ * Use this builder for showing rows of content which is composed of text, images, and actions. For
+ * more details {@see RowBuilder}.
+ * </p>
+ * <p>
+ * Slices can be displayed in different formats:
+ * <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 RowBuilder#setIsHeader(boolean)}.</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>
+ * </ul>
+ * </p>
+ *
+ * @see RowBuilder
+ */
+public class ListBuilder extends TemplateSliceBuilder {
+
+ public ListBuilder(@NonNull Uri uri) {
+ super(uri);
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ @Override
+ public void apply(androidx.app.slice.Slice.Builder builder) {
+
+ }
+
+ /**
+ * Add a subslice to this builder.
+ */
+ public ListBuilder add(RowBuilder builder) {
+ getBuilder().addSubSlice(builder.build());
+ return this;
+ }
+
+ /**
+ * Add a subslice to this builder.
+ */
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ public ListBuilder add(Consumer<RowBuilder> c) {
+ RowBuilder b = new RowBuilder(this);
+ c.accept(b);
+ return add(b);
+ }
+
+ /**
+ * Sets the color to tint items displayed by this template (e.g. icons).
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ public ListBuilder setColor(int color) {
+ getBuilder().addColor(color, null);
+ return this;
+ }
+}
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/MessagingSliceBuilder.java b/slices/builders/src/main/java/androidx/app/slice/builders/MessagingSliceBuilder.java
new file mode 100644
index 0000000..66bb345
--- /dev/null
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/MessagingSliceBuilder.java
@@ -0,0 +1,114 @@
+/*
+ * 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 androidx.app.slice.builders;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+
+import java.util.function.Consumer;
+
+import androidx.app.slice.Slice;
+
+/**
+ * Builder to construct slice content in a messaging format.
+ */
+public class MessagingSliceBuilder extends TemplateSliceBuilder {
+
+ /**
+ * The maximum number of messages that will be retained in the Slice itself (the
+ * number displayed is up to the platform).
+ */
+ public static final int MAXIMUM_RETAINED_MESSAGES = 50;
+
+ public MessagingSliceBuilder(@NonNull Uri uri) {
+ super(uri);
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ @Override
+ public void apply(androidx.app.slice.Slice.Builder builder) {
+
+ }
+
+ /**
+ * Add a subslice to this builder.
+ */
+ public MessagingSliceBuilder add(MessageBuilder builder) {
+ getBuilder().addSubSlice(builder.build());
+ return this;
+ }
+
+ /**
+ * 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);
+ return add(b);
+ }
+
+ /**
+ * Builder for adding a message to {@link MessagingSliceBuilder}.
+ */
+ public static final class MessageBuilder extends TemplateSliceBuilder {
+ /**
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public MessageBuilder(MessagingSliceBuilder parent) {
+ super(parent.createChildBuilder());
+ }
+
+ /**
+ * Add the icon used to display contact in the messaging experience
+ */
+ public MessageBuilder addSource(Icon source) {
+ getBuilder().addIcon(source, android.app.slice.Slice.SUBTYPE_SOURCE);
+ return this;
+ }
+
+ /**
+ * Add the text to be used for this message.
+ */
+ public MessageBuilder addText(CharSequence text) {
+ getBuilder().addText(text, null);
+ return this;
+ }
+
+ /**
+ * Add the time at which this message arrived in ms since Unix epoch
+ */
+ public MessageBuilder addTimestamp(long timestamp) {
+ getBuilder().addTimestamp(timestamp, null);
+ return this;
+ }
+
+ @Override
+ public void apply(Slice.Builder builder) {
+ }
+ }
+}
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/RowBuilder.java b/slices/builders/src/main/java/androidx/app/slice/builders/RowBuilder.java
new file mode 100644
index 0000000..f2e57ba
--- /dev/null
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/RowBuilder.java
@@ -0,0 +1,226 @@
+/*
+ * 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 androidx.app.slice.builders;
+
+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_SELECTED;
+import static android.app.slice.Slice.HINT_TITLE;
+import static android.app.slice.SliceItem.FORMAT_ACTION;
+import static android.app.slice.SliceItem.FORMAT_IMAGE;
+import static android.app.slice.SliceItem.FORMAT_TEXT;
+import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
+
+import static androidx.app.slice.core.SliceHints.SUBTYPE_TOGGLE;
+
+import android.app.PendingIntent;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+
+import java.util.ArrayList;
+
+import androidx.app.slice.Slice;
+import androidx.app.slice.SliceItem;
+import androidx.app.slice.core.SliceHints;
+
+/**
+ * Sub-builder to construct a row of slice content.
+ * <p>
+ * Row content can have:
+ * <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>
+ * <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>
+ * </ul>
+ * </p>
+ *
+ * @see ListBuilder
+ */
+public class RowBuilder extends TemplateSliceBuilder {
+
+ private boolean mIsHeader;
+ private PendingIntent mContentIntent;
+ private SliceItem mTitleItem;
+ private SliceItem mSubtitleItem;
+ private SliceItem mStartItem;
+ private ArrayList<SliceItem> mEndItems = new ArrayList<>();
+
+ public RowBuilder(ListBuilder parent) {
+ super(parent.createChildBuilder());
+ }
+
+ public RowBuilder(Uri uri) {
+ super(new Slice.Builder(uri));
+ }
+
+ /**
+ * Sets this row to be considered the header of the slice. This means that when the slice is
+ * requested to be show in small format, it will display only the contents specified in this
+ * row. If a slice has no header specified, the first row item will be used in the small format.
+ */
+ public RowBuilder setIsHeader(boolean isHeader) {
+ mIsHeader = isHeader;
+ return this;
+ }
+
+ /**
+ * Sets the title item to be the provided timestamp.
+ * <p>
+ * There can only be one title item, this will replace any other title
+ * items that may have been set.
+ */
+ public RowBuilder setTitleItem(long timeStamp) {
+ mStartItem = new SliceItem(timeStamp, FORMAT_TIMESTAMP, null, new String[0]);
+ return this;
+ }
+
+ /**
+ * Sets the title item to be the provided icon.
+ * <p>
+ * There can only be one title item, this will replace any other title
+ * items that may have been set.
+ */
+ public RowBuilder setTitleItem(Icon icon) {
+ mStartItem = new SliceItem(icon, FORMAT_IMAGE, null, new String[0]);
+ return this;
+ }
+
+ /**
+ * Sets the title item to be a tappable icon.
+ * <p>
+ * There can only be one title item, this will replace any other title
+ * items that may have been set.
+ */
+ public RowBuilder setTitleItem(@NonNull PendingIntent action, @NonNull Icon icon) {
+ Slice actionSlice = new Slice.Builder(getBuilder()).addIcon(icon, null).build();
+ mStartItem = new SliceItem(action, actionSlice, FORMAT_ACTION, null, new String[0]);
+ return this;
+ }
+
+ /**
+ * Sets the action to be invoked if the user taps on the main content of the template.
+ */
+ public RowBuilder setContentIntent(@NonNull PendingIntent action) {
+ mContentIntent = action;
+ return this;
+ }
+
+ /**
+ * Sets the title text.
+ */
+ public RowBuilder setTitle(CharSequence title) {
+ mTitleItem = new SliceItem(title, FORMAT_TEXT, null, new String[] {HINT_TITLE});
+ return this;
+ }
+
+ /**
+ * Sets the subtitle text.
+ */
+ public RowBuilder setSubtitle(CharSequence subtitle) {
+ mSubtitleItem = new SliceItem(subtitle, FORMAT_TEXT, null, new String[0]);
+ return this;
+ }
+
+ /**
+ * Adds a timestamp to be displayed at the end of the row.
+ */
+ public RowBuilder addEndItem(long timeStamp) {
+ // TODO -- should multiple timestamps be allowed at the end of the row?
+ mEndItems.add(new SliceItem(timeStamp, FORMAT_TIMESTAMP, null, new String[0]));
+ return this;
+ }
+
+ /**
+ * Adds an icon to be displayed at the end of the row.
+ */
+ public RowBuilder addEndItem(Icon icon) {
+ mEndItems.add(new SliceItem(icon, FORMAT_IMAGE, null,
+ new String[] {HINT_NO_TINT, HINT_LARGE}));
+ return this;
+ }
+
+ /**
+ * Adds a tappable icon to be displayed at the end of the row.
+ */
+ public RowBuilder addEndItem(@NonNull PendingIntent action, @NonNull Icon icon) {
+ Slice actionSlice = new Slice.Builder(getBuilder()).addIcon(icon, null).build();
+ mEndItems.add(new SliceItem(action, actionSlice, FORMAT_ACTION, null, new String[0]));
+ return this;
+ }
+
+ /**
+ * Adds a toggle action to the template. If there is a toggle to display, any end items
+ * that were added will not be shown.
+ */
+ public RowBuilder addToggle(@NonNull PendingIntent action, boolean isChecked) {
+ @Slice.SliceHint String[] hints = isChecked
+ ? new String[] {SUBTYPE_TOGGLE, HINT_SELECTED}
+ : new String[] {SUBTYPE_TOGGLE};
+ Slice s = new Slice.Builder(getBuilder()).addHints(hints).build();
+ mEndItems.add(0, new SliceItem(action, s, FORMAT_ACTION, null, hints));
+ return this;
+ }
+
+ /**
+ * Adds a toggle action to the template with custom icons to represent checked and unchecked
+ * state. If there is a toggle to display, any end items that were added will not be shown.
+ */
+ public RowBuilder addToggle(@NonNull PendingIntent action, @NonNull Icon icon,
+ boolean isChecked) {
+ @Slice.SliceHint String[] hints = isChecked
+ ? new String[] {SliceHints.SUBTYPE_TOGGLE, HINT_SELECTED}
+ : new String[] {SliceHints.SUBTYPE_TOGGLE};
+ Slice actionSlice = new Slice.Builder(getBuilder())
+ .addIcon(icon, null)
+ .addHints(hints).build();
+ mEndItems.add(0, new SliceItem(action, actionSlice, FORMAT_ACTION, null, hints));
+ return this;
+ }
+
+ @Override
+ public void apply(Slice.Builder b) {
+ Slice.Builder wrapped = b;
+ if (mContentIntent != null) {
+ b = new Slice.Builder(wrapped);
+ }
+ if (mStartItem != null) {
+ b.addItem(mStartItem);
+ }
+ if (mTitleItem != null) {
+ b.addItem(mTitleItem);
+ }
+ if (mSubtitleItem != null) {
+ b.addItem(mSubtitleItem);
+ }
+ for (int i = 0; i < mEndItems.size(); i++) {
+ SliceItem item = mEndItems.get(i);
+ b.addItem(item);
+ }
+ if (mContentIntent != null) {
+ wrapped.addAction(mContentIntent, b.build(), null);
+ }
+ wrapped.addHints(mIsHeader ? null : HINT_LIST_ITEM);
+ }
+}
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/TemplateSliceBuilder.java b/slices/builders/src/main/java/androidx/app/slice/builders/TemplateSliceBuilder.java
new file mode 100644
index 0000000..464f940
--- /dev/null
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/TemplateSliceBuilder.java
@@ -0,0 +1,74 @@
+/*
+ * 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 androidx.app.slice.builders;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY;
+
+import android.net.Uri;
+import android.support.annotation.RestrictTo;
+
+import androidx.app.slice.Slice;
+
+/**
+ * Base class of builders of various template types.
+ */
+public abstract class TemplateSliceBuilder {
+
+ private final Slice.Builder mSliceBuilder;
+
+ public TemplateSliceBuilder(Uri uri) {
+ mSliceBuilder = new Slice.Builder(uri);
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(LIBRARY)
+ protected TemplateSliceBuilder(Slice.Builder b) {
+ mSliceBuilder = b;
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(LIBRARY)
+ public Slice.Builder getBuilder() {
+ return mSliceBuilder;
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(LIBRARY)
+ public Slice.Builder createChildBuilder() {
+ return new Slice.Builder(mSliceBuilder);
+ }
+
+ /**
+ * Construct the slice.
+ */
+ public Slice build() {
+ apply(mSliceBuilder);
+ return mSliceBuilder.build();
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(LIBRARY)
+ public abstract void apply(Slice.Builder builder);
+}
diff --git a/slices/core/Android.mk b/slices/core/Android.mk
new file mode 100644
index 0000000..cd3a353
--- /dev/null
+++ b/slices/core/Android.mk
@@ -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.
+
+LOCAL_PATH := $(call my-dir)
+
+# Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+# LOCAL_STATIC_ANDROID_LIBRARIES := \
+# android-slices-core
+#
+# in their makefiles to include the resources and their dependencies in their package.
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_MODULE := android-slices-core
+LOCAL_MANIFEST_FILE := src/main/AndroidManifest.xml
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under,src/main/java) \
+ $(call all-Iaidl-files-under,src/main/java)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/src/main/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-compat \
+ android-support-annotations
+LOCAL_JAR_EXCLUDE_FILES := none
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/slices/core/api/current.txt b/slices/core/api/current.txt
new file mode 100644
index 0000000..cc628d6
--- /dev/null
+++ b/slices/core/api/current.txt
@@ -0,0 +1,60 @@
+package androidx.app.slice {
+
+ public final class Slice {
+ method public static androidx.app.slice.Slice bindSlice(android.content.Context, android.net.Uri);
+ method public static androidx.app.slice.Slice bindSlice(android.content.Context, android.content.Intent);
+ method public java.util.List<java.lang.String> getHints();
+ method public java.util.List<androidx.app.slice.SliceItem> getItems();
+ method public android.net.Uri getUri();
+ }
+
+ public static class Slice.Builder {
+ ctor public Slice.Builder(android.net.Uri);
+ ctor public Slice.Builder(androidx.app.slice.Slice.Builder);
+ method public androidx.app.slice.Slice.Builder addAction(android.app.PendingIntent, androidx.app.slice.Slice, java.lang.String);
+ method public androidx.app.slice.Slice.Builder addColor(int, java.lang.String, java.lang.String...);
+ method public androidx.app.slice.Slice.Builder addColor(int, java.lang.String, java.util.List<java.lang.String>);
+ method public androidx.app.slice.Slice.Builder addHints(java.lang.String...);
+ method public androidx.app.slice.Slice.Builder addHints(java.util.List<java.lang.String>);
+ method public androidx.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String, java.lang.String...);
+ method public androidx.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String, java.util.List<java.lang.String>);
+ method public androidx.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String, java.util.List<java.lang.String>);
+ method public androidx.app.slice.Slice.Builder addRemoteInput(android.app.RemoteInput, java.lang.String, java.lang.String...);
+ method public androidx.app.slice.Slice.Builder addSubSlice(androidx.app.slice.Slice);
+ method public androidx.app.slice.Slice.Builder addSubSlice(androidx.app.slice.Slice, java.lang.String);
+ method public androidx.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String, java.lang.String...);
+ method public androidx.app.slice.Slice.Builder addText(java.lang.CharSequence, java.lang.String, java.util.List<java.lang.String>);
+ method public androidx.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.lang.String...);
+ method public androidx.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.util.List<java.lang.String>);
+ method public androidx.app.slice.Slice build();
+ }
+
+ public class SliceConvert {
+ ctor public SliceConvert();
+ method public static android.app.slice.Slice unwrap(androidx.app.slice.Slice);
+ method public static androidx.app.slice.Slice wrap(android.app.slice.Slice);
+ }
+
+ public class SliceItem {
+ method public android.app.PendingIntent getAction();
+ method public int getColor();
+ method public java.lang.String getFormat();
+ method public java.util.List<java.lang.String> getHints();
+ method public android.graphics.drawable.Icon getIcon();
+ method public android.app.RemoteInput getRemoteInput();
+ method public androidx.app.slice.Slice getSlice();
+ method public java.lang.String getSubType();
+ method public java.lang.CharSequence getText();
+ method public long getTimestamp();
+ method public boolean hasHint(java.lang.String);
+ }
+
+ public abstract class SliceProvider extends android.content.ContentProvider {
+ ctor public SliceProvider();
+ method public abstract androidx.app.slice.Slice onBindSlice(android.net.Uri);
+ method public abstract boolean onCreateSliceProvider();
+ method public android.net.Uri onMapIntentToUri(android.content.Intent);
+ }
+
+}
+
diff --git a/slices/core/build.gradle b/slices/core/build.gradle
new file mode 100644
index 0000000..a0e9755
--- /dev/null
+++ b/slices/core/build.gradle
@@ -0,0 +1,49 @@
+/*
+ * 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 android.support.LibraryVersions
+import android.support.LibraryGroups
+
+plugins {
+ id("SupportAndroidLibraryPlugin")
+}
+
+dependencies {
+ implementation libs.support.annotations, libs.support_exclude_config
+ implementation libs.support.app_compat, libs.support_exclude_config
+}
+
+android {
+ defaultConfig {
+ minSdkVersion 19
+ }
+ sourceSets {
+ main.res.srcDirs = [
+ 'res',
+ 'res-public'
+ ]
+ }
+}
+
+supportLibrary {
+ name 'Common utilities for slices'
+ publish true
+ mavenVersion = LibraryVersions.SUPPORT_LIBRARY
+ mavenGroup = LibraryGroups.SLICES
+ inceptionYear '2017'
+ description "The slices core library provides utilities for the slices view and provider libraries"
+ java8Library false
+}
diff --git a/slices/core/lint-baseline.xml b/slices/core/lint-baseline.xml
new file mode 100644
index 0000000..2cadde1
--- /dev/null
+++ b/slices/core/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0">
+
+</issues>
diff --git a/slices/core/src/main/AndroidManifest.xml b/slices/core/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ca86418
--- /dev/null
+++ b/slices/core/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="androidx.app.slice.core">
+ <uses-sdk android:minSdkVersion="28"/>
+</manifest>
diff --git a/slices/core/src/main/java/androidx/app/slice/ArrayUtils.java b/slices/core/src/main/java/androidx/app/slice/ArrayUtils.java
new file mode 100644
index 0000000..669f66a
--- /dev/null
+++ b/slices/core/src/main/java/androidx/app/slice/ArrayUtils.java
@@ -0,0 +1,75 @@
+/*
+ * 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.app.slice;
+
+
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+
+import java.lang.reflect.Array;
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+@RestrictTo(Scope.LIBRARY)
+class ArrayUtils {
+
+ public static <T> boolean contains(T[] array, T item) {
+ for (T t : array) {
+ if (Objects.equals(t, item)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static <T> T[] appendElement(Class<T> kind, T[] array, T element) {
+ final T[] result;
+ final int end;
+ if (array != null) {
+ end = array.length;
+ result = (T[]) Array.newInstance(kind, end + 1);
+ System.arraycopy(array, 0, result, 0, end);
+ } else {
+ end = 0;
+ result = (T[]) Array.newInstance(kind, 1);
+ }
+ result[end] = element;
+ return result;
+ }
+
+ public static <T> T[] removeElement(Class<T> kind, T[] array, T element) {
+ if (array != null) {
+ if (!contains(array, element)) {
+ return array;
+ }
+ final int length = array.length;
+ for (int i = 0; i < length; i++) {
+ if (Objects.equals(array[i], element)) {
+ if (length == 1) {
+ return null;
+ }
+ T[] result = (T[]) Array.newInstance(kind, length - 1);
+ System.arraycopy(array, 0, result, 0, i);
+ System.arraycopy(array, i + 1, result, i, length - i - 1);
+ return result;
+ }
+ }
+ }
+ return array;
+ }
+}
diff --git a/slices/core/src/main/java/androidx/app/slice/Slice.java b/slices/core/src/main/java/androidx/app/slice/Slice.java
new file mode 100644
index 0000000..8033bb7
--- /dev/null
+++ b/slices/core/src/main/java/androidx/app/slice/Slice.java
@@ -0,0 +1,430 @@
+/*
+ * 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.app.slice;
+
+import static android.app.slice.Slice.HINT_ACTIONS;
+import static android.app.slice.Slice.HINT_HORIZONTAL;
+import static android.app.slice.Slice.HINT_LARGE;
+import static android.app.slice.Slice.HINT_LIST;
+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_SELECTED;
+import static android.app.slice.Slice.HINT_TITLE;
+import static android.app.slice.SliceItem.FORMAT_ACTION;
+import static android.app.slice.SliceItem.FORMAT_COLOR;
+import static android.app.slice.SliceItem.FORMAT_IMAGE;
+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 android.annotation.TargetApi;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.support.annotation.StringDef;
+import android.support.v4.os.BuildCompat;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import androidx.app.slice.compat.SliceProviderCompat;
+import androidx.app.slice.core.SliceHints;
+import androidx.app.slice.core.SliceSpecs;
+
+/**
+ * A slice is a piece of app content and actions that can be surfaced outside of the app.
+ *
+ * <p>They are constructed using {@link Builder} in a tree structure
+ * that provides the OS some information about how the content should be displayed.
+ */
+public final class Slice {
+
+ private static final String HINTS = "hints";
+ private static final String ITEMS = "items";
+ private static final String URI = "uri";
+
+ /**
+ * @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,
+ SliceHints.HINT_HIDDEN, SliceHints.SUBTYPE_TOGGLE})
+ public @interface SliceHint{ }
+
+ private final SliceItem[] mItems;
+ private final @SliceHint String[] mHints;
+ private Uri mUri;
+
+ /**
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY)
+ Slice(ArrayList<SliceItem> items, @SliceHint String[] hints, Uri uri) {
+ mHints = hints;
+ mItems = items.toArray(new SliceItem[items.size()]);
+ mUri = uri;
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY)
+ public Slice(Bundle in) {
+ mHints = in.getStringArray(HINTS);
+ Parcelable[] items = in.getParcelableArray(ITEMS);
+ mItems = new SliceItem[items.length];
+ for (int i = 0; i < mItems.length; i++) {
+ if (items[i] instanceof Bundle) {
+ mItems[i] = new SliceItem((Bundle) items[i]);
+ }
+ }
+ mUri = in.getParcelable(URI);
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY)
+ public Bundle toBundle() {
+ Bundle b = new Bundle();
+ b.putStringArray(HINTS, mHints);
+ Parcelable[] p = new Parcelable[mItems.length];
+ for (int i = 0; i < mItems.length; i++) {
+ p[i] = mItems[i].toBundle();
+ }
+ b.putParcelableArray(ITEMS, p);
+ b.putParcelable(URI, mUri);
+ return b;
+ }
+
+ /**
+ * @return The Uri that this Slice represents.
+ */
+ public Uri getUri() {
+ return mUri;
+ }
+
+ /**
+ * @return All child {@link SliceItem}s that this Slice contains.
+ */
+ public List<SliceItem> getItems() {
+ return Arrays.asList(mItems);
+ }
+
+ /**
+ * @return All hints associated with this Slice.
+ */
+ public @SliceHint List<String> getHints() {
+ return Arrays.asList(mHints);
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public boolean hasHint(@SliceHint String hint) {
+ return ArrayUtils.contains(mHints, hint);
+ }
+
+ /**
+ * A Builder used to construct {@link Slice}s
+ */
+ public static class Builder {
+
+ private final Uri mUri;
+ private ArrayList<SliceItem> mItems = new ArrayList<>();
+ private @SliceHint ArrayList<String> mHints = new ArrayList<>();
+
+ /**
+ * Create a builder which will construct a {@link Slice} for the Given Uri.
+ * @param uri Uri to tag for this slice.
+ */
+ public Builder(@NonNull Uri uri) {
+ mUri = uri;
+ }
+
+ /**
+ * Create a builder for a {@link Slice} that is a sub-slice of the slice
+ * being constructed by the provided builder.
+ * @param parent The builder constructing the parent slice
+ */
+ public Builder(@NonNull Slice.Builder parent) {
+ mUri = parent.mUri.buildUpon().appendPath("_gen")
+ .appendPath(String.valueOf(mItems.size())).build();
+ }
+
+ /**
+ * Add hints to the Slice being constructed
+ */
+ public Builder addHints(@SliceHint String... hints) {
+ mHints.addAll(Arrays.asList(hints));
+ return this;
+ }
+
+ /**
+ * Add hints to the Slice being constructed
+ */
+ public Builder addHints(@SliceHint List<String> hints) {
+ return addHints(hints.toArray(new String[hints.size()]));
+ }
+
+ /**
+ * Add a sub-slice to the slice being constructed
+ */
+ public Builder addSubSlice(@NonNull Slice slice) {
+ return addSubSlice(slice, null);
+ }
+
+ /**
+ * Add a sub-slice to the slice being constructed
+ * @param subType Optional template-specific type information
+ * @see {@link SliceItem#getSubType()}
+ */
+ public Builder addSubSlice(@NonNull Slice slice, String subType) {
+ mItems.add(new SliceItem(slice, FORMAT_SLICE, subType, slice.getHints().toArray(
+ new String[slice.getHints().size()])));
+ return this;
+ }
+
+ /**
+ * Add an action to the slice being constructed
+ * @param subType Optional template-specific type information
+ * @see {@link SliceItem#getSubType()}
+ */
+ public Slice.Builder addAction(@NonNull PendingIntent action,
+ @NonNull Slice s, @Nullable String subType) {
+ mItems.add(new SliceItem(action, s, FORMAT_ACTION, subType, new String[0]));
+ return this;
+ }
+
+ /**
+ * Add text to the slice being constructed
+ * @param subType Optional template-specific type information
+ * @see {@link SliceItem#getSubType()}
+ */
+ public Builder addText(CharSequence text, @Nullable String subType,
+ @SliceHint String... hints) {
+ mItems.add(new SliceItem(text, FORMAT_TEXT, subType, hints));
+ return this;
+ }
+
+ /**
+ * Add text to the slice being constructed
+ * @param subType Optional template-specific type information
+ * @see {@link SliceItem#getSubType()}
+ */
+ public Builder addText(CharSequence text, @Nullable String subType,
+ @SliceHint List<String> hints) {
+ return addText(text, subType, hints.toArray(new String[hints.size()]));
+ }
+
+ /**
+ * Add an image to the slice being constructed
+ * @param subType Optional template-specific type information
+ * @see {@link SliceItem#getSubType()}
+ */
+ public Builder addIcon(Icon icon, @Nullable String subType,
+ @SliceHint String... hints) {
+ mItems.add(new SliceItem(icon, FORMAT_IMAGE, subType, hints));
+ return this;
+ }
+
+ /**
+ * Add an image to the slice being constructed
+ * @param subType Optional template-specific type information
+ * @see {@link SliceItem#getSubType()}
+ */
+ public Builder addIcon(Icon icon, @Nullable String subType,
+ @SliceHint List<String> hints) {
+ return addIcon(icon, subType, hints.toArray(new String[hints.size()]));
+ }
+
+ /**
+ * Add remote input to the slice being constructed
+ * @param subType Optional template-specific type information
+ * @see {@link SliceItem#getSubType()}
+ */
+ public Slice.Builder addRemoteInput(RemoteInput remoteInput, @Nullable String subType,
+ @SliceHint List<String> hints) {
+ return addRemoteInput(remoteInput, subType, hints.toArray(new String[hints.size()]));
+ }
+
+ /**
+ * Add remote input to the slice being constructed
+ * @param subType Optional template-specific type information
+ * @see {@link SliceItem#getSubType()}
+ */
+ public Slice.Builder addRemoteInput(RemoteInput remoteInput, @Nullable String subType,
+ @SliceHint String... hints) {
+ mItems.add(new SliceItem(remoteInput, FORMAT_REMOTE_INPUT, subType, hints));
+ return this;
+ }
+
+ /**
+ * Add a color to the slice being constructed
+ * @param subType Optional template-specific type information
+ * @see {@link SliceItem#getSubType()}
+ */
+ public Builder addColor(int color, @Nullable String subType,
+ @SliceHint String... hints) {
+ mItems.add(new SliceItem(color, FORMAT_COLOR, subType, hints));
+ return this;
+ }
+
+ /**
+ * Add a color to the slice being constructed
+ * @param subType Optional template-specific type information
+ * @see {@link SliceItem#getSubType()}
+ */
+ public Builder addColor(int color, @Nullable String subType,
+ @SliceHint List<String> hints) {
+ return addColor(color, subType, hints.toArray(new String[hints.size()]));
+ }
+
+ /**
+ * Add a timestamp to the slice being constructed
+ * @param subType Optional template-specific type information
+ * @see {@link SliceItem#getSubType()}
+ */
+ public Slice.Builder addTimestamp(long time, @Nullable String subType,
+ @SliceHint String... hints) {
+ mItems.add(new SliceItem(time, FORMAT_TIMESTAMP, subType, hints));
+ return this;
+ }
+
+ /**
+ * Add a timestamp to the slice being constructed
+ * @param subType Optional template-specific type information
+ * @see {@link SliceItem#getSubType()}
+ */
+ public Slice.Builder addTimestamp(long time, @Nullable String subType,
+ @SliceHint List<String> hints) {
+ return addTimestamp(time, subType, hints.toArray(new String[hints.size()]));
+ }
+
+ /**
+ * Add a SliceItem to the slice being constructed.
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY)
+ public Slice.Builder addItem(SliceItem item) {
+ mItems.add(item);
+ return this;
+ }
+
+ /**
+ * Construct the slice.
+ */
+ public Slice build() {
+ return new Slice(mItems, mHints.toArray(new String[mHints.size()]), mUri);
+ }
+ }
+
+ /**
+ * @hide
+ * @return A string representation of this slice.
+ */
+ @RestrictTo(Scope.LIBRARY)
+ @Override
+ public String toString() {
+ return toString("");
+ }
+
+ private String toString(String indent) {
+ StringBuilder sb = new StringBuilder();
+ 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_TEXT.equals(mItems[i].getFormat())) {
+ sb.append("text: ");
+ sb.append(mItems[i].getText());
+ sb.append("\n");
+ } else {
+ sb.append(SliceItem.typeToString(mItems[i].getFormat()));
+ sb.append("\n");
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Turns a slice Uri into slice content.
+ *
+ * @param context Context to be used.
+ * @param uri The URI to a slice provider
+ * @return The Slice provided by the app or null if none is given.
+ * @see Slice
+ */
+ @SuppressWarnings("NewApi")
+ public static @Nullable Slice bindSlice(Context context, @NonNull Uri uri) {
+ if (BuildCompat.isAtLeastP()) {
+ return callBindSlice(context, uri);
+ } else {
+ return SliceProviderCompat.bindSlice(context, uri);
+ }
+ }
+
+ @TargetApi(28)
+ private static Slice callBindSlice(Context context, Uri uri) {
+ return SliceConvert.wrap(android.app.slice.Slice.bindSlice(
+ context.getContentResolver(), uri, SliceSpecs.SUPPORTED_SPECS));
+ }
+
+
+ /**
+ * Turns a slice intent into slice content. Expects an explicit intent. If there is no
+ * {@link ContentProvider} associated with the given intent this will throw
+ * {@link IllegalArgumentException}.
+ *
+ * @param context The context to use.
+ * @param intent The intent associated with a slice.
+ * @return The Slice provided by the app or null if none is given.
+ * @see Slice
+ * @see SliceProvider#onMapIntentToUri(Intent)
+ * @see Intent
+ */
+ @SuppressWarnings("NewApi")
+ public static @Nullable Slice bindSlice(Context context, @NonNull Intent intent) {
+ if (BuildCompat.isAtLeastP()) {
+ return callBindSlice(context, intent);
+ } else {
+ return SliceProviderCompat.bindSlice(context, intent);
+ }
+ }
+
+ @TargetApi(28)
+ private static Slice callBindSlice(Context context, Intent intent) {
+ return SliceConvert.wrap(android.app.slice.Slice.bindSlice(
+ context, intent, SliceSpecs.SUPPORTED_SPECS));
+ }
+}
diff --git a/slices/core/src/main/java/androidx/app/slice/SliceConvert.java b/slices/core/src/main/java/androidx/app/slice/SliceConvert.java
new file mode 100644
index 0000000..edbc293
--- /dev/null
+++ b/slices/core/src/main/java/androidx/app/slice/SliceConvert.java
@@ -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.app.slice;
+
+
+import static android.app.slice.SliceItem.FORMAT_ACTION;
+import static android.app.slice.SliceItem.FORMAT_COLOR;
+import static android.app.slice.SliceItem.FORMAT_IMAGE;
+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 android.support.annotation.RequiresApi;
+
+/**
+ * Convert between {@link androidx.app.slice.Slice} and {@link android.app.slice.Slice}
+ */
+@RequiresApi(28)
+public class SliceConvert {
+
+ /**
+ * Convert {@link androidx.app.slice.Slice} to {@link android.app.slice.Slice}
+ */
+ public static android.app.slice.Slice unwrap(androidx.app.slice.Slice slice) {
+ android.app.slice.Slice.Builder builder = new android.app.slice.Slice.Builder(
+ slice.getUri());
+ builder.addHints(slice.getHints());
+ for (androidx.app.slice.SliceItem item : slice.getItems()) {
+ switch (item.getFormat()) {
+ case FORMAT_SLICE:
+ builder.addSubSlice(unwrap(item.getSlice()), item.getSubType());
+ break;
+ case FORMAT_IMAGE:
+ builder.addIcon(item.getIcon(), item.getSubType(), item.getHints());
+ break;
+ case FORMAT_REMOTE_INPUT:
+ builder.addRemoteInput(item.getRemoteInput(), item.getSubType(),
+ item.getHints());
+ break;
+ case FORMAT_ACTION:
+ builder.addAction(item.getAction(), unwrap(item.getSlice()), item.getSubType());
+ break;
+ case FORMAT_TEXT:
+ builder.addText(item.getText(), item.getSubType(), item.getHints());
+ break;
+ case FORMAT_COLOR:
+ builder.addColor(item.getColor(), item.getSubType(), item.getHints());
+ break;
+ case FORMAT_TIMESTAMP:
+ builder.addTimestamp(item.getTimestamp(), item.getSubType(), item.getHints());
+ break;
+ }
+ }
+ return builder.build();
+ }
+
+ /**
+ * Convert {@link android.app.slice.Slice} to {@link androidx.app.slice.Slice}
+ */
+ public static androidx.app.slice.Slice wrap(android.app.slice.Slice slice) {
+ androidx.app.slice.Slice.Builder builder = new androidx.app.slice.Slice.Builder(
+ slice.getUri());
+ builder.addHints(slice.getHints());
+ for (android.app.slice.SliceItem item : slice.getItems()) {
+ switch (item.getFormat()) {
+ case FORMAT_SLICE:
+ builder.addSubSlice(wrap(item.getSlice()), item.getSubType());
+ break;
+ case FORMAT_IMAGE:
+ builder.addIcon(item.getIcon(), item.getSubType(), item.getHints());
+ break;
+ case FORMAT_REMOTE_INPUT:
+ builder.addRemoteInput(item.getRemoteInput(), item.getSubType(),
+ item.getHints());
+ break;
+ case FORMAT_ACTION:
+ builder.addAction(item.getAction(), wrap(item.getSlice()), item.getSubType());
+ break;
+ case FORMAT_TEXT:
+ builder.addText(item.getText(), item.getSubType(), item.getHints());
+ break;
+ case FORMAT_COLOR:
+ builder.addColor(item.getColor(), item.getSubType(), item.getHints());
+ break;
+ case FORMAT_TIMESTAMP:
+ builder.addTimestamp(item.getTimestamp(), item.getSubType(), item.getHints());
+ break;
+ }
+ }
+ return builder.build();
+ }
+}
diff --git a/slices/core/src/main/java/androidx/app/slice/SliceItem.java b/slices/core/src/main/java/androidx/app/slice/SliceItem.java
new file mode 100644
index 0000000..e4412d1
--- /dev/null
+++ b/slices/core/src/main/java/androidx/app/slice/SliceItem.java
@@ -0,0 +1,351 @@
+/*
+ * 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.app.slice;
+
+import static android.app.slice.SliceItem.FORMAT_ACTION;
+import static android.app.slice.SliceItem.FORMAT_COLOR;
+import static android.app.slice.SliceItem.FORMAT_IMAGE;
+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 android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.support.annotation.StringDef;
+import android.text.TextUtils;
+import android.util.Pair;
+
+import java.util.Arrays;
+import java.util.List;
+
+
+/**
+ * A SliceItem is a single unit in the tree structure of a {@link Slice}.
+ * <p>
+ * A SliceItem a piece of content and some hints about what that content
+ * means or how it should be displayed. The types of content can be:
+ * <li>{@link android.app.slice.SliceItem#FORMAT_SLICE}</li>
+ * <li>{@link android.app.slice.SliceItem#FORMAT_TEXT}</li>
+ * <li>{@link android.app.slice.SliceItem#FORMAT_IMAGE}</li>
+ * <li>{@link android.app.slice.SliceItem#FORMAT_ACTION}</li>
+ * <li>{@link android.app.slice.SliceItem#FORMAT_COLOR}</li>
+ * <li>{@link android.app.slice.SliceItem#FORMAT_TIMESTAMP}</li>
+ * <li>{@link android.app.slice.SliceItem#FORMAT_REMOTE_INPUT}</li>
+ * <p>
+ * The hints that a {@link SliceItem} are a set of strings which annotate
+ * the content. The hints that are guaranteed to be understood by the system
+ * are defined on {@link Slice}.
+ */
+public class SliceItem {
+
+ private static final String HINTS = "hints";
+ private static final String FORMAT = "format";
+ private static final String SUBTYPE = "subtype";
+ private static final String OBJ = "obj";
+ private static final String OBJ_2 = "obj_2";
+
+ /**
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY)
+ @StringDef({FORMAT_SLICE, FORMAT_TEXT, FORMAT_IMAGE, FORMAT_ACTION, FORMAT_COLOR,
+ FORMAT_TIMESTAMP, FORMAT_REMOTE_INPUT})
+ public @interface SliceType {
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY)
+ protected @Slice.SliceHint String[] mHints;
+ private final String mFormat;
+ private final String mSubType;
+ private final Object mObj;
+
+ /**
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY)
+ public SliceItem(Object obj, @SliceType String format, String subType,
+ @Slice.SliceHint String[] hints) {
+ mHints = hints;
+ mFormat = format;
+ mSubType = subType;
+ mObj = obj;
+ }
+
+ /**
+ * @hide
+ */
+ @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);
+ }
+
+ /**
+ * Gets all hints associated with this SliceItem.
+ *
+ * @return Array of hints.
+ */
+ public @NonNull @Slice.SliceHint List<String> getHints() {
+ return Arrays.asList(mHints);
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY)
+ public void addHint(@Slice.SliceHint String hint) {
+ mHints = ArrayUtils.appendElement(String.class, mHints, hint);
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY)
+ public void removeHint(String hint) {
+ ArrayUtils.removeElement(String.class, mHints, hint);
+ }
+
+ /**
+ * Get the format of this SliceItem.
+ * <p>
+ * The format will be one of the following types supported by the platform:
+ * <li>{@link android.app.slice.SliceItem#FORMAT_SLICE}</li>
+ * <li>{@link android.app.slice.SliceItem#FORMAT_TEXT}</li>
+ * <li>{@link android.app.slice.SliceItem#FORMAT_IMAGE}</li>
+ * <li>{@link android.app.slice.SliceItem#FORMAT_ACTION}</li>
+ * <li>{@link android.app.slice.SliceItem#FORMAT_COLOR}</li>
+ * <li>{@link android.app.slice.SliceItem#FORMAT_TIMESTAMP}</li>
+ * <li>{@link android.app.slice.SliceItem#FORMAT_REMOTE_INPUT}</li>
+ * @see #getSubType() ()
+ */
+ public @SliceType String getFormat() {
+ return mFormat;
+ }
+
+ /**
+ * Get the sub-type of this SliceItem.
+ * <p>
+ * Subtypes provide additional information about the type of this information beyond basic
+ * interpretations inferred by {@link #getFormat()}. For example a slice may contain
+ * many {@link android.app.slice.SliceItem#FORMAT_TEXT} items, but only some of them may be
+ * {@link android.app.slice.Slice#SUBTYPE_MESSAGE}.
+ * @see #getFormat()
+ */
+ public String getSubType() {
+ return mSubType;
+ }
+
+ /**
+ * @return The text held by this {@link android.app.slice.SliceItem#FORMAT_TEXT} SliceItem
+ */
+ public CharSequence getText() {
+ return (CharSequence) mObj;
+ }
+
+ /**
+ * @return The icon held by this {@link android.app.slice.SliceItem#FORMAT_IMAGE} SliceItem
+ */
+ @RequiresApi(23)
+ public Icon getIcon() {
+ return (Icon) mObj;
+ }
+
+ /**
+ * @return The pending intent held by this {@link android.app.slice.SliceItem#FORMAT_ACTION}
+ * SliceItem
+ */
+ public PendingIntent getAction() {
+ return ((Pair<PendingIntent, Slice>) mObj).first;
+ }
+
+ /**
+ * @return The remote input held by this {@link android.app.slice.SliceItem#FORMAT_REMOTE_INPUT}
+ * SliceItem
+ */
+ @RequiresApi(20)
+ public RemoteInput getRemoteInput() {
+ return (RemoteInput) mObj;
+ }
+
+ /**
+ * @return The color held by this {@link android.app.slice.SliceItem#FORMAT_COLOR} SliceItem
+ */
+ public int getColor() {
+ return (Integer) mObj;
+ }
+
+ /**
+ * @return The slice held by this {@link android.app.slice.SliceItem#FORMAT_ACTION} or
+ * {@link android.app.slice.SliceItem#FORMAT_SLICE} SliceItem
+ */
+ public Slice getSlice() {
+ if (FORMAT_ACTION.equals(getFormat())) {
+ return ((Pair<PendingIntent, Slice>) mObj).second;
+ }
+ return (Slice) mObj;
+ }
+
+ /**
+ * @return The timestamp held by this {@link android.app.slice.SliceItem#FORMAT_TIMESTAMP}
+ * SliceItem
+ */
+ public long getTimestamp() {
+ return (Long) mObj;
+ }
+
+ /**
+ * @param hint The hint to check for
+ * @return true if this item contains the given hint
+ */
+ public boolean hasHint(@Slice.SliceHint String hint) {
+ return ArrayUtils.contains(mHints, hint);
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY)
+ public SliceItem(Bundle in) {
+ mHints = in.getStringArray(HINTS);
+ mFormat = in.getString(FORMAT);
+ mSubType = in.getString(SUBTYPE);
+ mObj = readObj(mFormat, in);
+ }
+
+ /**
+ * @hide
+ * @return
+ */
+ @RestrictTo(Scope.LIBRARY)
+ public Bundle toBundle() {
+ Bundle b = new Bundle();
+ b.putStringArray(HINTS, mHints);
+ b.putString(FORMAT, mFormat);
+ b.putString(SUBTYPE, mSubType);
+ writeObj(b, mObj, mFormat);
+ return b;
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY)
+ public boolean hasHints(@Slice.SliceHint String[] hints) {
+ if (hints == null) return true;
+ for (String hint : hints) {
+ if (!TextUtils.isEmpty(hint) && !ArrayUtils.contains(mHints, hint)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY)
+ public boolean hasAnyHints(@Slice.SliceHint String[] hints) {
+ if (hints == null) return false;
+ for (String hint : hints) {
+ if (ArrayUtils.contains(mHints, hint)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void writeObj(Bundle dest, Object obj, String type) {
+ switch (type) {
+ case FORMAT_IMAGE:
+ case FORMAT_REMOTE_INPUT:
+ dest.putParcelable(OBJ, (Parcelable) obj);
+ break;
+ case FORMAT_SLICE:
+ 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());
+ break;
+ case FORMAT_TEXT:
+ dest.putCharSequence(OBJ, (CharSequence) obj);
+ break;
+ case FORMAT_COLOR:
+ dest.putInt(OBJ, (Integer) mObj);
+ break;
+ case FORMAT_TIMESTAMP:
+ dest.putLong(OBJ, (Long) mObj);
+ break;
+ }
+ }
+
+ private static Object readObj(String type, Bundle in) {
+ switch (type) {
+ case FORMAT_IMAGE:
+ case FORMAT_REMOTE_INPUT:
+ return in.getParcelable(OBJ);
+ case FORMAT_SLICE:
+ return new Slice(in.getBundle(OBJ));
+ case FORMAT_TEXT:
+ return in.getCharSequence(OBJ);
+ case FORMAT_ACTION:
+ return new Pair<>(
+ (PendingIntent) in.getParcelable(OBJ),
+ new Slice(in.getBundle(OBJ_2)));
+ case FORMAT_COLOR:
+ return in.getInt(OBJ);
+ case FORMAT_TIMESTAMP:
+ return in.getLong(OBJ);
+ }
+ throw new RuntimeException("Unsupported type " + type);
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY)
+ public static String typeToString(String format) {
+ switch (format) {
+ case FORMAT_SLICE:
+ return "Slice";
+ case FORMAT_TEXT:
+ return "Text";
+ case FORMAT_IMAGE:
+ return "Image";
+ case FORMAT_ACTION:
+ return "Action";
+ case FORMAT_COLOR:
+ return "Color";
+ case FORMAT_TIMESTAMP:
+ return "Timestamp";
+ case FORMAT_REMOTE_INPUT:
+ return "RemoteInput";
+ }
+ return "Unrecognized format: " + format;
+ }
+}
diff --git a/slices/core/src/main/java/androidx/app/slice/SliceProvider.java b/slices/core/src/main/java/androidx/app/slice/SliceProvider.java
new file mode 100644
index 0000000..a0c12f1
--- /dev/null
+++ b/slices/core/src/main/java/androidx/app/slice/SliceProvider.java
@@ -0,0 +1,130 @@
+/*
+ * 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.app.slice;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ProviderInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.v4.os.BuildCompat;
+
+import androidx.app.slice.compat.ContentProviderWrapper;
+import androidx.app.slice.compat.SliceProviderCompat;
+import androidx.app.slice.compat.SliceProviderWrapper;
+
+/**
+ * A SliceProvider allows an app to provide content to be displayed in system spaces. This content
+ * is templated and can contain actions, and the behavior of how it is surfaced is specific to the
+ * system surface.
+ * <p>
+ * Slices are not currently live content. They are bound once and shown to the user. If the content
+ * changes due to a callback from user interaction, then
+ * {@link ContentResolver#notifyChange(Uri, ContentObserver)} should be used to notify the system.
+ * </p>
+ * <p>
+ * The provider needs to be declared in the manifest to provide the authority for the app. The
+ * authority for most slices is expected to match the package of the application.
+ * </p>
+ *
+ * <pre class="prettyprint">
+ * {@literal
+ * <provider
+ * android:name="com.android.mypkg.MySliceProvider"
+ * android:authorities="com.android.mypkg" />}
+ * </pre>
+ * <p>
+ * Slices can be identified by a Uri or by an Intent. To link an Intent with a slice, the provider
+ * must have an {@link IntentFilter} matching the slice intent. When a slice is being requested via
+ * an intent, {@link #onMapIntentToUri(Intent)} can be called and is expected to return an
+ * appropriate Uri representing the slice.
+ *
+ * <pre class="prettyprint">
+ * {@literal
+ * <provider
+ * android:name="com.android.mypkg.MySliceProvider"
+ * android:authorities="com.android.mypkg">
+ * <intent-filter>
+ * <action android:name="android.intent.action.MY_SLICE_INTENT" />
+ * </intent-filter>
+ * </provider>}
+ * </pre>
+ *
+ * @see android.app.slice.Slice
+ */
+public abstract class SliceProvider extends ContentProviderWrapper {
+
+ @Override
+ public void attachInfo(Context context, ProviderInfo info) {
+ ContentProvider impl;
+ if (BuildCompat.isAtLeastP()) {
+ impl = new SliceProviderWrapper(this);
+ } else {
+ impl = new SliceProviderCompat(this);
+ }
+ super.attachInfo(context, info, impl);
+ }
+
+ /**
+ * Implement this to initialize your slice provider on startup.
+ * This method is called for all registered slice providers on the
+ * application main thread at application launch time. It must not perform
+ * lengthy operations, or application startup will be delayed.
+ *
+ * <p>You should defer nontrivial initialization (such as opening,
+ * upgrading, and scanning databases) until the slice provider is used
+ * (via #onBindSlice, etc). Deferred initialization
+ * keeps application startup fast, avoids unnecessary work if the provider
+ * turns out not to be needed, and stops database errors (such as a full
+ * disk) from halting application launch.
+ *
+ * @return true if the provider was successfully loaded, false otherwise
+ */
+ public abstract boolean onCreateSliceProvider();
+
+ /**
+ * Implemented to create a slice. Will be called on the main thread.
+ * <p>
+ * onBindSlice should return as quickly as possible so that the UI tied
+ * to this slice can be responsive. No network or other IO will be allowed
+ * during onBindSlice. Any loading that needs to be done should happen
+ * off the main thread with a call to {@link ContentResolver#notifyChange(Uri, ContentObserver)}
+ * when the app is ready to provide the complete data in onBindSlice.
+ * <p>
+ *
+ * @see {@link Slice}.
+ * @see {@link android.app.slice.Slice#HINT_PARTIAL}
+ */
+ // TODO: Provide alternate notifyChange that takes in the slice (i.e. notifyChange(Uri, Slice)).
+ public abstract Slice onBindSlice(Uri sliceUri);
+
+ /**
+ * This method must be overridden if an {@link IntentFilter} is specified on the SliceProvider.
+ * In that case, this method can be called and is expected to return a non-null Uri representing
+ * a slice. Otherwise this will throw {@link UnsupportedOperationException}.
+ *
+ * @return Uri representing the slice associated with the provided intent.
+ * @see {@link android.app.slice.Slice}
+ */
+ public @NonNull Uri onMapIntentToUri(Intent intent) {
+ throw new UnsupportedOperationException(
+ "This provider has not implemented intent to uri mapping");
+ }
+}
diff --git a/slices/core/src/main/java/androidx/app/slice/compat/ContentProviderWrapper.java b/slices/core/src/main/java/androidx/app/slice/compat/ContentProviderWrapper.java
new file mode 100644
index 0000000..9e02b3a
--- /dev/null
+++ b/slices/core/src/main/java/androidx/app/slice/compat/ContentProviderWrapper.java
@@ -0,0 +1,121 @@
+/*
+ * 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.app.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 android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+import android.support.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
+ 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
+ public final Uri canonicalize(@NonNull Uri url) {
+ return mImpl.canonicalize(url);
+ }
+}
diff --git a/slices/core/src/main/java/androidx/app/slice/compat/SliceProviderCompat.java b/slices/core/src/main/java/androidx/app/slice/compat/SliceProviderCompat.java
new file mode 100644
index 0000000..9fcac1b
--- /dev/null
+++ b/slices/core/src/main/java/androidx/app/slice/compat/SliceProviderCompat.java
@@ -0,0 +1,266 @@
+/*
+ * 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.app.slice.compat;
+
+import static android.app.slice.SliceProvider.SLICE_TYPE;
+
+import android.Manifest.permission;
+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.ResolveInfo;
+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.Parcelable;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.StrictMode;
+import android.os.StrictMode.ThreadPolicy;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+import android.util.Log;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+import androidx.app.slice.Slice;
+import androidx.app.slice.SliceProvider;
+
+/**
+ * @hide
+ */
+@RestrictTo(Scope.LIBRARY)
+public class SliceProviderCompat extends ContentProvider {
+
+ private static final String TAG = "SliceProvider";
+
+ public static final String EXTRA_BIND_URI = "slice_uri";
+ public static final String METHOD_SLICE = "bind_slice";
+ public static final String METHOD_MAP_INTENT = "map_slice";
+ public static final String EXTRA_INTENT = "slice_intent";
+ public static final String EXTRA_SLICE = "slice";
+
+ private static final boolean DEBUG = false;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private SliceProvider mSliceProvider;
+
+ public SliceProviderCompat(SliceProvider provider) {
+ mSliceProvider = provider;
+ }
+
+ @Override
+ public boolean onCreate() {
+ 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, permission.BIND_SLICE,
+ permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+ "Slice binding requires the permission BIND_SLICE");
+ }
+
+ Slice s = handleBindSlice(uri);
+ Bundle b = new Bundle();
+ b.putParcelable(EXTRA_SLICE, s.toBundle());
+ return b;
+ } else if (method.equals(METHOD_MAP_INTENT)) {
+ if (Binder.getCallingUid() != Process.myUid()) {
+ getContext().enforceCallingPermission(permission.BIND_SLICE,
+ "Slice binding requires the permission BIND_SLICE");
+ }
+ Intent intent = extras.getParcelable(EXTRA_INTENT);
+ Uri uri = mSliceProvider.onMapIntentToUri(intent);
+ Bundle b = new Bundle();
+ if (uri != null) {
+ Slice s = handleBindSlice(uri);
+ b.putParcelable(EXTRA_SLICE, s.toBundle());
+ } else {
+ b.putParcelable(EXTRA_SLICE, null);
+ }
+ return b;
+ }
+ return super.call(method, arg, extras);
+ }
+
+ private Slice handleBindSlice(final Uri sliceUri) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ return onBindSliceStrict(sliceUri);
+ } else {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final Slice[] output = new Slice[1];
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ output[0] = onBindSliceStrict(sliceUri);
+ latch.countDown();
+ }
+ });
+ try {
+ latch.await();
+ return output[0];
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private Slice onBindSliceStrict(Uri sliceUri) {
+ ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
+ try {
+ StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
+ .detectAll()
+ .penaltyDeath()
+ .build());
+ return mSliceProvider.onBindSlice(sliceUri);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
+ }
+
+ /**
+ * Compat version of {@link Slice#bindSlice(Context, Uri)}.
+ */
+ public static Slice bindSlice(Context context, Uri uri) {
+ ContentProviderClient provider = context.getContentResolver()
+ .acquireContentProviderClient(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(EXTRA_BIND_URI, uri);
+ final Bundle res = provider.call(METHOD_SLICE, null, extras);
+ if (res == null) {
+ return null;
+ }
+ Parcelable bundle = res.getParcelable(EXTRA_SLICE);
+ if (!(bundle instanceof Bundle)) {
+ return null;
+ }
+ return new Slice((Bundle) bundle);
+ } catch (RemoteException e) {
+ // Arbitrary and not worth documenting, as Activity
+ // Manager will kill this process shortly anyway.
+ return null;
+ } finally {
+ provider.close();
+ }
+ }
+
+ /**
+ * Compat version of {@link Slice#bindSlice(Context, Intent)}.
+ */
+ public static Slice bindSlice(Context context, Intent intent) {
+ ContentResolver resolver = context.getContentResolver();
+
+ // Check if the intent has data for the slice uri on it and use that
+ final Uri intentData = intent.getData();
+ if (intentData != null && SLICE_TYPE.equals(resolver.getType(intentData))) {
+ return bindSlice(context, intentData);
+ }
+ // Otherwise ask the app
+ List<ResolveInfo> providers =
+ context.getPackageManager().queryIntentContentProviders(intent, 0);
+ if (providers == null) {
+ throw new IllegalArgumentException("Unable to resolve intent " + intent);
+ }
+ String authority = providers.get(0).providerInfo.authority;
+ Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority).build();
+ ContentProviderClient provider = resolver.acquireContentProviderClient(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(EXTRA_INTENT, intent);
+ final Bundle res = provider.call(METHOD_MAP_INTENT, null, extras);
+ if (res == null) {
+ return null;
+ }
+ Parcelable bundle = res.getParcelable(EXTRA_SLICE);
+ if (!(bundle instanceof Bundle)) {
+ return null;
+ }
+ return new Slice((Bundle) bundle);
+ } catch (RemoteException e) {
+ // Arbitrary and not worth documenting, as Activity
+ // Manager will kill this process shortly anyway.
+ return null;
+ } finally {
+ provider.close();
+ }
+ }
+}
diff --git a/slices/core/src/main/java/androidx/app/slice/compat/SliceProviderWrapper.java b/slices/core/src/main/java/androidx/app/slice/compat/SliceProviderWrapper.java
new file mode 100644
index 0000000..3afed2b
--- /dev/null
+++ b/slices/core/src/main/java/androidx/app/slice/compat/SliceProviderWrapper.java
@@ -0,0 +1,62 @@
+/*
+ * 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.app.slice.compat;
+
+import android.annotation.TargetApi;
+import android.app.slice.Slice;
+import android.app.slice.SliceProvider;
+import android.app.slice.SliceSpec;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
+
+import java.util.List;
+
+import androidx.app.slice.SliceConvert;
+
+/**
+ * @hide
+ */
+@RestrictTo(Scope.LIBRARY)
+@TargetApi(28)
+public class SliceProviderWrapper extends SliceProvider {
+
+ private androidx.app.slice.SliceProvider mSliceProvider;
+
+ public SliceProviderWrapper(androidx.app.slice.SliceProvider provider) {
+ mSliceProvider = provider;
+ }
+
+ @Override
+ public boolean onCreate() {
+ return mSliceProvider.onCreateSliceProvider();
+ }
+
+ @Override
+ public Slice onBindSlice(Uri sliceUri, List<SliceSpec> supportedVersions) {
+ return SliceConvert.unwrap(mSliceProvider.onBindSlice(sliceUri));
+ }
+
+ /**
+ * Maps intents to uris.
+ */
+ @Override
+ public @NonNull Uri onMapIntentToUri(Intent intent) {
+ return mSliceProvider.onMapIntentToUri(intent);
+ }
+}
diff --git a/slices/core/src/main/java/androidx/app/slice/core/SliceHints.java b/slices/core/src/main/java/androidx/app/slice/core/SliceHints.java
new file mode 100644
index 0000000..96e2c3f
--- /dev/null
+++ b/slices/core/src/main/java/androidx/app/slice/core/SliceHints.java
@@ -0,0 +1,50 @@
+/*
+ * 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 androidx.app.slice.core;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.app.slice.Slice;
+import android.support.annotation.RestrictTo;
+
+/**
+ * Temporary class to contain hint constants for slices to be used.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+public class SliceHints {
+ /**
+ * Subtype to indicate that this content has a toggle action associated with it. To indicate
+ * that the toggle is on, use {@link Slice#HINT_SELECTED}. When the toggle state changes, the
+ * intent associated with it will be sent along with an extra {@link #EXTRA_TOGGLE_STATE}
+ * which can be retrieved to see the new state of the toggle.
+ */
+ public static final String SUBTYPE_TOGGLE = "toggle";
+
+ /**
+ * Key to retrieve an extra added to an intent when a control is changed.
+ */
+ public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
+ /**
+ * Hint to indicate that this content should not be shown in the
+ * {@link androidx.app.slice.widget.SliceView#MODE_SMALL}
+ * and {@link androidx.app.slice.widget.SliceView#MODE_LARGE} modes of SliceView.
+ * This content may be used to populate
+ * the {@link androidx.app.slice.widget.SliceView#MODE_SHORTCUT} format of the slice.
+ */
+ public static final String HINT_HIDDEN = "hidden";
+}
diff --git a/slices/core/src/main/java/androidx/app/slice/core/SliceQuery.java b/slices/core/src/main/java/androidx/app/slice/core/SliceQuery.java
new file mode 100644
index 0000000..8069e20
--- /dev/null
+++ b/slices/core/src/main/java/androidx/app/slice/core/SliceQuery.java
@@ -0,0 +1,344 @@
+/*
+ * 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 androidx.app.slice.core;
+
+import static android.app.slice.Slice.HINT_ACTIONS;
+import static android.app.slice.Slice.HINT_LIST;
+import static android.app.slice.Slice.HINT_LIST_ITEM;
+import static android.app.slice.SliceItem.FORMAT_ACTION;
+import static android.app.slice.SliceItem.FORMAT_COLOR;
+import static android.app.slice.SliceItem.FORMAT_IMAGE;
+import static android.app.slice.SliceItem.FORMAT_SLICE;
+import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
+
+import android.annotation.TargetApi;
+import android.support.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.app.slice.Slice;
+import androidx.app.slice.SliceItem;
+
+/**
+ * Utilities for finding content within a Slice.
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+// TODO: Not expect 24.
+@TargetApi(24)
+public class SliceQuery {
+
+ /**
+ * @return Whether this item is appropriate to be considered a "start" item, i.e. go in the
+ * front slot of a small slice.
+ */
+ public static boolean isStartType(SliceItem item) {
+ final String type = item.getFormat();
+ return (!item.hasHint(SliceHints.SUBTYPE_TOGGLE)
+ && (FORMAT_ACTION.equals(type) && (find(item, FORMAT_IMAGE) != null)))
+ || FORMAT_IMAGE.equals(type)
+ || FORMAT_TIMESTAMP.equals(type);
+ }
+
+ /**
+ * @return Finds the first slice that has non-slice children.
+ */
+ public static SliceItem findFirstSlice(SliceItem slice) {
+ if (!FORMAT_SLICE.equals(slice.getFormat())) {
+ return slice;
+ }
+ List<SliceItem> items = slice.getSlice().getItems();
+ for (int i = 0; i < items.size(); i++) {
+ if (FORMAT_SLICE.equals(items.get(i).getFormat())) {
+ SliceItem childSlice = items.get(i);
+ return findFirstSlice(childSlice);
+ } else {
+ // Doesn't have slice children so return it
+ return slice;
+ }
+ }
+ // Slices all the way down, just return it
+ return slice;
+ }
+
+ /**
+ * @return Whether this item is a simple action, i.e. an action that only has an icon.
+ */
+ public static boolean isSimpleAction(SliceItem item) {
+ if (FORMAT_ACTION.equals(item.getFormat())) {
+ List<SliceItem> items = item.getSlice().getItems();
+ boolean hasImage = false;
+ for (int i = 0; i < items.size(); i++) {
+ SliceItem child = items.get(i);
+ if (FORMAT_IMAGE.equals(child.getFormat()) && !hasImage) {
+ hasImage = true;
+ } else if (FORMAT_COLOR.equals(child.getFormat())) {
+ continue;
+ } else {
+ return false;
+ }
+ }
+ return hasImage;
+ }
+ return false;
+ }
+
+ /**
+ */
+ public static boolean hasAnyHints(SliceItem item, String... hints) {
+ if (hints == null) return false;
+ List<String> itemHints = item.getHints();
+ for (String hint : hints) {
+ if (itemHints.contains(hint)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ */
+ public static boolean hasHints(SliceItem item, String... hints) {
+ if (hints == null) return true;
+ List<String> itemHints = item.getHints();
+ for (String hint : hints) {
+ if (!TextUtils.isEmpty(hint) && !itemHints.contains(hint)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ */
+ public static boolean hasHints(Slice item, String... hints) {
+ if (hints == null) return true;
+ List<String> itemHints = item.getHints();
+ for (String hint : hints) {
+ if (!TextUtils.isEmpty(hint) && !itemHints.contains(hint)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ */
+ public static SliceItem getPrimaryIcon(Slice slice) {
+ for (SliceItem item : slice.getItems()) {
+ if (FORMAT_IMAGE.equals(item.getFormat())) {
+ return item;
+ }
+ if (!(FORMAT_SLICE.equals(item.getFormat()) && item.hasHint(HINT_LIST))
+ && !item.hasHint(HINT_ACTIONS)
+ && !item.hasHint(HINT_LIST_ITEM)
+ && !FORMAT_ACTION.equals(item.getFormat())) {
+ SliceItem icon = SliceQuery.find(item, FORMAT_IMAGE);
+ if (icon != null) {
+ return icon;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ */
+ public static SliceItem findNotContaining(SliceItem container, List<SliceItem> list) {
+ SliceItem ret = null;
+ while (ret == null && list.size() != 0) {
+ SliceItem remove = list.remove(0);
+ if (!contains(container, remove)) {
+ ret = remove;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ */
+ private static boolean contains(SliceItem container, final SliceItem item) {
+ if (container == null || item == null) return false;
+ return stream(container).filter(new Predicate<SliceItem>() {
+ @Override
+ public boolean test(SliceItem s) {
+ return s == item;
+ }
+ }).findAny().isPresent();
+ }
+
+ /**
+ */
+ public static List<SliceItem> findAll(SliceItem s, String format) {
+ return findAll(s, format, (String[]) null, null);
+ }
+
+ /**
+ */
+ public static List<SliceItem> findAll(Slice s, String format, String hints, String nonHints) {
+ return findAll(s, format, new String[]{ hints }, new String[]{ nonHints });
+ }
+
+ /**
+ */
+ public static List<SliceItem> findAll(SliceItem s, String format, String hints,
+ String nonHints) {
+ return findAll(s, format, new String[]{ hints }, new String[]{ nonHints });
+ }
+
+ /**
+ */
+ public static List<SliceItem> findAll(Slice s, final String format, final String[] hints,
+ final String[] nonHints) {
+ return stream(s).filter(new Predicate<SliceItem>() {
+ @Override
+ public boolean test(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>() {
+ @Override
+ public boolean test(SliceItem item) {
+ return checkFormat(item, format)
+ && (hasHints(item, hints) && !hasAnyHints(item, nonHints));
+ }
+ }).collect(Collectors.<SliceItem>toList());
+ }
+
+ /**
+ */
+ public static SliceItem find(Slice s, String format, String hints, String nonHints) {
+ return find(s, format, new String[]{ hints }, new String[]{ nonHints });
+ }
+
+ /**
+ */
+ public static SliceItem find(Slice s, String format) {
+ return find(s, format, (String[]) null, null);
+ }
+
+ /**
+ */
+ public static SliceItem find(SliceItem s, String format) {
+ return find(s, format, (String[]) null, null);
+ }
+
+ /**
+ */
+ public static SliceItem find(SliceItem s, String format, String hints, String nonHints) {
+ return find(s, format, new String[]{ hints }, new String[]{ nonHints });
+ }
+
+ /**
+ */
+ public static SliceItem find(Slice s, final String format, final String[] hints,
+ final String[] nonHints) {
+ return stream(s).filter(new Predicate<SliceItem>() {
+ @Override
+ public boolean test(SliceItem item) {
+ return checkFormat(item, format)
+ && (hasHints(item, hints) && !hasAnyHints(item, nonHints));
+ }
+ }).findFirst().orElse(null);
+ }
+
+ /**
+ */
+ public static SliceItem findSubtype(SliceItem s, final String format, final String subtype) {
+ return stream(s).filter(new Predicate<SliceItem>() {
+ @Override
+ public boolean test(SliceItem item) {
+ return checkFormat(item, format) && checkSubtype(item, subtype);
+ }
+ }).findFirst().orElse(null);
+ }
+
+ /**
+ */
+ public static SliceItem find(SliceItem s, final String format, final String[] hints,
+ final String[] nonHints) {
+ return stream(s).filter(new Predicate<SliceItem>() {
+ @Override
+ public boolean test(SliceItem item) {
+ return checkFormat(item, format)
+ && (hasHints(item, hints) && !hasAnyHints(item, nonHints));
+ }
+ }).findFirst().orElse(null);
+ }
+
+ private static boolean checkFormat(SliceItem item, String format) {
+ return format == null || format.equals(item.getFormat());
+ }
+
+ private static boolean checkSubtype(SliceItem item, String subtype) {
+ return subtype == null || subtype.equals(item.getSubType());
+ }
+
+ /**
+ */
+ public static Stream<SliceItem> stream(SliceItem slice) {
+ Queue<SliceItem> items = new ArrayDeque<>();
+ items.add(slice);
+ return getSliceItemStream(items);
+ }
+
+ /**
+ */
+ public static Stream<SliceItem> stream(Slice slice) {
+ Queue<SliceItem> items = new ArrayDeque<>();
+ items.addAll(slice.getItems());
+ return getSliceItemStream(items);
+ }
+
+ /**
+ */
+ private static Stream<SliceItem> getSliceItemStream(final Queue<SliceItem> items) {
+ Iterator<SliceItem> iterator = new Iterator<SliceItem>() {
+ @Override
+ public boolean hasNext() {
+ return items.size() != 0;
+ }
+
+ @Override
+ public SliceItem next() {
+ SliceItem item = items.poll();
+ if (FORMAT_SLICE.equals(item.getFormat())
+ || FORMAT_ACTION.equals(item.getFormat())) {
+ items.addAll(item.getSlice().getItems());
+ }
+ return item;
+ }
+ };
+ return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
+ }
+}
diff --git a/slices/core/src/main/java/androidx/app/slice/core/SliceSpecs.java b/slices/core/src/main/java/androidx/app/slice/core/SliceSpecs.java
new file mode 100644
index 0000000..a21633b
--- /dev/null
+++ b/slices/core/src/main/java/androidx/app/slice/core/SliceSpecs.java
@@ -0,0 +1,33 @@
+/*
+ * 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 androidx.app.slice.core;
+
+import android.app.slice.SliceSpec;
+import android.support.annotation.RestrictTo;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class SliceSpecs {
+
+ // TODO: Fill these in.
+ public static List<SliceSpec> SUPPORTED_SPECS = Collections.emptyList();
+}
diff --git a/slices/core/src/main/res-public/values-v28/strings.xml b/slices/core/src/main/res-public/values-v28/strings.xml
new file mode 100644
index 0000000..12dabc6
--- /dev/null
+++ b/slices/core/src/main/res-public/values-v28/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="slice_provider">androidx.app.slice.compat.SliceProviderWrapper</string>
+</resources>
\ No newline at end of file
diff --git a/slices/core/src/main/res-public/values/strings.xml b/slices/core/src/main/res-public/values/strings.xml
new file mode 100644
index 0000000..d492a38
--- /dev/null
+++ b/slices/core/src/main/res-public/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="slice_provider">androidx.app.slice.compat.SliceProviderCompat</string>
+</resources>
diff --git a/slices/view/Android.mk b/slices/view/Android.mk
new file mode 100644
index 0000000..6c487d0
--- /dev/null
+++ b/slices/view/Android.mk
@@ -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.
+
+LOCAL_PATH := $(call my-dir)
+
+# Here is the final static library that apps can link against.
+# Applications that use this library must specify
+#
+# LOCAL_STATIC_ANDROID_LIBRARIES := \
+# android-slices-core \
+# android-slices-view
+#
+# in their makefiles to include the resources and their dependencies in their package.
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_MODULE := android-slices-view
+LOCAL_MANIFEST_FILE := src/main/AndroidManifest.xml
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under,src/main/java) \
+ $(call all-Iaidl-files-under,src/main/java)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/src/main/res
+
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations \
+ android-support-v7-recyclerview \
+ android-support-v4 \
+ android-support-compat \
+ android-slices-core \
+ apptoolkit-lifecycle-extensions \
+ apptoolkit-arch-core-runtime
+
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-v7-appcompat \
+
+LOCAL_JAR_EXCLUDE_FILES := none
+LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/slices/view/api/current.txt b/slices/view/api/current.txt
new file mode 100644
index 0000000..d7ac0c5
--- /dev/null
+++ b/slices/view/api/current.txt
@@ -0,0 +1,25 @@
+package androidx.app.slice.widget {
+
+ public final class SliceLiveData {
+ ctor public SliceLiveData();
+ method public static android.arch.lifecycle.LiveData<androidx.app.slice.Slice> fromIntent(android.content.Context, android.content.Intent);
+ method public static android.arch.lifecycle.LiveData<androidx.app.slice.Slice> fromUri(android.content.Context, android.net.Uri);
+ }
+
+ public class SliceView extends android.view.ViewGroup implements android.arch.lifecycle.Observer {
+ 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);
+ ctor public SliceView(android.content.Context, android.util.AttributeSet, int, int);
+ method public int getMode();
+ method public void onChanged(androidx.app.slice.Slice);
+ method public void setMode(int);
+ method public void setScrollable(boolean);
+ method public void setSlice(androidx.app.slice.Slice);
+ field public static final int MODE_LARGE = 2; // 0x2
+ field public static final int MODE_SHORTCUT = 3; // 0x3
+ field public static final int MODE_SMALL = 1; // 0x1
+ }
+
+}
+
diff --git a/slices/view/build.gradle b/slices/view/build.gradle
new file mode 100644
index 0000000..97a445c
--- /dev/null
+++ b/slices/view/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 android.support.LibraryVersions
+import android.support.LibraryGroups
+
+plugins {
+ id("SupportAndroidLibraryPlugin")
+}
+
+dependencies {
+ implementation project(":slices-core")
+ implementation project(":slices-builders")
+ implementation libs.support.recyclerview, libs.support_exclude_config
+ implementation project(':lifecycle:extensions')
+}
+
+android {
+ defaultConfig {
+ minSdkVersion 19
+ }
+}
+
+supportLibrary {
+ name 'Slice views'
+ publish true
+ mavenVersion = LibraryVersions.SUPPORT_LIBRARY
+ mavenGroup = LibraryGroups.SLICES
+ inceptionYear '2017'
+ description "A library that handles rendering of slice content into supported templates"
+ java8Library false
+}
diff --git a/slices/view/lint-baseline.xml b/slices/view/lint-baseline.xml
new file mode 100644
index 0000000..49d372e
--- /dev/null
+++ b/slices/view/lint-baseline.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="4" by="lint 3.0.0">
+
+ <issue
+ id="WrongConstant"
+ message="Must be one of: SliceView.MODE_SMALL, SliceView.MODE_LARGE, SliceView.MODE_SHORTCUT"
+ errorLine1=" return mMode;"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/main/java/androidx/app/slice/widget/SliceView.java"
+ line="290"
+ column="16"/>
+ </issue>
+
+</issues>
diff --git a/slices/view/src/main/AndroidManifest.xml b/slices/view/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5e9dd17
--- /dev/null
+++ b/slices/view/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="androidx.app.slice.view">
+ <uses-sdk android:minSdkVersion="28"/>
+</manifest>
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/ActionRow.java b/slices/view/src/main/java/androidx/app/slice/widget/ActionRow.java
new file mode 100644
index 0000000..70cec4f
--- /dev/null
+++ b/slices/view/src/main/java/androidx/app/slice/widget/ActionRow.java
@@ -0,0 +1,231 @@
+/*
+ * 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 androidx.app.slice.widget;
+
+import static android.app.slice.Slice.HINT_NO_TINT;
+import static android.app.slice.SliceItem.FORMAT_ACTION;
+import static android.app.slice.SliceItem.FORMAT_COLOR;
+import static android.app.slice.SliceItem.FORMAT_IMAGE;
+import static android.app.slice.SliceItem.FORMAT_REMOTE_INPUT;
+
+import android.annotation.TargetApi;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.app.RemoteInput;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.drawable.Icon;
+import android.os.AsyncTask;
+import android.support.annotation.RestrictTo;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewParent;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.function.Consumer;
+
+import androidx.app.slice.SliceItem;
+import androidx.app.slice.core.SliceQuery;
+
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@TargetApi(24)
+public class ActionRow extends FrameLayout {
+
+ private static final int MAX_ACTIONS = 5;
+ private final int mSize;
+ private final int mIconPadding;
+ private final LinearLayout mActionsGroup;
+ private final boolean mFullActions;
+ private int mColor = Color.BLACK;
+
+ public ActionRow(Context context, boolean fullActions) {
+ super(context);
+ mFullActions = fullActions;
+ mSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48,
+ context.getResources().getDisplayMetrics());
+ mIconPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12,
+ context.getResources().getDisplayMetrics());
+ mActionsGroup = new LinearLayout(context);
+ mActionsGroup.setOrientation(LinearLayout.HORIZONTAL);
+ mActionsGroup.setLayoutParams(
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ addView(mActionsGroup);
+ }
+
+ private void setColor(int color) {
+ mColor = color;
+ for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
+ View view = mActionsGroup.getChildAt(i);
+ SliceItem item = (SliceItem) view.getTag();
+ boolean tint = !item.hasHint(HINT_NO_TINT);
+ if (tint) {
+ ((ImageView) view).setImageTintList(ColorStateList.valueOf(mColor));
+ }
+ }
+ }
+
+ private ImageView addAction(Icon icon, boolean allowTint, SliceItem image) {
+ ImageView imageView = new ImageView(getContext());
+ imageView.setPadding(mIconPadding, mIconPadding, mIconPadding, mIconPadding);
+ imageView.setScaleType(ScaleType.FIT_CENTER);
+ imageView.setImageIcon(icon);
+ if (allowTint) {
+ imageView.setImageTintList(ColorStateList.valueOf(mColor));
+ }
+ imageView.setBackground(SliceViewUtil.getDrawable(getContext(),
+ android.R.attr.selectableItemBackground));
+ imageView.setTag(image);
+ addAction(imageView);
+ return imageView;
+ }
+
+ /**
+ * Set the actions and color for this action row.
+ */
+ public void setActions(SliceItem actionRow, SliceItem defColor) {
+ removeAllViews();
+ mActionsGroup.removeAllViews();
+ addView(mActionsGroup);
+
+ SliceItem color = SliceQuery.find(actionRow, FORMAT_COLOR);
+ if (color == null) {
+ color = defColor;
+ }
+ if (color != null) {
+ setColor(color.getColor());
+ }
+ SliceQuery.findAll(actionRow, FORMAT_ACTION).forEach(new Consumer<SliceItem>() {
+ @Override
+ public void accept(final SliceItem action) {
+ if (mActionsGroup.getChildCount() >= MAX_ACTIONS) {
+ return;
+ }
+ SliceItem image = SliceQuery.find(action, FORMAT_IMAGE);
+ if (image == null) {
+ return;
+ }
+ boolean tint = !image.hasHint(HINT_NO_TINT);
+ final SliceItem input = SliceQuery.find(action, FORMAT_REMOTE_INPUT);
+ if (input != null && input.getRemoteInput().getAllowFreeFormInput()) {
+ addAction(image.getIcon(), tint, image).setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ handleRemoteInputClick(v, action.getAction(),
+ input.getRemoteInput());
+ }
+ });
+ createRemoteInputView(mColor, getContext());
+ } else {
+ addAction(image.getIcon(), tint, image).setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ AsyncTask.execute(new Runnable() {
+ @Override
+ public void run() {
+
+ try {
+ action.getAction().send();
+ } catch (CanceledException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+ });
+ }
+ }
+ });
+ setVisibility(getChildCount() != 0 ? View.VISIBLE : View.GONE);
+ }
+
+ private void addAction(View child) {
+ mActionsGroup.addView(child, new LinearLayout.LayoutParams(mSize, mSize, 1));
+ }
+
+ private void createRemoteInputView(int color, Context context) {
+ View riv = RemoteInputView.inflate(context, this);
+ riv.setVisibility(View.INVISIBLE);
+ addView(riv, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ riv.setBackgroundColor(color);
+ }
+
+ private boolean handleRemoteInputClick(View view, PendingIntent pendingIntent,
+ RemoteInput input) {
+ if (input == null) {
+ return false;
+ }
+
+ ViewParent p = view.getParent().getParent();
+ RemoteInputView riv = null;
+ while (p != null) {
+ if (p instanceof View) {
+ View pv = (View) p;
+ riv = findRemoteInputView(pv);
+ if (riv != null) {
+ break;
+ }
+ }
+ p = p.getParent();
+ }
+ if (riv == null) {
+ return false;
+ }
+
+ int width = view.getWidth();
+ if (view instanceof TextView) {
+ // Center the reveal on the text which might be off-center from the TextView
+ TextView tv = (TextView) view;
+ if (tv.getLayout() != null) {
+ int innerWidth = (int) tv.getLayout().getLineWidth(0);
+ innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight();
+ width = Math.min(width, innerWidth);
+ }
+ }
+ int cx = view.getLeft() + width / 2;
+ int cy = view.getTop() + view.getHeight() / 2;
+ int w = riv.getWidth();
+ int h = riv.getHeight();
+ int r = Math.max(
+ Math.max(cx + cy, cx + (h - cy)),
+ Math.max((w - cx) + cy, (w - cx) + (h - cy)));
+
+ riv.setRevealParameters(cx, cy, r);
+ riv.setPendingIntent(pendingIntent);
+ riv.setRemoteInput(new RemoteInput[] {
+ input
+ }, input);
+ riv.focusAnimated();
+ return true;
+ }
+
+ private RemoteInputView findRemoteInputView(View v) {
+ if (v == null) {
+ return null;
+ }
+ return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG);
+ }
+}
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/GridView.java b/slices/view/src/main/java/androidx/app/slice/widget/GridView.java
new file mode 100644
index 0000000..6484056
--- /dev/null
+++ b/slices/view/src/main/java/androidx/app/slice/widget/GridView.java
@@ -0,0 +1,203 @@
+/*
+ * 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 androidx.app.slice.widget;
+
+import static android.app.slice.Slice.HINT_LARGE;
+import static android.app.slice.Slice.HINT_TITLE;
+import static android.app.slice.SliceItem.FORMAT_COLOR;
+import static android.app.slice.SliceItem.FORMAT_IMAGE;
+import static android.app.slice.SliceItem.FORMAT_SLICE;
+import static android.app.slice.SliceItem.FORMAT_TEXT;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Color;
+import android.support.annotation.RestrictTo;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+import androidx.app.slice.SliceItem;
+import androidx.app.slice.core.SliceQuery;
+import androidx.app.slice.view.R;
+
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@TargetApi(24)
+public class GridView extends LinearLayout implements LargeSliceAdapter.SliceListView {
+
+ private static final String TAG = "GridView";
+
+ private static final int MAX_IMAGES = 3;
+ private static final int MAX_ALL = 5;
+ private boolean mIsAllImages;
+
+ public GridView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mIsAllImages) {
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = width / getChildCount();
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+ getLayoutParams().height = height;
+ for (int i = 0; i < getChildCount(); i++) {
+ getChildAt(i).getLayoutParams().height = height;
+ }
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public void setSliceItem(SliceItem slice, boolean isHeader) {
+ mIsAllImages = true;
+ removeAllViews();
+ int total = 1;
+ if (FORMAT_SLICE.equals(slice.getFormat())) {
+ List<SliceItem> items = slice.getSlice().getItems();
+ total = items.size();
+ for (int i = 0; i < total; i++) {
+ SliceItem item = items.get(i);
+ if (isFull()) {
+ continue;
+ }
+ if (!addItem(item)) {
+ mIsAllImages = false;
+ }
+ }
+ } else {
+ if (!isFull()) {
+ if (!addItem(slice)) {
+ mIsAllImages = false;
+ }
+ }
+ }
+ if (total > getChildCount() && mIsAllImages) {
+ addExtraCount(total - getChildCount());
+ }
+ }
+
+ @Override
+ public void setColor(SliceItem color) {
+
+ }
+
+ private void addExtraCount(int numExtra) {
+ View last = getChildAt(getChildCount() - 1);
+ FrameLayout frame = new FrameLayout(getContext());
+ frame.setLayoutParams(last.getLayoutParams());
+
+ removeView(last);
+ frame.addView(last, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+ TextView v = new TextView(getContext());
+ v.setTextColor(Color.WHITE);
+ v.setBackgroundColor(0x4d000000);
+ v.setText(getResources().getString(R.string.abc_slice_more_content, numExtra));
+ v.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
+ v.setGravity(Gravity.CENTER);
+ frame.addView(v, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+ addView(frame);
+ }
+
+ private boolean isFull() {
+ return getChildCount() >= (mIsAllImages ? MAX_IMAGES : MAX_ALL);
+ }
+
+ /**
+ * Returns true if this item is just an image.
+ */
+ private boolean addItem(final SliceItem item) {
+ if (FORMAT_IMAGE.equals(item.getFormat())) {
+ ImageView v = new ImageView(getContext());
+ v.setImageIcon(item.getIcon());
+ v.setScaleType(ScaleType.CENTER_CROP);
+ addView(v, new LayoutParams(0, MATCH_PARENT, 1));
+ return true;
+ } else {
+ final LinearLayout v = new LinearLayout(getContext());
+ int s = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ 12, getContext().getResources().getDisplayMetrics());
+ v.setPadding(0, s, 0, 0);
+ v.setOrientation(LinearLayout.VERTICAL);
+ v.setGravity(Gravity.CENTER_HORIZONTAL);
+ // TODO: Unify sporadic inflates that happen throughout the code.
+ ArrayList<SliceItem> items = new ArrayList<>();
+ if (FORMAT_SLICE.equals(item.getFormat())) {
+ items.addAll(item.getSlice().getItems());
+ }
+ items.forEach(new Consumer<SliceItem>() {
+ @Override
+ public void accept(SliceItem i) {
+ Context context = getContext();
+ switch (i.getFormat()) {
+ case FORMAT_TEXT:
+ boolean title = false;
+ if ((SliceQuery.hasAnyHints(item, new String[]{
+ HINT_LARGE, HINT_TITLE
+ }))) {
+ title = true;
+ }
+ TextView tv = (TextView) LayoutInflater.from(context).inflate(title
+ ? R.layout.abc_slice_title
+ : R.layout.abc_slice_secondary_text,
+ null);
+ tv.setText(i.getText());
+ v.addView(tv);
+ break;
+ case FORMAT_IMAGE:
+ ImageView iv = new ImageView(context);
+ iv.setImageIcon(i.getIcon());
+ if (item.hasHint(HINT_LARGE)) {
+ iv.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ } else {
+ int size = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ 48, context.getResources().getDisplayMetrics());
+ iv.setLayoutParams(new LayoutParams(size, size));
+ }
+ v.addView(iv);
+ break;
+ case FORMAT_COLOR:
+ // TODO: Support color to tint stuff here.
+ break;
+ }
+ }
+ });
+ addView(v, new LayoutParams(0, WRAP_CONTENT, 1));
+ return false;
+ }
+ }
+}
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/LargeSliceAdapter.java b/slices/view/src/main/java/androidx/app/slice/widget/LargeSliceAdapter.java
new file mode 100644
index 0000000..459723f
--- /dev/null
+++ b/slices/view/src/main/java/androidx/app/slice/widget/LargeSliceAdapter.java
@@ -0,0 +1,230 @@
+/*
+ * 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 androidx.app.slice.widget;
+
+import static android.app.slice.Slice.HINT_HORIZONTAL;
+import static android.app.slice.Slice.SUBTYPE_MESSAGE;
+import static android.app.slice.Slice.SUBTYPE_SOURCE;
+import static android.app.slice.SliceItem.FORMAT_COLOR;
+import static android.app.slice.SliceItem.FORMAT_IMAGE;
+import static android.app.slice.SliceItem.FORMAT_TEXT;
+
+import android.annotation.TargetApi;
+import android.app.slice.Slice;
+import android.content.Context;
+import android.support.annotation.RestrictTo;
+import android.support.v7.widget.RecyclerView;
+import android.util.ArrayMap;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import androidx.app.slice.SliceItem;
+import androidx.app.slice.core.SliceQuery;
+import androidx.app.slice.view.R;
+
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@TargetApi(24)
+public class LargeSliceAdapter extends RecyclerView.Adapter<LargeSliceAdapter.SliceViewHolder> {
+
+ public static final int TYPE_DEFAULT = 1;
+ public static final int TYPE_HEADER = 2; // TODO headers shouldn't scroll off
+ public static final int TYPE_GRID = 3;
+ public static final int TYPE_MESSAGE = 4;
+ public static final int TYPE_MESSAGE_LOCAL = 5;
+
+ private final IdGenerator mIdGen = new IdGenerator();
+ private final Context mContext;
+ private List<SliceWrapper> mSlices = new ArrayList<>();
+ private SliceItem mColor;
+
+ public LargeSliceAdapter(Context context) {
+ mContext = context;
+ setHasStableIds(true);
+ }
+
+ /**
+ * Set the {@link SliceItem}'s to be displayed in the adapter and the accent color.
+ */
+ public void setSliceItems(List<SliceItem> slices, SliceItem color) {
+ mColor = color;
+ mIdGen.resetUsage();
+ mSlices = slices.stream().map(new Function<SliceItem, SliceWrapper>() {
+ @Override
+ public SliceWrapper apply(SliceItem s) {
+ return new SliceWrapper(s, mIdGen);
+ }
+ }).collect(Collectors.<SliceWrapper>toList());
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public SliceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View v = inflateForType(viewType);
+ v.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ return new SliceViewHolder(v);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return mSlices.get(position).mType;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mSlices.get(position).mId;
+ }
+
+ @Override
+ public int getItemCount() {
+ return mSlices.size();
+ }
+
+ @Override
+ public void onBindViewHolder(SliceViewHolder holder, int position) {
+ SliceWrapper slice = mSlices.get(position);
+ if (holder.mSliceView != null) {
+ holder.mSliceView.setColor(mColor);
+ holder.mSliceView.setSliceItem(slice.mItem, position == 0 /* isHeader */);
+ }
+ }
+
+ private View inflateForType(int viewType) {
+ switch (viewType) {
+ case TYPE_GRID:
+ return LayoutInflater.from(mContext).inflate(R.layout.abc_slice_grid, null);
+ case TYPE_MESSAGE:
+ return LayoutInflater.from(mContext).inflate(R.layout.abc_slice_message, null);
+ case TYPE_MESSAGE_LOCAL:
+ return LayoutInflater.from(mContext).inflate(R.layout.abc_slice_message_local,
+ null);
+ }
+ return new RowView(mContext);
+ }
+
+ protected static class SliceWrapper {
+ private final SliceItem mItem;
+ private final int mType;
+ private final long mId;
+
+ public SliceWrapper(SliceItem item, IdGenerator idGen) {
+ mItem = item;
+ mType = getFormat(item);
+ mId = idGen.getId(item);
+ }
+
+ public static int getFormat(SliceItem item) {
+ if (SUBTYPE_MESSAGE.equals(item.getSubType())) {
+ // TODO: Better way to determine me or not? Something more like Messaging style.
+ if (SliceQuery.findSubtype(item, null, SUBTYPE_SOURCE) != null) {
+ return TYPE_MESSAGE;
+ } else {
+ return TYPE_MESSAGE_LOCAL;
+ }
+ }
+ if (item.hasHint(HINT_HORIZONTAL)) {
+ return TYPE_GRID;
+ }
+ if (!item.hasHint(Slice.HINT_LIST_ITEM)) {
+ return TYPE_HEADER;
+ }
+ return TYPE_DEFAULT;
+ }
+ }
+
+ /**
+ * A {@link RecyclerView.ViewHolder} for presenting slices in {@link LargeSliceAdapter}.
+ */
+ public static class SliceViewHolder extends RecyclerView.ViewHolder {
+ public final SliceListView mSliceView;
+
+ public SliceViewHolder(View itemView) {
+ super(itemView);
+ mSliceView = itemView instanceof SliceListView ? (SliceListView) itemView : null;
+ }
+ }
+
+ /**
+ * View slices being displayed in {@link LargeSliceAdapter}.
+ */
+ public interface SliceListView {
+ /**
+ * Set the slice item for this view.
+ */
+ void setSliceItem(SliceItem slice, boolean isHeader);
+
+ /**
+ * Set the color for the items in this view.
+ */
+ void setColor(SliceItem color);
+ }
+
+ private static class IdGenerator {
+ private long mNextLong = 0;
+ private final ArrayMap<String, Long> mCurrentIds = new ArrayMap<>();
+ private final ArrayMap<String, Integer> mUsedIds = new ArrayMap<>();
+
+ public long getId(SliceItem item) {
+ String str = genString(item);
+ if (!mCurrentIds.containsKey(str)) {
+ mCurrentIds.put(str, mNextLong++);
+ }
+ long id = mCurrentIds.get(str);
+ int index = mUsedIds.getOrDefault(str, 0);
+ mUsedIds.put(str, index + 1);
+ return id + index * 10000;
+ }
+
+ 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_COLOR:
+ builder.append(i.getColor());
+ break;
+ }
+ }
+ });
+ return builder.toString();
+ }
+
+ public void resetUsage() {
+ mUsedIds.clear();
+ }
+ }
+}
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/LargeTemplateView.java b/slices/view/src/main/java/androidx/app/slice/widget/LargeTemplateView.java
new file mode 100644
index 0000000..0160f59
--- /dev/null
+++ b/slices/view/src/main/java/androidx/app/slice/widget/LargeTemplateView.java
@@ -0,0 +1,133 @@
+/*
+ * 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 androidx.app.slice.widget;
+
+import static android.app.slice.Slice.HINT_ACTIONS;
+import static android.app.slice.Slice.HINT_LIST;
+import static android.app.slice.Slice.HINT_LIST_ITEM;
+import static android.app.slice.Slice.HINT_PARTIAL;
+import static android.app.slice.SliceItem.FORMAT_COLOR;
+import static android.app.slice.SliceItem.FORMAT_SLICE;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.support.annotation.RestrictTo;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.TypedValue;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+import androidx.app.slice.Slice;
+import androidx.app.slice.SliceItem;
+import androidx.app.slice.core.SliceQuery;
+
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@TargetApi(24)
+public class LargeTemplateView extends SliceView.SliceModeView {
+
+ private final LargeSliceAdapter mAdapter;
+ private final RecyclerView mRecyclerView;
+ private final int mDefaultHeight;
+ private final int mMaxHeight;
+ private Slice mSlice;
+ private boolean mIsScrollable;
+
+ public LargeTemplateView(Context context) {
+ super(context);
+
+ mRecyclerView = new RecyclerView(getContext());
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+ mAdapter = new LargeSliceAdapter(context);
+ mRecyclerView.setAdapter(mAdapter);
+ addView(mRecyclerView);
+ mDefaultHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200,
+ getResources().getDisplayMetrics());
+ mMaxHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200,
+ getResources().getDisplayMetrics());
+ }
+
+ @Override
+ public @SliceView.SliceMode int getMode() {
+ return SliceView.MODE_LARGE;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mRecyclerView.getLayoutParams().height = WRAP_CONTENT;
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mRecyclerView.getMeasuredHeight() > mMaxHeight
+ || (mSlice != null && SliceQuery.hasHints(mSlice, HINT_PARTIAL))) {
+ mRecyclerView.getLayoutParams().height = mDefaultHeight;
+ } else {
+ mRecyclerView.getLayoutParams().height = mRecyclerView.getMeasuredHeight();
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public void setSlice(Slice slice) {
+ SliceItem color = SliceQuery.find(slice, FORMAT_COLOR);
+ mSlice = slice;
+ final List<SliceItem> items = new ArrayList<>();
+ final boolean[] hasHeader = new boolean[1];
+ if (SliceQuery.hasHints(slice, HINT_LIST)) {
+ addList(slice, items);
+ } else {
+ slice.getItems().forEach(new Consumer<SliceItem>() {
+ @Override
+ public void accept(SliceItem item) {
+ if (item.hasHint(HINT_ACTIONS)) {
+ return;
+ } else if (FORMAT_COLOR.equals(item.getFormat())) {
+ return;
+ } else if (FORMAT_SLICE.equals(item.getFormat())
+ && item.hasHint(HINT_LIST)) {
+ addList(item.getSlice(), items);
+ } else if (item.hasHint(HINT_LIST_ITEM)) {
+ items.add(item);
+ } else if (!hasHeader[0]) {
+ hasHeader[0] = true;
+ items.add(0, item);
+ } else {
+ items.add(item);
+ }
+ }
+ });
+ }
+ mAdapter.setSliceItems(items, color);
+ }
+
+ private void addList(Slice slice, List<SliceItem> items) {
+ List<SliceItem> sliceItems = slice.getItems();
+ items.addAll(sliceItems);
+ }
+
+ /**
+ * Whether or not the content in this template should be scrollable.
+ */
+ public void setScrollable(boolean isScrollable) {
+ // TODO -- restrict / enable how much this view can show
+ mIsScrollable = isScrollable;
+ }
+}
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/MessageView.java b/slices/view/src/main/java/androidx/app/slice/widget/MessageView.java
new file mode 100644
index 0000000..e2678e1
--- /dev/null
+++ b/slices/view/src/main/java/androidx/app/slice/widget/MessageView.java
@@ -0,0 +1,94 @@
+/*
+ * 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 androidx.app.slice.widget;
+
+import static android.app.slice.Slice.SUBTYPE_SOURCE;
+import static android.app.slice.SliceItem.FORMAT_IMAGE;
+import static android.app.slice.SliceItem.FORMAT_TEXT;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.RestrictTo;
+import android.text.SpannableStringBuilder;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.function.Consumer;
+
+import androidx.app.slice.SliceItem;
+import androidx.app.slice.core.SliceQuery;
+
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@TargetApi(24)
+public class MessageView extends LinearLayout implements LargeSliceAdapter.SliceListView {
+
+ private TextView mDetails;
+ private ImageView mIcon;
+
+ public MessageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mDetails = findViewById(android.R.id.summary);
+ mIcon = findViewById(android.R.id.icon);
+ }
+
+ @Override
+ public void setSliceItem(SliceItem slice, boolean isHeader) {
+ SliceItem source = SliceQuery.findSubtype(slice, FORMAT_IMAGE, SUBTYPE_SOURCE);
+ if (source != null) {
+ final int iconSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ 24, getContext().getResources().getDisplayMetrics());
+ // TODO try and turn this into a drawable
+ Bitmap iconBm = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
+ Canvas iconCanvas = new Canvas(iconBm);
+ Drawable d = source.getIcon().loadDrawable(getContext());
+ d.setBounds(0, 0, iconSize, iconSize);
+ d.draw(iconCanvas);
+ 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());
+ }
+ });
+ mDetails.setText(builder.toString());
+ }
+
+ @Override
+ public void setColor(SliceItem color) {
+
+ }
+
+}
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/RemoteInputView.java b/slices/view/src/main/java/androidx/app/slice/widget/RemoteInputView.java
new file mode 100644
index 0000000..da35018
--- /dev/null
+++ b/slices/view/src/main/java/androidx/app/slice/widget/RemoteInputView.java
@@ -0,0 +1,425 @@
+/*
+ * 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 androidx.app.slice.widget;
+
+import android.animation.Animator;
+import android.annotation.TargetApi;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.annotation.RestrictTo;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.app.slice.view.R;
+
+/**
+ * Host for the remote input.
+ *
+ * @hide
+ */
+// TODO this should be unified with SystemUI RemoteInputView (b/67527720)
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@TargetApi(23)
+public class RemoteInputView extends LinearLayout implements View.OnClickListener, TextWatcher {
+
+ private static final String TAG = "RemoteInput";
+
+ /**
+ * A marker object that let's us easily find views of this class.
+ */
+ public static final Object VIEW_TAG = new Object();
+
+ private RemoteEditText mEditText;
+ private ImageButton mSendButton;
+ private ProgressBar mProgressBar;
+ private PendingIntent mPendingIntent;
+ private RemoteInput[] mRemoteInputs;
+ private RemoteInput mRemoteInput;
+
+ private int mRevealCx;
+ private int mRevealCy;
+ private int mRevealR;
+ private boolean mResetting;
+
+ public RemoteInputView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mProgressBar = findViewById(R.id.remote_input_progress);
+ mSendButton = findViewById(R.id.remote_input_send);
+ mSendButton.setOnClickListener(this);
+
+ mEditText = (RemoteEditText) getChildAt(0);
+ mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ final boolean isSoftImeEvent = event == null
+ && (actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT
+ || actionId == EditorInfo.IME_ACTION_SEND);
+ final boolean isKeyboardEnterKey = event != null
+ && isConfirmKey(event.getKeyCode())
+ && event.getAction() == KeyEvent.ACTION_DOWN;
+
+ if (isSoftImeEvent || isKeyboardEnterKey) {
+ if (mEditText.length() > 0) {
+ sendRemoteInput();
+ }
+ // Consume action to prevent IME from closing.
+ return true;
+ }
+ return false;
+ }
+ });
+ mEditText.addTextChangedListener(this);
+ mEditText.setInnerFocusable(false);
+ mEditText.mRemoteInputView = this;
+ }
+
+ private void sendRemoteInput() {
+ Bundle results = new Bundle();
+ results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
+ Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent,
+ results);
+
+ mEditText.setEnabled(false);
+ mSendButton.setVisibility(INVISIBLE);
+ mProgressBar.setVisibility(VISIBLE);
+ mEditText.mShowImeOnInputConnection = false;
+
+ // TODO: Figure out API for telling the system about slice interaction.
+ // Tell ShortcutManager that this package has been "activated". ShortcutManager
+ // will reset the throttling for this package.
+ // Strictly speaking, the intent receiver may be different from the intent creator,
+ // 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(),
+ // getContext().getUserId());
+
+ try {
+ mPendingIntent.send(getContext(), 0, fillInIntent);
+ reset();
+ } catch (PendingIntent.CanceledException e) {
+ Log.i(TAG, "Unable to send remote input result", e);
+ Toast.makeText(getContext(), "Failure sending pending intent for inline reply :(",
+ Toast.LENGTH_SHORT).show();
+ reset();
+ }
+ }
+
+ /**
+ * Creates a remote input view.
+ */
+ public static RemoteInputView inflate(Context context, ViewGroup root) {
+ RemoteInputView v = (RemoteInputView) LayoutInflater.from(context).inflate(
+ R.layout.abc_slice_remote_input, root, false);
+ v.setTag(VIEW_TAG);
+ return v;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v == mSendButton) {
+ sendRemoteInput();
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+
+ // We never want for a touch to escape to an outer view or one we covered.
+ return true;
+ }
+
+ private void onDefocus() {
+ setVisibility(INVISIBLE);
+ }
+
+ /**
+ * Set the pending intent for remote input.
+ */
+ public void setPendingIntent(PendingIntent pendingIntent) {
+ mPendingIntent = pendingIntent;
+ }
+
+ /**
+ * Set the remote inputs for this view.
+ */
+ public void setRemoteInput(RemoteInput[] remoteInputs, RemoteInput remoteInput) {
+ mRemoteInputs = remoteInputs;
+ mRemoteInput = remoteInput;
+ mEditText.setHint(mRemoteInput.getLabel());
+ }
+
+ /**
+ * Focuses the remote input view.
+ */
+ public void focusAnimated() {
+ if (getVisibility() != VISIBLE) {
+ Animator animator = ViewAnimationUtils.createCircularReveal(
+ this, mRevealCx, mRevealCy, 0, mRevealR);
+ animator.setDuration(200);
+ animator.start();
+ }
+ focus();
+ }
+
+ private void focus() {
+ setVisibility(VISIBLE);
+ mEditText.setInnerFocusable(true);
+ mEditText.mShowImeOnInputConnection = true;
+ mEditText.setSelection(mEditText.getText().length());
+ mEditText.requestFocus();
+ updateSendButton();
+ }
+
+ private void reset() {
+ mResetting = true;
+
+ mEditText.getText().clear();
+ mEditText.setEnabled(true);
+ mSendButton.setVisibility(VISIBLE);
+ mProgressBar.setVisibility(INVISIBLE);
+ updateSendButton();
+ onDefocus();
+
+ mResetting = false;
+ }
+
+ @Override
+ public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+ if (mResetting && child == mEditText) {
+ // Suppress text events if it happens during resetting. Ideally this would be
+ // suppressed by the text view not being shown, but that doesn't work here because it
+ // needs to stay visible for the animation.
+ return false;
+ }
+ return super.onRequestSendAccessibilityEvent(child, event);
+ }
+
+ private void updateSendButton() {
+ mSendButton.setEnabled(mEditText.getText().length() != 0);
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ updateSendButton();
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public void setRevealParameters(int cx, int cy, int r) {
+ mRevealCx = cx;
+ mRevealCy = cy;
+ mRevealR = r;
+ }
+
+ @Override
+ public void dispatchStartTemporaryDetach() {
+ super.dispatchStartTemporaryDetach();
+ // Detach the EditText temporarily such that it doesn't get onDetachedFromWindow and
+ // won't lose IME focus.
+ detachViewFromParent(mEditText);
+ }
+
+ @Override
+ public void dispatchFinishTemporaryDetach() {
+ if (isAttachedToWindow()) {
+ attachViewToParent(mEditText, 0, mEditText.getLayoutParams());
+ } else {
+ removeDetachedView(mEditText, false /* animate */);
+ }
+ super.dispatchFinishTemporaryDetach();
+ }
+
+ /**
+ * An EditText that changes appearance based on whether it's focusable and becomes un-focusable
+ * whenever the user navigates away from it or it becomes invisible.
+ */
+ public static class RemoteEditText extends EditText {
+
+ private final Drawable mBackground;
+ private RemoteInputView mRemoteInputView;
+ boolean mShowImeOnInputConnection;
+
+ public RemoteEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mBackground = getBackground();
+ }
+
+ private void defocusIfNeeded(boolean animate) {
+ if (mRemoteInputView != null || isTemporarilyDetached()) {
+ if (isTemporarilyDetached()) {
+ // 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.
+ }
+ return;
+ }
+ if (isFocusable() && isEnabled()) {
+ setInnerFocusable(false);
+ if (mRemoteInputView != null) {
+ mRemoteInputView.onDefocus();
+ }
+ mShowImeOnInputConnection = false;
+ }
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+
+ if (!isShown()) {
+ defocusIfNeeded(false /* animate */);
+ }
+ }
+
+ @Override
+ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+ super.onFocusChanged(focused, direction, previouslyFocusedRect);
+ if (!focused) {
+ defocusIfNeeded(true /* animate */);
+ }
+ }
+
+ @Override
+ public void getFocusedRect(Rect r) {
+ super.getFocusedRect(r);
+ r.top = getScrollY();
+ r.bottom = getScrollY() + (getBottom() - getTop());
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ // Eat the DOWN event here to prevent any default behavior.
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ defocusIfNeeded(true /* animate */);
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
+
+ if (mShowImeOnInputConnection && inputConnection != null) {
+ final InputMethodManager imm = getContext().getSystemService(
+ InputMethodManager.class);
+ if (imm != null) {
+ // onCreateInputConnection is called by InputMethodManager in the middle of
+ // setting up the connection to the IME; wait with requesting the IME until that
+ // work has completed.
+ post(new Runnable() {
+ @Override
+ public void run() {
+ imm.viewClicked(RemoteEditText.this);
+ imm.showSoftInput(RemoteEditText.this, 0);
+ }
+ });
+ }
+ }
+
+ return inputConnection;
+ }
+
+ @Override
+ public void onCommitCompletion(CompletionInfo text) {
+ clearComposingText();
+ setText(text.getText());
+ setSelection(getText().length());
+ }
+
+ void setInnerFocusable(boolean focusable) {
+ setFocusableInTouchMode(focusable);
+ setFocusable(focusable);
+ setCursorVisible(focusable);
+
+ if (focusable) {
+ requestFocus();
+ setBackground(mBackground);
+ } else {
+ setBackground(null);
+ }
+
+ }
+ }
+
+ /** Whether key will, by default, trigger a click on the focused view.
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public static final boolean isConfirmKey(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ case KeyEvent.KEYCODE_SPACE:
+ case KeyEvent.KEYCODE_NUMPAD_ENTER:
+ return true;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/RowView.java b/slices/view/src/main/java/androidx/app/slice/widget/RowView.java
new file mode 100644
index 0000000..7753a43
--- /dev/null
+++ b/slices/view/src/main/java/androidx/app/slice/widget/RowView.java
@@ -0,0 +1,469 @@
+/*
+ * 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 androidx.app.slice.widget;
+
+import static android.app.slice.Slice.HINT_LIST;
+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_SELECTED;
+import static android.app.slice.Slice.HINT_TITLE;
+import static android.app.slice.SliceItem.FORMAT_ACTION;
+import static android.app.slice.SliceItem.FORMAT_COLOR;
+import static android.app.slice.SliceItem.FORMAT_IMAGE;
+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.app.slice.core.SliceHints.EXTRA_TOGGLE_STATE;
+
+import android.annotation.TargetApi;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.os.AsyncTask;
+import android.support.annotation.RestrictTo;
+import android.util.Log;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.Switch;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
+import androidx.app.slice.Slice;
+import androidx.app.slice.SliceItem;
+import androidx.app.slice.core.SliceHints;
+import androidx.app.slice.core.SliceQuery;
+import androidx.app.slice.view.R;
+
+/**
+ * Row item is in small template format and can be used to construct list items for use
+ * with {@link LargeTemplateView}.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@TargetApi(23)
+public class RowView extends SliceView.SliceModeView implements
+ LargeSliceAdapter.SliceListView, View.OnClickListener {
+
+ private static final String TAG = "RowView";
+
+ // The number of items that fit on the right hand side of a small slice
+ private static final int MAX_END_ITEMS = 3;
+
+ private int mIconSize;
+ private int mPadding;
+
+ // If this is being used as a small template we don't allow a start item, for list items we do.
+ private boolean mAllowStartItem;
+
+ private LinearLayout mStartContainer;
+ private LinearLayout mContent;
+ private TextView mPrimaryText;
+ private TextView mSecondaryText;
+ private View mDivider;
+ private CompoundButton mToggle;
+ private LinearLayout mEndContainer;
+
+ private SliceItem mColorItem;
+ private SliceItem mRowAction;
+
+ public RowView(Context context) {
+ super(context);
+ mIconSize = getContext().getResources().getDimensionPixelSize(R.dimen.abc_slice_icon_size);
+ mPadding = getContext().getResources().getDimensionPixelSize(R.dimen.abc_slice_padding);
+ inflate(context, R.layout.abc_slice_small_template, this);
+
+ mStartContainer = (LinearLayout) findViewById(R.id.icon_frame);
+ mContent = (LinearLayout) findViewById(android.R.id.content);
+ mPrimaryText = (TextView) findViewById(android.R.id.title);
+ mSecondaryText = (TextView) findViewById(android.R.id.summary);
+ mDivider = findViewById(R.id.divider);
+ mEndContainer = (LinearLayout) findViewById(android.R.id.widget_frame);
+ }
+
+ @Override
+ public @SliceView.SliceMode int getMode() {
+ return SliceView.MODE_SMALL;
+ }
+
+ @Override
+ public void setColor(SliceItem color) {
+ mColorItem = color;
+ }
+
+ /**
+ * This is called when RowView is being used as a component in a large template.
+ */
+ @Override
+ public void setSliceItem(SliceItem slice, boolean isHeader) {
+ mAllowStartItem = !isHeader; // Headers don't show start items
+ populateViews(slice, slice);
+ }
+
+ /**
+ * This is called when RowView is being used as a small template.
+ */
+ @Override
+ public void setSlice(Slice slice) {
+ mAllowStartItem = false;
+ Slice.Builder sb = new Slice.Builder(slice.getUri());
+ sb.addSubSlice(slice);
+ Slice parentSlice = sb.build();
+ populateViews(parentSlice.getItems().get(0), getHeaderItem(slice));
+ }
+
+ private SliceItem getHeaderItem(Slice slice) {
+ List<SliceItem> items = slice.getItems();
+ // See if a header is specified
+ SliceItem header = SliceQuery.find(slice, FORMAT_SLICE, null, HINT_LIST_ITEM);
+ if (header != null) {
+ return header;
+ }
+ // Otherwise use the first non-color item and use it if it's a slice
+ SliceItem firstSlice = null;
+ for (int i = 0; i < items.size(); i++) {
+ if (!FORMAT_COLOR.equals(items.get(i).getFormat())) {
+ firstSlice = items.get(i);
+ break;
+ }
+ }
+ if (firstSlice != null && FORMAT_SLICE.equals(firstSlice.getFormat())) {
+ // Check if this slice is appropriate to use to populate small template
+ if (firstSlice.hasHint(HINT_LIST)) {
+ // Check for header, use that if it exists
+ SliceItem listHeader = SliceQuery.find(firstSlice, FORMAT_SLICE,
+ null,
+ new String[] {
+ HINT_LIST_ITEM, HINT_LIST
+ });
+ if (listHeader != null) {
+ return SliceQuery.findFirstSlice(listHeader);
+ } else {
+ // Otherwise use the first list item
+ SliceItem newFirst = firstSlice.getSlice().getItems().get(0);
+ return SliceQuery.findFirstSlice(newFirst);
+ }
+ } else {
+ // Not a list, find first slice with non-slice children
+ return SliceQuery.findFirstSlice(firstSlice);
+ }
+ }
+ // Fallback, just use this and convert to SliceItem type slice
+ Slice.Builder sb = new Slice.Builder(slice.getUri());
+ Slice s = sb.addSubSlice(slice).build();
+ return s.getItems().get(0);
+ }
+
+ @TargetApi(24)
+ private void populateViews(SliceItem fullSlice, SliceItem sliceItem) {
+ resetViews();
+ ArrayList<SliceItem> items = new ArrayList<>();
+ if (FORMAT_SLICE.equals(sliceItem.getFormat())) {
+ items = new ArrayList<>(sliceItem.getSlice().getItems());
+ } else {
+ items.add(sliceItem);
+ }
+
+ // These are the things that can go in our small template
+ SliceItem startItem = null;
+ SliceItem titleItem = null;
+ SliceItem subTitle = null;
+ ArrayList<SliceItem> endItems = new ArrayList<>();
+
+ // If the first item is an action let's check if it should be used to populate the content
+ // or if it should be in the start position.
+ SliceItem firstSlice = items.size() > 0 ? items.get(0) : null;
+ if (firstSlice != null && FORMAT_ACTION.equals(firstSlice.getFormat())) {
+ if (!SliceQuery.isSimpleAction(firstSlice)) {
+ mRowAction = firstSlice;
+ items.remove(0);
+ // Populating with first action, bias to use slice associated with this action
+ items.addAll(0, mRowAction.getSlice().getItems());
+ }
+ }
+
+ // Look through our items and try to figure out main content
+ for (int i = 0; i < items.size(); i++) {
+ SliceItem item = items.get(i);
+ List<String> hints = item.getHints();
+ String itemType = item.getFormat();
+ if (i == 0 && SliceQuery.isStartType((item))) {
+ startItem = item;
+ } else if (hints.contains(HINT_TITLE)) {
+ // Things with these hints could go in the title / start position
+ if ((startItem == null || !startItem.hasHint(HINT_TITLE))
+ && SliceQuery.isStartType(item)) {
+ startItem = item;
+ } else if ((titleItem == null || !titleItem.hasHint(HINT_TITLE))
+ && FORMAT_TEXT.equals(itemType)) {
+ titleItem = item;
+ } else {
+ endItems.add(item);
+ }
+ } else if (FORMAT_TEXT.equals(item.getFormat())) {
+ if (titleItem == null) {
+ titleItem = item;
+ } else if (subTitle == null) {
+ subTitle = item;
+ } else {
+ endItems.add(item);
+ }
+ } else if (FORMAT_SLICE.equals(item.getFormat())) {
+ List<SliceItem> subItems = item.getSlice().getItems();
+ for (int j = 0; j < subItems.size(); j++) {
+ endItems.add(subItems.get(j));
+ }
+ } else {
+ endItems.add(item);
+ }
+ }
+
+ SliceItem colorItem = SliceQuery.find(fullSlice, FORMAT_COLOR);
+ int color = colorItem != null
+ ? colorItem.getColor()
+ : (mColorItem != null)
+ ? mColorItem.getColor()
+ : -1;
+ // Populate main part of the template
+ if (startItem != null) {
+ if (mAllowStartItem) {
+ startItem = addItem(startItem, color, mStartContainer, 0 /* padding */)
+ ? startItem
+ : null;
+ if (startItem != null) {
+ endItems.remove(startItem);
+ }
+ } else {
+ startItem = null;
+ endItems.add(0, startItem);
+ }
+ }
+ mStartContainer.setVisibility(startItem != null ? View.VISIBLE : View.GONE);
+ if (titleItem != null) {
+ mPrimaryText.setText(titleItem.getText());
+ }
+ mPrimaryText.setVisibility(titleItem != null ? View.VISIBLE : View.GONE);
+ if (subTitle != null) {
+ mSecondaryText.setText(subTitle.getText());
+ }
+ mSecondaryText.setVisibility(subTitle != null ? View.VISIBLE : View.GONE);
+
+ // Figure out what end items we're showing
+ // If we're showing an action in this row check if it's a toggle
+ if (mRowAction != null && SliceQuery.hasHints(mRowAction.getSlice(),
+ SliceHints.SUBTYPE_TOGGLE) && addToggle(mRowAction, color)) {
+ // Can't show more end actions if we have a toggle so we're done
+ makeClickable(this);
+ return;
+ }
+ // Check if we have a toggle somewhere in our end items
+ SliceItem toggleItem = endItems.stream()
+ .filter(new Predicate<SliceItem>() {
+ @Override
+ public boolean test(SliceItem item) {
+ if (item == null) {
+ return false;
+ }
+ return FORMAT_ACTION.equals(item.getFormat())
+ && SliceQuery.hasHints(item.getSlice(), SliceHints.SUBTYPE_TOGGLE);
+ }
+ })
+ .findFirst().orElse(null);
+ if (toggleItem != null) {
+ if (addToggle(toggleItem, color)) {
+ mDivider.setVisibility(mRowAction != null ? View.VISIBLE : View.GONE);
+ makeClickable(mRowAction != null ? mContent : this);
+ // Can't show more end actions if we have a toggle so we're done
+ return;
+ }
+ }
+ boolean clickableEndItem = false;
+ int itemCount = 0;
+ for (int i = 0; i < endItems.size(); i++) {
+ SliceItem item = endItems.get(i);
+ if (itemCount <= MAX_END_ITEMS) {
+ if (FORMAT_ACTION.equals(item.getFormat())
+ && itemCount == 0
+ && SliceQuery.hasHints(item.getSlice(), SliceHints.SUBTYPE_TOGGLE)
+ && addToggle(item, color)) {
+ // If a toggle is added we're done
+ break;
+ } else if (addItem(item, color, mEndContainer, mPadding)) {
+ itemCount++;
+ }
+ }
+ }
+ if (mRowAction != null) {
+ makeClickable(clickableEndItem ? mContent : this);
+ }
+ }
+
+ /**
+ * @return Whether a toggle was added.
+ */
+ private boolean addToggle(final SliceItem toggleItem, int color) {
+ if (!FORMAT_ACTION.equals(toggleItem.getFormat())
+ || !SliceQuery.hasHints(toggleItem.getSlice(), SliceHints.SUBTYPE_TOGGLE)) {
+ return false;
+ }
+
+ // Check if this is a custom toggle
+ Icon checkedIcon = null;
+ List<SliceItem> sliceItems = toggleItem.getSlice().getItems();
+ if (sliceItems.size() > 0) {
+ checkedIcon = FORMAT_IMAGE.equals(sliceItems.get(0).getFormat())
+ ? sliceItems.get(0).getIcon()
+ : null;
+ }
+ if (checkedIcon != null) {
+ if (color != -1) {
+ // TODO - Should these be tinted? What if the app wants diff colors per state?
+ checkedIcon.setTint(color);
+ }
+ mToggle = new ToggleButton(getContext());
+ ((ToggleButton) mToggle).setTextOff("");
+ ((ToggleButton) mToggle).setTextOn("");
+ mToggle.setBackground(checkedIcon.loadDrawable(getContext()));
+ mEndContainer.addView(mToggle);
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mToggle.getLayoutParams();
+ lp.width = mIconSize;
+ lp.height = mIconSize;
+ } else {
+ mToggle = new Switch(getContext());
+ mEndContainer.addView(mToggle);
+ }
+ mToggle.setChecked(SliceQuery.hasHints(toggleItem.getSlice(), HINT_SELECTED));
+ mToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ try {
+ PendingIntent pi = toggleItem.getAction();
+ Intent i = new Intent().putExtra(EXTRA_TOGGLE_STATE, isChecked);
+ pi.send(getContext(), 0, i, null, null);
+ } catch (CanceledException e) {
+ mToggle.setSelected(!isChecked);
+ }
+ }
+ });
+ return true;
+ }
+
+ /**
+ * Adds simple items to a container. Simple items include actions with icons, images, or
+ * timestamps.
+ *
+ * @return Whether an item was added to the view.
+ */
+ private boolean addItem(SliceItem sliceItem, int color, LinearLayout container, int padding) {
+ SliceItem image = null;
+ SliceItem action = null;
+ SliceItem timeStamp = null;
+ if (FORMAT_ACTION.equals(sliceItem.getFormat())
+ && !sliceItem.hasHint(SliceHints.SUBTYPE_TOGGLE)) {
+ image = SliceQuery.find(sliceItem.getSlice(), FORMAT_IMAGE);
+ timeStamp = SliceQuery.find(sliceItem.getSlice(), FORMAT_TIMESTAMP);
+ action = sliceItem;
+ } else if (FORMAT_IMAGE.equals(sliceItem.getFormat())) {
+ image = sliceItem;
+ } else if (FORMAT_TIMESTAMP.equals(sliceItem.getFormat())) {
+ timeStamp = sliceItem;
+ }
+ View addedView = null;
+ if (image != null) {
+ ImageView iv = new ImageView(getContext());
+ iv.setImageIcon(image.getIcon());
+ if (color != -1 && !sliceItem.hasHint(HINT_NO_TINT)) {
+ iv.setColorFilter(color);
+ }
+ container.addView(iv);
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) iv.getLayoutParams();
+ lp.width = mIconSize;
+ lp.height = mIconSize;
+ lp.setMarginStart(padding);
+ addedView = iv;
+ } else if (timeStamp != null) {
+ TextView tv = new TextView(getContext());
+ tv.setText(SliceViewUtil.getRelativeTimeString(sliceItem.getTimestamp()));
+ container.addView(tv);
+ addedView = tv;
+ }
+ if (action != null && addedView != null) {
+ final SliceItem sliceAction = action;
+ addedView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ AsyncTask.execute(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ sliceAction.getAction().send();
+ } catch (CanceledException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+ });
+ addedView.setBackground(SliceViewUtil.getDrawable(getContext(),
+ android.R.attr.selectableItemBackground));
+ }
+ return addedView != null;
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (mRowAction != null && FORMAT_ACTION.equals(mRowAction.getFormat())) {
+ // Check for a row action
+ AsyncTask.execute(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mRowAction.getAction().send();
+ } catch (CanceledException e) {
+ Log.w(TAG, "PendingIntent for slice cannot be sent", e);
+ }
+ }
+ });
+ } else if (mToggle != null) {
+ // Or no row action so let's just toggle if we've got one
+ mToggle.toggle();
+ }
+ }
+
+ private void makeClickable(View layout) {
+ layout.setOnClickListener(this);
+ layout.setBackground(SliceViewUtil.getDrawable(getContext(),
+ android.R.attr.selectableItemBackground));
+ }
+
+ private void resetViews() {
+ mStartContainer.removeAllViews();
+ mEndContainer.removeAllViews();
+ mPrimaryText.setText(null);
+ mSecondaryText.setText(null);
+ mDivider.setVisibility(View.GONE);
+ }
+}
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/ShortcutView.java b/slices/view/src/main/java/androidx/app/slice/widget/ShortcutView.java
new file mode 100644
index 0000000..f93abc6
--- /dev/null
+++ b/slices/view/src/main/java/androidx/app/slice/widget/ShortcutView.java
@@ -0,0 +1,187 @@
+/*
+ * 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 androidx.app.slice.widget;
+
+import static android.app.slice.Slice.HINT_LARGE;
+import static android.app.slice.Slice.HINT_TITLE;
+import static android.app.slice.Slice.SUBTYPE_SOURCE;
+import static android.app.slice.SliceItem.FORMAT_ACTION;
+import static android.app.slice.SliceItem.FORMAT_COLOR;
+import static android.app.slice.SliceItem.FORMAT_IMAGE;
+import static android.app.slice.SliceItem.FORMAT_TEXT;
+
+import android.annotation.TargetApi;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.OvalShape;
+import android.net.Uri;
+import android.support.annotation.RestrictTo;
+
+import androidx.app.slice.Slice;
+import androidx.app.slice.SliceItem;
+import androidx.app.slice.core.SliceQuery;
+import androidx.app.slice.view.R;
+
+/**
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@TargetApi(23)
+public class ShortcutView extends androidx.app.slice.widget.SliceView.SliceModeView {
+
+ private static final String TAG = "ShortcutView";
+
+ private Uri mUri;
+ private PendingIntent mAction;
+ private SliceItem mLabel;
+ private SliceItem mIcon;
+
+ private int mLargeIconSize;
+ private int mSmallIconSize;
+
+ public ShortcutView(Context context) {
+ super(context);
+ final Resources res = getResources();
+ mSmallIconSize = res.getDimensionPixelSize(R.dimen.abc_slice_icon_size);
+ mLargeIconSize = res.getDimensionPixelSize(R.dimen.abc_slice_shortcut_size);
+ }
+
+ @Override
+ public void setSlice(Slice slice) {
+ mLabel = null;
+ mIcon = null;
+ mAction = null;
+ removeAllViews();
+ determineShortcutItems(getContext(), slice);
+ SliceItem colorItem = SliceQuery.find(slice, FORMAT_COLOR);
+ if (colorItem == null) {
+ colorItem = SliceQuery.find(slice, FORMAT_COLOR);
+ }
+ // TODO: pick better default colour
+ final int color = colorItem != null ? colorItem.getColor() : Color.GRAY;
+ ShapeDrawable circle = new ShapeDrawable(new OvalShape());
+ circle.setTint(color);
+ setBackground(circle);
+ if (mIcon != null) {
+ final boolean isLarge = mIcon.hasHint(HINT_LARGE)
+ || SUBTYPE_SOURCE.equals(mIcon.getSubType());
+ final int iconSize = isLarge ? mLargeIconSize : mSmallIconSize;
+ SliceViewUtil.createCircledIcon(getContext(), color, iconSize, mIcon.getIcon(),
+ isLarge, this /* parent */);
+ mUri = slice.getUri();
+ setClickable(true);
+ } else {
+ setClickable(false);
+ }
+ }
+
+ @Override
+ public @SliceView.SliceMode int getMode() {
+ return SliceView.MODE_SHORTCUT;
+ }
+
+ @Override
+ public boolean performClick() {
+ if (!callOnClick()) {
+ try {
+ if (mAction != null) {
+ mAction.send();
+ } else {
+ Intent intent = new Intent(Intent.ACTION_VIEW).setData(mUri);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ getContext().startActivity(intent);
+ }
+ } catch (CanceledException e) {
+ e.printStackTrace();
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Looks at the slice and determines which items are best to use to compose the shortcut.
+ */
+ private void determineShortcutItems(Context context, Slice slice) {
+ SliceItem titleItem = SliceQuery.find(slice, FORMAT_ACTION,
+ HINT_TITLE, null);
+
+ if (titleItem != null) {
+ // Preferred case: hinted action containing hinted image and text
+ mAction = titleItem.getAction();
+ mIcon = SliceQuery.find(titleItem.getSlice(), FORMAT_IMAGE, HINT_TITLE,
+ null);
+ mLabel = SliceQuery.find(titleItem.getSlice(), FORMAT_TEXT, HINT_TITLE,
+ null);
+ } else {
+ // No hinted action; just use the first one
+ SliceItem actionItem = SliceQuery.find(slice, FORMAT_ACTION, (String) null,
+ null);
+ mAction = (actionItem != null) ? actionItem.getAction() : null;
+ }
+ // First fallback: any hinted image and text
+ if (mIcon == null) {
+ mIcon = SliceQuery.find(slice, FORMAT_IMAGE, HINT_TITLE,
+ null);
+ }
+ if (mLabel == null) {
+ mLabel = SliceQuery.find(slice, FORMAT_TEXT, HINT_TITLE,
+ null);
+ }
+ // Second fallback: first image and text
+ if (mIcon == null) {
+ mIcon = SliceQuery.find(slice, FORMAT_IMAGE, (String) null,
+ null);
+ }
+ if (mLabel == null) {
+ mLabel = SliceQuery.find(slice, FORMAT_TEXT, (String) null,
+ null);
+ }
+ // Final fallback: use app info
+ if (mIcon == null || mLabel == null || mAction == null) {
+ PackageManager pm = context.getPackageManager();
+ ProviderInfo providerInfo = pm.resolveContentProvider(
+ slice.getUri().getAuthority(), 0);
+ ApplicationInfo appInfo = providerInfo.applicationInfo;
+ if (appInfo != null) {
+ if (mIcon == null) {
+ Slice.Builder sb = new Slice.Builder(slice.getUri());
+ Drawable icon = pm.getApplicationIcon(appInfo);
+ sb.addIcon(SliceViewUtil.createIconFromDrawable(icon), HINT_LARGE);
+ mIcon = sb.build().getItems().get(0);
+ }
+ if (mLabel == null) {
+ Slice.Builder sb = new Slice.Builder(slice.getUri());
+ sb.addText(pm.getApplicationLabel(appInfo), null);
+ mLabel = sb.build().getItems().get(0);
+ }
+ if (mAction == null) {
+ mAction = PendingIntent.getActivity(context, 0,
+ pm.getLaunchIntentForPackage(appInfo.packageName), 0);
+ }
+ }
+ }
+ }
+}
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/SliceLiveData.java b/slices/view/src/main/java/androidx/app/slice/widget/SliceLiveData.java
new file mode 100644
index 0000000..9b36ee1
--- /dev/null
+++ b/slices/view/src/main/java/androidx/app/slice/widget/SliceLiveData.java
@@ -0,0 +1,111 @@
+/*
+ * 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 androidx.app.slice.widget;
+
+import android.arch.lifecycle.LiveData;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.support.annotation.NonNull;
+
+import androidx.app.slice.Slice;
+
+/**
+ * Class with factory methods for creating LiveData that observes slices.
+ *
+ * @see #fromUri(Context, Uri)
+ * @see LiveData
+ */
+public final class SliceLiveData {
+
+ /**
+ * 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}).
+ */
+ public static LiveData<Slice> fromUri(Context context, Uri uri) {
+ return new SliceLiveDataImpl(context.getApplicationContext(), uri);
+ }
+
+ /**
+ * 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}).
+ */
+ public static LiveData<Slice> fromIntent(@NonNull Context context, @NonNull Intent intent) {
+ return new SliceLiveDataImpl(context.getApplicationContext(), intent);
+ }
+
+ private static class SliceLiveDataImpl extends LiveData<Slice> {
+ private final Context mContext;
+ private final Intent mIntent;
+ private Uri mUri;
+
+ private SliceLiveDataImpl(Context context, Uri uri) {
+ super();
+ mContext = context;
+ mUri = uri;
+ mIntent = null;
+ // TODO: Check if uri points at a Slice?
+ }
+
+ private SliceLiveDataImpl(Context context, Intent intent) {
+ super();
+ mContext = context;
+ mUri = null;
+ mIntent = intent;
+ }
+
+ @Override
+ protected void onActive() {
+ AsyncTask.execute(mUpdateSlice);
+ if (mUri != null) {
+ mContext.getContentResolver().registerContentObserver(mUri, false, mObserver);
+ }
+ }
+
+ @Override
+ protected void onInactive() {
+ if (mUri != null) {
+ mContext.getContentResolver().unregisterContentObserver(mObserver);
+ }
+ }
+
+ private final Runnable mUpdateSlice = new Runnable() {
+ @Override
+ public void run() {
+ Slice s = mUri != null ? Slice.bindSlice(mContext, mUri)
+ : Slice.bindSlice(mContext, mIntent);
+ if (mUri == null && s != null) {
+ mContext.getContentResolver().registerContentObserver(s.getUri(),
+ false, mObserver);
+ mUri = s.getUri();
+ }
+ postValue(s);
+ }
+ };
+
+ private final ContentObserver mObserver = new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ AsyncTask.execute(mUpdateSlice);
+ }
+ };
+ }
+}
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/SliceView.java b/slices/view/src/main/java/androidx/app/slice/widget/SliceView.java
new file mode 100644
index 0000000..cde1215
--- /dev/null
+++ b/slices/view/src/main/java/androidx/app/slice/widget/SliceView.java
@@ -0,0 +1,319 @@
+/*
+ * 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 androidx.app.slice.widget;
+
+import static android.app.slice.Slice.HINT_ACTIONS;
+import static android.app.slice.SliceItem.FORMAT_COLOR;
+import static android.app.slice.SliceItem.FORMAT_SLICE;
+
+import android.arch.lifecycle.Observer;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
+import android.support.annotation.IntDef;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import java.util.List;
+
+import androidx.app.slice.Slice;
+import androidx.app.slice.SliceItem;
+import androidx.app.slice.core.SliceQuery;
+import androidx.app.slice.view.R;
+
+/**
+ * A view for displaying a {@link Slice} which is a piece of app content and actions. SliceView is
+ * able to present slice content in a templated format outside of the associated app. The way this
+ * content is displayed depends on the structure of the slice, the hints associated with the
+ * content, and the mode that SliceView is configured for. The modes that SliceView supports are:
+ * <ul>
+ * <li><b>Shortcut</b>: A shortcut is presented as an icon and a text label representing the main
+ * content or action associated with the slice.</li>
+ * <li><b>Small</b>: The small format has a restricted height and can present a single
+ * {@link SliceItem} or a limited collection of items.</li>
+ * <li><b>Large</b>: The large format displays multiple small templates in a list, if scrolling is
+ * not enabled (see {@link #setScrollable(boolean)}) the view will show as many items as it can
+ * comfortably fit.</li>
+ * </ul>
+ * <p>
+ * When constructing a slice, the contents of it can be annotated with hints, these provide the OS
+ * with some information on how the content should be displayed. For example, text annotated with
+ * {@link android.app.slice.Slice#HINT_TITLE} would be placed in the title position of a template.
+ * A slice annotated with {@link android.app.slice.Slice#HINT_LIST} would present the child items
+ * of that slice in a list.
+ * <p>
+ * Example usage:
+ *
+ * <pre class="prettyprint">
+ * SliceView v = new SliceView(getContext());
+ * v.setMode(desiredMode);
+ * LiveData<Slice> liveData = SliceLiveData.fromUri(sliceUri);
+ * liveData.observe(lifecycleOwner, v);
+ * </pre>
+ * @see SliceLiveData
+ */
+public class SliceView extends ViewGroup implements Observer<Slice> {
+
+ private static final String TAG = "SliceView";
+
+ /**
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public abstract static class SliceModeView extends FrameLayout {
+
+ public SliceModeView(Context context) {
+ super(context);
+ }
+
+ /**
+ * @return the mode of the slice being presented.
+ */
+ public abstract int getMode();
+
+ /**
+ * @param slice the slice to show in this view.
+ */
+ public abstract void setSlice(Slice slice);
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @IntDef({
+ MODE_SMALL, MODE_LARGE, MODE_SHORTCUT
+ })
+ public @interface SliceMode {}
+
+ /**
+ * Mode indicating this slice should be presented in small template format.
+ */
+ public static final int MODE_SMALL = 1;
+ /**
+ * Mode indicating this slice should be presented in large template format.
+ */
+ public static final int MODE_LARGE = 2;
+ /**
+ * Mode indicating this slice should be presented as an icon. A shortcut requires an intent,
+ * icon, and label. This can be indicated by using {@link android.app.slice.Slice#HINT_TITLE}
+ * on an action in a slice.
+ */
+ public static final int MODE_SHORTCUT = 3;
+
+ /**
+ * Will select the type of slice binding based on size of the View. TODO: Put in some info about
+ * that selection.
+ */
+ private static final int MODE_AUTO = 0;
+
+ private int mMode = MODE_AUTO;
+ private SliceModeView mCurrentView;
+ private final ActionRow mActions;
+ private Slice mCurrentSlice;
+ private boolean mShowActions = true;
+ private boolean mIsScrollable;
+ private final int mShortcutSize;
+
+ public SliceView(Context context) {
+ this(context, null);
+ }
+
+ public SliceView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SliceView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public SliceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ mActions = new ActionRow(getContext(), true);
+ mActions.setBackground(new ColorDrawable(0xffeeeeee));
+ mCurrentView = new LargeTemplateView(getContext());
+ addView(mCurrentView, getChildLp(mCurrentView));
+ addView(mActions, getChildLp(mActions));
+ mShortcutSize = getContext().getResources()
+ .getDimensionPixelSize(R.dimen.abc_slice_shortcut_size);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int mode = MeasureSpec.getMode(widthMeasureSpec);
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ if (MODE_SHORTCUT == mMode) {
+ width = mShortcutSize;
+ }
+ if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.UNSPECIFIED) {
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
+ }
+ measureChildren(widthMeasureSpec, heightMeasureSpec);
+ int actionHeight = mActions.getVisibility() != View.GONE
+ ? mActions.getMeasuredHeight()
+ : 0;
+ int newHeightSpec = MeasureSpec.makeMeasureSpec(
+ mCurrentView.getMeasuredHeight() + actionHeight, MeasureSpec.EXACTLY);
+ setMeasuredDimension(width, newHeightSpec);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ mCurrentView.layout(0, 0, mCurrentView.getMeasuredWidth(),
+ mCurrentView.getMeasuredHeight());
+ if (mActions.getVisibility() != View.GONE) {
+ mActions.layout(0, mCurrentView.getMeasuredHeight(), mActions.getMeasuredWidth(),
+ mCurrentView.getMeasuredHeight() + mActions.getMeasuredHeight());
+ }
+ }
+
+ @Override
+ public void onChanged(@Nullable Slice slice) {
+ setSlice(slice);
+ }
+
+ /**
+ * Populates this view to the provided {@link Slice}.
+ *
+ * This will not update automatically if the slice content changes, for live
+ * content see {@link SliceLiveData}.
+ */
+ public void setSlice(@Nullable Slice slice) {
+ mCurrentSlice = slice;
+ reinflate();
+ }
+
+ /**
+ * Set the mode this view should present in.
+ */
+ public void setMode(@SliceMode int mode) {
+ setMode(mode, false /* animate */);
+ }
+
+ /**
+ * Set whether this view should allow scrollable content when presenting in {@link #MODE_LARGE}.
+ */
+ public void setScrollable(boolean isScrollable) {
+ mIsScrollable = isScrollable;
+ reinflate();
+ }
+
+ /**
+ * @hide
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public void setMode(@SliceMode int mode, boolean animate) {
+ if (animate) {
+ Log.e(TAG, "Animation not supported yet");
+ }
+ mMode = mode;
+ reinflate();
+ }
+
+ /**
+ * @return the mode this view is presenting in.
+ */
+ public @SliceMode int getMode() {
+ if (mMode == MODE_AUTO) {
+ return MODE_LARGE;
+ }
+ return mMode;
+ }
+
+ /**
+ * @hide
+ *
+ * Whether this view should show a row of actions with it.
+ */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public void setShowActionRow(boolean show) {
+ mShowActions = show;
+ reinflate();
+ }
+
+ private SliceModeView createView(int mode) {
+ switch (mode) {
+ case MODE_SHORTCUT:
+ return new ShortcutView(getContext());
+ case MODE_SMALL:
+ return new RowView(getContext());
+ }
+ return new LargeTemplateView(getContext());
+ }
+
+ private void reinflate() {
+ if (mCurrentSlice == null) {
+ return;
+ }
+ // TODO: Smarter mapping here from one state to the next.
+ SliceItem color = SliceQuery.find(mCurrentSlice, FORMAT_COLOR);
+ List<SliceItem> items = mCurrentSlice.getItems();
+ SliceItem actionRow = SliceQuery.find(mCurrentSlice, FORMAT_SLICE,
+ HINT_ACTIONS,
+ null);
+ int mode = getMode();
+ if (mMode == mCurrentView.getMode()) {
+ mCurrentView.setSlice(mCurrentSlice);
+ } else {
+ removeAllViews();
+ mCurrentView = createView(mode);
+ addView(mCurrentView, getChildLp(mCurrentView));
+ addView(mActions, getChildLp(mActions));
+ }
+ if (mode == MODE_LARGE) {
+ ((LargeTemplateView) mCurrentView).setScrollable(mIsScrollable);
+ }
+ if (items.size() > 1 || (items.size() != 0 && items.get(0) != actionRow)) {
+ mCurrentView.setVisibility(View.VISIBLE);
+ mCurrentView.setSlice(mCurrentSlice);
+ } else {
+ mCurrentView.setVisibility(View.GONE);
+ }
+
+ boolean showActions = mShowActions && actionRow != null
+ && mode != MODE_SHORTCUT;
+ if (showActions) {
+ mActions.setActions(actionRow, color);
+ mActions.setVisibility(View.VISIBLE);
+ } else {
+ mActions.setVisibility(View.GONE);
+ }
+ }
+
+ private LayoutParams getChildLp(View child) {
+ if (child instanceof ShortcutView) {
+ return new LayoutParams(mShortcutSize, mShortcutSize);
+ } else {
+ return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ }
+ }
+
+ private static void validate(Uri sliceUri) {
+ if (!ContentResolver.SCHEME_CONTENT.equals(sliceUri.getScheme())) {
+ throw new RuntimeException("Invalid uri " + sliceUri);
+ }
+ if (sliceUri.getPathSegments().size() == 0) {
+ throw new RuntimeException("Invalid uri " + sliceUri);
+ }
+ }
+}
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/SliceViewUtil.java b/slices/view/src/main/java/androidx/app/slice/widget/SliceViewUtil.java
new file mode 100644
index 0000000..c98215f
--- /dev/null
+++ b/slices/view/src/main/java/androidx/app/slice/widget/SliceViewUtil.java
@@ -0,0 +1,202 @@
+/*
+ * 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 androidx.app.slice.widget;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.support.annotation.AttrRes;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.text.format.DateUtils;
+import android.view.Gravity;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import java.util.Calendar;
+
+/**
+ * A bunch of utilities for slice UI.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@TargetApi(23)
+public class SliceViewUtil {
+
+ /**
+ */
+ @ColorInt
+ public static int getColorAccent(@NonNull Context context) {
+ return getColorAttr(context, android.R.attr.colorAccent);
+ }
+
+ /**
+ */
+ @ColorInt
+ public static int getColorError(@NonNull Context context) {
+ return getColorAttr(context, android.R.attr.colorError);
+ }
+
+ /**
+ */
+ @ColorInt
+ public static int getDefaultColor(@NonNull Context context, int resId) {
+ final ColorStateList list = context.getResources().getColorStateList(resId,
+ context.getTheme());
+
+ return list.getDefaultColor();
+ }
+
+ /**
+ */
+ @ColorInt
+ public static int getDisabled(@NonNull Context context, int inputColor) {
+ return applyAlphaAttr(context, android.R.attr.disabledAlpha, inputColor);
+ }
+
+ /**
+ */
+ @ColorInt
+ public static int applyAlphaAttr(@NonNull Context context, @AttrRes int attr, int inputColor) {
+ TypedArray ta = context.obtainStyledAttributes(new int[] {
+ attr
+ });
+ float alpha = ta.getFloat(0, 0);
+ ta.recycle();
+ return applyAlpha(alpha, inputColor);
+ }
+
+ /**
+ */
+ @ColorInt
+ public static int applyAlpha(float alpha, int inputColor) {
+ alpha *= Color.alpha(inputColor);
+ return Color.argb((int) (alpha), Color.red(inputColor), Color.green(inputColor),
+ Color.blue(inputColor));
+ }
+
+ /**
+ */
+ @ColorInt
+ public static int getColorAttr(@NonNull Context context, @AttrRes int attr) {
+ TypedArray ta = context.obtainStyledAttributes(new int[] {
+ attr
+ });
+ @ColorInt int colorAccent = ta.getColor(0, 0);
+ ta.recycle();
+ return colorAccent;
+ }
+
+ /**
+ */
+ public static int getThemeAttr(@NonNull Context context, @AttrRes int attr) {
+ TypedArray ta = context.obtainStyledAttributes(new int[] {
+ attr
+ });
+ int theme = ta.getResourceId(0, 0);
+ ta.recycle();
+ return theme;
+ }
+
+ /**
+ */
+ public static Drawable getDrawable(@NonNull Context context, @AttrRes int attr) {
+ TypedArray ta = context.obtainStyledAttributes(new int[] {
+ attr
+ });
+ Drawable drawable = ta.getDrawable(0);
+ ta.recycle();
+ return drawable;
+ }
+
+ /**
+ */
+ public static Icon createIconFromDrawable(Drawable d) {
+ if (d instanceof BitmapDrawable) {
+ return Icon.createWithBitmap(((BitmapDrawable) d).getBitmap());
+ }
+ Bitmap b = Bitmap.createBitmap(d.getIntrinsicWidth(), d.getIntrinsicHeight(),
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(b);
+ d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ d.draw(canvas);
+ return Icon.createWithBitmap(b);
+ }
+
+ /**
+ */
+ @TargetApi(28)
+ public static void createCircledIcon(@NonNull Context context, int color, int iconSizePx,
+ Icon icon, boolean isLarge, ViewGroup parent) {
+ ImageView v = new ImageView(context);
+ v.setImageIcon(icon);
+ parent.addView(v);
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams();
+ if (isLarge) {
+ // XXX better way to convert from icon -> bitmap or crop an icon (?)
+ Bitmap iconBm = Bitmap.createBitmap(iconSizePx, iconSizePx, Config.ARGB_8888);
+ Canvas iconCanvas = new Canvas(iconBm);
+ v.layout(0, 0, iconSizePx, iconSizePx);
+ v.draw(iconCanvas);
+ v.setImageBitmap(getCircularBitmap(iconBm));
+ } else {
+ v.setColorFilter(Color.WHITE);
+ }
+ lp.width = iconSizePx;
+ lp.height = iconSizePx;
+ lp.gravity = Gravity.CENTER;
+ }
+
+ /**
+ */
+ public static @NonNull Bitmap getCircularBitmap(Bitmap bitmap) {
+ Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
+ bitmap.getHeight(), Config.ARGB_8888);
+ Canvas canvas = new Canvas(output);
+ final Paint paint = new Paint();
+ final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ paint.setAntiAlias(true);
+ canvas.drawARGB(0, 0, 0, 0);
+ canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2,
+ bitmap.getWidth() / 2, paint);
+ paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+ canvas.drawBitmap(bitmap, rect, rect, paint);
+ return output;
+ }
+
+ /**
+ */
+ public static CharSequence getRelativeTimeString(long time) {
+ return DateUtils.getRelativeTimeSpanString(time, Calendar.getInstance().getTimeInMillis(),
+ DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE);
+ }
+}
diff --git a/slices/view/src/main/res/drawable/abc_ic_slice_send.xml b/slices/view/src/main/res/drawable/abc_ic_slice_send.xml
new file mode 100644
index 0000000..9c18ac8
--- /dev/null
+++ b/slices/view/src/main/res/drawable/abc_ic_slice_send.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4.02,42.0L46.0,24.0 4.02,6.0 4.0,20.0l30.0,4.0 -30.0,4.0z"/>
+</vector>
\ No newline at end of file
diff --git a/slices/view/src/main/res/drawable/abc_slice_remote_input_bg.xml b/slices/view/src/main/res/drawable/abc_slice_remote_input_bg.xml
new file mode 100644
index 0000000..64ac7bf
--- /dev/null
+++ b/slices/view/src/main/res/drawable/abc_slice_remote_input_bg.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#ff6c6c6c" />
+ <corners
+ android:bottomRightRadius="16dp"
+ android:bottomLeftRadius="16dp"/>
+</shape>
diff --git a/slices/view/src/main/res/drawable/abc_slice_ripple_drawable.xml b/slices/view/src/main/res/drawable/abc_slice_ripple_drawable.xml
new file mode 100644
index 0000000..22239f3
--- /dev/null
+++ b/slices/view/src/main/res/drawable/abc_slice_ripple_drawable.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:targetApi="24"
+ android:color="?android:attr/colorControlHighlight" />
diff --git a/slices/view/src/main/res/layout-v21/abc_slice_small_template.xml b/slices/view/src/main/res/layout-v21/abc_slice_small_template.xml
new file mode 100644
index 0000000..3639d1d
--- /dev/null
+++ b/slices/view/src/main/res/layout-v21/abc_slice_small_template.xml
@@ -0,0 +1,75 @@
+<?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.
+ -->
+<LinearLayout
+ 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:background="?android:attr/activatedBackgroundIndicator"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:orientation="horizontal"
+ android:paddingEnd="12dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"/>
+
+ <LinearLayout
+ android:id="@android:id/content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <TextView android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:textAppearance="?android:attr/textAppearanceListItem" />
+
+ <TextView android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10" />
+
+ </LinearLayout>
+
+ <View
+ android:id="@+id/divider"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:background="?android:attr/listDivider"
+ android:visibility="gone"/>
+
+ <LinearLayout android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="end|center_vertical"
+ android:orientation="horizontal" />
+
+</LinearLayout>
diff --git a/slices/view/src/main/res/layout/abc_slice_grid.xml b/slices/view/src/main/res/layout/abc_slice_grid.xml
new file mode 100644
index 0000000..1d72a62
--- /dev/null
+++ b/slices/view/src/main/res/layout/abc_slice_grid.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+<androidx.app.slice.widget.GridView
+ 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:background="?android:attr/activatedBackgroundIndicator"
+ android:clipToPadding="false">
+</androidx.app.slice.widget.GridView>
diff --git a/slices/view/src/main/res/layout/abc_slice_message.xml b/slices/view/src/main/res/layout/abc_slice_message.xml
new file mode 100644
index 0000000..29368fb
--- /dev/null
+++ b/slices/view/src/main/res/layout/abc_slice_message.xml
@@ -0,0 +1,52 @@
+<?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.
+ -->
+<androidx.app.slice.widget.MessageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="12dp"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/abc_icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="-4dp"
+ android:gravity="start|center_vertical"
+ android:orientation="horizontal"
+ android:paddingEnd="12dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <!-- TODO: Support text source -->
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxWidth="48dp"
+ android:maxHeight="48dp" />
+ </LinearLayout>
+
+ <TextView android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:maxLines="10" />
+</androidx.app.slice.widget.MessageView>
diff --git a/slices/view/src/main/res/layout/abc_slice_message_local.xml b/slices/view/src/main/res/layout/abc_slice_message_local.xml
new file mode 100644
index 0000000..d35bd60
--- /dev/null
+++ b/slices/view/src/main/res/layout/abc_slice_message_local.xml
@@ -0,0 +1,39 @@
+<?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.
+ -->
+<androidx.app.slice.widget.MessageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical|end"
+ android:paddingTop="12dp"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:clipToPadding="false">
+
+ <TextView android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignStart="@android:id/title"
+ android:layout_gravity="end"
+ android:gravity="end"
+ android:padding="8dp"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:background="#ffeeeeee"
+ android:maxLines="10" />
+
+</androidx.app.slice.widget.MessageView>
diff --git a/slices/view/src/main/res/layout/abc_slice_remote_input.xml b/slices/view/src/main/res/layout/abc_slice_remote_input.xml
new file mode 100644
index 0000000..293c95a
--- /dev/null
+++ b/slices/view/src/main/res/layout/abc_slice_remote_input.xml
@@ -0,0 +1,77 @@
+<?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.
+ -->
+<!-- LinearLayout -->
+<androidx.app.slice.widget.RemoteInputView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/remote_input"
+ android:background="@drawable/abc_slice_remote_input_bg"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+
+ <view class="androidx.app.slice.widget.RemoteInputView$RemoteEditText"
+ android:id="@+id/remote_input_text"
+ android:layout_height="match_parent"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:paddingTop="2dp"
+ android:paddingBottom="4dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="12dp"
+ android:gravity="start|center_vertical"
+ android:textAppearance="?android:attr/textAppearance"
+ android:textColor="#FFFFFFFF"
+ android:textColorHint="#99ffffff"
+ android:textSize="16sp"
+ android:background="@null"
+ android:singleLine="true"
+ android:ellipsize="start"
+ android:inputType="textShortMessage|textAutoCorrect|textCapSentences"
+ android:imeOptions="actionSend|flagNoExtractUi|flagNoFullscreen" />
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical">
+
+ <ImageButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:paddingStart="12dp"
+ android:paddingEnd="24dp"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:id="@+id/remote_input_send"
+ android:src="@drawable/abc_ic_slice_send"
+ android:tint="#FFFFFF"
+ android:tintMode="src_in"
+ android:background="@drawable/abc_slice_ripple_drawable" />
+
+ <ProgressBar
+ android:id="@+id/remote_input_progress"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginEnd="6dp"
+ android:layout_gravity="center"
+ android:visibility="invisible"
+ android:indeterminate="true"
+ style="?android:attr/progressBarStyleSmall" />
+
+ </FrameLayout>
+
+</androidx.app.slice.widget.RemoteInputView>
\ No newline at end of file
diff --git a/slices/view/src/main/res/layout/abc_slice_secondary_text.xml b/slices/view/src/main/res/layout/abc_slice_secondary_text.xml
new file mode 100644
index 0000000..b446ddd
--- /dev/null
+++ b/slices/view/src/main/res/layout/abc_slice_secondary_text.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+<!-- LinearLayout -->
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:gravity="center"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:layout_width="match_parent" />
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
new file mode 100644
index 0000000..c07ce01
--- /dev/null
+++ b/slices/view/src/main/res/layout/abc_slice_small_template.xml
@@ -0,0 +1,75 @@
+<?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.
+ -->
+<LinearLayout
+ 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:background="?android:attr/activatedBackgroundIndicator"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:orientation="horizontal"
+ android:paddingEnd="8dp"/>
+
+ <LinearLayout
+ android:id="@android:id/content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <TextView android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:textColor="?android:attr/textColorPrimary"
+ android:maxLines="2"/>
+
+ <TextView android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10" />
+
+ </LinearLayout>
+
+ <View
+ android:id="@+id/divider"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:background="?android:attr/listDivider"
+ android:visibility="gone"/>
+
+ <LinearLayout android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingStart="8dp"
+ android:gravity="end|center_vertical"
+ android:orientation="horizontal" />
+
+</LinearLayout>
diff --git a/slices/view/src/main/res/layout/abc_slice_title.xml b/slices/view/src/main/res/layout/abc_slice_title.xml
new file mode 100644
index 0000000..e1bdf03
--- /dev/null
+++ b/slices/view/src/main/res/layout/abc_slice_title.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorPrimary"
+ android:gravity="center"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:layout_width="match_parent" />
diff --git a/slices/view/src/main/res/values/dimens.xml b/slices/view/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..c48a215
--- /dev/null
+++ b/slices/view/src/main/res/values/dimens.xml
@@ -0,0 +1,25 @@
+<?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>
+ <!-- Size of a slice shortcut view -->
+ <dimen name="abc_slice_shortcut_size">56dp</dimen>
+ <!-- Size of action icons in a slice -->
+ <dimen name="abc_slice_icon_size">24dp</dimen>
+ <!-- Standard padding used in a slice view -->
+ <dimen name="abc_slice_padding">16dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/slices/view/src/main/res/values/strings.xml b/slices/view/src/main/res/values/strings.xml
new file mode 100644
index 0000000..b72c986
--- /dev/null
+++ b/slices/view/src/main/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?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:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Format string for indicating there is more content in a slice view -->
+ <string name="abc_slice_more_content">+ <xliff:g id="number" example="5">%1$d</xliff:g></string>
+</resources>
\ No newline at end of file
diff --git a/transition/Android.mk b/transition/Android.mk
index 8c76d6b..f57ca0b 100644
--- a/transition/Android.mk
+++ b/transition/Android.mk
@@ -29,8 +29,9 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under,src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-annotations \
android-support-v4
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
diff --git a/tv-provider/Android.mk b/tv-provider/Android.mk
index 9427d0d..673a350 100644
--- a/tv-provider/Android.mk
+++ b/tv-provider/Android.mk
@@ -25,9 +25,10 @@
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-compat \
+LOCAL_JAVA_LIBRARIES := \
android-support-annotations
+LOCAL_SHARED_ANDROID_LIBRARIES := \
+ android-support-compat
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/v13/Android.mk b/v13/Android.mk
index 1a95b75..d316032 100644
--- a/v13/Android.mk
+++ b/v13/Android.mk
@@ -33,9 +33,10 @@
# android-support-v13, so we need to keep it static until they can be fixed.
LOCAL_STATIC_ANDROID_LIBRARIES := \
android-support-v4
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-v4 \
- android-support-annotations
+ android-support-v4
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
LOCAL_JAR_EXCLUDE_FILES := none
diff --git a/v14/preference/Android.mk b/v14/preference/Android.mk
index 4885c3e..5228624 100644
--- a/v14/preference/Android.mk
+++ b/v14/preference/Android.mk
@@ -31,12 +31,13 @@
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under,src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-v7-preference \
android-support-v7-appcompat \
android-support-v7-recyclerview \
- android-support-v4 \
- android-support-annotations
+ android-support-v4
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/v4/Android.mk b/v4/Android.mk
index 84fd5c3..03ae26a 100644
--- a/v4/Android.mk
+++ b/v4/Android.mk
@@ -27,13 +27,14 @@
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
# Some projects expect to inherit android-support-annotations from
# android-support-v4, so we need to keep it static until they can be fixed.
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_STATIC_ANDROID_LIBRARIES := \
android-support-compat \
android-support-media-compat \
android-support-core-utils \
android-support-core-ui \
- android-support-fragment \
- android-support-annotations
+ android-support-fragment
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
diff --git a/v7/appcompat/res-public/values/public_attrs.xml b/v7/appcompat/res-public/values/public_attrs.xml
index 8c9f9e9..3b811ec 100644
--- a/v7/appcompat/res-public/values/public_attrs.xml
+++ b/v7/appcompat/res-public/values/public_attrs.xml
@@ -102,6 +102,7 @@
<public type="attr" name="customNavigationLayout"/>
<public type="attr" name="dialogPreferredPadding"/>
<public type="attr" name="dialogTheme"/>
+ <public type="attr" name="dialogCornerRadius"/>
<public type="attr" name="displayOptions"/>
<public type="attr" name="divider"/>
<public type="attr" name="dividerHorizontal"/>
diff --git a/v7/appcompat/res/drawable-v21/abc_dialog_material_background.xml b/v7/appcompat/res/drawable-v21/abc_dialog_material_background.xml
new file mode 100644
index 0000000..7ef438b
--- /dev/null
+++ b/v7/appcompat/res/drawable-v21/abc_dialog_material_background.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetLeft="16dp"
+ android:insetTop="16dp"
+ android:insetRight="16dp"
+ android:insetBottom="16dp">
+ <shape android:shape="rectangle">
+ <corners android:radius="?attr/dialogCornerRadius" />
+ <solid android:color="@android:color/white" />
+ </shape>
+</inset>
\ No newline at end of file
diff --git a/v7/appcompat/res/drawable/abc_dialog_material_background.xml b/v7/appcompat/res/drawable/abc_dialog_material_background.xml
index 18560fc..978565b 100644
--- a/v7/appcompat/res/drawable/abc_dialog_material_background.xml
+++ b/v7/appcompat/res/drawable/abc_dialog_material_background.xml
@@ -20,7 +20,7 @@
android:insetRight="16dp"
android:insetBottom="16dp">
<shape android:shape="rectangle">
- <corners android:radius="2dp" />
+ <corners android:radius="@dimen/abc_dialog_corner_radius_material" />
<solid android:color="@android:color/white" />
</shape>
</inset>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-hi/strings.xml b/v7/appcompat/res/values-hi/strings.xml
index 3a393c7..a31ab90 100644
--- a/v7/appcompat/res/values-hi/strings.xml
+++ b/v7/appcompat/res/values-hi/strings.xml
@@ -29,8 +29,8 @@
<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>
- <string name="abc_shareactionprovider_share_with_application" msgid="3300176832234831527">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> के साथ साझा करें"</string>
- <string name="abc_shareactionprovider_share_with" msgid="3421042268587513524">"इसके द्वारा साझा करें"</string>
+ <string name="abc_shareactionprovider_share_with_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>
diff --git a/v7/appcompat/res/values-ta/strings.xml b/v7/appcompat/res/values-ta/strings.xml
index 4a2ad2f..971a3db 100644
--- a/v7/appcompat/res/values-ta/strings.xml
+++ b/v7/appcompat/res/values-ta/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-v28/themes_base.xml b/v7/appcompat/res/values-v28/themes_base.xml
new file mode 100644
index 0000000..7dea6d5
--- /dev/null
+++ b/v7/appcompat/res/values-v28/themes_base.xml
@@ -0,0 +1,33 @@
+<?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>
+
+ <style name="Base.Theme.AppCompat" parent="Base.V28.Theme.AppCompat" />
+ <style name="Base.Theme.AppCompat.Light" parent="Base.V28.Theme.AppCompat.Light" />
+
+ <style name="Base.V28.Theme.AppCompat" parent="Base.V26.Theme.AppCompat">
+ <!-- We can use the platform styles on API 28+ -->
+ <item name="dialogCornerRadius">?android:attr/dialogCornerRadius</item>
+ </style>
+
+ <style name="Base.V28.Theme.AppCompat.Light" parent="Base.V26.Theme.AppCompat.Light">
+ <!-- We can use the platform styles on API 28+ -->
+ <item name="dialogCornerRadius">?android:attr/dialogCornerRadius</item>
+ </style>
+
+</resources>
diff --git a/v7/appcompat/res/values-vi/strings.xml b/v7/appcompat/res/values-vi/strings.xml
index 9587bed..4560b4b 100644
--- a/v7/appcompat/res/values-vi/strings.xml
+++ b/v7/appcompat/res/values-vi/strings.xml
@@ -19,7 +19,7 @@
<string name="abc_action_mode_done" msgid="4076576682505996667">"Xong"</string>
<string name="abc_action_bar_home_description" msgid="4600421777120114993">"Điều hướng về trang chủ"</string>
<string name="abc_action_bar_up_description" msgid="1594238315039666878">"Điều hướng lên trên"</string>
- <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Thêm tùy chọn"</string>
+ <string name="abc_action_menu_overflow_description" msgid="3588849162933574182">"Tùy chọn khác"</string>
<string name="abc_toolbar_collapse_description" msgid="1603543279005712093">"Thu gọn"</string>
<string name="abc_searchview_description_search" msgid="8264924765203268293">"Tìm kiếm"</string>
<string name="abc_search_hint" msgid="7723749260725869598">"Tìm kiếm…"</string>
diff --git a/v7/appcompat/res/values/attrs.xml b/v7/appcompat/res/values/attrs.xml
index c48b7ba..7d26a32 100644
--- a/v7/appcompat/res/values/attrs.xml
+++ b/v7/appcompat/res/values/attrs.xml
@@ -189,6 +189,8 @@
<attr name="dialogPreferredPadding" format="dimension" />
<!-- The list divider used in alert dialogs. -->
<attr name="listDividerAlertDialog" format="reference" />
+ <!-- Preferred corner radius of dialogs. -->
+ <attr name="dialogCornerRadius" format="dimension" />
<!-- =================== -->
<!-- Other widget styles -->
diff --git a/v7/appcompat/res/values/dimens.xml b/v7/appcompat/res/values/dimens.xml
index 5bcd4b4..6357caa 100644
--- a/v7/appcompat/res/values/dimens.xml
+++ b/v7/appcompat/res/values/dimens.xml
@@ -75,6 +75,7 @@
<dimen name="abc_dialog_title_divider_material">8dp</dimen>
<dimen name="abc_dialog_list_padding_top_no_title">8dp</dimen>
<dimen name="abc_dialog_list_padding_bottom_no_buttons">8dp</dimen>
+ <dimen name="abc_dialog_corner_radius_material">2dp</dimen>
<!-- Dialog button bar height -->
<dimen name="abc_alert_dialog_button_bar_height">48dp</dimen>
diff --git a/v7/appcompat/res/values/themes_base.xml b/v7/appcompat/res/values/themes_base.xml
index a9acfce..6b4461f 100644
--- a/v7/appcompat/res/values/themes_base.xml
+++ b/v7/appcompat/res/values/themes_base.xml
@@ -265,6 +265,7 @@
<!-- Dialog attributes -->
<item name="dialogTheme">@style/ThemeOverlay.AppCompat.Dialog</item>
<item name="dialogPreferredPadding">@dimen/abc_dialog_padding_material</item>
+ <item name="dialogCornerRadius">@dimen/abc_dialog_corner_radius_material</item>
<item name="alertDialogTheme">@style/ThemeOverlay.AppCompat.Dialog.Alert</item>
<item name="alertDialogStyle">@style/AlertDialog.AppCompat</item>
@@ -435,6 +436,7 @@
<!-- Dialog attributes -->
<item name="dialogTheme">@style/ThemeOverlay.AppCompat.Dialog</item>
<item name="dialogPreferredPadding">@dimen/abc_dialog_padding_material</item>
+ <item name="dialogCornerRadius">@dimen/abc_dialog_corner_radius_material</item>
<item name="alertDialogTheme">@style/ThemeOverlay.AppCompat.Dialog.Alert</item>
<item name="alertDialogStyle">@style/AlertDialog.AppCompat.Light</item>
diff --git a/v7/appcompat/src/main/java/android/support/v7/widget/TooltipCompatHandler.java b/v7/appcompat/src/main/java/android/support/v7/widget/TooltipCompatHandler.java
index 3d3c300..63a6198 100644
--- a/v7/appcompat/src/main/java/android/support/v7/widget/TooltipCompatHandler.java
+++ b/v7/appcompat/src/main/java/android/support/v7/widget/TooltipCompatHandler.java
@@ -82,10 +82,10 @@
public static void setTooltipText(View view, CharSequence tooltipText) {
// The code below is not attempting to update the tooltip text
// for a pending or currently active tooltip, because it may lead
- // to updating the wrong tooltipin in some rare cases (e.g. when
+ // to updating the wrong tooltip in in some rare cases (e.g. when
// action menu item views are recycled). Instead, the tooltip is
// canceled/hidden. This might still be the wrong tooltip,
- // but hiding wrong tooltip is less disruptive UX.
+ // but hiding a wrong tooltip is less disruptive UX.
if (sPendingHandler != null && sPendingHandler.mAnchor == view) {
setPendingHandler(null);
}
diff --git a/v7/cardview/Android.mk b/v7/cardview/Android.mk
index 19d377c..2d9492d 100644
--- a/v7/cardview/Android.mk
+++ b/v7/cardview/Android.mk
@@ -27,7 +27,7 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under,src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_SHARED_ANDROID_LIBRARIES := \
+LOCAL_JAVA_LIBRARIES := \
android-support-annotations
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
diff --git a/v7/gridlayout/Android.mk b/v7/gridlayout/Android.mk
index 5091081..81791da 100644
--- a/v7/gridlayout/Android.mk
+++ b/v7/gridlayout/Android.mk
@@ -29,10 +29,11 @@
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under, src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-compat \
- android-support-core-ui \
- android-support-annotations
+ android-support-core-ui
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/v7/mediarouter/res/values-da/strings.xml b/v7/mediarouter/res/values-da/strings.xml
index 44da892..d280d2c 100644
--- a/v7/mediarouter/res/values-da/strings.xml
+++ b/v7/mediarouter/res/values-da/strings.xml
@@ -34,7 +34,7 @@
<string name="mr_controller_collapse_group" msgid="7924809056904240926">"Skjul"</string>
<string name="mr_controller_album_art" msgid="6422801843540543585">"Albumgrafik"</string>
<string name="mr_controller_volume_slider" msgid="2361785992211841709">"Lydstyrkeskyder"</string>
- <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Der er ikke valgt nogen medier"</string>
+ <string name="mr_controller_no_media_selected" msgid="6547130360349182381">"Ingen medier er markeret"</string>
<string name="mr_controller_no_info_available" msgid="5585418471741142924">"Der er ingen tilgængelige oplysninger"</string>
<string name="mr_controller_casting_screen" msgid="4868457957151124867">"Skærmen castes"</string>
</resources>
diff --git a/v7/mediarouter/res/values-eu/strings.xml b/v7/mediarouter/res/values-eu/strings.xml
index f9b9f8d..11b1d00 100644
--- a/v7/mediarouter/res/values-eu/strings.xml
+++ b/v7/mediarouter/res/values-eu/strings.xml
@@ -22,7 +22,7 @@
<string name="mr_cast_button_disconnected" msgid="816305490427819240">"Igortzeko botoia. Deskonektatuta"</string>
<string name="mr_cast_button_connecting" msgid="2187642765091873834">"Igortzeko botoia. Konektatzen"</string>
<string name="mr_cast_button_connected" msgid="5088427771788648085">"Igortzeko botoia. Konektatuta"</string>
- <string name="mr_chooser_title" msgid="414301941546135990">"Igorri hona"</string>
+ <string name="mr_chooser_title" msgid="414301941546135990">"Igorri hona:"</string>
<string name="mr_chooser_searching" msgid="6349900579507521956">"Gailuak bilatzen"</string>
<string name="mr_controller_disconnect" msgid="1227264889412989580">"Deskonektatu"</string>
<string name="mr_controller_stop_casting" msgid="8857886794086583226">"Utzi igortzeari"</string>
diff --git a/v7/mediarouter/res/values-in/strings.xml b/v7/mediarouter/res/values-in/strings.xml
index 5e537af..e76b3e8 100644
--- a/v7/mediarouter/res/values-in/strings.xml
+++ b/v7/mediarouter/res/values-in/strings.xml
@@ -18,10 +18,10 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
<string name="mr_user_route_category_name" msgid="7498112907524977311">"Perangkat"</string>
- <string name="mr_button_content_description" msgid="3698378085901466129">"Tombol transmisi"</string>
- <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Tombol transmisi. Terputus"</string>
- <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Tombol transmisi. Menghubungkan"</string>
- <string name="mr_cast_button_connected" msgid="5088427771788648085">"Tombol transmisi. Terhubung"</string>
+ <string name="mr_button_content_description" msgid="3698378085901466129">"Tombol Cast"</string>
+ <string name="mr_cast_button_disconnected" msgid="816305490427819240">"Tombol Cast. Terputus"</string>
+ <string name="mr_cast_button_connecting" msgid="2187642765091873834">"Tombol Cast. Menghubungkan"</string>
+ <string name="mr_cast_button_connected" msgid="5088427771788648085">"Tombol Cast. Terhubung"</string>
<string name="mr_chooser_title" msgid="414301941546135990">"Transmisikan ke"</string>
<string name="mr_chooser_searching" msgid="6349900579507521956">"Mencari perangkat"</string>
<string name="mr_controller_disconnect" msgid="1227264889412989580">"Putuskan sambungan"</string>
diff --git a/v7/mediarouter/res/values-ml/strings.xml b/v7/mediarouter/res/values-ml/strings.xml
index c7b50be..62258fb 100644
--- a/v7/mediarouter/res/values-ml/strings.xml
+++ b/v7/mediarouter/res/values-ml/strings.xml
@@ -26,7 +26,7 @@
<string name="mr_chooser_searching" msgid="6349900579507521956">"ഉപകരണങ്ങൾ കണ്ടെത്തുന്നു"</string>
<string name="mr_controller_disconnect" msgid="1227264889412989580">"വിച്ഛേദിക്കുക"</string>
<string name="mr_controller_stop_casting" msgid="8857886794086583226">"കാസ്റ്റുചെയ്യൽ നിർത്തുക"</string>
- <string name="mr_controller_close_description" msgid="7333862312480583260">"അടയ്ക്കുക"</string>
+ <string name="mr_controller_close_description" msgid="7333862312480583260">"അവസാനിപ്പിക്കുക"</string>
<string name="mr_controller_play" msgid="683634565969987458">"പ്ലേ ചെയ്യുക"</string>
<string name="mr_controller_pause" msgid="5451884435510905406">"തൽക്കാലം നിർത്തൂ"</string>
<string name="mr_controller_stop" msgid="735874641921425123">"നിര്ത്തുക"</string>
diff --git a/v7/mediarouter/res/values-mr/strings.xml b/v7/mediarouter/res/values-mr/strings.xml
index 27923d1..596b56a 100644
--- a/v7/mediarouter/res/values-mr/strings.xml
+++ b/v7/mediarouter/res/values-mr/strings.xml
@@ -20,7 +20,7 @@
<string name="mr_user_route_category_name" msgid="7498112907524977311">"डिव्हाइसेस"</string>
<string name="mr_button_content_description" msgid="3698378085901466129">"कास्ट बटण"</string>
<string name="mr_cast_button_disconnected" msgid="816305490427819240">"कास्ट बटण. डिस्कनेक्ट केले"</string>
- <string name="mr_cast_button_connecting" msgid="2187642765091873834">"कास्ट बटण. कनेक्ट करीत आहे"</string>
+ <string name="mr_cast_button_connecting" msgid="2187642765091873834">"कास्ट बटण. कनेक्ट करत आहे"</string>
<string name="mr_cast_button_connected" msgid="5088427771788648085">"कास्ट बटण. कनेक्ट केले"</string>
<string name="mr_chooser_title" msgid="414301941546135990">"यावर कास्ट करा"</string>
<string name="mr_chooser_searching" msgid="6349900579507521956">"डिव्हाइसेस शोधत आहे"</string>
@@ -36,5 +36,5 @@
<string name="mr_controller_volume_slider" msgid="2361785992211841709">"व्हॉल्यूम स्लायडर"</string>
<string name="mr_controller_no_media_selected" msgid="6547130360349182381">"मीडिया निवडला नाही"</string>
<string name="mr_controller_no_info_available" msgid="5585418471741142924">"कोणतीही माहिती उपलब्ध नाही"</string>
- <string name="mr_controller_casting_screen" msgid="4868457957151124867">"स्क्रीन कास्ट करीत आहे"</string>
+ <string name="mr_controller_casting_screen" msgid="4868457957151124867">"स्क्रीन कास्ट करत आहे"</string>
</resources>
diff --git a/v7/mediarouter/res/values-ta/strings.xml b/v7/mediarouter/res/values-ta/strings.xml
index 99c6172..9888472 100644
--- a/v7/mediarouter/res/values-ta/strings.xml
+++ b/v7/mediarouter/res/values-ta/strings.xml
@@ -22,7 +22,7 @@
<string name="mr_cast_button_disconnected" msgid="816305490427819240">"அனுப்புதல் பொத்தான். துண்டிக்கப்பட்டது"</string>
<string name="mr_cast_button_connecting" msgid="2187642765091873834">"அனுப்புதல் பொத்தான். இணைக்கிறது"</string>
<string name="mr_cast_button_connected" msgid="5088427771788648085">"அனுப்புதல் பொத்தான். இணைக்கப்பட்டது"</string>
- <string name="mr_chooser_title" msgid="414301941546135990">"இதில் திரையிடு"</string>
+ <string name="mr_chooser_title" msgid="414301941546135990">"இதற்கு அனுப்பு"</string>
<string name="mr_chooser_searching" msgid="6349900579507521956">"சாதனங்களைத் தேடுகிறது"</string>
<string name="mr_controller_disconnect" msgid="1227264889412989580">"தொடர்பைத் துண்டி"</string>
<string name="mr_controller_stop_casting" msgid="8857886794086583226">"அனுப்புவதை நிறுத்து"</string>
diff --git a/v7/mediarouter/res/values-tr/strings.xml b/v7/mediarouter/res/values-tr/strings.xml
index a0eb2e4..8189092 100644
--- a/v7/mediarouter/res/values-tr/strings.xml
+++ b/v7/mediarouter/res/values-tr/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mr_system_route_name" msgid="5441529851481176817">"Sistem"</string>
<string name="mr_user_route_category_name" msgid="7498112907524977311">"Cihazlar"</string>
- <string name="mr_button_content_description" msgid="3698378085901466129">"Yayın düğmesi"</string>
+ <string name="mr_button_content_description" msgid="3698378085901466129">"Yayınla düğmesi"</string>
<string name="mr_cast_button_disconnected" msgid="816305490427819240">"Yayınla düğmesi. Bağlantı kesildi"</string>
<string name="mr_cast_button_connecting" msgid="2187642765091873834">"Yayınla düğmesi. Bağlanıyor"</string>
<string name="mr_cast_button_connected" msgid="5088427771788648085">"Yayınla düğmesi. Bağlandı"</string>
diff --git a/v7/palette/Android.mk b/v7/palette/Android.mk
index f823d30..4d680cf 100644
--- a/v7/palette/Android.mk
+++ b/v7/palette/Android.mk
@@ -30,10 +30,11 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src/main)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/src/main/res
LOCAL_MANIFEST_FILE := AndroidManifest.xml
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-compat \
- android-support-core-utils \
- android-support-annotations
+ android-support-core-utils
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
diff --git a/v7/preference/Android.mk b/v7/preference/Android.mk
index 1e3e0b6..5a3f57a 100644
--- a/v7/preference/Android.mk
+++ b/v7/preference/Android.mk
@@ -31,12 +31,14 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under,src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-v7-appcompat \
android-support-v7-recyclerview \
- android-support-v4 \
- android-support-annotations
+ android-support-v4
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
+LOCAL_EXPORT_PROGUARD_FLAG_FILES := proguard-rules.pro
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/v7/recyclerview/Android.mk b/v7/recyclerview/Android.mk
index dcebe88..a62c3cd 100644
--- a/v7/recyclerview/Android.mk
+++ b/v7/recyclerview/Android.mk
@@ -29,11 +29,13 @@
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under,src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
android-support-compat \
- android-support-core-ui \
- android-support-annotations
+ android-support-core-ui
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly
+LOCAL_EXPORT_PROGUARD_FLAG_FILES := proguard-rules.pro
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/v7/recyclerview/tests/Android.mk b/v7/recyclerview/tests/Android.mk
deleted file mode 100644
index c6299d7..0000000
--- a/v7/recyclerview/tests/Android.mk
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT 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)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_RESOURCE_DIR := \
- $(LOCAL_PATH)/res \
- $(LOCAL_PATH)/../res
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-v7-recyclerview \
- android-support-v4 \
- android-support-annotations
-
-LOCAL_PACKAGE_NAME := RecyclerViewTests
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_AAPT_FLAGS := \
- --auto-add-overlay \
- --extra-packages android.support.v7.recyclerview
-
-include $(BUILD_PACKAGE)
diff --git a/wear/Android.mk b/wear/Android.mk
index fe9b8d3..2be9bfa 100644
--- a/wear/Android.mk
+++ b/wear/Android.mk
@@ -36,14 +36,16 @@
LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
LOCAL_SRC_FILES := $(call all-java-files-under,src/main/java)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAVA_LIBRARIES := \
+ android-support-annotations
LOCAL_SHARED_ANDROID_LIBRARIES := \
- android-support-core-ui \
- android-support-annotations \
- android-support-percent \
- android-support-v7-recyclerview \
- android-support-v4
+ android-support-core-ui \
+ android-support-percent \
+ android-support-v7-recyclerview \
+ android-support-v4 \
+ android-support-constraint-layout
LOCAL_STATIC_JAVA_LIBRARIES := \
- prebuilt-com.google.android.wearable-stubs
+ prebuilt-com.google.android.wearable-stubs
LOCAL_JAR_EXCLUDE_FILES := none
LOCAL_JAVA_LANGUAGE_VERSION := 1.7
LOCAL_AAPT_FLAGS := --add-javadoc-annotation doconly