BottomBar.java revision 39f8a7647aa4051a1c260b9496c2db82f93f8667
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.ui;
180744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen
1939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Spragueimport android.animation.Animator;
2039f8a7647aa4051a1c260b9496c2db82f93f8667Spike Spragueimport android.animation.AnimatorListenerAdapter;
2139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Spragueimport android.animation.ValueAnimator;
2239f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
230744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissenimport android.content.Context;
240744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissenimport android.content.res.Configuration;
2556688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgrenimport android.os.Handler;
2656688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgrenimport android.os.HandlerThread;
2756688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgrenimport android.os.Looper;
280744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissenimport android.util.AttributeSet;
290744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissenimport android.view.Gravity;
304efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgrenimport android.view.MotionEvent;
310744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissenimport android.view.View;
323b92e2f8e6d91af1b83c8bee95bd93e7153b96daSpike Spragueimport android.view.ViewGroup;
330744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissenimport android.widget.FrameLayout;
341648c36927501ff32bf2918e8a445118d5208731Erin Dahlgrenimport android.widget.LinearLayout;
350744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen
3639f8a7647aa4051a1c260b9496c2db82f93f8667Spike Spragueimport com.android.camera.ShutterButton;
374efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgrenimport com.android.camera.ToggleImageButton;
3839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Spragueimport com.android.camera.util.Gusterpolator;
3939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Spragueimport com.android.camera2.R;
4039f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
4139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Spragueimport android.graphics.Canvas;
4239f8a7647aa4051a1c260b9496c2db82f93f8667Spike Spragueimport android.graphics.Path;
4339f8a7647aa4051a1c260b9496c2db82f93f8667Spike Spragueimport android.graphics.Paint;
4439f8a7647aa4051a1c260b9496c2db82f93f8667Spike Spragueimport android.graphics.drawable.Drawable;
4539f8a7647aa4051a1c260b9496c2db82f93f8667Spike Spragueimport android.graphics.drawable.TransitionDrawable;
4670da918464276b110c43868caa272c97baadb89eDoris Liu
470744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen/**
480744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * BottomBar swaps its width and height on rotation. In addition, it also changes
490744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * gravity and layout orientation based on the new orientation. Specifically, in
500744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * landscape it aligns to the right side of its parent and lays out its children
510744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * vertically, whereas in portrait, it stays at the bottom of the parent and has
520744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen * a horizontal layout orientation.
5339f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague*/
5470da918464276b110c43868caa272c97baadb89eDoris Liupublic class BottomBar extends FrameLayout
5556688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren    implements PreviewStatusListener.PreviewAreaSizeChangedListener,
5656688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren               PreviewOverlay.OnPreviewTouchedListener {
573b92e2f8e6d91af1b83c8bee95bd93e7153b96daSpike Sprague
5870da918464276b110c43868caa272c97baadb89eDoris Liu    private static final String TAG = "BottomBar";
5939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
6056688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren    private static final int BOTTOMBAR_OPTIONS_TIMEOUT_MS = 2000;
6156688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren
6239f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    private static final float CIRCLE_RADIUS = 64.0f;
6339f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    private static final int CIRCLE_ANIM_DURATION_MS = 450;
6439f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
6570da918464276b110c43868caa272c97baadb89eDoris Liu    private int mWidth;
6670da918464276b110c43868caa272c97baadb89eDoris Liu    private int mHeight;
6770da918464276b110c43868caa272c97baadb89eDoris Liu    private float mOffsetShorterEdge;
6870da918464276b110c43868caa272c97baadb89eDoris Liu    private float mOffsetLongerEdge;
6970da918464276b110c43868caa272c97baadb89eDoris Liu
7070da918464276b110c43868caa272c97baadb89eDoris Liu    private final int mOptimalHeight;
7170da918464276b110c43868caa272c97baadb89eDoris Liu    private boolean mOverLayBottomBar;
720744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen
7356688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren    private ToggleImageButton mOptionsToggle;
7456688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren
7556688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren    private TopRightMostOverlay mOptionsOverlay;
7656688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren    private TopRightWeightedLayout mOptionsLayout;
774efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    private FrameLayout mCaptureLayout;
784efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    private TopRightWeightedLayout mIntentLayout;
7956688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren    private boolean mIsCaptureIntent = false;
8056688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren
8156688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren    /**
8256688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren     * A generic Runnable for setting the options toggle
8356688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren     * to the capture layout state and performing the state
8456688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren     * transition.
8556688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren     */
8656688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren    private final Runnable mCloseOptionsRunnable =
8756688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        new Runnable() {
8856688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren            @Override
8956688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren            public void run() {
9056688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren                if (mOptionsToggle != null) {
9156688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren                    mOptionsToggle.setState(0, true);
9256688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren                }
9356688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren            }
9456688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        };
954efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren
9639f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    private ShutterButton mShutterButton;
9739f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
9839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    private int mBackgroundColor;
9939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    private Paint mCirclePaint = new Paint();
10039f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    private Path mCirclePath = new Path();
10139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
1020744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen    public BottomBar(Context context, AttributeSet attrs) {
1030744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen        super(context, attrs);
10470da918464276b110c43868caa272c97baadb89eDoris Liu        mOptimalHeight = getResources().getDimensionPixelSize(R.dimen.bottom_bar_height_optimal);
1050744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen    }
1060744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen
1074efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    @Override
1084efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    public void onFinishInflate() {
10956688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        mOptionsOverlay
11056688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren            = (TopRightMostOverlay) findViewById(R.id.bottombar_options_overlay);
11156688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        mOptionsLayout
11256688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren            = (TopRightWeightedLayout) findViewById(R.id.bottombar_options);
1134efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        mCaptureLayout
1144efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren            = (FrameLayout) findViewById(R.id.bottombar_capture);
1154efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        mIntentLayout
1164efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren            = (TopRightWeightedLayout) findViewById(R.id.bottombar_intent);
11739f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        mShutterButton
11839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague            = (ShutterButton) findViewById(R.id.shutter_button);
11956688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren
12056688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        mOptionsOverlay.setOnTouchListener(new View.OnTouchListener() {
12139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague            @Override
12239f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague            public boolean onTouch(View v, MotionEvent event) {
12339f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
12439f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                    // close options immediately.
12539f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                    closeModeOptionsDelayed(BOTTOMBAR_OPTIONS_TIMEOUT_MS);
12656688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren                }
12739f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                // Let touch event reach mode options or shutter.
12839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                return false;
12939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague            }
13039f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        });
13156688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren    }
13256688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren
13356688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren    @Override
13456688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren    public void onPreviewTouched(MotionEvent ev) {
13556688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        // close options immediately.
13656688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        closeModeOptionsDelayed(0);
13756688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren    }
13856688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren
13956688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren    /**
14056688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren     * Schedule (or re-schedule) the options menu to be closed
14156688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren     * after a number of milliseconds.  If the options menu
14256688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren     * is already closed, nothing is scheduled.
14356688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren     */
14456688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren    private void closeModeOptionsDelayed(int milliseconds) {
14556688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        // Check that the bottom bar options are visible.
14656688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        if (mOptionsLayout.getVisibility() != View.VISIBLE) {
14756688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren            return;
14856688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        }
14956688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren
15056688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        // Remove queued callbacks.
15156688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        removeCallbacks(mCloseOptionsRunnable);
15256688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren
15356688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        // Close the bottom bar options view in n milliseconds.
15456688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        postDelayed(mCloseOptionsRunnable, milliseconds);
1554efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    }
1564efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren
1574efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    /**
1584efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren     * Initializes the bottom bar toggle for switching between
15956688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren     * capture and the bottom bar options.
1604efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren     */
16156688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren    public void setupToggle(boolean isCaptureIntent) {
16256688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        mIsCaptureIntent = isCaptureIntent;
16356688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren
1644efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        // Of type ToggleImageButton because ToggleButton
1654efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        // has a non-removable spacing for text on the right-hand side.
16656688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        mOptionsToggle = (ToggleImageButton) findViewById(R.id.bottombar_options_toggle);
16756688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        mOptionsToggle.setState(0, false);
16856688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        mOptionsToggle.setOnStateChangeListener(new ToggleImageButton.OnStateChangeListener() {
1693b92e2f8e6d91af1b83c8bee95bd93e7153b96daSpike Sprague            @Override
17056688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren            public void stateChanged(View view, boolean toOptions) {
17156688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren                if (toOptions) {
17256688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren                    if (mIsCaptureIntent) {
1733b92e2f8e6d91af1b83c8bee95bd93e7153b96daSpike Sprague                        hideIntentLayout();
1743b92e2f8e6d91af1b83c8bee95bd93e7153b96daSpike Sprague                    }
17556688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren                    transitionToOptions();
1763b92e2f8e6d91af1b83c8bee95bd93e7153b96daSpike Sprague                } else {
17756688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren                    if (mIsCaptureIntent) {
1783b92e2f8e6d91af1b83c8bee95bd93e7153b96daSpike Sprague                        transitionToIntentLayout();
1794efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren                    } else {
1803b92e2f8e6d91af1b83c8bee95bd93e7153b96daSpike Sprague                        transitionToCapture();
1814efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren                    }
1824efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren                }
1833b92e2f8e6d91af1b83c8bee95bd93e7153b96daSpike Sprague            }
1843b92e2f8e6d91af1b83c8bee95bd93e7153b96daSpike Sprague        });
18556688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        mOptionsOverlay.setReferenceViewParent(mOptionsLayout);
1864efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    }
1874efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren
1884efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    /**
1894efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren     * Hide the intent layout.  This is necessary for switching between
19056688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren     * the intent capture layout and the bottom bar options.
1914efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren     */
1924efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    private void hideIntentLayout() {
1934efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        mIntentLayout.setVisibility(View.INVISIBLE);
1944efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    }
1954efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren
1964efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    /**
19756688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren     * Perform a transition from the bottom bar options layout to the
19856688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren     * bottom bar capture layout.
1994efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren     */
2004efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    private void transitionToCapture() {
20156688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        mOptionsLayout.setVisibility(View.INVISIBLE);
2024efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        mCaptureLayout.setVisibility(View.VISIBLE);
2034efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    }
2044efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren
2054efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    /**
20656688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren     * Perform a transition from the bottom bar capture layout to the
20756688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren     * bottom bar options layout.
2084efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren     */
20956688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren    private void transitionToOptions() {
2103b92e2f8e6d91af1b83c8bee95bd93e7153b96daSpike Sprague        mCaptureLayout.setVisibility(View.INVISIBLE);
21156688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        mOptionsLayout.setVisibility(View.VISIBLE);
2124efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    }
2134efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren
2140744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen    /**
2154efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren     * Perform a transition to the global intent layout.  The current
2164efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren     * layout state of the bottom bar is irrelevant.
2170f3c4b4fc9ac87d7d4c3d04c3cb3426c711f57eaSpike Sprague     */
2184efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    public void transitionToIntentLayout() {
2194efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        mCaptureLayout.setVisibility(View.VISIBLE);
22056688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        mOptionsLayout.setVisibility(View.INVISIBLE);
22156688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        mOptionsOverlay.setVisibility(View.VISIBLE);
2224efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        mIntentLayout.setVisibility(View.VISIBLE);
2234efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren
2244efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        View button;
2254efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        button = mIntentLayout.findViewById(R.id.done_button);
2264efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        button.setVisibility(View.INVISIBLE);
2274efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        button = mIntentLayout.findViewById(R.id.retake_button);
2284efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        button.setVisibility(View.INVISIBLE);
2294efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    }
2304efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren
2314efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    /**
2324efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren     * Perform a transition to the global intent review layout.
2334efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren     * The current layout state of the bottom bar is irrelevant.
2344efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren     */
2354efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren    public void transitionToIntentReviewLayout() {
2364efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        mCaptureLayout.setVisibility(View.INVISIBLE);
23756688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        mOptionsLayout.setVisibility(View.INVISIBLE);
23856688f7019fbe4dee110b7600349f48da9fff601Erin Dahlgren        mOptionsOverlay.setVisibility(View.INVISIBLE);
2394efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren
2404efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        View button;
2414efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        button = mIntentLayout.findViewById(R.id.done_button);
2424efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        button.setVisibility(View.VISIBLE);
2434efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        button = mIntentLayout.findViewById(R.id.retake_button);
2444efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        button.setVisibility(View.VISIBLE);
2454efa8b54c1df4e06f2d3caed2568015a737f9ddaErin Dahlgren        mIntentLayout.setVisibility(View.VISIBLE);
2460f3c4b4fc9ac87d7d4c3d04c3cb3426c711f57eaSpike Sprague    }
2470f3c4b4fc9ac87d7d4c3d04c3cb3426c711f57eaSpike Sprague
24870da918464276b110c43868caa272c97baadb89eDoris Liu    @Override
24970da918464276b110c43868caa272c97baadb89eDoris Liu    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
25070da918464276b110c43868caa272c97baadb89eDoris Liu        mWidth = MeasureSpec.getSize(widthMeasureSpec);
25170da918464276b110c43868caa272c97baadb89eDoris Liu        mHeight = MeasureSpec.getSize(heightMeasureSpec);
25270da918464276b110c43868caa272c97baadb89eDoris Liu        if (mWidth == 0 || mHeight == 0) {
25370da918464276b110c43868caa272c97baadb89eDoris Liu            return;
25470da918464276b110c43868caa272c97baadb89eDoris Liu        }
25570da918464276b110c43868caa272c97baadb89eDoris Liu
25670da918464276b110c43868caa272c97baadb89eDoris Liu        if (mOffsetShorterEdge != 0 && mOffsetLongerEdge != 0) {
25770da918464276b110c43868caa272c97baadb89eDoris Liu            float previewAspectRatio =
25870da918464276b110c43868caa272c97baadb89eDoris Liu                    mOffsetLongerEdge / mOffsetShorterEdge;
25970da918464276b110c43868caa272c97baadb89eDoris Liu            if (previewAspectRatio < 1.0) {
26070da918464276b110c43868caa272c97baadb89eDoris Liu                previewAspectRatio = 1.0f/previewAspectRatio;
26170da918464276b110c43868caa272c97baadb89eDoris Liu            }
26270da918464276b110c43868caa272c97baadb89eDoris Liu            float screenAspectRatio = (float) mWidth / (float) mHeight;
26370da918464276b110c43868caa272c97baadb89eDoris Liu            if (screenAspectRatio < 1.0) {
26470da918464276b110c43868caa272c97baadb89eDoris Liu                screenAspectRatio = 1.0f/screenAspectRatio;
26570da918464276b110c43868caa272c97baadb89eDoris Liu            }
26670da918464276b110c43868caa272c97baadb89eDoris Liu            if (previewAspectRatio >= screenAspectRatio) {
26770da918464276b110c43868caa272c97baadb89eDoris Liu                mOverLayBottomBar = true;
26839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                setBackgroundAlpha(128);
26970da918464276b110c43868caa272c97baadb89eDoris Liu            } else {
27070da918464276b110c43868caa272c97baadb89eDoris Liu                mOverLayBottomBar = false;
27139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                setBackgroundAlpha(255);
27270da918464276b110c43868caa272c97baadb89eDoris Liu            }
27370da918464276b110c43868caa272c97baadb89eDoris Liu        }
27470da918464276b110c43868caa272c97baadb89eDoris Liu
27570da918464276b110c43868caa272c97baadb89eDoris Liu        // Calculates the width and height needed for the bar.
27670da918464276b110c43868caa272c97baadb89eDoris Liu        int barWidth, barHeight;
27770da918464276b110c43868caa272c97baadb89eDoris Liu        if (mWidth > mHeight) {
2781648c36927501ff32bf2918e8a445118d5208731Erin Dahlgren            // TODO: The bottom bar should not need to care about the
2791648c36927501ff32bf2918e8a445118d5208731Erin Dahlgren            // the type of its parent.  Handle this in the parent layout.
2801648c36927501ff32bf2918e8a445118d5208731Erin Dahlgren            ((LinearLayout.LayoutParams) getLayoutParams()).gravity = Gravity.RIGHT;
28170da918464276b110c43868caa272c97baadb89eDoris Liu            if ((mOffsetLongerEdge == 0 && mOffsetShorterEdge == 0) || mOverLayBottomBar) {
28270da918464276b110c43868caa272c97baadb89eDoris Liu                barWidth = mOptimalHeight;
28370da918464276b110c43868caa272c97baadb89eDoris Liu                barHeight = mHeight;
28470da918464276b110c43868caa272c97baadb89eDoris Liu            } else {
28570da918464276b110c43868caa272c97baadb89eDoris Liu                barWidth = (int) (mWidth - mOffsetLongerEdge);
28670da918464276b110c43868caa272c97baadb89eDoris Liu                barHeight = mHeight;
28770da918464276b110c43868caa272c97baadb89eDoris Liu            }
2880744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen        } else {
2891648c36927501ff32bf2918e8a445118d5208731Erin Dahlgren            ((LinearLayout.LayoutParams) getLayoutParams()).gravity = Gravity.BOTTOM;
29070da918464276b110c43868caa272c97baadb89eDoris Liu            if ((mOffsetLongerEdge == 0 && mOffsetShorterEdge == 0) || mOverLayBottomBar) {
29170da918464276b110c43868caa272c97baadb89eDoris Liu                barWidth = mWidth;
29270da918464276b110c43868caa272c97baadb89eDoris Liu                barHeight = mOptimalHeight;
29370da918464276b110c43868caa272c97baadb89eDoris Liu            } else {
29470da918464276b110c43868caa272c97baadb89eDoris Liu                barWidth = mWidth;
29570da918464276b110c43868caa272c97baadb89eDoris Liu                barHeight = (int) (mHeight - mOffsetLongerEdge);
29670da918464276b110c43868caa272c97baadb89eDoris Liu            }
2970744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen        }
29839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
29939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        mCirclePath.addCircle(
30039f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague            barWidth/2,
30139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague            barHeight/2,
30239f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague            (int)(diagonalLength(barWidth, barHeight)/2),
30339f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague            Path.Direction.CW);
30439f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
30570da918464276b110c43868caa272c97baadb89eDoris Liu        super.onMeasure(MeasureSpec.makeMeasureSpec(barWidth, MeasureSpec.EXACTLY),
30670da918464276b110c43868caa272c97baadb89eDoris Liu                MeasureSpec.makeMeasureSpec(barHeight, MeasureSpec.EXACTLY));
30770da918464276b110c43868caa272c97baadb89eDoris Liu    }
30870da918464276b110c43868caa272c97baadb89eDoris Liu
30970da918464276b110c43868caa272c97baadb89eDoris Liu    private void adjustBottomBar(float scaledTextureWidth,
31070da918464276b110c43868caa272c97baadb89eDoris Liu                                 float scaledTextureHeight) {
31170da918464276b110c43868caa272c97baadb89eDoris Liu        setOffset(scaledTextureWidth, scaledTextureHeight);
31270da918464276b110c43868caa272c97baadb89eDoris Liu    }
31370da918464276b110c43868caa272c97baadb89eDoris Liu
31470da918464276b110c43868caa272c97baadb89eDoris Liu    @Override
315482de029dc20e0a577388a602985fb31c3200309Doris Liu    public void onPreviewAreaSizeChanged(float scaledTextureWidth,
316482de029dc20e0a577388a602985fb31c3200309Doris Liu                                         float scaledTextureHeight) {
31770da918464276b110c43868caa272c97baadb89eDoris Liu        adjustBottomBar(scaledTextureWidth, scaledTextureHeight);
31870da918464276b110c43868caa272c97baadb89eDoris Liu    }
31970da918464276b110c43868caa272c97baadb89eDoris Liu
32070da918464276b110c43868caa272c97baadb89eDoris Liu    private void setOffset(float scaledTextureWidth, float scaledTextureHeight) {
32170da918464276b110c43868caa272c97baadb89eDoris Liu        float offsetLongerEdge, offsetShorterEdge;
32270da918464276b110c43868caa272c97baadb89eDoris Liu        if (scaledTextureHeight > scaledTextureWidth) {
32370da918464276b110c43868caa272c97baadb89eDoris Liu            offsetLongerEdge = scaledTextureHeight;
32470da918464276b110c43868caa272c97baadb89eDoris Liu            offsetShorterEdge = scaledTextureWidth;
3250744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen        } else {
32670da918464276b110c43868caa272c97baadb89eDoris Liu            offsetLongerEdge = scaledTextureWidth;
32770da918464276b110c43868caa272c97baadb89eDoris Liu            offsetShorterEdge = scaledTextureHeight;
32870da918464276b110c43868caa272c97baadb89eDoris Liu        }
32970da918464276b110c43868caa272c97baadb89eDoris Liu        if (mOffsetLongerEdge != offsetLongerEdge || mOffsetShorterEdge != offsetShorterEdge) {
33070da918464276b110c43868caa272c97baadb89eDoris Liu            mOffsetLongerEdge = offsetLongerEdge;
33170da918464276b110c43868caa272c97baadb89eDoris Liu            mOffsetShorterEdge = offsetShorterEdge;
33270da918464276b110c43868caa272c97baadb89eDoris Liu            requestLayout();
3330744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen        }
3340744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen    }
3350744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen
3360744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen    @Override
3370744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen    protected void onConfigurationChanged(Configuration config) {
3380744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen        super.onConfigurationChanged(config);
3390744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen    }
3400744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen
341e3b0e946924e45f6788db69aaa8f48a2d81031f0Spike Sprague    // prevent touches on bottom bar (not its children)
342e3b0e946924e45f6788db69aaa8f48a2d81031f0Spike Sprague    // from triggering a touch event on preview area
343e3b0e946924e45f6788db69aaa8f48a2d81031f0Spike Sprague    @Override
344e3b0e946924e45f6788db69aaa8f48a2d81031f0Spike Sprague    public boolean onTouchEvent(MotionEvent event) {
345e3b0e946924e45f6788db69aaa8f48a2d81031f0Spike Sprague        return true;
346e3b0e946924e45f6788db69aaa8f48a2d81031f0Spike Sprague    }
34739f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
34839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    @Override
34939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    public void onDraw(Canvas canvas) {
35039f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        canvas.drawPath(mCirclePath, mCirclePaint);
35139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
35239f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        super.onDraw(canvas);
35339f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    }
35439f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
35539f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    @Override
35639f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    public void setBackgroundColor(int color) {
35739f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        mBackgroundColor = color;
35839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        mCirclePaint.setColor(mBackgroundColor);
35939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    }
36039f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
36139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    public void setBackgroundAlpha(int alpha) {
36239f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        setBackgroundColor((alpha << 24) | (mBackgroundColor & 0x00FFFFFF));
36339f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    }
36439f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
36539f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    private double diagonalLength(double w, double h) {
36639f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        return Math.sqrt((w*w) + (h*h));
36739f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    }
36839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    private double diagonalLength() {
36939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        return diagonalLength(getWidth(), getHeight());
37039f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    }
37139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
37239f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    private TransitionDrawable crossfadeDrawable(Drawable from, Drawable to) {
37339f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        Drawable [] arrayDrawable = new Drawable[2];
37439f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        arrayDrawable[0] = from;
37539f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        arrayDrawable[1] = to;
37639f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        TransitionDrawable transitionDrawable = new TransitionDrawable(arrayDrawable);
37739f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        transitionDrawable.setCrossFadeEnabled(true);
37839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        return transitionDrawable;
37939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    }
38039f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
38139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    /**
38239f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague     * Set the shutter button's icon resource
38339f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague     */
38439f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    public void setShutterButtonIcon(int resId) {
38539f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        mShutterButton.setImageResource(resId);
38639f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    }
38739f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
38839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    /**
38939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague     * Animates bar to a single stop button
39039f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague     */
39139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    public void animateToCircle(int resId) {
39239f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        final ValueAnimator radiusAnimator = ValueAnimator.ofFloat(
39339f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                                                 (float) diagonalLength()/2,
39439f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                                                 CIRCLE_RADIUS);
39539f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        radiusAnimator.setDuration(CIRCLE_ANIM_DURATION_MS);
39639f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        radiusAnimator.setInterpolator(Gusterpolator.INSTANCE);
39739f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
39839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        radiusAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
39939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague            @Override
40039f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague            public void onAnimationUpdate(ValueAnimator animation) {
40139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                mCirclePath.reset();
40239f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                mCirclePath.addCircle(
40339f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                    getWidth()/2,
40439f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                    getHeight()/2,
40539f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                    (Float) animation.getAnimatedValue(),
40639f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                    Path.Direction.CW);
40739f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
40839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                invalidate();
40939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague            }
41039f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        });
41139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
41239f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        TransitionDrawable transitionDrawable = crossfadeDrawable(
41339f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                mShutterButton.getDrawable(),
41439f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                getResources().getDrawable(resId));
41539f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        mShutterButton.setImageDrawable(transitionDrawable);
41639f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
41739f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        View optionsOverlay = findViewById(R.id.bottombar_options_overlay);
41839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        optionsOverlay.setVisibility(View.INVISIBLE);
41939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
42039f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
42139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        transitionDrawable.startTransition(CIRCLE_ANIM_DURATION_MS);
42239f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        radiusAnimator.start();
42339f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    }
42439f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
42539f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    /**
42639f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague     * Animates bar to full width / length with video capture icon
42739f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague     */
42839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    public void animateToFullSize(int resId) {
42939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        final ValueAnimator radiusAnimator = ValueAnimator.ofFloat(
43039f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                                                 CIRCLE_RADIUS,
43139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                                                 (float) diagonalLength()/2);
43239f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        radiusAnimator.setDuration(CIRCLE_ANIM_DURATION_MS);
43339f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        radiusAnimator.setInterpolator(Gusterpolator.INSTANCE);
43439f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        radiusAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
43539f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague            @Override
43639f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague            public void onAnimationUpdate(ValueAnimator animation) {
43739f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                mCirclePath.reset();
43839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                mCirclePath.addCircle(
43939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                    getWidth()/2,
44039f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                    getHeight()/2,
44139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                    (Float) animation.getAnimatedValue(),
44239f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                    Path.Direction.CW);
44339f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
44439f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                invalidate();
44539f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague            }
44639f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        });
44739f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        radiusAnimator.addListener(new AnimatorListenerAdapter() {
44839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague            @Override
44939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague            public void onAnimationEnd(Animator animation) {
45039f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                View optionsOverlay = findViewById(R.id.bottombar_options_overlay);
45139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                optionsOverlay.setVisibility(View.VISIBLE);
45239f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague            }
45339f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        });
45439f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
45539f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        TransitionDrawable transitionDrawable = crossfadeDrawable(
45639f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                mShutterButton.getDrawable(),
45739f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague                getResources().getDrawable(resId));
45839f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        mShutterButton.setImageDrawable(transitionDrawable);
45939f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague
46039f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        transitionDrawable.startTransition(CIRCLE_ANIM_DURATION_MS);
46139f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague        radiusAnimator.start();
46239f8a7647aa4051a1c260b9496c2db82f93f8667Spike Sprague    }
4630744e4ab3ec5f0270bdad33e7c477f52ba680bbdMarco Nelissen}
464