michael@0: /* michael@0: * Copyright 2012 Roman Nurik michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: package org.mozilla.gecko.widget; michael@0: michael@0: import java.util.LinkedList; michael@0: michael@0: import org.mozilla.gecko.R; michael@0: import org.mozilla.gecko.animation.PropertyAnimator; michael@0: michael@0: import android.graphics.drawable.Drawable; michael@0: import android.os.Bundle; michael@0: import android.os.Handler; michael@0: import android.view.View; michael@0: import android.widget.Button; michael@0: import android.widget.TextView; michael@0: michael@0: public class ButtonToast { michael@0: private final static String LOGTAG = "GeckoButtonToast"; michael@0: private final static int TOAST_DURATION = 5000; michael@0: michael@0: private final View mView; michael@0: private final TextView mMessageView; michael@0: private final Button mButton; michael@0: private final Handler mHideHandler = new Handler(); michael@0: michael@0: private final ToastListener mListener; michael@0: private final LinkedList mQueue = new LinkedList(); michael@0: private Toast mCurrentToast; michael@0: michael@0: public enum ReasonHidden { michael@0: CLICKED, michael@0: TIMEOUT, michael@0: STARTUP michael@0: } michael@0: michael@0: // State objects michael@0: private static class Toast { michael@0: public final CharSequence buttonMessage; michael@0: public Drawable buttonDrawable; michael@0: public final CharSequence message; michael@0: public ToastListener listener; michael@0: michael@0: public Toast(CharSequence aMessage, CharSequence aButtonMessage, michael@0: Drawable aDrawable, ToastListener aListener) { michael@0: message = aMessage; michael@0: buttonMessage = aButtonMessage; michael@0: buttonDrawable = aDrawable; michael@0: listener = aListener; michael@0: } michael@0: } michael@0: michael@0: public interface ToastListener { michael@0: void onButtonClicked(); michael@0: void onToastHidden(ReasonHidden reason); michael@0: } michael@0: michael@0: public ButtonToast(View view) { michael@0: mView = view; michael@0: mListener = null; michael@0: mMessageView = (TextView) mView.findViewById(R.id.toast_message); michael@0: mButton = (Button) mView.findViewById(R.id.toast_button); michael@0: mButton.setOnClickListener(new View.OnClickListener() { michael@0: @Override michael@0: public void onClick(View view) { michael@0: Toast t = mCurrentToast; michael@0: if (t == null) michael@0: return; michael@0: michael@0: hide(false, ReasonHidden.CLICKED); michael@0: if (t.listener != null) { michael@0: t.listener.onButtonClicked(); michael@0: } michael@0: } michael@0: }); michael@0: michael@0: hide(true, ReasonHidden.STARTUP); michael@0: } michael@0: michael@0: public void show(boolean immediate, CharSequence message, michael@0: CharSequence buttonMessage, Drawable buttonDrawable, michael@0: ToastListener listener) { michael@0: show(new Toast(message, buttonMessage, buttonDrawable, listener), immediate); michael@0: } michael@0: michael@0: private void show(Toast t, boolean immediate) { michael@0: // If we're already showing a toast, add this one to the queue to show later michael@0: if (mView.getVisibility() == View.VISIBLE) { michael@0: mQueue.offer(t); michael@0: return; michael@0: } michael@0: michael@0: mCurrentToast = t; michael@0: mButton.setEnabled(true); michael@0: michael@0: mMessageView.setText(t.message); michael@0: mButton.setText(t.buttonMessage); michael@0: mButton.setCompoundDrawablePadding(mView.getContext().getResources().getDimensionPixelSize(R.dimen.toast_button_padding)); michael@0: mButton.setCompoundDrawablesWithIntrinsicBounds(t.buttonDrawable, null, null, null); michael@0: michael@0: mHideHandler.removeCallbacks(mHideRunnable); michael@0: mHideHandler.postDelayed(mHideRunnable, TOAST_DURATION); michael@0: michael@0: mView.setVisibility(View.VISIBLE); michael@0: int duration = immediate ? 0 : mView.getResources().getInteger(android.R.integer.config_longAnimTime); michael@0: michael@0: PropertyAnimator animator = new PropertyAnimator(duration); michael@0: animator.attach(mView, PropertyAnimator.Property.ALPHA, 1.0f); michael@0: animator.start(); michael@0: } michael@0: michael@0: public void hide(boolean immediate, ReasonHidden reason) { michael@0: if (mButton.isPressed() && reason != ReasonHidden.CLICKED) { michael@0: mHideHandler.postDelayed(mHideRunnable, TOAST_DURATION); michael@0: return; michael@0: } michael@0: michael@0: if (mCurrentToast != null && mCurrentToast.listener != null) { michael@0: mCurrentToast.listener.onToastHidden(reason); michael@0: } michael@0: mCurrentToast = null; michael@0: mButton.setEnabled(false); michael@0: mHideHandler.removeCallbacks(mHideRunnable); michael@0: int duration = immediate ? 0 : mView.getResources().getInteger(android.R.integer.config_longAnimTime); michael@0: michael@0: mView.clearAnimation(); michael@0: if (immediate) { michael@0: mView.setVisibility(View.GONE); michael@0: showNextInQueue(); michael@0: } else { michael@0: // Using Android's animation frameworks will not correctly turn off clicking. michael@0: // See bug 885717. michael@0: PropertyAnimator animator = new PropertyAnimator(duration); michael@0: animator.attach(mView, PropertyAnimator.Property.ALPHA, 0.0f); michael@0: animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener () { michael@0: // If we are showing a toast and go in the background michael@0: // onAnimationEnd will be called when the app is restored michael@0: public void onPropertyAnimationEnd() { michael@0: mView.setVisibility(View.GONE); michael@0: showNextInQueue(); michael@0: } michael@0: public void onPropertyAnimationStart() { } michael@0: }); michael@0: animator.start(); michael@0: } michael@0: } michael@0: michael@0: public void onSaveInstanceState(Bundle outState) { michael@0: // Add whatever toast we're currently showing to the front of the queue michael@0: if (mCurrentToast != null) { michael@0: mQueue.add(0, mCurrentToast); michael@0: } michael@0: } michael@0: michael@0: private void showNextInQueue() { michael@0: Toast t = mQueue.poll(); michael@0: if (t != null) { michael@0: show(t, false); michael@0: } michael@0: } michael@0: michael@0: private Runnable mHideRunnable = new Runnable() { michael@0: @Override michael@0: public void run() { michael@0: hide(false, ReasonHidden.TIMEOUT); michael@0: } michael@0: }; michael@0: }