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
17package com.android.gallery3d.ui;
18
19import android.content.Context;
20import android.view.MotionEvent;
21
22import com.android.gallery3d.R;
23import com.android.gallery3d.common.Utils;
24import com.android.gallery3d.glrenderer.GLCanvas;
25import com.android.gallery3d.glrenderer.NinePatchTexture;
26import com.android.gallery3d.glrenderer.ResourceTexture;
27import com.android.gallery3d.glrenderer.StringTexture;
28import com.android.gallery3d.util.GalleryUtils;
29
30public class UndoBarView extends GLView {
31    @SuppressWarnings("unused")
32    private static final String TAG = "UndoBarView";
33
34    private static final int WHITE = 0xFFFFFFFF;
35    private static final int GRAY = 0xFFAAAAAA;
36
37    private final NinePatchTexture mPanel;
38    private final StringTexture mUndoText;
39    private final StringTexture mDeletedText;
40    private final ResourceTexture mUndoIcon;
41    private final int mBarHeight;
42    private final int mBarMargin;
43    private final int mUndoTextMargin;
44    private final int mIconSize;
45    private final int mIconMargin;
46    private final int mSeparatorTopMargin;
47    private final int mSeparatorBottomMargin;
48    private final int mSeparatorRightMargin;
49    private final int mSeparatorWidth;
50    private final int mDeletedTextMargin;
51    private final int mClickRegion;
52
53    private OnClickListener mOnClickListener;
54    private boolean mDownOnButton;
55
56    // This is the layout of UndoBarView. The unit is dp.
57    //
58    //    +-+----+----------------+-+--+----+-+------+--+-+
59    // 48 | |    | Deleted        | |  | <- | | UNDO |  | |
60    //    +-+----+----------------+-+--+----+-+------+--+-+
61    //     4  16                   1 12  32  8        16 4
62    public UndoBarView(Context context) {
63        mBarHeight = GalleryUtils.dpToPixel(48);
64        mBarMargin = GalleryUtils.dpToPixel(4);
65        mUndoTextMargin = GalleryUtils.dpToPixel(16);
66        mIconMargin = GalleryUtils.dpToPixel(8);
67        mIconSize = GalleryUtils.dpToPixel(32);
68        mSeparatorRightMargin = GalleryUtils.dpToPixel(12);
69        mSeparatorTopMargin = GalleryUtils.dpToPixel(10);
70        mSeparatorBottomMargin = GalleryUtils.dpToPixel(10);
71        mSeparatorWidth = GalleryUtils.dpToPixel(1);
72        mDeletedTextMargin = GalleryUtils.dpToPixel(16);
73
74        mPanel = new NinePatchTexture(context, R.drawable.panel_undo_holo);
75        mUndoText = StringTexture.newInstance(context.getString(R.string.undo),
76                GalleryUtils.dpToPixel(12), GRAY, 0, true);
77        mDeletedText = StringTexture.newInstance(
78                context.getString(R.string.deleted),
79                GalleryUtils.dpToPixel(16), WHITE);
80        mUndoIcon = new ResourceTexture(
81                context, R.drawable.ic_menu_revert_holo_dark);
82        mClickRegion = mBarMargin + mUndoTextMargin + mUndoText.getWidth()
83                + mIconMargin + mIconSize + mSeparatorRightMargin;
84    }
85
86    public void setOnClickListener(OnClickListener listener) {
87        mOnClickListener = listener;
88    }
89
90    @Override
91    protected void onMeasure(int widthSpec, int heightSpec) {
92        setMeasuredSize(0 /* unused */, mBarHeight);
93    }
94
95    @Override
96    protected void render(GLCanvas canvas) {
97        super.render(canvas);
98        advanceAnimation();
99
100        canvas.save(GLCanvas.SAVE_FLAG_ALPHA);
101        canvas.multiplyAlpha(mAlpha);
102
103        int w = getWidth();
104        int h = getHeight();
105        mPanel.draw(canvas, mBarMargin, 0, w - mBarMargin * 2, mBarHeight);
106
107        int x = w - mBarMargin;
108        int y;
109
110        x -= mUndoTextMargin + mUndoText.getWidth();
111        y = (mBarHeight - mUndoText.getHeight()) / 2;
112        mUndoText.draw(canvas, x, y);
113
114        x -= mIconMargin + mIconSize;
115        y = (mBarHeight - mIconSize) / 2;
116        mUndoIcon.draw(canvas, x, y, mIconSize, mIconSize);
117
118        x -= mSeparatorRightMargin + mSeparatorWidth;
119        y = mSeparatorTopMargin;
120        canvas.fillRect(x, y, mSeparatorWidth,
121                mBarHeight - mSeparatorTopMargin - mSeparatorBottomMargin, GRAY);
122
123        x = mBarMargin + mDeletedTextMargin;
124        y = (mBarHeight - mDeletedText.getHeight()) / 2;
125        mDeletedText.draw(canvas, x, y);
126
127        canvas.restore();
128    }
129
130    @Override
131    protected boolean onTouch(MotionEvent event) {
132        switch (event.getAction()) {
133            case MotionEvent.ACTION_DOWN:
134                mDownOnButton = inUndoButton(event);
135                break;
136            case MotionEvent.ACTION_UP:
137                if (mDownOnButton) {
138                    if (mOnClickListener != null && inUndoButton(event)) {
139                        mOnClickListener.onClick(this);
140                    }
141                    mDownOnButton = false;
142                }
143                break;
144            case MotionEvent.ACTION_CANCEL:
145                mDownOnButton = false;
146                break;
147        }
148        return true;
149    }
150
151    // Check if the event is on the right of the separator
152    private boolean inUndoButton(MotionEvent event) {
153        float x = event.getX();
154        float y = event.getY();
155        int w = getWidth();
156        int h = getHeight();
157        return (x >= w - mClickRegion && x < w && y >= 0 && y < h);
158    }
159
160    ////////////////////////////////////////////////////////////////////////////
161    //  Alpha Animation
162    ////////////////////////////////////////////////////////////////////////////
163
164    private static final long NO_ANIMATION = -1;
165    private static long ANIM_TIME = 200;
166    private long mAnimationStartTime = NO_ANIMATION;
167    private float mFromAlpha, mToAlpha;
168    private float mAlpha;
169
170    private static float getTargetAlpha(int visibility) {
171        return (visibility == VISIBLE) ? 1f : 0f;
172    }
173
174    @Override
175    public void setVisibility(int visibility) {
176        mAlpha = getTargetAlpha(visibility);
177        mAnimationStartTime = NO_ANIMATION;
178        super.setVisibility(visibility);
179        invalidate();
180    }
181
182    public void animateVisibility(int visibility) {
183        float target = getTargetAlpha(visibility);
184        if (mAnimationStartTime == NO_ANIMATION && mAlpha == target) return;
185        if (mAnimationStartTime != NO_ANIMATION && mToAlpha == target) return;
186
187        mFromAlpha = mAlpha;
188        mToAlpha = target;
189        mAnimationStartTime = AnimationTime.startTime();
190
191        super.setVisibility(VISIBLE);
192        invalidate();
193    }
194
195    private void advanceAnimation() {
196        if (mAnimationStartTime == NO_ANIMATION) return;
197
198        float delta = (float) (AnimationTime.get() - mAnimationStartTime) /
199                ANIM_TIME;
200        mAlpha = mFromAlpha + ((mToAlpha > mFromAlpha) ? delta : -delta);
201        mAlpha = Utils.clamp(mAlpha, 0f, 1f);
202
203        if (mAlpha == mToAlpha) {
204            mAnimationStartTime = NO_ANIMATION;
205            if (mAlpha == 0) {
206                super.setVisibility(INVISIBLE);
207            }
208        }
209        invalidate();
210    }
211}
212