Loading core/java/android/content/ContentProvider.java +2 −1 Original line number Diff line number Diff line Loading @@ -2099,7 +2099,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 { public static Uri maybeAddUserId(Uri uri, int userId) { if (uri == null) return null; if (userId != UserHandle.USER_CURRENT && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { && (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) || ContentResolver.SCHEME_SLICE.equals(uri.getScheme()))) { if (!uriHasUserId(uri)) { //We don't add the user Id if there's already one Uri.Builder builder = uri.buildUpon(); Loading core/java/android/slice/Slice.java +37 −1 Original line number Diff line number Diff line Loading @@ -35,6 +35,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.widget.RemoteViews; import com.android.internal.util.ArrayUtils; import java.util.ArrayList; import java.util.Arrays; Loading Loading @@ -136,7 +138,7 @@ public final class Slice implements Parcelable { } /** * @return The Uri that this slice represents. * @return The Uri that this Slice represents. */ public Uri getUri() { return mUri; Loading Loading @@ -190,6 +192,13 @@ public final class Slice implements Parcelable { return 0; } /** * @hide */ public boolean hasHint(@SliceHint String hint) { return ArrayUtils.contains(mHints, hint); } /** * A Builder used to construct {@link Slice}s */ Loading Loading @@ -308,4 +317,31 @@ public final class Slice implements Parcelable { return new Slice[size]; } }; /** * @hide * @return A string representation of this slice. */ public String getString() { return getString(""); } private String getString(String indent) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < mItems.length; i++) { sb.append(indent); if (mItems[i].getType() == TYPE_SLICE) { sb.append("slice:\n"); sb.append(mItems[i].getSlice().getString(indent + " ")); } else if (mItems[i].getType() == TYPE_TEXT) { sb.append("text: "); sb.append(mItems[i].getText()); sb.append("\n"); } else { sb.append(SliceItem.typeToString(mItems[i].getType())); sb.append("\n"); } } return sb.toString(); } } core/java/android/slice/SliceItem.java +34 −2 Original line number Diff line number Diff line Loading @@ -132,6 +132,13 @@ public final class SliceItem implements Parcelable { mHints = ArrayUtils.appendElement(String.class, mHints, hint); } /** * @hide */ public void removeHint(String hint) { ArrayUtils.removeElement(String.class, mHints, hint); } public @SliceType int getType() { return mType; } Loading Loading @@ -230,7 +237,7 @@ public final class SliceItem implements Parcelable { public boolean hasHints(@SliceHint String[] hints) { if (hints == null) return true; for (String hint : hints) { if (!ArrayUtils.contains(mHints, hint)) { if (!TextUtils.isEmpty(hint) && !ArrayUtils.contains(mHints, hint)) { return false; } } Loading @@ -241,7 +248,7 @@ public final class SliceItem implements Parcelable { * @hide */ public boolean hasAnyHints(@SliceHint String[] hints) { if (hints == null) return true; if (hints == null) return false; for (String hint : hints) { if (ArrayUtils.contains(mHints, hint)) { return true; Loading Loading @@ -309,4 +316,29 @@ public final class SliceItem implements Parcelable { return new SliceItem[size]; } }; /** * @hide */ public static String typeToString(int type) { switch (type) { case TYPE_SLICE: return "Slice"; case TYPE_TEXT: return "Text"; case TYPE_IMAGE: return "Image"; case TYPE_ACTION: return "Action"; case TYPE_REMOTE_VIEW: return "RemoteView"; case TYPE_COLOR: return "Color"; case TYPE_TIMESTAMP: return "Timestamp"; case TYPE_REMOTE_INPUT: return "RemoteInput"; } return "Unrecognized type: " + type; } } core/java/android/slice/SliceQuery.java +14 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,13 @@ public class SliceQuery { return stream(container).filter(s -> (s == item)).findAny().isPresent(); } /** * @hide */ public static List<SliceItem> findAll(SliceItem s, int type) { return findAll(s, type, (String[]) null, null); } /** * @hide */ Loading @@ -82,6 +89,13 @@ public class SliceQuery { return find(s, type, new String[]{ hints }, new String[]{ nonHints }); } /** * @hide */ public static SliceItem find(Slice s, int type) { return find(s, type, (String[]) null, null); } /** * @hide */ Loading core/java/android/slice/views/ActionRow.java 0 → 100644 +201 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.slice.views; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; import android.app.RemoteInput; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; import android.graphics.drawable.Icon; import android.os.AsyncTask; import android.slice.Slice; import android.slice.SliceItem; import android.slice.SliceQuery; import android.util.TypedValue; import android.view.View; import android.view.ViewParent; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ImageView.ScaleType; import android.widget.LinearLayout; import android.widget.TextView; /** * @hide */ public class ActionRow extends FrameLayout { private static final int MAX_ACTIONS = 5; private final int mSize; private final int mIconPadding; private final LinearLayout mActionsGroup; private final boolean mFullActions; private int mColor = Color.BLACK; public ActionRow(Context context, boolean fullActions) { super(context); mFullActions = fullActions; mSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, context.getResources().getDisplayMetrics()); mIconPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12, context.getResources().getDisplayMetrics()); mActionsGroup = new LinearLayout(context); mActionsGroup.setOrientation(LinearLayout.HORIZONTAL); mActionsGroup.setLayoutParams( new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); addView(mActionsGroup); } private void setColor(int color) { mColor = color; for (int i = 0; i < mActionsGroup.getChildCount(); i++) { View view = mActionsGroup.getChildAt(i); SliceItem item = (SliceItem) view.getTag(); boolean tint = !item.hasHint(Slice.HINT_NO_TINT); if (tint) { ((ImageView) view).setImageTintList(ColorStateList.valueOf(mColor)); } } } private ImageView addAction(Icon icon, boolean allowTint, SliceItem image) { ImageView imageView = new ImageView(getContext()); imageView.setPadding(mIconPadding, mIconPadding, mIconPadding, mIconPadding); imageView.setScaleType(ScaleType.FIT_CENTER); imageView.setImageIcon(icon); if (allowTint) { imageView.setImageTintList(ColorStateList.valueOf(mColor)); } imageView.setBackground(SliceViewUtil.getDrawable(getContext(), android.R.attr.selectableItemBackground)); imageView.setTag(image); addAction(imageView); return imageView; } /** * Set the actions and color for this action row. */ public void setActions(SliceItem actionRow, SliceItem defColor) { removeAllViews(); mActionsGroup.removeAllViews(); addView(mActionsGroup); SliceItem color = SliceQuery.find(actionRow, SliceItem.TYPE_COLOR); if (color == null) { color = defColor; } if (color != null) { setColor(color.getColor()); } SliceQuery.findAll(actionRow, SliceItem.TYPE_ACTION).forEach(action -> { if (mActionsGroup.getChildCount() >= MAX_ACTIONS) { return; } SliceItem image = SliceQuery.find(action, SliceItem.TYPE_IMAGE); if (image == null) { return; } boolean tint = !image.hasHint(Slice.HINT_NO_TINT); SliceItem input = SliceQuery.find(action, SliceItem.TYPE_REMOTE_INPUT); if (input != null && input.getRemoteInput().getAllowFreeFormInput()) { addAction(image.getIcon(), tint, image).setOnClickListener( v -> handleRemoteInputClick(v, action.getAction(), input.getRemoteInput())); createRemoteInputView(mColor, getContext()); } else { addAction(image.getIcon(), tint, image).setOnClickListener(v -> AsyncTask.execute( () -> { try { action.getAction().send(); } catch (CanceledException e) { e.printStackTrace(); } })); } }); setVisibility(getChildCount() != 0 ? View.VISIBLE : View.GONE); } private void addAction(View child) { mActionsGroup.addView(child, new LinearLayout.LayoutParams(mSize, mSize, 1)); } private void createRemoteInputView(int color, Context context) { View riv = RemoteInputView.inflate(context, this); riv.setVisibility(View.INVISIBLE); addView(riv, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); riv.setBackgroundColor(color); } private boolean handleRemoteInputClick(View view, PendingIntent pendingIntent, RemoteInput input) { if (input == null) { return false; } ViewParent p = view.getParent().getParent(); RemoteInputView riv = null; while (p != null) { if (p instanceof View) { View pv = (View) p; riv = findRemoteInputView(pv); if (riv != null) { break; } } p = p.getParent(); } if (riv == null) { return false; } int width = view.getWidth(); if (view instanceof TextView) { // Center the reveal on the text which might be off-center from the TextView TextView tv = (TextView) view; if (tv.getLayout() != null) { int innerWidth = (int) tv.getLayout().getLineWidth(0); innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight(); width = Math.min(width, innerWidth); } } int cx = view.getLeft() + width / 2; int cy = view.getTop() + view.getHeight() / 2; int w = riv.getWidth(); int h = riv.getHeight(); int r = Math.max( Math.max(cx + cy, cx + (h - cy)), Math.max((w - cx) + cy, (w - cx) + (h - cy))); riv.setRevealParameters(cx, cy, r); riv.setPendingIntent(pendingIntent); riv.setRemoteInput(new RemoteInput[] { input }, input); riv.focusAnimated(); return true; } private RemoteInputView findRemoteInputView(View v) { if (v == null) { return null; } return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG); } } Loading
core/java/android/content/ContentProvider.java +2 −1 Original line number Diff line number Diff line Loading @@ -2099,7 +2099,8 @@ public abstract class ContentProvider implements ComponentCallbacks2 { public static Uri maybeAddUserId(Uri uri, int userId) { if (uri == null) return null; if (userId != UserHandle.USER_CURRENT && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { && (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) || ContentResolver.SCHEME_SLICE.equals(uri.getScheme()))) { if (!uriHasUserId(uri)) { //We don't add the user Id if there's already one Uri.Builder builder = uri.buildUpon(); Loading
core/java/android/slice/Slice.java +37 −1 Original line number Diff line number Diff line Loading @@ -35,6 +35,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.widget.RemoteViews; import com.android.internal.util.ArrayUtils; import java.util.ArrayList; import java.util.Arrays; Loading Loading @@ -136,7 +138,7 @@ public final class Slice implements Parcelable { } /** * @return The Uri that this slice represents. * @return The Uri that this Slice represents. */ public Uri getUri() { return mUri; Loading Loading @@ -190,6 +192,13 @@ public final class Slice implements Parcelable { return 0; } /** * @hide */ public boolean hasHint(@SliceHint String hint) { return ArrayUtils.contains(mHints, hint); } /** * A Builder used to construct {@link Slice}s */ Loading Loading @@ -308,4 +317,31 @@ public final class Slice implements Parcelable { return new Slice[size]; } }; /** * @hide * @return A string representation of this slice. */ public String getString() { return getString(""); } private String getString(String indent) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < mItems.length; i++) { sb.append(indent); if (mItems[i].getType() == TYPE_SLICE) { sb.append("slice:\n"); sb.append(mItems[i].getSlice().getString(indent + " ")); } else if (mItems[i].getType() == TYPE_TEXT) { sb.append("text: "); sb.append(mItems[i].getText()); sb.append("\n"); } else { sb.append(SliceItem.typeToString(mItems[i].getType())); sb.append("\n"); } } return sb.toString(); } }
core/java/android/slice/SliceItem.java +34 −2 Original line number Diff line number Diff line Loading @@ -132,6 +132,13 @@ public final class SliceItem implements Parcelable { mHints = ArrayUtils.appendElement(String.class, mHints, hint); } /** * @hide */ public void removeHint(String hint) { ArrayUtils.removeElement(String.class, mHints, hint); } public @SliceType int getType() { return mType; } Loading Loading @@ -230,7 +237,7 @@ public final class SliceItem implements Parcelable { public boolean hasHints(@SliceHint String[] hints) { if (hints == null) return true; for (String hint : hints) { if (!ArrayUtils.contains(mHints, hint)) { if (!TextUtils.isEmpty(hint) && !ArrayUtils.contains(mHints, hint)) { return false; } } Loading @@ -241,7 +248,7 @@ public final class SliceItem implements Parcelable { * @hide */ public boolean hasAnyHints(@SliceHint String[] hints) { if (hints == null) return true; if (hints == null) return false; for (String hint : hints) { if (ArrayUtils.contains(mHints, hint)) { return true; Loading Loading @@ -309,4 +316,29 @@ public final class SliceItem implements Parcelable { return new SliceItem[size]; } }; /** * @hide */ public static String typeToString(int type) { switch (type) { case TYPE_SLICE: return "Slice"; case TYPE_TEXT: return "Text"; case TYPE_IMAGE: return "Image"; case TYPE_ACTION: return "Action"; case TYPE_REMOTE_VIEW: return "RemoteView"; case TYPE_COLOR: return "Color"; case TYPE_TIMESTAMP: return "Timestamp"; case TYPE_REMOTE_INPUT: return "RemoteInput"; } return "Unrecognized type: " + type; } }
core/java/android/slice/SliceQuery.java +14 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,13 @@ public class SliceQuery { return stream(container).filter(s -> (s == item)).findAny().isPresent(); } /** * @hide */ public static List<SliceItem> findAll(SliceItem s, int type) { return findAll(s, type, (String[]) null, null); } /** * @hide */ Loading @@ -82,6 +89,13 @@ public class SliceQuery { return find(s, type, new String[]{ hints }, new String[]{ nonHints }); } /** * @hide */ public static SliceItem find(Slice s, int type) { return find(s, type, (String[]) null, null); } /** * @hide */ Loading
core/java/android/slice/views/ActionRow.java 0 → 100644 +201 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.slice.views; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; import android.app.RemoteInput; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; import android.graphics.drawable.Icon; import android.os.AsyncTask; import android.slice.Slice; import android.slice.SliceItem; import android.slice.SliceQuery; import android.util.TypedValue; import android.view.View; import android.view.ViewParent; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ImageView.ScaleType; import android.widget.LinearLayout; import android.widget.TextView; /** * @hide */ public class ActionRow extends FrameLayout { private static final int MAX_ACTIONS = 5; private final int mSize; private final int mIconPadding; private final LinearLayout mActionsGroup; private final boolean mFullActions; private int mColor = Color.BLACK; public ActionRow(Context context, boolean fullActions) { super(context); mFullActions = fullActions; mSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, context.getResources().getDisplayMetrics()); mIconPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12, context.getResources().getDisplayMetrics()); mActionsGroup = new LinearLayout(context); mActionsGroup.setOrientation(LinearLayout.HORIZONTAL); mActionsGroup.setLayoutParams( new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); addView(mActionsGroup); } private void setColor(int color) { mColor = color; for (int i = 0; i < mActionsGroup.getChildCount(); i++) { View view = mActionsGroup.getChildAt(i); SliceItem item = (SliceItem) view.getTag(); boolean tint = !item.hasHint(Slice.HINT_NO_TINT); if (tint) { ((ImageView) view).setImageTintList(ColorStateList.valueOf(mColor)); } } } private ImageView addAction(Icon icon, boolean allowTint, SliceItem image) { ImageView imageView = new ImageView(getContext()); imageView.setPadding(mIconPadding, mIconPadding, mIconPadding, mIconPadding); imageView.setScaleType(ScaleType.FIT_CENTER); imageView.setImageIcon(icon); if (allowTint) { imageView.setImageTintList(ColorStateList.valueOf(mColor)); } imageView.setBackground(SliceViewUtil.getDrawable(getContext(), android.R.attr.selectableItemBackground)); imageView.setTag(image); addAction(imageView); return imageView; } /** * Set the actions and color for this action row. */ public void setActions(SliceItem actionRow, SliceItem defColor) { removeAllViews(); mActionsGroup.removeAllViews(); addView(mActionsGroup); SliceItem color = SliceQuery.find(actionRow, SliceItem.TYPE_COLOR); if (color == null) { color = defColor; } if (color != null) { setColor(color.getColor()); } SliceQuery.findAll(actionRow, SliceItem.TYPE_ACTION).forEach(action -> { if (mActionsGroup.getChildCount() >= MAX_ACTIONS) { return; } SliceItem image = SliceQuery.find(action, SliceItem.TYPE_IMAGE); if (image == null) { return; } boolean tint = !image.hasHint(Slice.HINT_NO_TINT); SliceItem input = SliceQuery.find(action, SliceItem.TYPE_REMOTE_INPUT); if (input != null && input.getRemoteInput().getAllowFreeFormInput()) { addAction(image.getIcon(), tint, image).setOnClickListener( v -> handleRemoteInputClick(v, action.getAction(), input.getRemoteInput())); createRemoteInputView(mColor, getContext()); } else { addAction(image.getIcon(), tint, image).setOnClickListener(v -> AsyncTask.execute( () -> { try { action.getAction().send(); } catch (CanceledException e) { e.printStackTrace(); } })); } }); setVisibility(getChildCount() != 0 ? View.VISIBLE : View.GONE); } private void addAction(View child) { mActionsGroup.addView(child, new LinearLayout.LayoutParams(mSize, mSize, 1)); } private void createRemoteInputView(int color, Context context) { View riv = RemoteInputView.inflate(context, this); riv.setVisibility(View.INVISIBLE); addView(riv, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); riv.setBackgroundColor(color); } private boolean handleRemoteInputClick(View view, PendingIntent pendingIntent, RemoteInput input) { if (input == null) { return false; } ViewParent p = view.getParent().getParent(); RemoteInputView riv = null; while (p != null) { if (p instanceof View) { View pv = (View) p; riv = findRemoteInputView(pv); if (riv != null) { break; } } p = p.getParent(); } if (riv == null) { return false; } int width = view.getWidth(); if (view instanceof TextView) { // Center the reveal on the text which might be off-center from the TextView TextView tv = (TextView) view; if (tv.getLayout() != null) { int innerWidth = (int) tv.getLayout().getLineWidth(0); innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight(); width = Math.min(width, innerWidth); } } int cx = view.getLeft() + width / 2; int cy = view.getTop() + view.getHeight() / 2; int w = riv.getWidth(); int h = riv.getHeight(); int r = Math.max( Math.max(cx + cy, cx + (h - cy)), Math.max((w - cx) + cy, (w - cx) + (h - cy))); riv.setRevealParameters(cx, cy, r); riv.setPendingIntent(pendingIntent); riv.setRemoteInput(new RemoteInput[] { input }, input); riv.focusAnimated(); return true; } private RemoteInputView findRemoteInputView(View v) { if (v == null) { return null; } return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG); } }