blob: 55bbca12c5652ba180a22f82943dbdd1b8ec85a4 [file] [log] [blame]
/*
* 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 androidx.slice.widget;
import static android.app.slice.Slice.HINT_NO_TINT;
import static android.app.slice.SliceItem.FORMAT_IMAGE;
import static android.app.slice.SliceItem.FORMAT_REMOTE_INPUT;
import static androidx.slice.core.SliceHints.ICON_IMAGE;
import android.app.PendingIntent.CanceledException;
import android.app.RemoteInput;
import android.app.slice.Slice;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.os.Build;
import android.util.Log;
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;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
import androidx.core.widget.ImageViewCompat;
import androidx.slice.SliceItem;
import androidx.slice.core.SliceActionImpl;
import androidx.slice.core.SliceQuery;
import java.util.List;
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
public class ActionRow extends FrameLayout {
private static final int MAX_ACTIONS = 5;
private static final String TAG = "ActionRow";
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);
int mode = (Integer) view.getTag();
boolean tint = mode == ICON_IMAGE;
if (tint) {
ImageViewCompat.setImageTintList((ImageView) view, ColorStateList.valueOf(mColor));
}
}
}
private ImageView addAction(IconCompat icon, boolean allowTint) {
ImageView imageView = new ImageView(getContext());
imageView.setPadding(mIconPadding, mIconPadding, mIconPadding, mIconPadding);
imageView.setScaleType(ScaleType.FIT_CENTER);
imageView.setImageDrawable(icon.loadDrawable(getContext()));
if (allowTint) {
ImageViewCompat.setImageTintList(imageView, ColorStateList.valueOf(mColor));
}
imageView.setBackground(SliceViewUtil.getDrawable(getContext(),
android.R.attr.selectableItemBackground));
imageView.setTag(allowTint);
addAction(imageView);
return imageView;
}
/**
* Set the actions and color for this action row.
*/
public void setActions(@NonNull List<SliceItem> actions, int color) {
removeAllViews();
mActionsGroup.removeAllViews();
addView(mActionsGroup);
if (color != -1) {
setColor(color);
}
for (final SliceItem action : actions) {
if (mActionsGroup.getChildCount() >= MAX_ACTIONS) {
return;
}
final SliceItem input = SliceQuery.find(action, FORMAT_REMOTE_INPUT);
final SliceItem image = SliceQuery.find(action, FORMAT_IMAGE);
if (input != null && image != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
handleSetRemoteInputActions(input, image, action);
} else {
Log.w(TAG, "Received RemoteInput on API <20 " + input);
}
} else if (action.hasHint(Slice.HINT_SHORTCUT)) {
final SliceActionImpl ac = new SliceActionImpl(action);
IconCompat iconItem = ac.getIcon();
if (iconItem != null && ac.getActionItem() != null) {
boolean tint = ac.getImageMode() == ICON_IMAGE;
addAction(iconItem, tint).setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
try {
// TODO - should log events here
ac.getActionItem().fireAction(null, null);
} 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));
}
@RequiresApi(21)
private void handleSetRemoteInputActions(final SliceItem input, SliceItem image,
final SliceItem action) {
if (input.getRemoteInput().getAllowFreeFormInput()) {
boolean tint = !image.hasHint(HINT_NO_TINT);
addAction(image.getIcon(), tint).setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
handleRemoteInputClick(v, action,
input.getRemoteInput());
}
});
createRemoteInputView(mColor, getContext());
}
}
@RequiresApi(21)
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);
}
@RequiresApi(21)
private boolean handleRemoteInputClick(View view, SliceItem action,
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.setAction(action);
riv.setRemoteInput(new RemoteInput[] {
input
}, input);
riv.focusAnimated();
return true;
}
@RequiresApi(21)
private RemoteInputView findRemoteInputView(View v) {
if (v == null) {
return null;
}
return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG);
}
}