Merge "Add SliceSpec to Slice API"

This commit is contained in:
Jason Monk
2017-11-21 13:50:09 +00:00
committed by Android (Google) Code Review
7 changed files with 242 additions and 29 deletions

View File

@@ -6973,11 +6973,12 @@ package android.app.slice {
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 @@ package android.app.slice {
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 @@ package android.app.slice {
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 @@ package android.app.slice {
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 {

View File

@@ -7417,11 +7417,12 @@ package android.app.slice {
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 @@ package android.app.slice {
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 @@ package android.app.slice {
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 @@ package android.app.slice {
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 @@ package android.util {
}
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);
}

View File

@@ -7047,11 +7047,12 @@ package android.app.slice {
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 @@ package android.app.slice {
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 @@ package android.app.slice {
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 @@ package android.app.slice {
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 {

View File

@@ -143,12 +143,14 @@ public final class Slice implements Parcelable {
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 @@ public final class Slice implements Parcelable {
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 @@ public final class Slice implements Parcelable {
mItems[i].writeToParcel(dest, flags);
}
mUri.writeToParcel(dest, 0);
dest.writeTypedObject(mSpec, flags);
}
@Override
@@ -212,6 +223,7 @@ public final class Slice implements Parcelable {
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.
@@ -246,12 +258,22 @@ public final class Slice implements Parcelable {
return addHints(hints.toArray(new String[hints.size()]));
}
/**
* 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 @@ public final class Slice implements Parcelable {
* 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 @@ public final class Slice implements Parcelable {
* 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 @@ public final class Slice implements Parcelable {
*
* @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 @@ public final class Slice implements Parcelable {
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 @@ public final class Slice implements Parcelable {
*
* @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 @@ public final class Slice implements Parcelable {
// 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 @@ public final class Slice implements Parcelable {
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) {

View File

@@ -36,6 +36,7 @@ import android.os.StrictMode.ThreadPolicy;
import android.os.UserHandle;
import android.util.Log;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
@@ -89,6 +90,10 @@ public abstract class SliceProvider extends ContentProvider {
* @hide
*/
public static final String EXTRA_BIND_URI = "slice_uri";
/**
* @hide
*/
public static final String EXTRA_SUPPORTED_SPECS = "supported_specs";
/**
* @hide
*/
@@ -117,12 +122,25 @@ public abstract class SliceProvider extends ContentProvider {
* 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 @@ public abstract class SliceProvider extends ContentProvider {
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 @@ public abstract class SliceProvider extends ContentProvider {
"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 @@ public abstract class SliceProvider extends ContentProvider {
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 @@ public abstract class SliceProvider extends ContentProvider {
}
}
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);
}

View File

@@ -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];
}
};
}

View File

@@ -33,6 +33,8 @@ import com.android.internal.graphics.ColorUtils;
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 @@ public class KeyguardSliceView extends LinearLayout {
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 @@ public class KeyguardSliceView extends LinearLayout {
@Override
public void onChange(boolean selfChange, Uri uri) {
showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri));
showSlice(Slice.bindSlice(getContext().getContentResolver(), mKeyguardSliceUri,
Collections.emptyList()));
}
}
}