blob: 14c7e73689b3b0d3bbf198e178a75020d0f39a67 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.camera.ui;
import android.content.Context;
import android.content.res.Configuration;
import android.view.animation.Interpolator;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import com.android.camera.CaptureLayoutHelper;
import com.android.camera.debug.Log;
import com.android.camera.ui.motion.InterpolatorHelper;
import com.android.camera.widget.ModeOptions;
import com.android.camera.widget.ModeOptionsOverlay;
import com.android.camera.widget.RoundedThumbnailView;
import com.android.camera2.R;
/**
* The goal of this class is to ensure mode options and capture indicator is
* always laid out to the left of or above bottom bar in landscape or portrait
* respectively. All the other children in this view group can be expected to
* be laid out the same way as they are in a normal FrameLayout.
*/
public class StickyBottomCaptureLayout extends FrameLayout {
private final static Log.Tag TAG = new Log.Tag("StickyBotCapLayout");
private RoundedThumbnailView mRoundedThumbnailView;
private ModeOptionsOverlay mModeOptionsOverlay;
private View mBottomBar;
private CaptureLayoutHelper mCaptureLayoutHelper = null;
private ModeOptions.Listener mModeOptionsListener = new ModeOptions.Listener() {
@Override
public void onBeginToShowModeOptions() {
final PointF thumbnailViewPosition = getRoundedThumbnailPosition(
mCaptureLayoutHelper.getUncoveredPreviewRect(),
false,
mModeOptionsOverlay.getModeOptionsToggleWidth());
final int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
animateCaptureIndicatorToY(thumbnailViewPosition.y);
} else {
animateCaptureIndicatorToX(thumbnailViewPosition.x);
}
}
@Override
public void onBeginToHideModeOptions() {
final PointF thumbnailViewPosition = getRoundedThumbnailPosition(
mCaptureLayoutHelper.getUncoveredPreviewRect(),
true,
mModeOptionsOverlay.getModeOptionsToggleWidth());
final int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
animateCaptureIndicatorToY(thumbnailViewPosition.y);
} else {
animateCaptureIndicatorToX(thumbnailViewPosition.x);
}
}
};
public StickyBottomCaptureLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void onFinishInflate() {
mRoundedThumbnailView = (RoundedThumbnailView) findViewById(R.id.rounded_thumbnail_view);
mModeOptionsOverlay = (ModeOptionsOverlay) findViewById(R.id.mode_options_overlay);
mModeOptionsOverlay.setModeOptionsListener(mModeOptionsListener);
mBottomBar = findViewById(R.id.bottom_bar);
}
/**
* Sets a capture layout helper to query layout rect from.
*/
public void setCaptureLayoutHelper(CaptureLayoutHelper helper) {
mCaptureLayoutHelper = helper;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (mCaptureLayoutHelper == null) {
Log.e(TAG, "Capture layout helper needs to be set first.");
return;
}
// Layout mode options overlay.
RectF uncoveredPreviewRect = mCaptureLayoutHelper.getUncoveredPreviewRect();
mModeOptionsOverlay.layout((int) uncoveredPreviewRect.left, (int) uncoveredPreviewRect.top,
(int) uncoveredPreviewRect.right, (int) uncoveredPreviewRect.bottom);
// Layout capture indicator.
PointF roundedThumbnailViewPosition = getRoundedThumbnailPosition(
uncoveredPreviewRect,
mModeOptionsOverlay.isModeOptionsHidden(),
mModeOptionsOverlay.getModeOptionsToggleWidth());
mRoundedThumbnailView.layout(
(int) roundedThumbnailViewPosition.x,
(int) roundedThumbnailViewPosition.y,
(int) roundedThumbnailViewPosition.x + mRoundedThumbnailView.getMeasuredWidth(),
(int) roundedThumbnailViewPosition.y + mRoundedThumbnailView.getMeasuredHeight());
// Layout bottom bar.
RectF bottomBarRect = mCaptureLayoutHelper.getBottomBarRect();
mBottomBar.layout((int) bottomBarRect.left, (int) bottomBarRect.top,
(int) bottomBarRect.right, (int) bottomBarRect.bottom);
}
/**
* Calculates the desired layout of capture indicator.
*
* @param uncoveredPreviewRect The uncovered preview bound which contains mode option
* overlay and capture indicator.
* @param isModeOptionsHidden Whether the mode options button are hidden.
* @param modeOptionsToggleWidth The width of mode options toggle (three dots button).
* @return the desired view bound for capture indicator.
*/
private PointF getRoundedThumbnailPosition(
RectF uncoveredPreviewRect, boolean isModeOptionsHidden, float modeOptionsToggleWidth) {
final float threeDotsButtonDiameter =
getResources().getDimension(R.dimen.option_button_circle_size);
final float threeDotsButtonPadding =
getResources().getDimension(R.dimen.mode_options_toggle_padding);
final float modeOptionsHeight = getResources().getDimension(R.dimen.mode_options_height);
final float roundedThumbnailViewSize = mRoundedThumbnailView.getMeasuredWidth();
final float roundedThumbnailFinalSize = mRoundedThumbnailView.getThumbnailFinalDiameter();
final float roundedThumbnailViewPadding = mRoundedThumbnailView.getThumbnailPadding();
// The view bound is based on the maximal ripple ring diameter. This is the diff of maximal
// ripple ring radius and the final thumbnail radius.
final float radiusDiffBetweenViewAndThumbnail =
(roundedThumbnailViewSize - roundedThumbnailFinalSize) / 2.0f;
final float distanceFromModeOptions = roundedThumbnailViewPadding +
roundedThumbnailFinalSize + radiusDiffBetweenViewAndThumbnail;
final int orientation = getResources().getConfiguration().orientation;
float x = 0;
float y = 0;
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
// The view finder of 16:9 aspect ratio might have a black padding.
x = uncoveredPreviewRect.right - distanceFromModeOptions;
y = uncoveredPreviewRect.bottom;
if (isModeOptionsHidden) {
y -= threeDotsButtonPadding + threeDotsButtonDiameter;
} else {
y -= modeOptionsHeight;
}
y -= distanceFromModeOptions;
}
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
if (isModeOptionsHidden) {
x = uncoveredPreviewRect.right - threeDotsButtonPadding - modeOptionsToggleWidth;
} else {
x = uncoveredPreviewRect.right - modeOptionsHeight;
}
x -= distanceFromModeOptions;
y = uncoveredPreviewRect.top + roundedThumbnailViewPadding -
radiusDiffBetweenViewAndThumbnail;
}
return new PointF(x, y);
}
private void animateCaptureIndicatorToX(float x) {
final Interpolator interpolator =
InterpolatorHelper.getLinearOutSlowInInterpolator(getContext());
mRoundedThumbnailView.animate()
.setDuration(ModeOptions.PADDING_ANIMATION_TIME)
.setInterpolator(interpolator)
.x(x)
.withEndAction(new Runnable() {
@Override
public void run() {
mRoundedThumbnailView.setTranslationX(0.0f);
requestLayout();
}
});
}
private void animateCaptureIndicatorToY(float y) {
final Interpolator interpolator =
InterpolatorHelper.getLinearOutSlowInInterpolator(getContext());
mRoundedThumbnailView.animate()
.setDuration(ModeOptions.PADDING_ANIMATION_TIME)
.setInterpolator(interpolator)
.y(y)
.withEndAction(new Runnable() {
@Override
public void run() {
mRoundedThumbnailView.setTranslationY(0.0f);
requestLayout();
}
});
}
}