1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/widget/ButtonToast.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,181 @@ 1.4 +/* 1.5 + * Copyright 2012 Roman Nurik 1.6 + * 1.7 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.8 + * you may not use this file except in compliance with the License. 1.9 + * You may obtain a copy of the License at 1.10 + * 1.11 + * http://www.apache.org/licenses/LICENSE-2.0 1.12 + * 1.13 + * Unless required by applicable law or agreed to in writing, software 1.14 + * distributed under the License is distributed on an "AS IS" BASIS, 1.15 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.16 + * See the License for the specific language governing permissions and 1.17 + * limitations under the License. 1.18 + */ 1.19 + 1.20 +package org.mozilla.gecko.widget; 1.21 + 1.22 +import java.util.LinkedList; 1.23 + 1.24 +import org.mozilla.gecko.R; 1.25 +import org.mozilla.gecko.animation.PropertyAnimator; 1.26 + 1.27 +import android.graphics.drawable.Drawable; 1.28 +import android.os.Bundle; 1.29 +import android.os.Handler; 1.30 +import android.view.View; 1.31 +import android.widget.Button; 1.32 +import android.widget.TextView; 1.33 + 1.34 +public class ButtonToast { 1.35 + private final static String LOGTAG = "GeckoButtonToast"; 1.36 + private final static int TOAST_DURATION = 5000; 1.37 + 1.38 + private final View mView; 1.39 + private final TextView mMessageView; 1.40 + private final Button mButton; 1.41 + private final Handler mHideHandler = new Handler(); 1.42 + 1.43 + private final ToastListener mListener; 1.44 + private final LinkedList<Toast> mQueue = new LinkedList<Toast>(); 1.45 + private Toast mCurrentToast; 1.46 + 1.47 + public enum ReasonHidden { 1.48 + CLICKED, 1.49 + TIMEOUT, 1.50 + STARTUP 1.51 + } 1.52 + 1.53 + // State objects 1.54 + private static class Toast { 1.55 + public final CharSequence buttonMessage; 1.56 + public Drawable buttonDrawable; 1.57 + public final CharSequence message; 1.58 + public ToastListener listener; 1.59 + 1.60 + public Toast(CharSequence aMessage, CharSequence aButtonMessage, 1.61 + Drawable aDrawable, ToastListener aListener) { 1.62 + message = aMessage; 1.63 + buttonMessage = aButtonMessage; 1.64 + buttonDrawable = aDrawable; 1.65 + listener = aListener; 1.66 + } 1.67 + } 1.68 + 1.69 + public interface ToastListener { 1.70 + void onButtonClicked(); 1.71 + void onToastHidden(ReasonHidden reason); 1.72 + } 1.73 + 1.74 + public ButtonToast(View view) { 1.75 + mView = view; 1.76 + mListener = null; 1.77 + mMessageView = (TextView) mView.findViewById(R.id.toast_message); 1.78 + mButton = (Button) mView.findViewById(R.id.toast_button); 1.79 + mButton.setOnClickListener(new View.OnClickListener() { 1.80 + @Override 1.81 + public void onClick(View view) { 1.82 + Toast t = mCurrentToast; 1.83 + if (t == null) 1.84 + return; 1.85 + 1.86 + hide(false, ReasonHidden.CLICKED); 1.87 + if (t.listener != null) { 1.88 + t.listener.onButtonClicked(); 1.89 + } 1.90 + } 1.91 + }); 1.92 + 1.93 + hide(true, ReasonHidden.STARTUP); 1.94 + } 1.95 + 1.96 + public void show(boolean immediate, CharSequence message, 1.97 + CharSequence buttonMessage, Drawable buttonDrawable, 1.98 + ToastListener listener) { 1.99 + show(new Toast(message, buttonMessage, buttonDrawable, listener), immediate); 1.100 + } 1.101 + 1.102 + private void show(Toast t, boolean immediate) { 1.103 + // If we're already showing a toast, add this one to the queue to show later 1.104 + if (mView.getVisibility() == View.VISIBLE) { 1.105 + mQueue.offer(t); 1.106 + return; 1.107 + } 1.108 + 1.109 + mCurrentToast = t; 1.110 + mButton.setEnabled(true); 1.111 + 1.112 + mMessageView.setText(t.message); 1.113 + mButton.setText(t.buttonMessage); 1.114 + mButton.setCompoundDrawablePadding(mView.getContext().getResources().getDimensionPixelSize(R.dimen.toast_button_padding)); 1.115 + mButton.setCompoundDrawablesWithIntrinsicBounds(t.buttonDrawable, null, null, null); 1.116 + 1.117 + mHideHandler.removeCallbacks(mHideRunnable); 1.118 + mHideHandler.postDelayed(mHideRunnable, TOAST_DURATION); 1.119 + 1.120 + mView.setVisibility(View.VISIBLE); 1.121 + int duration = immediate ? 0 : mView.getResources().getInteger(android.R.integer.config_longAnimTime); 1.122 + 1.123 + PropertyAnimator animator = new PropertyAnimator(duration); 1.124 + animator.attach(mView, PropertyAnimator.Property.ALPHA, 1.0f); 1.125 + animator.start(); 1.126 + } 1.127 + 1.128 + public void hide(boolean immediate, ReasonHidden reason) { 1.129 + if (mButton.isPressed() && reason != ReasonHidden.CLICKED) { 1.130 + mHideHandler.postDelayed(mHideRunnable, TOAST_DURATION); 1.131 + return; 1.132 + } 1.133 + 1.134 + if (mCurrentToast != null && mCurrentToast.listener != null) { 1.135 + mCurrentToast.listener.onToastHidden(reason); 1.136 + } 1.137 + mCurrentToast = null; 1.138 + mButton.setEnabled(false); 1.139 + mHideHandler.removeCallbacks(mHideRunnable); 1.140 + int duration = immediate ? 0 : mView.getResources().getInteger(android.R.integer.config_longAnimTime); 1.141 + 1.142 + mView.clearAnimation(); 1.143 + if (immediate) { 1.144 + mView.setVisibility(View.GONE); 1.145 + showNextInQueue(); 1.146 + } else { 1.147 + // Using Android's animation frameworks will not correctly turn off clicking. 1.148 + // See bug 885717. 1.149 + PropertyAnimator animator = new PropertyAnimator(duration); 1.150 + animator.attach(mView, PropertyAnimator.Property.ALPHA, 0.0f); 1.151 + animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener () { 1.152 + // If we are showing a toast and go in the background 1.153 + // onAnimationEnd will be called when the app is restored 1.154 + public void onPropertyAnimationEnd() { 1.155 + mView.setVisibility(View.GONE); 1.156 + showNextInQueue(); 1.157 + } 1.158 + public void onPropertyAnimationStart() { } 1.159 + }); 1.160 + animator.start(); 1.161 + } 1.162 + } 1.163 + 1.164 + public void onSaveInstanceState(Bundle outState) { 1.165 + // Add whatever toast we're currently showing to the front of the queue 1.166 + if (mCurrentToast != null) { 1.167 + mQueue.add(0, mCurrentToast); 1.168 + } 1.169 + } 1.170 + 1.171 + private void showNextInQueue() { 1.172 + Toast t = mQueue.poll(); 1.173 + if (t != null) { 1.174 + show(t, false); 1.175 + } 1.176 + } 1.177 + 1.178 + private Runnable mHideRunnable = new Runnable() { 1.179 + @Override 1.180 + public void run() { 1.181 + hide(false, ReasonHidden.TIMEOUT); 1.182 + } 1.183 + }; 1.184 +}