Merge "Add SliceSpec to Slice API"
diff --git a/api/current.txt b/api/current.txt
index c6d5430..7e4ee1a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6973,11 +6973,12 @@
public final class Slice implements android.os.Parcelable {
ctor protected Slice(android.os.Parcel);
- method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri);
- method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent);
+ method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
method public int describeContents();
method public java.util.List<java.lang.String> getHints();
method public java.util.List<android.app.slice.SliceItem> getItems();
+ method public android.app.slice.SliceSpec getSpec();
method public android.net.Uri getUri();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.slice.Slice> CREATOR;
@@ -7012,6 +7013,7 @@
method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String...);
method public android.app.slice.Slice.Builder addTimestamp(long, java.util.List<java.lang.String>);
method public android.app.slice.Slice build();
+ method public android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec);
}
public final class SliceItem implements android.os.Parcelable {
@@ -7042,7 +7044,8 @@
method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
method public final java.lang.String getType(android.net.Uri);
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
- method public abstract android.app.slice.Slice onBindSlice(android.net.Uri);
+ method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri);
method public android.net.Uri onMapIntentToUri(android.content.Intent);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
@@ -7051,6 +7054,16 @@
field public static final java.lang.String SLICE_TYPE = "vnd.android.slice";
}
+ public final class SliceSpec implements android.os.Parcelable {
+ ctor public SliceSpec(java.lang.String, int);
+ method public boolean canRender(android.app.slice.SliceSpec);
+ method public int describeContents();
+ method public int getRevision();
+ method public java.lang.String getType();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.slice.SliceSpec> CREATOR;
+ }
+
}
package android.app.usage {
diff --git a/api/system-current.txt b/api/system-current.txt
index 94e3786..a6e1eab 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7417,11 +7417,12 @@
public final class Slice implements android.os.Parcelable {
ctor protected Slice(android.os.Parcel);
- method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri);
- method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent);
+ method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
method public int describeContents();
method public java.util.List<java.lang.String> getHints();
method public java.util.List<android.app.slice.SliceItem> getItems();
+ method public android.app.slice.SliceSpec getSpec();
method public android.net.Uri getUri();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.slice.Slice> CREATOR;
@@ -7456,6 +7457,7 @@
method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String...);
method public android.app.slice.Slice.Builder addTimestamp(long, java.util.List<java.lang.String>);
method public android.app.slice.Slice build();
+ method public android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec);
}
public final class SliceItem implements android.os.Parcelable {
@@ -7486,7 +7488,8 @@
method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
method public final java.lang.String getType(android.net.Uri);
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
- method public abstract android.app.slice.Slice onBindSlice(android.net.Uri);
+ method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri);
method public android.net.Uri onMapIntentToUri(android.content.Intent);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
@@ -7495,6 +7498,16 @@
field public static final java.lang.String SLICE_TYPE = "vnd.android.slice";
}
+ public final class SliceSpec implements android.os.Parcelable {
+ ctor public SliceSpec(java.lang.String, int);
+ method public boolean canRender(android.app.slice.SliceSpec);
+ method public int describeContents();
+ method public int getRevision();
+ method public java.lang.String getType();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.slice.SliceSpec> CREATOR;
+ }
+
}
package android.app.usage {
@@ -48173,8 +48186,8 @@
}
public final class StatsManager {
- method public byte[] getData(java.lang.String);
method public boolean addConfiguration(java.lang.String, byte[], java.lang.String, java.lang.String);
+ method public byte[] getData(java.lang.String);
method public boolean removeConfiguration(java.lang.String);
}
diff --git a/api/test-current.txt b/api/test-current.txt
index aebf380..f9dd65c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -7047,11 +7047,12 @@
public final class Slice implements android.os.Parcelable {
ctor protected Slice(android.os.Parcel);
- method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri);
- method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent);
+ method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
method public int describeContents();
method public java.util.List<java.lang.String> getHints();
method public java.util.List<android.app.slice.SliceItem> getItems();
+ method public android.app.slice.SliceSpec getSpec();
method public android.net.Uri getUri();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.slice.Slice> CREATOR;
@@ -7086,6 +7087,7 @@
method public android.app.slice.Slice.Builder addTimestamp(long, java.lang.String...);
method public android.app.slice.Slice.Builder addTimestamp(long, java.util.List<java.lang.String>);
method public android.app.slice.Slice build();
+ method public android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec);
}
public final class SliceItem implements android.os.Parcelable {
@@ -7116,7 +7118,8 @@
method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
method public final java.lang.String getType(android.net.Uri);
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
- method public abstract android.app.slice.Slice onBindSlice(android.net.Uri);
+ method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri);
method public android.net.Uri onMapIntentToUri(android.content.Intent);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
@@ -7125,6 +7128,16 @@
field public static final java.lang.String SLICE_TYPE = "vnd.android.slice";
}
+ public final class SliceSpec implements android.os.Parcelable {
+ ctor public SliceSpec(java.lang.String, int);
+ method public boolean canRender(android.app.slice.SliceSpec);
+ method public int describeContents();
+ method public int getRevision();
+ method public java.lang.String getType();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.slice.SliceSpec> CREATOR;
+ }
+
}
package android.app.usage {
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index a4aeb67..58fb260 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -143,12 +143,14 @@
private final SliceItem[] mItems;
private final @SliceHint String[] mHints;
+ private SliceSpec mSpec;
private Uri mUri;
- Slice(ArrayList<SliceItem> items, @SliceHint String[] hints, Uri uri) {
+ Slice(ArrayList<SliceItem> items, @SliceHint String[] hints, Uri uri, SliceSpec spec) {
mHints = hints;
mItems = items.toArray(new SliceItem[items.size()]);
mUri = uri;
+ mSpec = spec;
}
protected Slice(Parcel in) {
@@ -159,6 +161,14 @@
mItems[i] = SliceItem.CREATOR.createFromParcel(in);
}
mUri = Uri.CREATOR.createFromParcel(in);
+ mSpec = in.readTypedObject(SliceSpec.CREATOR);
+ }
+
+ /**
+ * @return The spec for this slice
+ */
+ public @Nullable SliceSpec getSpec() {
+ return mSpec;
}
/**
@@ -190,6 +200,7 @@
mItems[i].writeToParcel(dest, flags);
}
mUri.writeToParcel(dest, 0);
+ dest.writeTypedObject(mSpec, flags);
}
@Override
@@ -212,6 +223,7 @@
private final Uri mUri;
private ArrayList<SliceItem> mItems = new ArrayList<>();
private @SliceHint ArrayList<String> mHints = new ArrayList<>();
+ private SliceSpec mSpec;
/**
* Create a builder which will construct a {@link Slice} for the Given Uri.
@@ -247,11 +259,21 @@
}
/**
+ * Add the spec for this slice.
+ */
+ public Builder setSpec(SliceSpec spec) {
+ mSpec = spec;
+ return this;
+ }
+
+ /**
* Add a sub-slice to the slice being constructed
*/
public Builder addSubSlice(@NonNull Slice slice) {
- mItems.add(new SliceItem(slice, SliceItem.TYPE_SLICE, slice.getHints().toArray(
- new String[slice.getHints().size()])));
+ List<String> hints = slice.getHints();
+ slice.mSpec = null;
+ mItems.add(new SliceItem(slice, SliceItem.TYPE_SLICE, hints.toArray(
+ new String[hints.size()])));
return this;
}
@@ -259,7 +281,10 @@
* Add an action to the slice being constructed
*/
public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s) {
- mItems.add(new SliceItem(action, s, SliceItem.TYPE_ACTION, new String[0]));
+ List<String> hints = s.getHints();
+ s.mSpec = null;
+ mItems.add(new SliceItem(action, s, SliceItem.TYPE_ACTION, hints.toArray(
+ new String[hints.size()])));
return this;
}
@@ -351,7 +376,7 @@
* Construct the slice.
*/
public Slice build() {
- return new Slice(mItems, mHints.toArray(new String[mHints.size()]), mUri);
+ return new Slice(mItems, mHints.toArray(new String[mHints.size()]), mUri, mSpec);
}
}
@@ -399,10 +424,12 @@
*
* @param resolver ContentResolver to be used.
* @param uri The URI to a slice provider
+ * @param supportedSpecs List of supported specs.
* @return The Slice provided by the app or null if none is given.
* @see Slice
*/
- public static @Nullable Slice bindSlice(ContentResolver resolver, @NonNull Uri uri) {
+ public static @Nullable Slice bindSlice(ContentResolver resolver, @NonNull Uri uri,
+ List<SliceSpec> supportedSpecs) {
Preconditions.checkNotNull(uri, "uri");
IContentProvider provider = resolver.acquireProvider(uri);
if (provider == null) {
@@ -411,6 +438,8 @@
try {
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
+ extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+ new ArrayList<>(supportedSpecs));
final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE,
null, extras);
Bundle.setDefusable(res, true);
@@ -434,12 +463,14 @@
*
* @param context The context to use.
* @param intent The intent associated with a slice.
+ * @param supportedSpecs List of supported specs.
* @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) {
+ public static @Nullable Slice bindSlice(Context context, @NonNull Intent intent,
+ List<SliceSpec> supportedSpecs) {
Preconditions.checkNotNull(intent, "intent");
Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
"Slice intent must be explicit " + intent);
@@ -448,7 +479,7 @@
// Check if the intent has data for the slice uri on it and use that
final Uri intentData = intent.getData();
if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
- return bindSlice(resolver, intentData);
+ return bindSlice(resolver, intentData, supportedSpecs);
}
// Otherwise ask the app
List<ResolveInfo> providers =
@@ -466,6 +497,8 @@
try {
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+ extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+ new ArrayList<>(supportedSpecs));
final Bundle res = provider.call(resolver.getPackageName(),
SliceProvider.METHOD_MAP_INTENT, null, extras);
if (res == null) {
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 4f9c168..ac5365c 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -36,6 +36,7 @@
import android.os.UserHandle;
import android.util.Log;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
@@ -92,6 +93,10 @@
/**
* @hide
*/
+ public static final String EXTRA_SUPPORTED_SPECS = "supported_specs";
+ /**
+ * @hide
+ */
public static final String METHOD_SLICE = "bind_slice";
/**
* @hide
@@ -117,12 +122,25 @@
* 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>
+ * The slice returned should have a spec that is compatible with one of
+ * the supported specs.
*
+ * @param sliceUri Uri to bind.
+ * @param supportedSpecs List of supported specs.
* @see {@link Slice}.
* @see {@link Slice#HINT_PARTIAL}
*/
- // TODO: Provide alternate notifyChange that takes in the slice (i.e. notifyChange(Uri, Slice)).
- public abstract Slice onBindSlice(Uri sliceUri);
+ public Slice onBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
+ return onBindSlice(sliceUri);
+ }
+
+ /**
+ * @deprecated migrating to {@link #onBindSlice(Uri, List)}
+ */
+ @Deprecated
+ public Slice onBindSlice(Uri sliceUri) {
+ return null;
+ }
/**
* This method must be overridden if an {@link IntentFilter} is specified on the SliceProvider.
@@ -193,8 +211,9 @@
Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
"Slice binding requires the permission BIND_SLICE");
}
+ List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
- Slice s = handleBindSlice(uri);
+ Slice s = handleBindSlice(uri, supportedSpecs);
Bundle b = new Bundle();
b.putParcelable(EXTRA_SLICE, s);
return b;
@@ -203,9 +222,10 @@
"Slice binding requires the permission BIND_SLICE");
Intent intent = extras.getParcelable(EXTRA_INTENT);
Uri uri = onMapIntentToUri(intent);
+ List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
Bundle b = new Bundle();
if (uri != null) {
- Slice s = handleBindSlice(uri);
+ Slice s = handleBindSlice(uri, supportedSpecs);
b.putParcelable(EXTRA_SLICE, s);
} else {
b.putParcelable(EXTRA_SLICE, null);
@@ -215,14 +235,14 @@
return super.call(method, arg, extras);
}
- private Slice handleBindSlice(Uri sliceUri) {
+ private Slice handleBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
if (Looper.myLooper() == Looper.getMainLooper()) {
- return onBindSliceStrict(sliceUri);
+ return onBindSliceStrict(sliceUri, supportedSpecs);
} else {
CountDownLatch latch = new CountDownLatch(1);
Slice[] output = new Slice[1];
Handler.getMain().post(() -> {
- output[0] = onBindSliceStrict(sliceUri);
+ output[0] = onBindSliceStrict(sliceUri, supportedSpecs);
latch.countDown();
});
try {
@@ -234,14 +254,14 @@
}
}
- private Slice onBindSliceStrict(Uri sliceUri) {
+ private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs) {
ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
try {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyDeath()
.build());
- return onBindSlice(sliceUri);
+ return onBindSlice(sliceUri, supportedSpecs);
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
diff --git a/core/java/android/app/slice/SliceSpec.java b/core/java/android/app/slice/SliceSpec.java
new file mode 100644
index 0000000..433b67e
--- /dev/null
+++ b/core/java/android/app/slice/SliceSpec.java
@@ -0,0 +1,117 @@
+/*
+ * 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.app.slice;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Class describing the structure of the data contained within a slice.
+ * <p>
+ * A data version contains a string which describes the type of structure
+ * and a revision which denotes this specific implementation. Revisions are expected
+ * to be backwards compatible and monotonically increasing. Meaning if a
+ * SliceSpec has the same type and an equal or lesser revision,
+ * it is expected to be compatible.
+ * <p>
+ * Apps rendering slices will provide a list of supported versions to the OS which
+ * will also be given to the app. Apps should only return a {@link Slice} with a
+ * {@link SliceSpec} that one of the supported {@link SliceSpec}s provided
+ * {@link #canRender}.
+ *
+ * @see Slice
+ * @see SliceProvider#onBindSlice(Uri)
+ */
+public final class SliceSpec implements Parcelable {
+
+ private final String mType;
+ private final int mRevision;
+
+ public SliceSpec(@NonNull String type, int revision) {
+ mType = type;
+ mRevision = revision;
+ }
+
+ /**
+ * @hide
+ */
+ public SliceSpec(Parcel source) {
+ mType = source.readString();
+ mRevision = source.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mType);
+ dest.writeInt(mRevision);
+ }
+
+ /**
+ * Gets the type of the version.
+ */
+ public String getType() {
+ return mType;
+ }
+
+ /**
+ * Gets the revision of the version.
+ */
+ public int getRevision() {
+ return mRevision;
+ }
+
+ /**
+ * Indicates that this spec can be used to render the specified spec.
+ * <p>
+ * Rendering support is not bi-directional (e.g. Spec v3 can render
+ * Spec v2, but Spec v2 cannot render Spec v3).
+ *
+ * @param candidate candidate format of data.
+ * @return true if versions are compatible.
+ * @see androidx.app.slice.widget.SliceView
+ */
+ public boolean canRender(@NonNull SliceSpec candidate) {
+ if (!mType.equals(candidate.mType)) return false;
+ return mRevision >= candidate.mRevision;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof SliceSpec)) return false;
+ SliceSpec other = (SliceSpec) obj;
+ return mType.equals(other.mType) && mRevision == other.mRevision;
+ }
+
+ public static final Creator<SliceSpec> CREATOR = new Creator<SliceSpec>() {
+ @Override
+ public SliceSpec createFromParcel(Parcel source) {
+ return new SliceSpec(source);
+ }
+
+ @Override
+ public SliceSpec[] newArray(int size) {
+ return new SliceSpec[size];
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index c18f9b6..104a77d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -33,6 +33,8 @@
import com.android.systemui.R;
import com.android.systemui.keyguard.KeyguardSliceProvider;
+import java.util.Collections;
+
/**
* View visible under the clock on the lock screen and AoD.
*/
@@ -75,7 +77,8 @@
super.onAttachedToWindow();
// Set initial content
- showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri));
+ showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri,
+ Collections.emptyList()));
// Make sure we always have the most current slice
getContext().getContentResolver().registerContentObserver(mKeyguardSliceUri,
@@ -154,7 +157,8 @@
@Override
public void onChange(boolean selfChange, Uri uri) {
- showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri));
+ showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri,
+ Collections.emptyList()));
}
}
}