1 /** 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 17 package com.android.inputmethod.dictionarypack; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.content.Context; 22 import android.util.AttributeSet; 23 import android.view.View; 24 import android.view.ViewPropertyAnimator; 25 import android.widget.Button; 26 import android.widget.FrameLayout; 27 28 import com.android.inputmethod.latin.R; 29 30 /** 31 * A view that handles buttons inside it according to a status. 32 */ 33 public class ButtonSwitcher extends FrameLayout { 34 public static final int NOT_INITIALIZED = -1; 35 public static final int STATUS_NO_BUTTON = 0; 36 public static final int STATUS_INSTALL = 1; 37 public static final int STATUS_CANCEL = 2; 38 public static final int STATUS_DELETE = 3; 39 // One of the above 40 private int mStatus = NOT_INITIALIZED; 41 private int mAnimateToStatus = NOT_INITIALIZED; 42 43 // Animation directions 44 public static final int ANIMATION_IN = 1; 45 public static final int ANIMATION_OUT = 2; 46 47 private Button mInstallButton; 48 private Button mCancelButton; 49 private Button mDeleteButton; 50 private DictionaryListInterfaceState mInterfaceState; 51 private OnClickListener mOnClickListener; 52 53 public ButtonSwitcher(Context context, AttributeSet attrs) { 54 super(context, attrs); 55 } 56 57 public ButtonSwitcher(Context context, AttributeSet attrs, int defStyle) { 58 super(context, attrs, defStyle); 59 } 60 61 public void reset(final DictionaryListInterfaceState interfaceState) { 62 mStatus = NOT_INITIALIZED; 63 mAnimateToStatus = NOT_INITIALIZED; 64 mInterfaceState = interfaceState; 65 } 66 67 @Override 68 protected void onLayout(final boolean changed, final int left, final int top, final int right, 69 final int bottom) { 70 super.onLayout(changed, left, top, right, bottom); 71 mInstallButton = (Button)findViewById(R.id.dict_install_button); 72 mCancelButton = (Button)findViewById(R.id.dict_cancel_button); 73 mDeleteButton = (Button)findViewById(R.id.dict_delete_button); 74 setInternalOnClickListener(mOnClickListener); 75 setButtonPositionWithoutAnimation(mStatus); 76 if (mAnimateToStatus != NOT_INITIALIZED) { 77 // We have been asked to animate before we were ready, so we took a note of it. 78 // We are now ready: launch the animation. 79 animateButtonPosition(mStatus, mAnimateToStatus); 80 mStatus = mAnimateToStatus; 81 mAnimateToStatus = NOT_INITIALIZED; 82 } 83 } 84 85 private Button getButton(final int status) { 86 switch(status) { 87 case STATUS_INSTALL: 88 return mInstallButton; 89 case STATUS_CANCEL: 90 return mCancelButton; 91 case STATUS_DELETE: 92 return mDeleteButton; 93 default: 94 return null; 95 } 96 } 97 98 public void setStatusAndUpdateVisuals(final int status) { 99 if (mStatus == NOT_INITIALIZED) { 100 setButtonPositionWithoutAnimation(status); 101 mStatus = status; 102 } else { 103 if (null == mInstallButton) { 104 // We may come here before we have been layout. In this case we don't know our 105 // size yet so we can't start animations so we need to remember what animation to 106 // start once layout has gone through. 107 mAnimateToStatus = status; 108 } else { 109 animateButtonPosition(mStatus, status); 110 mStatus = status; 111 } 112 } 113 } 114 115 private void setButtonPositionWithoutAnimation(final int status) { 116 // This may be called by setStatus() before the layout has come yet. 117 if (null == mInstallButton) return; 118 final int width = getWidth(); 119 // Set to out of the screen if that's not the currently displayed status 120 mInstallButton.setTranslationX(STATUS_INSTALL == status ? 0 : width); 121 mCancelButton.setTranslationX(STATUS_CANCEL == status ? 0 : width); 122 mDeleteButton.setTranslationX(STATUS_DELETE == status ? 0 : width); 123 } 124 125 private void animateButtonPosition(final int oldStatus, final int newStatus) { 126 final View oldButton = getButton(oldStatus); 127 final View newButton = getButton(newStatus); 128 if (null != oldButton && null != newButton) { 129 // Transition between two buttons : animate out, then in 130 animateButton(oldButton, ANIMATION_OUT).setListener( 131 new AnimatorListenerAdapter() { 132 @Override 133 public void onAnimationEnd(final Animator animation) { 134 if (newStatus != mStatus) return; 135 animateButton(newButton, ANIMATION_IN); 136 } 137 }); 138 } else if (null != oldButton) { 139 animateButton(oldButton, ANIMATION_OUT); 140 } else if (null != newButton) { 141 animateButton(newButton, ANIMATION_IN); 142 } 143 } 144 145 public void setInternalOnClickListener(final OnClickListener listener) { 146 mOnClickListener = listener; 147 if (null != mInstallButton) { 148 // Already laid out : do it now 149 mInstallButton.setOnClickListener(mOnClickListener); 150 mCancelButton.setOnClickListener(mOnClickListener); 151 mDeleteButton.setOnClickListener(mOnClickListener); 152 } 153 } 154 155 private ViewPropertyAnimator animateButton(final View button, final int direction) { 156 final float outerX = getWidth(); 157 final float innerX = button.getX() - button.getTranslationX(); 158 mInterfaceState.removeFromCache((View)getParent()); 159 if (ANIMATION_IN == direction) { 160 button.setClickable(true); 161 return button.animate().translationX(0); 162 } else { 163 button.setClickable(false); 164 return button.animate().translationX(outerX - innerX); 165 } 166 } 167 } 168