| /* |
| * Copyright (C) 2013 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.app.Activity; |
| import android.content.Context; |
| import android.content.res.Configuration; |
| import android.graphics.Rect; |
| import android.hardware.display.DisplayManager; |
| import android.hardware.display.DisplayManager.DisplayListener; |
| import android.util.AttributeSet; |
| import android.view.View; |
| import android.widget.FrameLayout; |
| |
| import com.android.camera.Util; |
| import com.android.gallery3d.R; |
| import com.android.gallery3d.common.ApiHelper; |
| |
| public class CameraControls extends RotatableLayout { |
| |
| private static final String TAG = "CAM_Controls"; |
| |
| private View mBackgroundView; |
| private View mShutter; |
| private View mSwitcher; |
| private View mMenu; |
| private View mIndicators; |
| private View mPreview; |
| private Object mDisplayListener = null; |
| private int mLastRotation = 0; |
| |
| public CameraControls(Context context, AttributeSet attrs) { |
| super(context, attrs); |
| initDisplayListener(); |
| } |
| |
| public CameraControls(Context context) { |
| super(context); |
| initDisplayListener(); |
| } |
| |
| public void initDisplayListener() { |
| if (ApiHelper.HAS_DISPLAY_LISTENER) { |
| mDisplayListener = new DisplayListener() { |
| |
| @Override |
| public void onDisplayAdded(int arg0) {} |
| |
| @Override |
| public void onDisplayChanged(int arg0) { |
| checkLayoutFlip(); |
| } |
| |
| @Override |
| public void onDisplayRemoved(int arg0) {} |
| }; |
| } |
| } |
| |
| private void checkLayoutFlip() { |
| int currentRotation = Util.getDisplayRotation((Activity) getContext()); |
| if ((currentRotation - mLastRotation + 360) % 360 == 180) { |
| mLastRotation = currentRotation; |
| flipChildren(); |
| getParent().requestLayout(); |
| } |
| } |
| |
| @Override |
| public void onFinishInflate() { |
| super.onFinishInflate(); |
| mBackgroundView = findViewById(R.id.blocker); |
| mSwitcher = findViewById(R.id.camera_switcher); |
| mShutter = findViewById(R.id.shutter_button); |
| mMenu = findViewById(R.id.menu); |
| mIndicators = findViewById(R.id.on_screen_indicators); |
| mPreview = findViewById(R.id.preview_thumb); |
| } |
| |
| @Override |
| public void onAttachedToWindow() { |
| super.onAttachedToWindow(); |
| adjustControlsToRightPosition(); |
| mLastRotation = Util.getDisplayRotation((Activity) getContext()); |
| if (ApiHelper.HAS_DISPLAY_LISTENER) { |
| ((DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE)) |
| .registerDisplayListener((DisplayListener) mDisplayListener, null); |
| } |
| } |
| |
| @Override |
| public void onWindowVisibilityChanged(int visibility) { |
| if (visibility == View.VISIBLE) { |
| // Make sure when coming back from onPause, the layout is rotated correctly |
| checkLayoutFlip(); |
| } |
| } |
| |
| @Override |
| public void onDetachedFromWindow () { |
| super.onDetachedFromWindow(); |
| if (ApiHelper.HAS_DISPLAY_LISTENER) { |
| ((DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE)) |
| .unregisterDisplayListener((DisplayListener) mDisplayListener); |
| } |
| } |
| |
| @Override |
| public void onLayout(boolean changed, int l, int t, int r, int b) { |
| mLastRotation = Util.getDisplayRotation((Activity) getContext()); |
| int orientation = getResources().getConfiguration().orientation; |
| int size = getResources().getDimensionPixelSize(R.dimen.camera_controls_size); |
| int rotation = getUnifiedRotation(); |
| adjustBackground(); |
| // As l,t,r,b are positions relative to parents, we need to convert them |
| // to child's coordinates |
| r = r - l; |
| b = b - t; |
| l = 0; |
| t = 0; |
| for (int i = 0; i < getChildCount(); i++) { |
| View v = getChildAt(i); |
| v.layout(l, t, r, b); |
| } |
| Rect shutter = new Rect(); |
| topRight(mPreview, l, t, r, b); |
| if (size > 0) { |
| // restrict controls to size |
| switch (rotation) { |
| case 0: |
| case 180: |
| l = (l + r - size) / 2; |
| r = l + size; |
| break; |
| case 90: |
| case 270: |
| t = (t + b - size) / 2; |
| b = t + size; |
| break; |
| } |
| } |
| center(mShutter, l, t, r, b, orientation, rotation, shutter); |
| center(mBackgroundView, l, t, r, b, orientation, rotation, new Rect()); |
| toLeft(mSwitcher, shutter, rotation); |
| toRight(mMenu, shutter, rotation); |
| toRight(mIndicators, shutter, rotation); |
| View retake = findViewById(R.id.btn_retake); |
| if (retake != null) { |
| center(retake, shutter, rotation); |
| View cancel = findViewById(R.id.btn_cancel); |
| toLeft(cancel, shutter, rotation); |
| View done = findViewById(R.id.btn_done); |
| toRight(done, shutter, rotation); |
| } |
| } |
| |
| private int getUnifiedRotation() { |
| // all the layout code assumes camera device orientation to be portrait |
| // adjust rotation for landscape |
| int orientation = getResources().getConfiguration().orientation; |
| int rotation = Util.getDisplayRotation((Activity) getContext()); |
| int camOrientation = (rotation % 180 == 0) ? Configuration.ORIENTATION_PORTRAIT |
| : Configuration.ORIENTATION_LANDSCAPE; |
| if (camOrientation != orientation) { |
| return (rotation + 90) % 360; |
| } |
| return rotation; |
| } |
| |
| private void center(View v, int l, int t, int r, int b, int orientation, int rotation, Rect result) { |
| FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams(); |
| int tw = lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin; |
| int th = lp.topMargin + v.getMeasuredHeight() + lp.bottomMargin; |
| switch (rotation) { |
| case 0: |
| // phone portrait; controls bottom |
| result.left = (r + l) / 2 - tw / 2 + lp.leftMargin; |
| result.right = (r + l) / 2 + tw / 2 - lp.rightMargin; |
| result.bottom = b - lp.bottomMargin; |
| result.top = b - th + lp.topMargin; |
| break; |
| case 90: |
| // phone landscape: controls right |
| result.right = r - lp.rightMargin; |
| result.left = r - tw + lp.leftMargin; |
| result.top = (b + t) / 2 - th / 2 + lp.topMargin; |
| result.bottom = (b + t) / 2 + th / 2 - lp.bottomMargin; |
| break; |
| case 180: |
| // phone upside down: controls top |
| result.left = (r + l) / 2 - tw / 2 + lp.leftMargin; |
| result.right = (r + l) / 2 + tw / 2 - lp.rightMargin; |
| result.top = t + lp.topMargin; |
| result.bottom = t + th - lp.bottomMargin; |
| break; |
| case 270: |
| // reverse landscape: controls left |
| result.left = l + lp.leftMargin; |
| result.right = l + tw - lp.rightMargin; |
| result.top = (b + t) / 2 - th / 2 + lp.topMargin; |
| result.bottom = (b + t) / 2 + th / 2 - lp.bottomMargin; |
| break; |
| } |
| v.layout(result.left, result.top, result.right, result.bottom); |
| } |
| |
| private void center(View v, Rect other, int rotation) { |
| FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams(); |
| int tw = lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin; |
| int th = lp.topMargin + v.getMeasuredHeight() + lp.bottomMargin; |
| int cx = (other.left + other.right) / 2; |
| int cy = (other.top + other.bottom) / 2; |
| v.layout(cx - tw / 2 + lp.leftMargin, |
| cy - th / 2 + lp.topMargin, |
| cx + tw / 2 - lp.rightMargin, |
| cy + th / 2 - lp.bottomMargin); |
| } |
| |
| private void toLeft(View v, Rect other, int rotation) { |
| FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams(); |
| int tw = lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin; |
| int th = lp.topMargin + v.getMeasuredHeight() + lp.bottomMargin; |
| int cx = (other.left + other.right) / 2; |
| int cy = (other.top + other.bottom) / 2; |
| int l = 0, r = 0, t = 0, b = 0; |
| switch (rotation) { |
| case 0: |
| // portrait, to left of anchor at bottom |
| l = other.left - tw + lp.leftMargin; |
| r = other.left - lp.rightMargin; |
| t = cy - th / 2 + lp.topMargin; |
| b = cy + th / 2 - lp.bottomMargin; |
| break; |
| case 90: |
| // phone landscape: below anchor on right |
| l = cx - tw / 2 + lp.leftMargin; |
| r = cx + tw / 2 - lp.rightMargin; |
| t = other.bottom + lp.topMargin; |
| b = other.bottom + th - lp.bottomMargin; |
| break; |
| case 180: |
| // phone upside down: right of anchor at top |
| l = other.right + lp.leftMargin; |
| r = other.right + tw - lp.rightMargin; |
| t = cy - th / 2 + lp.topMargin; |
| b = cy + th / 2 - lp.bottomMargin; |
| break; |
| case 270: |
| // reverse landscape: above anchor on left |
| l = cx - tw / 2 + lp.leftMargin; |
| r = cx + tw / 2 - lp.rightMargin; |
| t = other.top - th + lp.topMargin; |
| b = other.top - lp.bottomMargin; |
| break; |
| } |
| v.layout(l, t, r, b); |
| } |
| |
| private void toRight(View v, Rect other, int rotation) { |
| FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams(); |
| int tw = lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin; |
| int th = lp.topMargin + v.getMeasuredHeight() + lp.bottomMargin; |
| int cx = (other.left + other.right) / 2; |
| int cy = (other.top + other.bottom) / 2; |
| int l = 0, r = 0, t = 0, b = 0; |
| switch (rotation) { |
| case 0: |
| l = other.right + lp.leftMargin; |
| r = other.right + tw - lp.rightMargin; |
| t = cy - th / 2 + lp.topMargin; |
| b = cy + th / 2 - lp.bottomMargin; |
| break; |
| case 90: |
| l = cx - tw / 2 + lp.leftMargin; |
| r = cx + tw / 2 - lp.rightMargin; |
| t = other.top - th + lp.topMargin; |
| b = other.top - lp.bottomMargin; |
| break; |
| case 180: |
| l = other.left - tw + lp.leftMargin; |
| r = other.left - lp.rightMargin; |
| t = cy - th / 2 + lp.topMargin; |
| b = cy + th / 2 - lp.bottomMargin; |
| break; |
| case 270: |
| l = cx - tw / 2 + lp.leftMargin; |
| r = cx + tw / 2 - lp.rightMargin; |
| t = other.bottom + lp.topMargin; |
| b = other.bottom + th - lp.bottomMargin; |
| break; |
| } |
| v.layout(l, t, r, b); |
| } |
| |
| private void topRight(View v, int l, int t, int r, int b) { |
| // layout using the specific margins; the rotation code messes up the others |
| int mt = getContext().getResources().getDimensionPixelSize(R.dimen.capture_margin_top); |
| int mr = getContext().getResources().getDimensionPixelSize(R.dimen.capture_margin_right); |
| v.layout(r - v.getMeasuredWidth() - mr, t + mt, r - mr, t + mt + v.getMeasuredHeight()); |
| } |
| |
| // In reverse landscape and reverse portrait, camera controls will be laid out |
| // on the wrong side of the screen. We need to make adjustment to move the controls |
| // to the USB side |
| public void adjustControlsToRightPosition() { |
| int orientation = getUnifiedRotation(); |
| if (orientation >= 180) { |
| flipChildren(); |
| } |
| } |
| |
| private void adjustBackground() { |
| int rotation = getUnifiedRotation(); |
| // remove current drawable and reset rotation |
| mBackgroundView.setBackgroundDrawable(null); |
| mBackgroundView.setRotationX(0); |
| mBackgroundView.setRotationY(0); |
| // if the switcher background is top aligned we need to flip the background |
| // drawable vertically; if left aligned, flip horizontally |
| switch (rotation) { |
| case 180: |
| mBackgroundView.setRotationX(180); |
| break; |
| case 270: |
| mBackgroundView.setRotationY(180); |
| break; |
| default: |
| break; |
| } |
| mBackgroundView.setBackgroundResource(R.drawable.switcher_bg); |
| } |
| |
| } |