summaryrefslogtreecommitdiffstats
path: root/src/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/mail/ui/AbstractActivityController.java2
-rw-r--r--src/com/android/mail/ui/ActionableToastBar.java261
-rw-r--r--src/com/android/mail/ui/OnePaneController.java4
-rw-r--r--src/com/android/mail/ui/TwoPaneController.java51
4 files changed, 121 insertions, 197 deletions
diff --git a/src/com/android/mail/ui/AbstractActivityController.java b/src/com/android/mail/ui/AbstractActivityController.java
index 0b291d11e..90772f36d 100644
--- a/src/com/android/mail/ui/AbstractActivityController.java
+++ b/src/com/android/mail/ui/AbstractActivityController.java
@@ -4062,9 +4062,7 @@ public abstract class AbstractActivityController implements ActivityController,
return;
}
mToastBar.show(listener,
- R.drawable.ic_alert_white,
Utils.getSyncStatusText(mActivity.getActivityContext(), lastSyncResult),
- false, /* showActionIcon */
actionTextResourceId,
replaceVisibleToast,
new ToastBarOperation(1, 0, ToastBarOperation.ERROR, false, folder));
diff --git a/src/com/android/mail/ui/ActionableToastBar.java b/src/com/android/mail/ui/ActionableToastBar.java
index e940f1b68..69765e23e 100644
--- a/src/com/android/mail/ui/ActionableToastBar.java
+++ b/src/com/android/mail/ui/ActionableToastBar.java
@@ -17,30 +17,23 @@ package com.android.mail.ui;
import android.animation.Animator;
import android.animation.AnimatorInflater;
-import android.annotation.SuppressLint;
import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.ClipDrawable;
-import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.support.annotation.StringRes;
+import android.text.TextUtils;
import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
+import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.mail.R;
-import com.android.mail.utils.Utils;
-import com.android.mail.utils.ViewUtils;
/**
* A custom {@link View} that exposes an action to the user.
*/
-public class ActionableToastBar extends LinearLayout {
+public class ActionableToastBar extends FrameLayout {
+
private boolean mHidden = false;
private Animator mShowAnimation;
private Animator mHideAnimation;
@@ -50,21 +43,21 @@ public class ActionableToastBar extends LinearLayout {
/** How long toast will last in ms */
private static final long TOAST_LIFETIME = 15*1000L;
- /** Icon for the description. */
- private ImageView mActionDescriptionIcon;
- /** The clickable view */
- private View mActionButton;
- /** The divider between the description and the action button. */
- private View mDivider;
- /** Icon for the action button. */
- private View mActionIcon;
- /** The view that contains the description. */
- private TextView mActionDescriptionView;
- /** The view that contains the text for the action button. */
- private TextView mActionText;
- private ToastBarOperation mOperation;
+ /** The view that contains the description when laid out as a single line. */
+ private TextView mSingleLineDescriptionView;
+
+ /** The view that contains the text for the action button when laid out as a single line. */
+ private TextView mSingleLineActionView;
- private ClipBoundsDrawable mButtonDrawable;
+ /** The view that contains the description when laid out as a multiple lines;
+ * always <tt>null</tt> in two-pane layouts. */
+ private TextView mMultiLineDescriptionView;
+
+ /** The view that contains the text for the action button when laid out as a multiple lines;
+ * always <tt>null</tt> in two-pane layouts. */
+ private TextView mMultiLineActionView;
+
+ private ToastBarOperation mOperation;
public ActionableToastBar(Context context) {
this(context, null);
@@ -80,48 +73,40 @@ public class ActionableToastBar extends LinearLayout {
mRunnable = new Runnable() {
@Override
public void run() {
- if(!mHidden) {
+ if (!mHidden) {
hide(true, false /* actionClicked */);
}
}
};
- LayoutInflater.from(context).inflate(R.layout.actionable_toast_row, this, true);
}
@Override
- @SuppressLint("NewApi")
protected void onFinishInflate() {
super.onFinishInflate();
- mActionDescriptionIcon = (ImageView) findViewById(R.id.description_icon);
- mActionDescriptionView = (TextView) findViewById(R.id.description_text);
- mActionButton = findViewById(R.id.action_button);
- mDivider = findViewById(R.id.divider);
- mActionIcon = findViewById(R.id.action_icon);
- mActionText = (TextView) findViewById(R.id.action_text);
-
- if (Utils.isRunningKitkatOrLater()) {
- // Wrap the drawable so we can clip the bounds (see explanation in onLayout).
- final Drawable buttonToastBackground = mActionButton.getBackground();
- mActionButton.setBackground(null);
- mButtonDrawable = new ClipBoundsDrawable(buttonToastBackground);
- mActionButton.setBackground(mButtonDrawable);
- }
+ mSingleLineDescriptionView = (TextView) findViewById(R.id.description_text);
+ mSingleLineActionView = (TextView) findViewById(R.id.action_text);
+ mMultiLineDescriptionView = (TextView) findViewById(R.id.multiline_description_text);
+ mMultiLineActionView = (TextView) findViewById(R.id.multiline_action_text);
}
@Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
-
- // The button has the same background on pressed state so it will have rounded corners
- // on both the right edge. We clip the background before the divider to remove the
- // rounded edge there, creating a split-pill button effect.
- if (mButtonDrawable != null) {
- final boolean isRtl = ViewUtils.isViewRtl(this);
- mButtonDrawable.setClipBounds(
- (isRtl ? 0 : mDivider.getLeft()), 0,
- (isRtl ? mDivider.getRight() : mActionButton.getWidth()),
- mActionButton.getHeight());
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final boolean showAction = !TextUtils.isEmpty(mSingleLineActionView.getText());
+
+ // configure the UI assuming the description fits on a single line
+ setVisibility(false /* multiLine */, showAction);
+
+ // measure the view and its content
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ // if the description does not fit, switch to multi line display if one is present
+ final boolean descriptionIsMultiLine = mSingleLineDescriptionView.getLineCount() > 1;
+ final boolean haveMultiLineView = mMultiLineDescriptionView != null;
+ if (descriptionIsMultiLine && haveMultiLineView) {
+ setVisibility(true /* multiLine */, showAction);
+
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
@@ -134,28 +119,25 @@ public class ActionableToastBar extends LinearLayout {
* to return <code>true</code>, the
* {@link ToastBarOperation#onActionClicked(android.content.Context)}
* will override this listener and be called instead.
- * @param descriptionIconResourceId resource ID for the description icon or
- * 0 if no icon should be shown
* @param descriptionText a description text to show in the toast bar
- * @param showActionIcon if true, the action button icon should be shown
- * @param actionTextResource resource ID for the text to show in the action button
+ * @param actionTextResourceId resource ID for the text to show in the action button
* @param replaceVisibleToast if true, this toast should replace any currently visible toast.
* Otherwise, skip showing this toast.
* @param op the operation that corresponds to the specific toast being shown
*/
- public void show(final ActionClickedListener listener, int descriptionIconResourceId,
- CharSequence descriptionText, boolean showActionIcon, int actionTextResource,
- boolean replaceVisibleToast, final ToastBarOperation op) {
-
+ public void show(final ActionClickedListener listener, final CharSequence descriptionText,
+ @StringRes final int actionTextResourceId, final boolean replaceVisibleToast,
+ final ToastBarOperation op) {
if (!mHidden && !replaceVisibleToast) {
return;
}
+
// Remove any running delayed animations first
mFadeOutHandler.removeCallbacks(mRunnable);
mOperation = op;
- mActionButton.setOnClickListener(new OnClickListener() {
+ setActionClickListener(new OnClickListener() {
@Override
public void onClick(View widget) {
if (op.shouldTakeOnActionClickedPrecedence()) {
@@ -167,17 +149,8 @@ public class ActionableToastBar extends LinearLayout {
}
});
- // Set description icon.
- if (descriptionIconResourceId == 0) {
- mActionDescriptionIcon.setVisibility(GONE);
- } else {
- mActionDescriptionIcon.setVisibility(VISIBLE);
- mActionDescriptionIcon.setImageResource(descriptionIconResourceId);
- }
-
- mActionDescriptionView.setText(descriptionText);
- mActionIcon.setVisibility(showActionIcon ? VISIBLE : GONE);
- mActionText.setText(actionTextResource);
+ setDescriptionText(descriptionText);
+ setActionText(actionTextResourceId);
mHidden = false;
getShowAnimation().start();
@@ -197,8 +170,8 @@ public class ActionableToastBar extends LinearLayout {
mHidden = true;
mFadeOutHandler.removeCallbacks(mRunnable);
if (getVisibility() == View.VISIBLE) {
- mActionDescriptionView.setText("");
- mActionButton.setOnClickListener(null);
+ setDescriptionText("");
+ setActionClickListener(null);
// Hide view once it's clicked.
if (animate) {
getHideAnimation().start();
@@ -213,24 +186,41 @@ public class ActionableToastBar extends LinearLayout {
}
}
+ public boolean isAnimating() {
+ return mShowAnimation != null && mShowAnimation.isStarted();
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ mFadeOutHandler.removeCallbacks(mRunnable);
+ super.onDetachedFromWindow();
+ }
+
+ public boolean isEventInToastBar(MotionEvent event) {
+ if (!isShown()) {
+ return false;
+ }
+ int[] xy = new int[2];
+ float x = event.getX();
+ float y = event.getY();
+ getLocationOnScreen(xy);
+ return (x > xy[0] && x < (xy[0] + getWidth()) && y > xy[1] && y < xy[1] + getHeight());
+ }
+
private Animator getShowAnimation() {
if (mShowAnimation == null) {
- mShowAnimation = AnimatorInflater.loadAnimator(getContext(),
- R.anim.fade_in);
+ mShowAnimation = AnimatorInflater.loadAnimator(getContext(), R.anim.fade_in);
mShowAnimation.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
setVisibility(View.VISIBLE);
}
@Override
- public void onAnimationEnd(Animator animation) {
- }
+ public void onAnimationEnd(Animator animation) { }
@Override
- public void onAnimationCancel(Animator animation) {
- }
+ public void onAnimationCancel(Animator animation) { }
@Override
- public void onAnimationRepeat(Animator animation) {
- }
+ public void onAnimationRepeat(Animator animation) { }
});
mShowAnimation.setTarget(this);
}
@@ -239,47 +229,70 @@ public class ActionableToastBar extends LinearLayout {
private Animator getHideAnimation() {
if (mHideAnimation == null) {
- mHideAnimation = AnimatorInflater.loadAnimator(getContext(),
- R.anim.fade_out);
+ mHideAnimation = AnimatorInflater.loadAnimator(getContext(), R.anim.fade_out);
mHideAnimation.addListener(new Animator.AnimatorListener() {
@Override
- public void onAnimationStart(Animator animation) {
- }
+ public void onAnimationStart(Animator animation) { }
@Override
- public void onAnimationRepeat(Animator animation) {
- }
+ public void onAnimationRepeat(Animator animation) { }
@Override
public void onAnimationEnd(Animator animation) {
setVisibility(View.GONE);
}
@Override
- public void onAnimationCancel(Animator animation) {
- }
+ public void onAnimationCancel(Animator animation) { }
});
mHideAnimation.setTarget(this);
}
return mHideAnimation;
}
- public boolean isEventInToastBar(MotionEvent event) {
- if (!isShown()) {
- return false;
+ /**
+ * If the View requires multiple lines to fully display the toast description then make the
+ * multi-line view visible and hide the single line view; otherwise vice versa. If the action
+ * text is present, display it, otherwise hide it.
+ *
+ * @param multiLine <tt>true</tt> if the View requires multiple lines to display the toast
+ * @param showAction <tt>true</tt> if the action text is present and should be shown
+ */
+ private void setVisibility(boolean multiLine, boolean showAction) {
+ mSingleLineDescriptionView.setVisibility(!multiLine ? View.VISIBLE : View.GONE);
+ mSingleLineActionView.setVisibility(!multiLine && showAction ? View.VISIBLE : View.GONE);
+ if (mMultiLineDescriptionView != null) {
+ mMultiLineDescriptionView.setVisibility(multiLine ? View.VISIBLE : View.GONE);
+ }
+ if (mMultiLineActionView != null) {
+ mMultiLineActionView.setVisibility(multiLine && showAction ? View.VISIBLE : View.GONE);
}
- int[] xy = new int[2];
- float x = event.getX();
- float y = event.getY();
- getLocationOnScreen(xy);
- return (x > xy[0] && x < (xy[0] + getWidth()) && y > xy[1] && y < xy[1] + getHeight());
}
- public boolean isAnimating() {
- return mShowAnimation != null && mShowAnimation.isStarted();
+ private void setDescriptionText(CharSequence description) {
+ mSingleLineDescriptionView.setText(description);
+ if (mMultiLineDescriptionView != null) {
+ mMultiLineDescriptionView.setText(description);
+ }
}
- @Override
- public void onDetachedFromWindow() {
- mFadeOutHandler.removeCallbacks(mRunnable);
- super.onDetachedFromWindow();
+ private void setActionText(@StringRes int actionTextResourceId) {
+ if (actionTextResourceId == 0) {
+ mSingleLineActionView.setText("");
+ if (mMultiLineActionView != null) {
+ mMultiLineActionView.setText("");
+ }
+ } else {
+ mSingleLineActionView.setText(actionTextResourceId);
+ if (mMultiLineActionView != null) {
+ mMultiLineActionView.setText(actionTextResourceId);
+ }
+ }
+ }
+
+ private void setActionClickListener(OnClickListener listener) {
+ mSingleLineActionView.setOnClickListener(listener);
+
+ if (mMultiLineActionView != null) {
+ mMultiLineActionView.setOnClickListener(listener);
+ }
}
/**
@@ -289,36 +302,4 @@ public class ActionableToastBar extends LinearLayout {
public interface ActionClickedListener {
public void onActionClicked(Context context);
}
-
- /**
- * A wrapper that allows a drawable to be clipped at specific bounds. {@link ClipDrawable} only
- * supports clipping based on a relative level. This extends {@link ClipDrawable} since it is
- * the simplest base class that will delegate the rest of the methods to the wrapped drawable.
- *
- * <br/><br/><b>Note: Only use on JBMR2 or later as clipRect is not supported until API 18.</b>
- */
- private static class ClipBoundsDrawable extends ClipDrawable {
- private final Drawable mDrawable;
- private final Rect mClipRect = new Rect();
-
- public ClipBoundsDrawable(Drawable drawable) {
- super(drawable, Gravity.START, ClipDrawable.HORIZONTAL);
- mDrawable = drawable;
- }
-
- public void setClipBounds(int left, int top, int right, int bottom) {
- mClipRect.left = left;
- mClipRect.top = top;
- mClipRect.right = right;
- mClipRect.bottom = bottom;
- }
-
- @Override
- public void draw(Canvas canvas) {
- canvas.save();
- canvas.clipRect(mClipRect);
- mDrawable.draw(canvas);
- canvas.restore();
- }
- }
-}
+} \ No newline at end of file
diff --git a/src/com/android/mail/ui/OnePaneController.java b/src/com/android/mail/ui/OnePaneController.java
index 758c352d1..4a9a91611 100644
--- a/src/com/android/mail/ui/OnePaneController.java
+++ b/src/com/android/mail/ui/OnePaneController.java
@@ -426,10 +426,8 @@ public final class OnePaneController extends AbstractActivityController {
case ViewMode.CONVERSATION:
mToastBar.show(getUndoClickedListener(
convList != null ? convList.getAnimatedAdapter() : null),
- 0,
Utils.convertHtmlToPlainText
(op.getDescription(mActivity.getActivityContext())),
- true, /* showActionIcon */
R.string.undo,
true, /* replaceVisibleToast */
op);
@@ -439,10 +437,8 @@ public final class OnePaneController extends AbstractActivityController {
if (convList != null) {
mToastBar.show(
getUndoClickedListener(convList.getAnimatedAdapter()),
- 0,
Utils.convertHtmlToPlainText
(op.getDescription(mActivity.getActivityContext())),
- true, /* showActionIcon */
R.string.undo,
true, /* replaceVisibleToast */
op);
diff --git a/src/com/android/mail/ui/TwoPaneController.java b/src/com/android/mail/ui/TwoPaneController.java
index 6991a728d..bfe2aac67 100644
--- a/src/com/android/mail/ui/TwoPaneController.java
+++ b/src/com/android/mail/ui/TwoPaneController.java
@@ -26,7 +26,6 @@ import android.os.Bundle;
import android.support.annotation.LayoutRes;
import android.support.v4.widget.DrawerLayout;
import android.view.Gravity;
-import android.widget.FrameLayout;
import android.widget.ListView;
import com.android.mail.ConversationListContext;
@@ -500,8 +499,6 @@ public final class TwoPaneController extends AbstractActivityController {
final int mode = mViewMode.getMode();
final ConversationListFragment convList = getConversationListFragment();
- repositionToastBar(op);
-
switch (mode) {
case ViewMode.SEARCH_RESULTS_LIST:
case ViewMode.CONVERSATION_LIST:
@@ -509,10 +506,8 @@ public final class TwoPaneController extends AbstractActivityController {
case ViewMode.CONVERSATION:
if (convList != null) {
mToastBar.show(getUndoClickedListener(convList.getAnimatedAdapter()),
- 0,
Utils.convertHtmlToPlainText
(op.getDescription(mActivity.getActivityContext())),
- true, /* showActionIcon */
R.string.undo,
true, /* replaceVisibleToast */
op);
@@ -520,48 +515,6 @@ public final class TwoPaneController extends AbstractActivityController {
}
}
- public void repositionToastBar(ToastBarOperation op) {
- repositionToastBar(op.isBatchUndo());
- }
-
- /**
- * Set the toast bar's layout params to position it in the right place
- * depending the current view mode.
- *
- * @param convModeShowInList if we're in conversation mode, should the toast
- * bar appear over the list? no effect when not in conversation mode.
- */
- private void repositionToastBar(boolean convModeShowInList) {
- final int mode = mViewMode.getMode();
- final FrameLayout.LayoutParams params =
- (FrameLayout.LayoutParams) mToastBar.getLayoutParams();
- switch (mode) {
- case ViewMode.SEARCH_RESULTS_LIST:
- case ViewMode.CONVERSATION_LIST:
- params.width = mLayout.computeConversationListWidth() - params.leftMargin
- - params.rightMargin;
- params.gravity = Gravity.BOTTOM | Gravity.END;
- mToastBar.setLayoutParams(params);
- break;
- case ViewMode.SEARCH_RESULTS_CONVERSATION:
- case ViewMode.CONVERSATION:
- if (convModeShowInList && !mLayout.isConversationListCollapsed()) {
- // Show undo bar in the conversation list.
- params.gravity = Gravity.BOTTOM | Gravity.START;
- params.width = mLayout.computeConversationListWidth() - params.leftMargin
- - params.rightMargin;
- mToastBar.setLayoutParams(params);
- } else {
- // Show undo bar in the conversation.
- params.gravity = Gravity.BOTTOM | Gravity.END;
- params.width = mLayout.computeConversationWidth() - params.leftMargin
- - params.rightMargin;
- mToastBar.setLayoutParams(params);
- }
- break;
- }
- }
-
@Override
protected void hideOrRepositionToastBar(final boolean animated) {
final int oldViewMode = mViewMode.getMode();
@@ -571,9 +524,6 @@ public final class TwoPaneController extends AbstractActivityController {
if (/* the touch did not open a conversation */oldViewMode == mViewMode.getMode() ||
/* animation has ended */!mToastBar.isAnimating()) {
mToastBar.hide(animated, false /* actionClicked */);
- } else {
- // the touch opened a conversation, reposition undo bar
- repositionToastBar(mToastBar.getOperation());
}
}
},
@@ -583,7 +533,6 @@ public final class TwoPaneController extends AbstractActivityController {
@Override
public void onError(final Folder folder, boolean replaceVisibleToast) {
- repositionToastBar(true /* convModeShowInList */);
showErrorToast(folder, replaceVisibleToast);
}