Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4575aa52 authored by Mady Mellor's avatar Mady Mellor
Browse files

First version of SliceView (hidden for now)

Very basic template support

Test: to be added
Change-Id: Ie5720345e23909dd12d0b0c2facfee687da2ae1a
parent 67e48e87
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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();
+37 −1
Original line number Diff line number Diff line
@@ -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;

@@ -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;
@@ -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
     */
@@ -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();
    }
}
+34 −2
Original line number Diff line number Diff line
@@ -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;
    }
@@ -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;
            }
        }
@@ -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;
@@ -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;
    }
}
+14 −0
Original line number Diff line number Diff line
@@ -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
     */
@@ -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
     */
+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