Compat for slices
Support back to v19.
Test: Run samples on API 28 and API 27
Change-Id: Ib8f7b30575f6c2126eab4a285780a68858629396
diff --git a/samples/SupportSliceDemos/src/main/AndroidManifest.xml b/samples/SupportSliceDemos/src/main/AndroidManifest.xml
index 232724c..2ee7667 100644
--- a/samples/SupportSliceDemos/src/main/AndroidManifest.xml
+++ b/samples/SupportSliceDemos/src/main/AndroidManifest.xml
@@ -44,7 +44,8 @@
<provider android:authorities="com.example.androidx.slice.demos"
android:name=".SampleSliceProvider"
- android:grantUriPermissions="true" />
+ android:grantUriPermissions="true">
+ </provider>
<receiver
android:name=".SliceBroadcastReceiver"
diff --git a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
index 06de29a..d143c44 100644
--- a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
+++ b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
@@ -16,25 +16,26 @@
package com.example.androidx.slice.demos;
+import static android.app.slice.Slice.HINT_SELECTED;
+import static android.app.slice.Slice.HINT_TITLE;
+
import android.app.PendingIntent;
-import android.app.slice.Slice;
-import android.app.slice.SliceProvider;
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.RequiresApi;
import android.text.format.DateUtils;
+import androidx.app.slice.Slice;
+import androidx.app.slice.SliceProvider;
import androidx.app.slice.builders.MessagingSliceBuilder;
import androidx.app.slice.core.SliceHints;
/**
* Examples of using slice template builders.
*/
-@RequiresApi(api = 28)
public class SampleSliceProvider extends SliceProvider {
public static final Uri MESSAGE =
Uri.parse("content://com.example.androidx.slice.demos/message");
@@ -45,7 +46,7 @@
"com.android.settings.slice.action.WIFI_CHANGED";
@Override
- public boolean onCreate() {
+ public boolean onCreateSliceProvider() {
return true;
}
@@ -107,7 +108,7 @@
break;
}
if (wifiEnabled) {
- toggleHints = new String[] {SliceHints.HINT_TOGGLE, Slice.HINT_SELECTED};
+ toggleHints = new String[] {SliceHints.HINT_TOGGLE, HINT_SELECTED};
} else {
toggleHints = new String[] {SliceHints.HINT_TOGGLE};
}
@@ -120,12 +121,12 @@
.addText(state, null)
.addIcon(Icon.createWithResource(getContext(),
R.drawable.ic_settings_wifi), null, SliceHints.HINT_HIDDEN)
- .addHints(Slice.HINT_TITLE)
- .build())
+ .addHints(HINT_TITLE)
+ .build(), null)
.addAction(getBroadcastIntent(ACTION_WIFI_CHANGED),
new Slice.Builder(b)
.addHints(toggleHints)
- .build())
+ .build(), null)
.build());
return b.build();
}
diff --git a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
index a4f28c1..0d5e153 100644
--- a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
+++ b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
@@ -16,7 +16,6 @@
package com.example.androidx.slice.demos;
-import android.app.slice.Slice;
import android.arch.lifecycle.LiveData;
import android.content.ContentResolver;
import android.content.pm.ActivityInfo;
@@ -44,6 +43,7 @@
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;
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
index bf59a5a..816fddd 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/MessagingSliceBuilder.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/MessagingSliceBuilder.java
@@ -18,12 +18,13 @@
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import android.app.slice.Slice;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.RestrictTo;
+import androidx.app.slice.Slice;
+
/**
* Builder to construct slice content in a messaging format.
*/
@@ -58,7 +59,7 @@
@Override
public void add(SubTemplateSliceBuilder builder) {
- getBuilder().addSubSlice(builder.build(), Slice.SUBTYPE_MESSAGE);
+ getBuilder().addSubSlice(builder.build(), android.app.slice.Slice.SUBTYPE_MESSAGE);
}
/**
@@ -78,7 +79,7 @@
* Add the icon used to display contact in the messaging experience
*/
public MessageBuilder addSource(Icon source) {
- getBuilder().addIcon(source, Slice.SUBTYPE_SOURCE);
+ getBuilder().addIcon(source, android.app.slice.Slice.SUBTYPE_SOURCE);
return this;
}
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
index 61fea7f..102ee00 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/TemplateSliceBuilder.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/TemplateSliceBuilder.java
@@ -17,10 +17,11 @@
package androidx.app.slice.builders;
-import android.app.slice.Slice;
import android.net.Uri;
import android.support.annotation.RestrictTo;
+import androidx.app.slice.Slice;
+
/**
* Base class of builders of various template types.
*/
diff --git a/slices/core/build.gradle b/slices/core/build.gradle
index b145ded..237389a 100644
--- a/slices/core/build.gradle
+++ b/slices/core/build.gradle
@@ -20,12 +20,19 @@
dependencies {
implementation libs.support.annotations, libs.support_exclude_config
+ implementation libs.support.app_compat, libs.support_exclude_config
}
android {
defaultConfig {
minSdkVersion 28
}
+ sourceSets {
+ main.res.srcDirs = [
+ 'res',
+ 'res-public'
+ ]
+ }
}
supportLibrary {
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..e7c6284
--- /dev/null
+++ b/slices/core/src/main/java/androidx/app/slice/Slice.java
@@ -0,0 +1,406 @@
+/*
+ * 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.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.HINT_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()]));
+ }
+
+ /**
+ * 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
+ */
+ public static @Nullable Slice bindSlice(Context context, @NonNull Uri uri) {
+ if (BuildCompat.isAtLeastP()) {
+ return SliceConvert.wrap(android.app.slice.Slice.bindSlice(
+ context.getContentResolver(), uri, SliceSpecs.SUPPORTED_SPECS));
+ } else {
+ return SliceProviderCompat.bindSlice(context, uri);
+ }
+ }
+
+ /**
+ * 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
+ */
+ public static @Nullable Slice bindSlice(Context context, @NonNull Intent intent) {
+ if (BuildCompat.isAtLeastP()) {
+ return SliceConvert.wrap(android.app.slice.Slice.bindSlice(
+ context, intent, SliceSpecs.SUPPORTED_SPECS));
+ } else {
+ return SliceProviderCompat.bindSlice(context, intent);
+ }
+ }
+}
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..1c87b56
--- /dev/null
+++ b/slices/core/src/main/java/androidx/app/slice/SliceConvert.java
@@ -0,0 +1,103 @@
+/*
+ * 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;
+
+/**
+ * Convert between {@link androidx.app.slice.Slice} and {@link android.app.slice.Slice}
+ */
+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..031d43c
--- /dev/null
+++ b/slices/core/src/main/java/androidx/app/slice/SliceItem.java
@@ -0,0 +1,348 @@
+/*
+ * 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.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
+ */
+ 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
+ */
+ 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.getParcelable(OBJ));
+ case FORMAT_TEXT:
+ return in.getCharSequence(OBJ);
+ case FORMAT_ACTION:
+ return new Pair<PendingIntent, Slice>(
+ 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..9da25ac
--- /dev/null
+++ b/slices/core/src/main/java/androidx/app/slice/compat/ContentProviderWrapper.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.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.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;
+ mImpl.attachInfo(context, info);
+ super.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
+ 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..bc907da
--- /dev/null
+++ b/slices/core/src/main/java/androidx/app/slice/compat/SliceProviderCompat.java
@@ -0,0 +1,261 @@
+/*
+ * 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)) {
+ 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(Uri sliceUri) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ return onBindSliceStrict(sliceUri);
+ } else {
+ CountDownLatch latch = new CountDownLatch(1);
+ Slice[] output = new Slice[1];
+ mHandler.post(() -> {
+ 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..61f8eb9
--- /dev/null
+++ b/slices/core/src/main/java/androidx/app/slice/compat/SliceProviderWrapper.java
@@ -0,0 +1,60 @@
+/*
+ * 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.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)
+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/SliceQuery.java b/slices/core/src/main/java/androidx/app/slice/core/SliceQuery.java
index eec898e..5f87126 100644
--- a/slices/core/src/main/java/androidx/app/slice/core/SliceQuery.java
+++ b/slices/core/src/main/java/androidx/app/slice/core/SliceQuery.java
@@ -16,14 +16,15 @@
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.app.slice.Slice;
-import android.app.slice.SliceItem;
import android.support.annotation.RestrictTo;
import android.text.TextUtils;
@@ -36,6 +37,9 @@
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
@@ -143,9 +147,9 @@
if (FORMAT_IMAGE.equals(item.getFormat())) {
return item;
}
- if (!(FORMAT_SLICE.equals(item.getFormat()) && item.hasHint(Slice.HINT_LIST))
- && !item.hasHint(Slice.HINT_ACTIONS)
- && !item.hasHint(Slice.HINT_LIST_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) {
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/src/main/java/androidx/app/slice/widget/ActionRow.java b/slices/view/src/main/java/androidx/app/slice/widget/ActionRow.java
index c38088c..a38441f 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/ActionRow.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/ActionRow.java
@@ -16,6 +16,7 @@
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;
@@ -24,8 +25,6 @@
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.app.RemoteInput;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
@@ -41,6 +40,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.app.slice.SliceItem;
import androidx.app.slice.core.SliceQuery;
/**
@@ -75,7 +75,7 @@
for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
View view = mActionsGroup.getChildAt(i);
SliceItem item = (SliceItem) view.getTag();
- boolean tint = !item.hasHint(Slice.HINT_NO_TINT);
+ boolean tint = !item.hasHint(HINT_NO_TINT);
if (tint) {
((ImageView) view).setImageTintList(ColorStateList.valueOf(mColor));
}
@@ -120,7 +120,7 @@
if (image == null) {
return;
}
- boolean tint = !image.hasHint(Slice.HINT_NO_TINT);
+ boolean tint = !image.hasHint(HINT_NO_TINT);
SliceItem input = SliceQuery.find(action, FORMAT_REMOTE_INPUT);
if (input != null && input.getRemoteInput().getAllowFreeFormInput()) {
addAction(image.getIcon(), tint, image).setOnClickListener(
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
index 9fa8a5d..231f90a 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/GridView.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/GridView.java
@@ -16,6 +16,8 @@
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;
@@ -23,8 +25,6 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
import android.content.Context;
import android.graphics.Color;
import android.support.annotation.RestrictTo;
@@ -42,6 +42,7 @@
import java.util.ArrayList;
import java.util.List;
+import androidx.app.slice.SliceItem;
import androidx.app.slice.core.SliceQuery;
import androidx.app.slice.view.R;
@@ -155,7 +156,7 @@
case FORMAT_TEXT:
boolean title = false;
if ((SliceQuery.hasAnyHints(item, new String[] {
- Slice.HINT_LARGE, Slice.HINT_TITLE
+ HINT_LARGE, HINT_TITLE
}))) {
title = true;
}
@@ -168,7 +169,7 @@
case FORMAT_IMAGE:
ImageView iv = new ImageView(context);
iv.setImageIcon(i.getIcon());
- if (item.hasHint(Slice.HINT_LARGE)) {
+ if (item.hasHint(HINT_LARGE)) {
iv.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
} else {
int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
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
index 11928fd..bb1fc17 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/LargeSliceAdapter.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/LargeSliceAdapter.java
@@ -16,14 +16,13 @@
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.app.slice.Slice;
-import android.app.slice.SliceItem;
import android.content.Context;
import android.support.annotation.RestrictTo;
import android.support.v7.widget.RecyclerView;
@@ -37,6 +36,7 @@
import java.util.List;
import java.util.stream.Collectors;
+import androidx.app.slice.SliceItem;
import androidx.app.slice.core.SliceQuery;
import androidx.app.slice.view.R;
@@ -137,7 +137,7 @@
return TYPE_MESSAGE_LOCAL;
}
}
- if (item.hasHint(Slice.HINT_HORIZONTAL)) {
+ if (item.hasHint(HINT_HORIZONTAL)) {
return TYPE_GRID;
}
return TYPE_DEFAULT;
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
index c59b056..0188c21 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/LargeTemplateView.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/LargeTemplateView.java
@@ -16,12 +16,14 @@
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.app.slice.Slice;
-import android.app.slice.SliceItem;
import android.content.Context;
import android.support.annotation.RestrictTo;
import android.support.v7.widget.LinearLayoutManager;
@@ -31,6 +33,8 @@
import java.util.ArrayList;
import java.util.List;
+import androidx.app.slice.Slice;
+import androidx.app.slice.SliceItem;
import androidx.app.slice.core.SliceQuery;
/**
@@ -70,7 +74,7 @@
mRecyclerView.getLayoutParams().height = WRAP_CONTENT;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mRecyclerView.getMeasuredHeight() > mMaxHeight
- || (mSlice != null && SliceQuery.hasHints(mSlice, Slice.HINT_PARTIAL))) {
+ || (mSlice != null && SliceQuery.hasHints(mSlice, HINT_PARTIAL))) {
mRecyclerView.getLayoutParams().height = mDefaultHeight;
} else {
mRecyclerView.getLayoutParams().height = mRecyclerView.getMeasuredHeight();
@@ -84,18 +88,18 @@
mSlice = slice;
List<SliceItem> items = new ArrayList<>();
boolean[] hasHeader = new boolean[1];
- if (SliceQuery.hasHints(slice, Slice.HINT_LIST)) {
+ if (SliceQuery.hasHints(slice, HINT_LIST)) {
addList(slice, items);
} else {
slice.getItems().forEach(item -> {
- if (item.hasHint(Slice.HINT_ACTIONS)) {
+ if (item.hasHint(HINT_ACTIONS)) {
return;
} else if (FORMAT_COLOR.equals(item.getFormat())) {
return;
} else if (FORMAT_SLICE.equals(item.getFormat())
- && item.hasHint(Slice.HINT_LIST)) {
+ && item.hasHint(HINT_LIST)) {
addList(item.getSlice(), items);
- } else if (item.hasHint(Slice.HINT_LIST_ITEM)) {
+ } else if (item.hasHint(HINT_LIST_ITEM)) {
items.add(item);
} else if (!hasHeader[0]) {
hasHeader[0] = true;
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
index a7ccd36..5611f05 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/MessageView.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/MessageView.java
@@ -16,11 +16,11 @@
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.app.slice.Slice;
-import android.app.slice.SliceItem;
+
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -33,6 +33,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.app.slice.SliceItem;
import androidx.app.slice.core.SliceQuery;
/**
@@ -57,7 +58,7 @@
@Override
public void setSliceItem(SliceItem slice) {
- SliceItem source = SliceQuery.findSubtype(slice, FORMAT_IMAGE, Slice.SUBTYPE_SOURCE);
+ 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());
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
index 136d0ff..e553653 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/ShortcutView.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/ShortcutView.java
@@ -16,6 +16,8 @@
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;
@@ -24,8 +26,6 @@
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -39,6 +39,8 @@
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;
@@ -79,7 +81,7 @@
circle.setTint(color);
setBackground(circle);
if (mIcon != null) {
- final boolean isLarge = mIcon.hasHint(Slice.HINT_LARGE)
+ 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(),
@@ -119,14 +121,14 @@
*/
private void determineShortcutItems(Context context, Slice slice) {
SliceItem titleItem = SliceQuery.find(slice, FORMAT_ACTION,
- Slice.HINT_TITLE, null);
+ 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, Slice.HINT_TITLE,
+ mIcon = SliceQuery.find(titleItem.getSlice(), FORMAT_IMAGE, HINT_TITLE,
null);
- mLabel = SliceQuery.find(titleItem.getSlice(), FORMAT_TEXT, Slice.HINT_TITLE,
+ mLabel = SliceQuery.find(titleItem.getSlice(), FORMAT_TEXT, HINT_TITLE,
null);
} else {
// No hinted action; just use the first one
@@ -136,11 +138,11 @@
}
// First fallback: any hinted image and text
if (mIcon == null) {
- mIcon = SliceQuery.find(slice, FORMAT_IMAGE, Slice.HINT_TITLE,
+ mIcon = SliceQuery.find(slice, FORMAT_IMAGE, HINT_TITLE,
null);
}
if (mLabel == null) {
- mLabel = SliceQuery.find(slice, FORMAT_TEXT, Slice.HINT_TITLE,
+ mLabel = SliceQuery.find(slice, FORMAT_TEXT, HINT_TITLE,
null);
}
// Second fallback: first image and text
@@ -162,7 +164,7 @@
if (mIcon == null) {
Slice.Builder sb = new Slice.Builder(slice.getUri());
Drawable icon = pm.getApplicationIcon(appInfo);
- sb.addIcon(SliceViewUtil.createIconFromDrawable(icon), Slice.HINT_LARGE);
+ sb.addIcon(SliceViewUtil.createIconFromDrawable(icon), HINT_LARGE);
mIcon = sb.build().getItems().get(0);
}
if (mLabel == null) {
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
index b0c0139..458aa83 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/SliceLiveData.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/SliceLiveData.java
@@ -15,7 +15,6 @@
*/
package androidx.app.slice.widget;
-import android.app.slice.Slice;
import android.arch.lifecycle.LiveData;
import android.content.Context;
import android.database.ContentObserver;
@@ -23,7 +22,7 @@
import android.os.AsyncTask;
import android.os.Handler;
-import androidx.app.slice.core.SliceSpecs;
+import androidx.app.slice.Slice;
/**
* Class with factory methods for creating LiveData that observes slices.
@@ -65,8 +64,7 @@
}
private void updateSlice() {
- postValue(Slice.bindSlice(mContext.getContentResolver(), mUri,
- SliceSpecs.SUPPORTED_SPECS));
+ postValue(Slice.bindSlice(mContext, mUri));
}
private final ContentObserver mObserver = new ContentObserver(new Handler()) {
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
index df63be9..306f0af 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/SliceView.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/SliceView.java
@@ -16,11 +16,10 @@
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.app.slice.Slice;
-import android.app.slice.SliceItem;
import android.arch.lifecycle.Observer;
import android.content.ContentResolver;
import android.content.Context;
@@ -37,6 +36,8 @@
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;
@@ -57,8 +58,9 @@
* <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 Slice#HINT_TITLE} would be placed in the title position of a template. A slice annotated
- * with {@link Slice#HINT_LIST} would present the child items of that slice in a list.
+ * {@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:
*
@@ -114,8 +116,8 @@
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 Slice#HINT_TITLE} on an action in a
- * slice.
+ * 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;
@@ -267,7 +269,7 @@
SliceItem color = SliceQuery.find(mCurrentSlice, FORMAT_COLOR);
List<SliceItem> items = mCurrentSlice.getItems();
SliceItem actionRow = SliceQuery.find(mCurrentSlice, FORMAT_SLICE,
- Slice.HINT_ACTIONS,
+ HINT_ACTIONS,
null);
int mode = getMode();
if (mode != mCurrentView.getMode()) {
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/SmallTemplateView.java b/slices/view/src/main/java/androidx/app/slice/widget/SmallTemplateView.java
index d20c096..1a94e0a 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/SmallTemplateView.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/SmallTemplateView.java
@@ -16,6 +16,11 @@
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;
@@ -25,8 +30,6 @@
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
@@ -41,6 +44,8 @@
import java.util.ArrayList;
import java.util.List;
+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;
@@ -116,12 +121,12 @@
if (items.size() > 0 && FORMAT_SLICE.equals(items.get(0).getFormat())) {
// Check if this slice is appropriate to use to populate small template
SliceItem firstSlice = items.get(0);
- if (firstSlice.hasHint(Slice.HINT_LIST)) {
+ if (firstSlice.hasHint(HINT_LIST)) {
// Check for header, use that if it exists
SliceItem header = SliceQuery.find(firstSlice, FORMAT_SLICE,
null,
new String[] {
- Slice.HINT_LIST_ITEM, Slice.HINT_LIST
+ HINT_LIST_ITEM, HINT_LIST
});
if (header != null) {
return SliceQuery.findFirstSlice(header);
@@ -176,12 +181,12 @@
SliceItem item = items.get(i);
List<String> hints = item.getHints();
String itemType = item.getFormat();
- if (hints.contains(Slice.HINT_TITLE)) {
+ if (hints.contains(HINT_TITLE)) {
// Things with these hints could go in the title / start position
- if ((startItem == null || !startItem.hasHint(Slice.HINT_TITLE))
+ if ((startItem == null || !startItem.hasHint(HINT_TITLE))
&& SliceQuery.isStartType(item)) {
startItem = item;
- } else if ((titleItem == null || !titleItem.hasHint(Slice.HINT_TITLE))
+ } else if ((titleItem == null || !titleItem.hasHint(HINT_TITLE))
&& FORMAT_TEXT.equals(itemType)) {
titleItem = item;
} else {
@@ -287,7 +292,7 @@
}
mToggle = new Switch(getContext());
mEndContainer.addView(mToggle);
- mToggle.setChecked(SliceQuery.hasHints(toggleItem.getSlice(), Slice.HINT_SELECTED));
+ mToggle.setChecked(SliceQuery.hasHints(toggleItem.getSlice(), HINT_SELECTED));
mToggle.setOnCheckedChangeListener((buttonView, isChecked) -> {
try {
PendingIntent pi = toggleItem.getAction();
@@ -328,7 +333,7 @@
iv.setBackground(SliceViewUtil.getDrawable(getContext(),
android.R.attr.selectableItemBackground));
}
- if (color != -1 && !sliceItem.hasHint(Slice.HINT_NO_TINT)) {
+ if (color != -1 && !sliceItem.hasHint(HINT_NO_TINT)) {
iv.setColorFilter(color);
}
container.addView(iv);