1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.deskclock.widget; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorInflater; 21 import android.animation.AnimatorListenerAdapter; 22 import android.content.Context; 23 import android.util.AttributeSet; 24 import android.view.LayoutInflater; 25 import android.view.MotionEvent; 26 import android.view.View; 27 import android.widget.FrameLayout; 28 import android.widget.ImageView; 29 import android.widget.LinearLayout; 30 import android.widget.TextView; 31 32 import com.android.deskclock.R; 33 34 /** 35 * A custom {@link View} that exposes an action to the user. 36 * <p> 37 * This is a copy of packages/apps/UnifiedEmail/src/com/android/mail/ui/ActionableToastBar.java 38 * with minor modifications. 39 */ 40 public class ActionableToastBar extends LinearLayout { 41 private boolean mHidden = false; 42 private Animator mShowAnimation; 43 private Animator mHideAnimation; 44 private final int mBottomMarginSizeInConversation; 45 46 /** Icon for the description. */ 47 private ImageView mActionDescriptionIcon; 48 /** The clickable view */ 49 private View mActionButton; 50 /** Icon for the action button. */ 51 private View mActionIcon; 52 /** The view that contains the description. */ 53 private TextView mActionDescriptionView; 54 /** The view that contains the text for the action button. */ 55 private TextView mActionText; 56 //private ToastBarOperation mOperation; 57 58 public ActionableToastBar(Context context) { 59 this(context, null); 60 } 61 62 public ActionableToastBar(Context context, AttributeSet attrs) { 63 this(context, attrs, 0); 64 } 65 66 public ActionableToastBar(Context context, AttributeSet attrs, int defStyle) { 67 super(context, attrs, defStyle); 68 mBottomMarginSizeInConversation = context.getResources().getDimensionPixelSize( 69 R.dimen.toast_bar_bottom_margin_in_conversation); 70 LayoutInflater.from(context).inflate(R.layout.actionable_toast_row, this, true); 71 } 72 73 @Override 74 protected void onFinishInflate() { 75 super.onFinishInflate(); 76 77 mActionDescriptionIcon = (ImageView) findViewById(R.id.description_icon); 78 mActionDescriptionView = (TextView) findViewById(R.id.description_text); 79 mActionButton = findViewById(R.id.action_button); 80 mActionIcon = findViewById(R.id.action_icon); 81 mActionText = (TextView) findViewById(R.id.action_text); 82 } 83 84 /** 85 * Tells the view that it will be appearing in the conversation pane 86 * and should adjust its layout parameters accordingly. 87 * @param isInConversationMode true if the view will be shown in the conversation view 88 */ 89 public void setConversationMode(boolean isInConversationMode) { 90 final FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) getLayoutParams(); 91 params.bottomMargin = isInConversationMode ? mBottomMarginSizeInConversation : 0; 92 setLayoutParams(params); 93 } 94 95 /** 96 * Displays the toast bar and makes it visible. Allows the setting of 97 * parameters to customize the display. 98 * @param listener performs some action when the action button is clicked 99 * @param descriptionIconResourceId resource ID for the description icon or 100 * 0 if no icon should be shown 101 * @param descriptionText a description text to show in the toast bar 102 * @param showActionIcon if true, the action button icon should be shown 103 * @param actionTextResource resource ID for the text to show in the action button 104 * @param replaceVisibleToast if true, this toast should replace any currently visible toast. 105 * Otherwise, skip showing this toast. 106 */ 107 public void show(final ActionClickedListener listener, int descriptionIconResourceId, 108 CharSequence descriptionText, boolean showActionIcon, int actionTextResource, 109 boolean replaceVisibleToast) { 110 111 if (!mHidden && !replaceVisibleToast) { 112 return; 113 } 114 115 mActionButton.setOnClickListener(new OnClickListener() { 116 @Override 117 public void onClick(View widget) { 118 if (listener != null) { 119 listener.onActionClicked(); 120 } 121 hide(true); 122 } 123 }); 124 125 // Set description icon. 126 if (descriptionIconResourceId == 0) { 127 mActionDescriptionIcon.setVisibility(GONE); 128 } else { 129 mActionDescriptionIcon.setVisibility(VISIBLE); 130 mActionDescriptionIcon.setImageResource(descriptionIconResourceId); 131 } 132 133 mActionDescriptionView.setText(descriptionText); 134 mActionIcon.setVisibility(showActionIcon ? VISIBLE : GONE); 135 mActionText.setText(actionTextResource); 136 137 mHidden = false; 138 getShowAnimation().start(); 139 } 140 141 /** 142 * Hides the view and resets the state. 143 */ 144 public void hide(boolean animate) { 145 // Prevent multiple call to hide. 146 // Also prevent hiding if show animation is going on. 147 if (!mHidden && !getShowAnimation().isRunning()) { 148 mHidden = true; 149 if (getVisibility() == View.VISIBLE) { 150 mActionDescriptionView.setText(""); 151 mActionButton.setOnClickListener(null); 152 // Hide view once it's clicked. 153 if (animate) { 154 getHideAnimation().start(); 155 } else { 156 setAlpha(0); 157 setVisibility(View.GONE); 158 } 159 } 160 } 161 } 162 163 private Animator getShowAnimation() { 164 if (mShowAnimation == null) { 165 mShowAnimation = AnimatorInflater.loadAnimator(getContext(), R.animator.fade_in); 166 mShowAnimation.addListener(new AnimatorListenerAdapter() { 167 @Override 168 public void onAnimationStart(Animator animation) { 169 setVisibility(View.VISIBLE); 170 } 171 172 @Override 173 public void onAnimationEnd(Animator animation) { 174 // There is a tiny change that and hide animation could have finished right 175 // before the show animation finished. In that case, the hide will mark the 176 // view as GONE. We need to make sure the last one wins. 177 setVisibility(View.VISIBLE); 178 } 179 }); 180 mShowAnimation.setTarget(this); 181 } 182 return mShowAnimation; 183 } 184 185 private Animator getHideAnimation() { 186 if (mHideAnimation == null) { 187 mHideAnimation = AnimatorInflater.loadAnimator(getContext(), R.animator.fade_out); 188 mHideAnimation.addListener(new AnimatorListenerAdapter() { 189 @Override 190 public void onAnimationEnd(Animator animation) { 191 setVisibility(View.GONE); 192 } 193 }); 194 mHideAnimation.setTarget(this); 195 } 196 return mHideAnimation; 197 } 198 199 public boolean isEventInToastBar(MotionEvent event) { 200 if (!isShown()) { 201 return false; 202 } 203 int[] xy = new int[2]; 204 float x = event.getX(); 205 float y = event.getY(); 206 getLocationOnScreen(xy); 207 return (x > xy[0] && x < (xy[0] + getWidth()) && y > xy[1] && y < xy[1] + getHeight()); 208 } 209 210 /** 211 * Classes that wish to perform some action when the action button is clicked 212 * should implement this interface. 213 */ 214 public interface ActionClickedListener { 215 public void onActionClicked(); 216 } 217 } 218