Home | History | Annotate | Download | only in ui
      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 
     80     public StickyBottomCaptureLayout(Context context, AttributeSet attrs) {
     81         super(context, attrs);
     82     }
     83 
     84     @Override
     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      */
     95     public void setCaptureLayoutHelper(CaptureLayoutHelper helper) {
     96         mCaptureLayoutHelper = helper;
     97     }
     98 
     99     @Override
    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      */
    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 
    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 
    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