• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 
17 package com.android.camera.ui;
18 
19 import android.content.Context;
20 import android.content.res.Configuration;
21 import android.view.animation.Interpolator;
22 import android.graphics.PointF;
23 import android.graphics.RectF;
24 import android.util.AttributeSet;
25 import android.view.View;
26 import android.widget.FrameLayout;
27 
28 import com.android.camera.CaptureLayoutHelper;
29 import com.android.camera.debug.Log;
30 import com.android.camera.ui.motion.InterpolatorHelper;
31 import com.android.camera.widget.ModeOptions;
32 import com.android.camera.widget.ModeOptionsOverlay;
33 import com.android.camera.widget.RoundedThumbnailView;
34 import com.android.camera2.R;
35 
36 /**
37  * The goal of this class is to ensure mode options and capture indicator is
38  * always laid out to the left of or above bottom bar in landscape or portrait
39  * respectively. All the other children in this view group can be expected to
40  * be laid out the same way as they are in a normal FrameLayout.
41  */
42 public class StickyBottomCaptureLayout extends FrameLayout {
43 
44     private final static Log.Tag TAG = new Log.Tag("StickyBotCapLayout");
45     private RoundedThumbnailView mRoundedThumbnailView;
46     private ModeOptionsOverlay mModeOptionsOverlay;
47     private View mBottomBar;
48     private CaptureLayoutHelper mCaptureLayoutHelper = null;
49 
50     private ModeOptions.Listener mModeOptionsListener = new ModeOptions.Listener() {
51         @Override
52         public void onBeginToShowModeOptions() {
53             final PointF thumbnailViewPosition = getRoundedThumbnailPosition(
54                     mCaptureLayoutHelper.getUncoveredPreviewRect(),
55                     false,
56                     mModeOptionsOverlay.getModeOptionsToggleWidth());
57             final int orientation = getResources().getConfiguration().orientation;
58             if (orientation == Configuration.ORIENTATION_PORTRAIT) {
59                 animateCaptureIndicatorToY(thumbnailViewPosition.y);
60             } else {
61                 animateCaptureIndicatorToX(thumbnailViewPosition.x);
62             }
63         }
64 
65         @Override
66         public void onBeginToHideModeOptions() {
67             final PointF thumbnailViewPosition = getRoundedThumbnailPosition(
68                     mCaptureLayoutHelper.getUncoveredPreviewRect(),
69                     true,
70                     mModeOptionsOverlay.getModeOptionsToggleWidth());
71             final int orientation = getResources().getConfiguration().orientation;
72             if (orientation == Configuration.ORIENTATION_PORTRAIT) {
73                 animateCaptureIndicatorToY(thumbnailViewPosition.y);
74             } else {
75                 animateCaptureIndicatorToX(thumbnailViewPosition.x);
76             }
77         }
78     };
79 
StickyBottomCaptureLayout(Context context, AttributeSet attrs)80     public StickyBottomCaptureLayout(Context context, AttributeSet attrs) {
81         super(context, attrs);
82     }
83 
84     @Override
onFinishInflate()85     public void onFinishInflate() {
86         mRoundedThumbnailView = (RoundedThumbnailView) findViewById(R.id.rounded_thumbnail_view);
87         mModeOptionsOverlay = (ModeOptionsOverlay) findViewById(R.id.mode_options_overlay);
88         mModeOptionsOverlay.setModeOptionsListener(mModeOptionsListener);
89         mBottomBar = findViewById(R.id.bottom_bar);
90     }
91 
92     /**
93      * Sets a capture layout helper to query layout rect from.
94      */
setCaptureLayoutHelper(CaptureLayoutHelper helper)95     public void setCaptureLayoutHelper(CaptureLayoutHelper helper) {
96         mCaptureLayoutHelper = helper;
97     }
98 
99     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)100     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
101         if (mCaptureLayoutHelper == null) {
102             Log.e(TAG, "Capture layout helper needs to be set first.");
103             return;
104         }
105         // Layout mode options overlay.
106         RectF uncoveredPreviewRect = mCaptureLayoutHelper.getUncoveredPreviewRect();
107         mModeOptionsOverlay.layout((int) uncoveredPreviewRect.left, (int) uncoveredPreviewRect.top,
108                 (int) uncoveredPreviewRect.right, (int) uncoveredPreviewRect.bottom);
109 
110         // Layout capture indicator.
111         PointF roundedThumbnailViewPosition = getRoundedThumbnailPosition(
112                 uncoveredPreviewRect,
113                 mModeOptionsOverlay.isModeOptionsHidden(),
114                 mModeOptionsOverlay.getModeOptionsToggleWidth());
115         mRoundedThumbnailView.layout(
116                 (int) roundedThumbnailViewPosition.x,
117                 (int) roundedThumbnailViewPosition.y,
118                 (int) roundedThumbnailViewPosition.x + mRoundedThumbnailView.getMeasuredWidth(),
119                 (int) roundedThumbnailViewPosition.y + mRoundedThumbnailView.getMeasuredHeight());
120 
121         // Layout bottom bar.
122         RectF bottomBarRect = mCaptureLayoutHelper.getBottomBarRect();
123         mBottomBar.layout((int) bottomBarRect.left, (int) bottomBarRect.top,
124                 (int) bottomBarRect.right, (int) bottomBarRect.bottom);
125     }
126 
127     /**
128      * Calculates the desired layout of capture indicator.
129      *
130      * @param uncoveredPreviewRect The uncovered preview bound which contains mode option
131      *                             overlay and capture indicator.
132      * @param isModeOptionsHidden Whether the mode options button are hidden.
133      * @param modeOptionsToggleWidth The width of mode options toggle (three dots button).
134      * @return the desired view bound for capture indicator.
135      */
getRoundedThumbnailPosition( RectF uncoveredPreviewRect, boolean isModeOptionsHidden, float modeOptionsToggleWidth)136     private PointF getRoundedThumbnailPosition(
137             RectF uncoveredPreviewRect, boolean isModeOptionsHidden, float modeOptionsToggleWidth) {
138         final float threeDotsButtonDiameter =
139                 getResources().getDimension(R.dimen.option_button_circle_size);
140         final float threeDotsButtonPadding =
141                 getResources().getDimension(R.dimen.mode_options_toggle_padding);
142         final float modeOptionsHeight = getResources().getDimension(R.dimen.mode_options_height);
143 
144         final float roundedThumbnailViewSize = mRoundedThumbnailView.getMeasuredWidth();
145         final float roundedThumbnailFinalSize = mRoundedThumbnailView.getThumbnailFinalDiameter();
146         final float roundedThumbnailViewPadding = mRoundedThumbnailView.getThumbnailPadding();
147 
148         // The view bound is based on the maximal ripple ring diameter. This is the diff of maximal
149         // ripple ring radius and the final thumbnail radius.
150         final float radiusDiffBetweenViewAndThumbnail =
151                 (roundedThumbnailViewSize - roundedThumbnailFinalSize) / 2.0f;
152         final float distanceFromModeOptions = roundedThumbnailViewPadding +
153                 roundedThumbnailFinalSize + radiusDiffBetweenViewAndThumbnail;
154 
155         final int orientation = getResources().getConfiguration().orientation;
156 
157         float x = 0;
158         float y = 0;
159         if (orientation == Configuration.ORIENTATION_PORTRAIT) {
160             // The view finder of 16:9 aspect ratio might have a black padding.
161             x = uncoveredPreviewRect.right - distanceFromModeOptions;
162 
163             y = uncoveredPreviewRect.bottom;
164             if (isModeOptionsHidden) {
165                 y -= threeDotsButtonPadding + threeDotsButtonDiameter;
166             } else {
167                 y -= modeOptionsHeight;
168             }
169             y -= distanceFromModeOptions;
170         }
171         if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
172             if (isModeOptionsHidden) {
173                 x = uncoveredPreviewRect.right - threeDotsButtonPadding - modeOptionsToggleWidth;
174             } else {
175                 x = uncoveredPreviewRect.right - modeOptionsHeight;
176             }
177             x -= distanceFromModeOptions;
178             y = uncoveredPreviewRect.top + roundedThumbnailViewPadding -
179                     radiusDiffBetweenViewAndThumbnail;
180         }
181         return new PointF(x, y);
182     }
183 
animateCaptureIndicatorToX(float x)184     private void animateCaptureIndicatorToX(float x) {
185         final Interpolator interpolator =
186                 InterpolatorHelper.getLinearOutSlowInInterpolator(getContext());
187         mRoundedThumbnailView.animate()
188                 .setDuration(ModeOptions.PADDING_ANIMATION_TIME)
189                 .setInterpolator(interpolator)
190                 .x(x)
191                 .withEndAction(new Runnable() {
192                     @Override
193                     public void run() {
194                         mRoundedThumbnailView.setTranslationX(0.0f);
195                         requestLayout();
196                     }
197                 });
198     }
199 
animateCaptureIndicatorToY(float y)200     private void animateCaptureIndicatorToY(float y) {
201         final Interpolator interpolator =
202                 InterpolatorHelper.getLinearOutSlowInInterpolator(getContext());
203         mRoundedThumbnailView.animate()
204                 .setDuration(ModeOptions.PADDING_ANIMATION_TIME)
205                 .setInterpolator(interpolator)
206                 .y(y)
207                 .withEndAction(new Runnable() {
208                     @Override
209                     public void run() {
210                         mRoundedThumbnailView.setTranslationY(0.0f);
211                         requestLayout();
212                     }
213                 });
214     }
215 }
216