10744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen/* 20744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * Copyright (C) 2013 The Android Open Source Project 30744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * 40744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * Licensed under the Apache License, Version 2.0 (the "License"); 50744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * you may not use this file except in compliance with the License. 60744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * You may obtain a copy of the License at 70744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * 80744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * http://www.apache.org/licenses/LICENSE-2.0 90744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * 100744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * Unless required by applicable law or agreed to in writing, software 110744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * distributed under the License is distributed on an "AS IS" BASIS, 120744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * See the License for the specific language governing permissions and 140744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * limitations under the License. 150744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen */ 160744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen 170744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissenpackage com.android.camera; 180744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen 1918b22b5963e8871e539edf669d96862eec72bb5dSpike Spragueimport android.animation.Animator; 2018b22b5963e8871e539edf669d96862eec72bb5dSpike Spragueimport android.animation.AnimatorListenerAdapter; 213c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Spragueimport android.animation.AnimatorSet; 2218b22b5963e8871e539edf669d96862eec72bb5dSpike Spragueimport android.animation.ValueAnimator; 230744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissenimport android.content.Context; 240744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissenimport android.content.res.TypedArray; 2518b22b5963e8871e539edf669d96862eec72bb5dSpike Spragueimport android.graphics.Bitmap; 2618b22b5963e8871e539edf669d96862eec72bb5dSpike Spragueimport android.graphics.Canvas; 2718b22b5963e8871e539edf669d96862eec72bb5dSpike Spragueimport android.graphics.Matrix; 2866a3e6ceb306833355acb740711ccb5c53576e0aSpike Spragueimport android.graphics.drawable.Drawable; 293c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Spragueimport android.os.AsyncTask; 300744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissenimport android.util.AttributeSet; 310744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissenimport android.view.View; 3218b22b5963e8871e539edf669d96862eec72bb5dSpike Spragueimport android.widget.ImageButton; 3318b22b5963e8871e539edf669d96862eec72bb5dSpike Spragueimport android.widget.ImageView; 340744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen 3518b22b5963e8871e539edf669d96862eec72bb5dSpike Spragueimport com.android.camera.util.Gusterpolator; 360744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissenimport com.android.camera2.R; 370744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen 380744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen/* 390744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * A toggle button that supports two or more states with images rendererd on top 400744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * for each state. 410744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * The button is initialized in an XML layout file with an array reference of 420744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * image ids (e.g. imageIds="@array/camera_flashmode_icons"). 430744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * Each image in the referenced array represents a single integer state. 440744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * Every time the user touches the button it gets set to next state in line, 450744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * with the corresponding image drawn onto the face of the button. 460744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * State wraps back to 0 on user touch when button is already at n-1 state. 470744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen */ 480744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissenpublic class MultiToggleImageButton extends ImageButton { 490744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen /* 5018b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague * Listener interface for button state changes. 510744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen */ 520744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen public interface OnStateChangeListener { 530744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen /* 540744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * @param view the MultiToggleImageButton that received the touch event 550744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * @param state the new state the button is in 560744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen */ 570744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen public abstract void stateChanged(View view, int state); 580744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen } 590744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen 6018b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague public static final int ANIM_DIRECTION_VERTICAL = 0; 6118b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague public static final int ANIM_DIRECTION_HORIZONTAL = 1; 6218b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 633c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague private static final int ANIM_DURATION_MS = 250; 6418b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague private static final int UNSET = -1; 6518b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 660744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen private OnStateChangeListener mOnStateChangeListener; 6718b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague private int mState = UNSET; 680744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen private int[] mImageIds; 6979718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague private int[] mDescIds; 7066a3e6ceb306833355acb740711ccb5c53576e0aSpike Sprague private int mLevel; 7118b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague private boolean mClickEnabled = true; 7218b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague private int mParentSize; 7318b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague private int mAnimDirection; 743c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague private Matrix mMatrix = new Matrix(); 753c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague private ValueAnimator mAnimator; 760744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen 770744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen public MultiToggleImageButton(Context context) { 780744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen super(context); 790744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen init(); 800744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen } 810744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen 820744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen public MultiToggleImageButton(Context context, AttributeSet attrs) { 830744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen super(context, attrs); 840744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen init(); 850744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen parseAttributes(context, attrs); 860744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen setState(0); 870744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen } 880744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen 890744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen public MultiToggleImageButton(Context context, AttributeSet attrs, int defStyle) { 900744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen super(context, attrs, defStyle); 910744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen init(); 920744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen parseAttributes(context, attrs); 930744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen setState(0); 940744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen } 950744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen 960744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen /* 970744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * Set the state change listener. 980744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * 990744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * @param onStateChangeListener the listener to set 1000744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen */ 1010744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen public void setOnStateChangeListener(OnStateChangeListener onStateChangeListener) { 1020744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen mOnStateChangeListener = onStateChangeListener; 1030744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen } 1040744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen 1050744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen /* 1060744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * Get the current button state. 1070744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * 1080744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen */ 1090744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen public int getState() { 1100744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen return mState; 1110744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen } 1120744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen 1130744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen /* 1140744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * Set the current button state, thus causing the state change listener to 1150744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * get called. 1160744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * 1170744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * @param state the desired state 1180744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen */ 1190744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen public void setState(int state) { 1200744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen setState(state, true); 1210744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen } 1220744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen 1230744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen /* 1240744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * Set the current button state. 1250744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * 1260744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * @param state the desired state 1270744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * @param callListener should the state change listener be called? 1280744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen */ 12918b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague public void setState(final int state, final boolean callListener) { 1303c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague setStateAnimatedInternal(state, callListener); 131c91c8d273b8884468dfd66a5b82526dc8245934eAlan Newberger } 132c91c8d273b8884468dfd66a5b82526dc8245934eAlan Newberger 133c91c8d273b8884468dfd66a5b82526dc8245934eAlan Newberger /** 134c91c8d273b8884468dfd66a5b82526dc8245934eAlan Newberger * Set the current button state via an animated transition. 135c91c8d273b8884468dfd66a5b82526dc8245934eAlan Newberger * 136c91c8d273b8884468dfd66a5b82526dc8245934eAlan Newberger * @param state 137c91c8d273b8884468dfd66a5b82526dc8245934eAlan Newberger * @param callListener 138c91c8d273b8884468dfd66a5b82526dc8245934eAlan Newberger */ 139c91c8d273b8884468dfd66a5b82526dc8245934eAlan Newberger private void setStateAnimatedInternal(final int state, final boolean callListener) { 14018b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague if (mState == state || mState == UNSET) { 14118b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague setStateInternal(state, callListener); 14218b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague return; 14318b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague } 14418b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 14518b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague if (mImageIds == null) { 14618b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague return; 14718b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague } 14818b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 1493c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague new AsyncTask<Integer, Void, Bitmap>() { 15018b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague @Override 1513c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague protected Bitmap doInBackground(Integer... params) { 1523c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague return combine(params[0], params[1]); 15318b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague } 15418b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 15518b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague @Override 1563c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague protected void onPostExecute(Bitmap bitmap) { 1573c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague if (bitmap == null) { 1583c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague setStateInternal(state, callListener); 1593c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague } else { 1603c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague setImageBitmap(bitmap); 16118b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 1623c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague int offset; 1633c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague if (mAnimDirection == ANIM_DIRECTION_VERTICAL) { 1643c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague offset = (mParentSize+getHeight())/2; 1653c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague } else if (mAnimDirection == ANIM_DIRECTION_HORIZONTAL) { 1663c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague offset = (mParentSize+getWidth())/2; 1673c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague } else { 1683c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague return; 1693c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague } 1703c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague 1713c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague mAnimator.setFloatValues(-offset, 0.0f); 1723c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague AnimatorSet s = new AnimatorSet(); 1733c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague s.play(mAnimator); 1743c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague s.addListener(new AnimatorListenerAdapter() { 1753c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague @Override 1763c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague public void onAnimationStart(Animator animation) { 1773c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague setClickEnabled(false); 1783c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague } 1793c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague 1803c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague @Override 1813c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague public void onAnimationEnd(Animator animation) { 1823c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague setStateInternal(state, callListener); 1833c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague setClickEnabled(true); 1843c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague } 1853c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague }); 1863c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague s.start(); 1873c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague } 18818b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague } 1893c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague }.execute(mState, state); 19018b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague } 19118b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 19266d3d0d0633324893b89e1ddec14a4710d8034c3Spike Sprague /** 19366d3d0d0633324893b89e1ddec14a4710d8034c3Spike Sprague * Enable or disable click reactions for this button 19466d3d0d0633324893b89e1ddec14a4710d8034c3Spike Sprague * without affecting visual state. 19566d3d0d0633324893b89e1ddec14a4710d8034c3Spike Sprague * For most cases you'll want to use {@link #setEnabled(boolean)}. 19666d3d0d0633324893b89e1ddec14a4710d8034c3Spike Sprague * @param enabled True if click enabled, false otherwise. 19766d3d0d0633324893b89e1ddec14a4710d8034c3Spike Sprague */ 19866d3d0d0633324893b89e1ddec14a4710d8034c3Spike Sprague public void setClickEnabled(boolean enabled) { 19966d3d0d0633324893b89e1ddec14a4710d8034c3Spike Sprague mClickEnabled = enabled; 20066d3d0d0633324893b89e1ddec14a4710d8034c3Spike Sprague } 20166d3d0d0633324893b89e1ddec14a4710d8034c3Spike Sprague 20218b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague private void setStateInternal(int state, boolean callListener) { 2030744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen mState = state; 204f80ac9ee5c66671c3a77ac6628fefc86cd39d57eErin Dahlgren if (mImageIds != null) { 20518b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague setImageByState(mState); 206f80ac9ee5c66671c3a77ac6628fefc86cd39d57eErin Dahlgren } 20718b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 208f80ac9ee5c66671c3a77ac6628fefc86cd39d57eErin Dahlgren if (mDescIds != null) { 209f80ac9ee5c66671c3a77ac6628fefc86cd39d57eErin Dahlgren String oldContentDescription = String.valueOf(getContentDescription()); 210f80ac9ee5c66671c3a77ac6628fefc86cd39d57eErin Dahlgren String newContentDescription = getResources().getString(mDescIds[mState]); 211f80ac9ee5c66671c3a77ac6628fefc86cd39d57eErin Dahlgren if (oldContentDescription != null && !oldContentDescription.isEmpty() 212f80ac9ee5c66671c3a77ac6628fefc86cd39d57eErin Dahlgren && !oldContentDescription.equals(newContentDescription)) { 213f80ac9ee5c66671c3a77ac6628fefc86cd39d57eErin Dahlgren setContentDescription(newContentDescription); 214f80ac9ee5c66671c3a77ac6628fefc86cd39d57eErin Dahlgren String announceChange = getResources().getString( 2155c62dc726ef2818283e767cde292148c263bcbf8Alan Newberger R.string.button_change_announcement, newContentDescription); 216f80ac9ee5c66671c3a77ac6628fefc86cd39d57eErin Dahlgren announceForAccessibility(announceChange); 217f80ac9ee5c66671c3a77ac6628fefc86cd39d57eErin Dahlgren } 2185c62dc726ef2818283e767cde292148c263bcbf8Alan Newberger } 21966a3e6ceb306833355acb740711ccb5c53576e0aSpike Sprague super.setImageLevel(mLevel); 22018b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 2210744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen if (callListener && mOnStateChangeListener != null) { 22218b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague mOnStateChangeListener.stateChanged(MultiToggleImageButton.this, getState()); 2230744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen } 2240744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen } 2250744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen 2260744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen private void nextState() { 2270744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen int state = mState + 1; 2280744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen if (state >= mImageIds.length) { 2290744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen state = 0; 2300744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen } 2310744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen setState(state); 2320744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen } 2330744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen 2340744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen protected void init() { 2350744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen this.setOnClickListener(new View.OnClickListener() { 2360744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen @Override 2370744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen public void onClick(View v) { 23818b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague if (mClickEnabled) { 23918b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague nextState(); 24018b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague } 2410744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen } 2420744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen }); 24318b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague setScaleType(ImageView.ScaleType.MATRIX); 2443c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague 2453c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague mAnimator = ValueAnimator.ofFloat(0.0f, 0.0f); 2463c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague mAnimator.setDuration(ANIM_DURATION_MS); 2473c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague mAnimator.setInterpolator(Gusterpolator.INSTANCE); 2483c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 2493c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague @Override 2503c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague public void onAnimationUpdate(ValueAnimator animation) { 2513c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague mMatrix.reset(); 2523c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague if (mAnimDirection == ANIM_DIRECTION_VERTICAL) { 2533c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague mMatrix.setTranslate(0.0f, (Float) animation.getAnimatedValue()); 2543c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague } else if (mAnimDirection == ANIM_DIRECTION_HORIZONTAL) { 2553c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague mMatrix.setTranslate((Float) animation.getAnimatedValue(), 0.0f); 2563c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague } 2573c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague 2583c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague setImageMatrix(mMatrix); 2593c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague invalidate(); 2603c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague } 2613c3b31dfd5ec7a43e89cd4695cc8c498afc3a91dSpike Sprague }); 2620744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen } 2630744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen 2640744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen private void parseAttributes(Context context, AttributeSet attrs) { 2650744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen TypedArray a = context.getTheme().obtainStyledAttributes( 2660744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen attrs, 2670744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen R.styleable.MultiToggleImageButton, 2680744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen 0, 0); 26979718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague int imageIds = a.getResourceId(R.styleable.MultiToggleImageButton_imageIds, 0); 270f80ac9ee5c66671c3a77ac6628fefc86cd39d57eErin Dahlgren if (imageIds > 0) { 271f80ac9ee5c66671c3a77ac6628fefc86cd39d57eErin Dahlgren overrideImageIds(imageIds); 272f80ac9ee5c66671c3a77ac6628fefc86cd39d57eErin Dahlgren } 27379718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague int descIds = a.getResourceId(R.styleable.MultiToggleImageButton_contentDescriptionIds, 0); 274f80ac9ee5c66671c3a77ac6628fefc86cd39d57eErin Dahlgren if (descIds > 0) { 275f80ac9ee5c66671c3a77ac6628fefc86cd39d57eErin Dahlgren overrideContentDescriptions(descIds); 276f80ac9ee5c66671c3a77ac6628fefc86cd39d57eErin Dahlgren } 277a07e94c1ed57fa014e7428345927223768532d32Erin Dahlgren a.recycle(); 278a07e94c1ed57fa014e7428345927223768532d32Erin Dahlgren } 279a07e94c1ed57fa014e7428345927223768532d32Erin Dahlgren 280a07e94c1ed57fa014e7428345927223768532d32Erin Dahlgren /** 281a07e94c1ed57fa014e7428345927223768532d32Erin Dahlgren * Override the image ids of this button. 282a07e94c1ed57fa014e7428345927223768532d32Erin Dahlgren */ 283a07e94c1ed57fa014e7428345927223768532d32Erin Dahlgren public void overrideImageIds(int resId) { 2840744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen TypedArray ids = null; 2850744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen try { 286a07e94c1ed57fa014e7428345927223768532d32Erin Dahlgren ids = getResources().obtainTypedArray(resId); 287a07e94c1ed57fa014e7428345927223768532d32Erin Dahlgren mImageIds = new int[ids.length()]; 288a07e94c1ed57fa014e7428345927223768532d32Erin Dahlgren for (int i = 0; i < ids.length(); i++) { 289a07e94c1ed57fa014e7428345927223768532d32Erin Dahlgren mImageIds[i] = ids.getResourceId(i, 0); 290a07e94c1ed57fa014e7428345927223768532d32Erin Dahlgren } 2910744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen } finally { 2920744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen if (ids != null) { 2930744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen ids.recycle(); 2940744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen } 2950744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen } 2960744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen } 29766a3e6ceb306833355acb740711ccb5c53576e0aSpike Sprague 29879718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague /** 29979718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague * Override the content descriptions of this button. 30079718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague */ 30179718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague public void overrideContentDescriptions(int resId) { 30279718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague TypedArray ids = null; 30379718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague try { 30479718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague ids = getResources().obtainTypedArray(resId); 30579718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague mDescIds = new int[ids.length()]; 30679718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague for (int i = 0; i < ids.length(); i++) { 30779718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague mDescIds[i] = ids.getResourceId(i, 0); 30879718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague } 30979718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague } finally { 31079718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague if (ids != null) { 31179718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague ids.recycle(); 31279718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague } 31379718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague } 31479718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague } 31579718f6c6fb60aedd09edeedb2c16d1e2f9fe02dSpike Sprague 31618b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague /** 31718b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague * Set size info (either width or height, as necessary) of the view containing 31818b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague * this button. Used for offset calculations during animation. 31918b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague * @param s The size. 32018b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague */ 32118b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague public void setParentSize(int s) { 32218b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague mParentSize = s; 32318b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague } 32418b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 32518b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague /** 32618b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague * Set the animation direction. 32718b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague * @param d Either ANIM_DIRECTION_VERTICAL or ANIM_DIRECTION_HORIZONTAL. 32818b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague */ 32918b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague public void setAnimDirection(int d) { 33018b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague mAnimDirection = d; 33118b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague } 33218b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 33366a3e6ceb306833355acb740711ccb5c53576e0aSpike Sprague @Override 33466a3e6ceb306833355acb740711ccb5c53576e0aSpike Sprague public void setImageLevel(int level) { 33566a3e6ceb306833355acb740711ccb5c53576e0aSpike Sprague super.setImageLevel(level); 33666a3e6ceb306833355acb740711ccb5c53576e0aSpike Sprague mLevel = level; 33766a3e6ceb306833355acb740711ccb5c53576e0aSpike Sprague } 33818b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 33918b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague private void setImageByState(int state) { 34018b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague if (mImageIds != null) { 34118b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague setImageResource(mImageIds[state]); 34218b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague } 34318b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague super.setImageLevel(mLevel); 34418b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague } 34518b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 34618b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague private Bitmap combine(int oldState, int newState) { 34739384da05ba9ced9086dba64932878ce447c6230Spike Sprague // in some cases, a new set of image Ids are set via overrideImageIds() 34839384da05ba9ced9086dba64932878ce447c6230Spike Sprague // and oldState overruns the array. 34939384da05ba9ced9086dba64932878ce447c6230Spike Sprague // check here for that. 35039384da05ba9ced9086dba64932878ce447c6230Spike Sprague if (oldState >= mImageIds.length) { 35139384da05ba9ced9086dba64932878ce447c6230Spike Sprague return null; 35239384da05ba9ced9086dba64932878ce447c6230Spike Sprague } 35339384da05ba9ced9086dba64932878ce447c6230Spike Sprague 35418b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague int width = getWidth(); 35518b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague int height = getHeight(); 35618b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 35718b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague if (width <= 0 || height <= 0) { 35818b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague return null; 35918b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague } 36018b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 36118b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague int[] enabledState = new int[] {android.R.attr.state_enabled}; 36218b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 36318b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague // new state 36418b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague Drawable newDrawable = getResources().getDrawable(mImageIds[newState]).mutate(); 36518b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague newDrawable.setState(enabledState); 36618b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 36718b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague // old state 36818b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague Drawable oldDrawable = getResources().getDrawable(mImageIds[oldState]).mutate(); 36918b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague oldDrawable.setState(enabledState); 37018b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 37118b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague // combine 'em 37218b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague Bitmap bitmap = null; 37318b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague if (mAnimDirection == ANIM_DIRECTION_VERTICAL) { 37418b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague int bitmapHeight = (height*2) + ((mParentSize - height)/2); 37518b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague int oldBitmapOffset = height + ((mParentSize - height)/2); 37618b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague bitmap = Bitmap.createBitmap(width, bitmapHeight, Bitmap.Config.ARGB_8888); 37718b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague Canvas canvas = new Canvas(bitmap); 37818b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague newDrawable.setBounds(0, 0, newDrawable.getIntrinsicWidth(), newDrawable.getIntrinsicHeight()); 37918b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague oldDrawable.setBounds(0, oldBitmapOffset, oldDrawable.getIntrinsicWidth(), oldDrawable.getIntrinsicHeight()+oldBitmapOffset); 38018b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague newDrawable.draw(canvas); 38118b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague oldDrawable.draw(canvas); 38218b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague } else if (mAnimDirection == ANIM_DIRECTION_HORIZONTAL) { 38318b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague int bitmapWidth = (width*2) + ((mParentSize - width)/2); 38418b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague int oldBitmapOffset = width + ((mParentSize - width)/2); 38518b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague bitmap = Bitmap.createBitmap(bitmapWidth, height, Bitmap.Config.ARGB_8888); 38618b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague Canvas canvas = new Canvas(bitmap); 38718b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague newDrawable.setBounds(0, 0, newDrawable.getIntrinsicWidth(), newDrawable.getIntrinsicHeight()); 38818b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague oldDrawable.setBounds(oldBitmapOffset, 0, oldDrawable.getIntrinsicWidth()+oldBitmapOffset, oldDrawable.getIntrinsicHeight()); 38918b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague newDrawable.draw(canvas); 39018b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague oldDrawable.draw(canvas); 39118b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague } 39218b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague 39318b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague return bitmap; 39418b22b5963e8871e539edf669d96862eec72bb5dSpike Sprague } 3950744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen}