blob: 2f85461a8412eea5d275313efe75d4b64071c3ae [file] [log] [blame]
/*
* Copyright (C) 2015 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 com.android.messaging.ui;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.messaging.Factory;
import com.android.messaging.R;
import com.android.messaging.util.Assert;
import java.util.ArrayList;
import java.util.List;
public class SnackBar {
public static final int LONG_DURATION_IN_MS = 5000;
public static final int SHORT_DURATION_IN_MS = 1000;
public static final int MAX_DURATION_IN_MS = 10000;
public interface SnackBarListener {
void onActionClick();
}
/**
* Defines an action to be performed when the user clicks on the action button on the snack bar
*/
public static class Action {
private final Runnable mActionRunnable;
private final String mActionLabel;
public final static int SNACK_BAR_UNDO = 0;
public final static int SNACK_BAR_RETRY = 1;
private Action(@Nullable Runnable actionRunnable, @Nullable String actionLabel) {
mActionRunnable = actionRunnable;
mActionLabel = actionLabel;
}
Runnable getActionRunnable() {
return mActionRunnable;
}
String getActionLabel() {
return mActionLabel;
}
public static Action createUndoAction(final Runnable undoRunnable) {
return createCustomAction(undoRunnable, Factory.get().getApplicationContext()
.getString(R.string.snack_bar_undo));
}
public static Action createRetryAction(final Runnable retryRunnable) {
return createCustomAction(retryRunnable, Factory.get().getApplicationContext()
.getString(R.string.snack_bar_retry));
}
public static Action createCustomAction(final Runnable runnable, final String actionLabel) {
return new Action(runnable, actionLabel);
}
}
/**
* Defines the placement of the snack bar (e.g. anchored view, anchor gravity).
*/
public static class Placement {
private final View mAnchorView;
private final boolean mAnchorAbove;
private Placement(@NonNull final View anchorView, final boolean anchorAbove) {
Assert.notNull(anchorView);
mAnchorView = anchorView;
mAnchorAbove = anchorAbove;
}
public View getAnchorView() {
return mAnchorView;
}
public boolean getAnchorAbove() {
return mAnchorAbove;
}
/**
* Anchor the snack bar above the given {@code anchorView}.
*/
public static Placement above(final View anchorView) {
return new Placement(anchorView, true);
}
/**
* Anchor the snack bar below the given {@code anchorView}.
*/
public static Placement below(final View anchorView) {
return new Placement(anchorView, false);
}
}
public static class Builder {
private static final List<SnackBarInteraction> NO_INTERACTIONS =
new ArrayList<SnackBarInteraction>();
private final Context mContext;
private final SnackBarManager mSnackBarManager;
private String mSnackBarMessage;
private int mDuration = LONG_DURATION_IN_MS;
private List<SnackBarInteraction> mInteractions = NO_INTERACTIONS;
private Action mAction;
private Placement mPlacement;
// The parent view is only used to get a window token and doesn't affect the layout
private View mParentView;
public Builder(final SnackBarManager snackBarManager, final View parentView) {
Assert.notNull(snackBarManager);
Assert.notNull(parentView);
mSnackBarManager = snackBarManager;
mContext = parentView.getContext();
mParentView = parentView;
}
public Builder setText(final String snackBarMessage) {
Assert.isTrue(!TextUtils.isEmpty(snackBarMessage));
mSnackBarMessage = snackBarMessage;
return this;
}
public Builder setAction(final Action action) {
mAction = action;
return this;
}
/**
* Sets the duration to show this toast for in milliseconds.
*/
public Builder setDuration(final int duration) {
Assert.isTrue(0 < duration && duration < MAX_DURATION_IN_MS);
mDuration = duration;
return this;
}
/**
* Sets the components that this toast's animation will interact with. These components may
* be animated to make room for the toast.
*/
public Builder withInteractions(final List<SnackBarInteraction> interactions) {
mInteractions = interactions;
return this;
}
/**
* Place the snack bar with the given placement requirement.
*/
public Builder withPlacement(final Placement placement) {
Assert.isNull(mPlacement);
mPlacement = placement;
return this;
}
public SnackBar build() {
return new SnackBar(this);
}
public void show() {
mSnackBarManager.show(build());
}
}
private final View mRootView;
private final Context mContext;
private final View mSnackBarView;
private final String mText;
private final int mDuration;
private final List<SnackBarInteraction> mInteractions;
private final Action mAction;
private final Placement mPlacement;
private final TextView mActionTextView;
private final TextView mMessageView;
private final FrameLayout mMessageWrapper;
private final View mParentView;
private SnackBarListener mListener;
private SnackBar(final Builder builder) {
mContext = builder.mContext;
mRootView = LayoutInflater.from(mContext).inflate(R.layout.snack_bar,
null /* WindowManager will show this in show() below */);
mSnackBarView = mRootView.findViewById(R.id.snack_bar);
mText = builder.mSnackBarMessage;
mDuration = builder.mDuration;
mAction = builder.mAction;
mPlacement = builder.mPlacement;
mParentView = builder.mParentView;
if (builder.mInteractions == null) {
mInteractions = new ArrayList<SnackBarInteraction>();
} else {
mInteractions = builder.mInteractions;
}
mActionTextView = (TextView) mRootView.findViewById(R.id.snack_bar_action);
mMessageView = (TextView) mRootView.findViewById(R.id.snack_bar_message);
mMessageWrapper = (FrameLayout) mRootView.findViewById(R.id.snack_bar_message_wrapper);
setUpButton();
setUpTextLines();
}
public Context getContext() {
return mContext;
}
public View getRootView() {
return mRootView;
}
public View getParentView() {
return mParentView;
}
public View getSnackBarView() {
return mSnackBarView;
}
public String getMessageText() {
return mText;
}
public String getActionLabel() {
if (mAction == null) {
return null;
}
return mAction.getActionLabel();
}
public int getDuration() {
return mDuration;
}
public Placement getPlacement() {
return mPlacement;
}
public List<SnackBarInteraction> getInteractions() {
return mInteractions;
}
public void setEnabled(final boolean enabled) {
mActionTextView.setClickable(enabled);
}
public void setListener(final SnackBarListener snackBarListener) {
mListener = snackBarListener;
}
private void setUpButton() {
if (mAction == null || mAction.getActionRunnable() == null) {
mActionTextView.setVisibility(View.GONE);
// In the XML layout we add left/right padding to the button to add space between
// the message text and the button and on the right side we add padding to put space
// between the button and the edge of the snack bar. This is so the button can use the
// padding area as part of it's click target. Since we have no button, we need to put
// some margin on the right side. While the left margin is already set on the wrapper,
// we're setting it again to not have to make a special case for RTL.
final MarginLayoutParams lp = (MarginLayoutParams) mMessageWrapper.getLayoutParams();
final int leftRightMargin = mContext.getResources().getDimensionPixelSize(
R.dimen.snack_bar_left_right_margin);
lp.leftMargin = leftRightMargin;
lp.rightMargin = leftRightMargin;
mMessageWrapper.setLayoutParams(lp);
} else {
mActionTextView.setVisibility(View.VISIBLE);
mActionTextView.setText(mAction.getActionLabel());
mActionTextView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
mAction.getActionRunnable().run();
if (mListener != null) {
mListener.onActionClick();
}
}
});
}
}
private void setUpTextLines() {
if (mText == null) {
mMessageView.setVisibility(View.GONE);
} else {
mMessageView.setVisibility(View.VISIBLE);
mMessageView.setText(mText);
}
}
}