Doris Liu | 048a61c | 2013-04-02 17:48:56 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2013 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.app.Activity; |
| 20 | import android.content.Context; |
| 21 | import android.content.res.Configuration; |
Michael Kolb | b22b852 | 2013-04-22 10:22:21 -0700 | [diff] [blame] | 22 | import android.graphics.Rect; |
Doris Liu | 048a61c | 2013-04-02 17:48:56 -0700 | [diff] [blame] | 23 | import android.util.AttributeSet; |
| 24 | import android.view.Gravity; |
| 25 | import android.view.View; |
Michael Kolb | b22b852 | 2013-04-22 10:22:21 -0700 | [diff] [blame] | 26 | import android.widget.FrameLayout; |
Doris Liu | 048a61c | 2013-04-02 17:48:56 -0700 | [diff] [blame] | 27 | |
| 28 | import com.android.camera.Util; |
| 29 | import com.android.gallery3d.R; |
| 30 | |
Michael Kolb | b22b852 | 2013-04-22 10:22:21 -0700 | [diff] [blame] | 31 | public class CameraControls extends RotatableLayout { |
| 32 | |
| 33 | private static final String TAG = "CAM_Controls"; |
| 34 | |
Doris Liu | 048a61c | 2013-04-02 17:48:56 -0700 | [diff] [blame] | 35 | private View mBackgroundView; |
Michael Kolb | b22b852 | 2013-04-22 10:22:21 -0700 | [diff] [blame] | 36 | private View mShutter; |
| 37 | private View mSwitcher; |
| 38 | private View mMenu; |
| 39 | private View mIndicators; |
Michael Kolb | c43e5b6 | 2013-04-30 15:34:19 -0700 | [diff] [blame] | 40 | private View mPreview; |
Michael Kolb | b22b852 | 2013-04-22 10:22:21 -0700 | [diff] [blame] | 41 | |
Doris Liu | 048a61c | 2013-04-02 17:48:56 -0700 | [diff] [blame] | 42 | public CameraControls(Context context, AttributeSet attrs) { |
| 43 | super(context, attrs); |
| 44 | } |
| 45 | |
| 46 | public CameraControls(Context context) { |
| 47 | super(context); |
| 48 | } |
| 49 | |
| 50 | @Override |
| 51 | public void onConfigurationChanged(Configuration config) { |
| 52 | super.onConfigurationChanged(config); |
| 53 | adjustBackground(); |
| 54 | } |
| 55 | |
| 56 | @Override |
| 57 | public void onFinishInflate() { |
| 58 | super.onFinishInflate(); |
| 59 | mBackgroundView = findViewById(R.id.blocker); |
Michael Kolb | b22b852 | 2013-04-22 10:22:21 -0700 | [diff] [blame] | 60 | mSwitcher = findViewById(R.id.camera_switcher); |
| 61 | mShutter = findViewById(R.id.shutter_button); |
| 62 | mMenu = findViewById(R.id.menu); |
| 63 | mIndicators = findViewById(R.id.on_screen_indicators); |
Michael Kolb | c43e5b6 | 2013-04-30 15:34:19 -0700 | [diff] [blame] | 64 | mPreview = findViewById(R.id.preview_thumb); |
Michael Kolb | b22b852 | 2013-04-22 10:22:21 -0700 | [diff] [blame] | 65 | } |
| 66 | |
| 67 | @Override |
| 68 | public void onLayout(boolean changed, int l, int t, int r, int b) { |
| 69 | int orientation = getResources().getConfiguration().orientation; |
| 70 | int rotation = Util.getDisplayRotation((Activity) getContext()); |
Michael Kolb | f2c86c1 | 2013-05-01 13:28:24 -0700 | [diff] [blame] | 71 | int size = getResources().getDimensionPixelSize(R.dimen.camera_controls_size); |
Michael Kolb | b22b852 | 2013-04-22 10:22:21 -0700 | [diff] [blame] | 72 | rotation = correctRotation(rotation, orientation); |
| 73 | super.onLayout(changed, l, t, r, b); |
| 74 | Rect shutter = new Rect(); |
Michael Kolb | f2c86c1 | 2013-05-01 13:28:24 -0700 | [diff] [blame] | 75 | topRight(mPreview, l, t, r, b, orientation, rotation); |
| 76 | if (size > 0) { |
| 77 | // restrict controls to size |
| 78 | switch (rotation) { |
| 79 | case 0: |
| 80 | case 180: |
| 81 | l = (l + r - size) / 2; |
| 82 | r = l + size; |
| 83 | break; |
| 84 | case 90: |
| 85 | case 270: |
| 86 | t = (t + b - size) / 2; |
| 87 | b = t + size; |
| 88 | break; |
| 89 | } |
| 90 | } |
Michael Kolb | b22b852 | 2013-04-22 10:22:21 -0700 | [diff] [blame] | 91 | center(mShutter, l, t, r, b, orientation, rotation, shutter); |
| 92 | center(mBackgroundView, l, t, r, b, orientation, rotation, new Rect()); |
| 93 | toLeft(mSwitcher, l, t, r, b, orientation, rotation, shutter); |
| 94 | toRight(mMenu, l, t, r, b, orientation, rotation, shutter); |
| 95 | toRight(mIndicators, l, t, r, b, orientation, rotation, shutter); |
Michael Kolb | 574e68d | 2013-04-30 13:46:17 -0700 | [diff] [blame] | 96 | View retake = findViewById(R.id.btn_retake); |
| 97 | if (retake != null) { |
| 98 | Rect retakeRect = new Rect(); |
| 99 | center(retake, l, t, r, b, orientation, rotation, retakeRect); |
| 100 | View cancel = findViewById(R.id.btn_cancel); |
| 101 | toLeft(cancel, l, t, r, b, orientation, rotation, shutter); |
| 102 | View done = findViewById(R.id.btn_done); |
| 103 | toRight(done, l, t, r, b, orientation, rotation, shutter); |
| 104 | } |
Michael Kolb | b22b852 | 2013-04-22 10:22:21 -0700 | [diff] [blame] | 105 | } |
| 106 | |
| 107 | private int correctRotation(int rotation, int orientation) { |
| 108 | // all the layout code assumes camera device orientation to be portrait |
| 109 | // adjust rotation for landscape |
| 110 | int camOrientation = (rotation % 180 == 0) ? Configuration.ORIENTATION_PORTRAIT |
| 111 | : Configuration.ORIENTATION_LANDSCAPE; |
| 112 | if (camOrientation != orientation) { |
| 113 | return (rotation + 90) % 360; |
| 114 | } |
| 115 | return rotation; |
| 116 | } |
Michael Kolb | c43e5b6 | 2013-04-30 15:34:19 -0700 | [diff] [blame] | 117 | |
Michael Kolb | b22b852 | 2013-04-22 10:22:21 -0700 | [diff] [blame] | 118 | private void center(View v, int l, int t, int r, int b, int orientation, int rotation, Rect result) { |
| 119 | FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams(); |
| 120 | int tw = lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin; |
| 121 | int th = lp.topMargin + v.getMeasuredHeight() + lp.bottomMargin; |
| 122 | switch (rotation) { |
| 123 | case 0: |
| 124 | // phone portrait; controls bottom |
Michael Kolb | f2c86c1 | 2013-05-01 13:28:24 -0700 | [diff] [blame] | 125 | result.left = (r + l) / 2 - tw / 2 + lp.leftMargin; |
| 126 | result.right = (r + l) / 2 + tw / 2 - lp.rightMargin; |
Michael Kolb | b22b852 | 2013-04-22 10:22:21 -0700 | [diff] [blame] | 127 | result.bottom = b - lp.bottomMargin; |
| 128 | result.top = b - th + lp.topMargin; |
| 129 | break; |
| 130 | case 90: |
| 131 | // phone landscape: controls right |
| 132 | result.right = r - lp.rightMargin; |
| 133 | result.left = r - tw + lp.leftMargin; |
Michael Kolb | f2c86c1 | 2013-05-01 13:28:24 -0700 | [diff] [blame] | 134 | result.top = (b + t) / 2 - th / 2 + lp.topMargin; |
| 135 | result.bottom = (b + t) / 2 + th / 2 - lp.bottomMargin; |
Michael Kolb | b22b852 | 2013-04-22 10:22:21 -0700 | [diff] [blame] | 136 | break; |
| 137 | case 180: |
| 138 | // phone upside down: controls top |
Michael Kolb | f2c86c1 | 2013-05-01 13:28:24 -0700 | [diff] [blame] | 139 | result.left = (r + l) / 2 - tw / 2 + lp.leftMargin; |
| 140 | result.right = (r + l) / 2 + tw / 2 - lp.rightMargin; |
Michael Kolb | b22b852 | 2013-04-22 10:22:21 -0700 | [diff] [blame] | 141 | result.top = t + lp.topMargin; |
| 142 | result.bottom = t + th - lp.bottomMargin; |
| 143 | break; |
| 144 | case 270: |
| 145 | // reverse landscape: controls left |
| 146 | result.left = l + lp.leftMargin; |
| 147 | result.right = l + tw - lp.rightMargin; |
Michael Kolb | f2c86c1 | 2013-05-01 13:28:24 -0700 | [diff] [blame] | 148 | result.top = (b + t) / 2 - th / 2 + lp.topMargin; |
| 149 | result.bottom = (b + t) / 2 + th / 2 - lp.bottomMargin; |
Michael Kolb | b22b852 | 2013-04-22 10:22:21 -0700 | [diff] [blame] | 150 | break; |
| 151 | } |
| 152 | v.layout(result.left, result.top, result.right, result.bottom); |
| 153 | } |
| 154 | |
| 155 | private void toLeft(View v, int l, int t, int r, int b, int orientation, int rotation, Rect anchor) { |
| 156 | FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams(); |
| 157 | int tw = lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin; |
| 158 | int th = lp.topMargin + v.getMeasuredHeight() + lp.bottomMargin; |
| 159 | Rect result = new Rect(); |
| 160 | switch (rotation) { |
| 161 | case 0: |
| 162 | // portrait, to left of anchor at bottom |
| 163 | result.right = anchor.left - lp.rightMargin; |
| 164 | result.left = anchor.left - tw + lp.leftMargin; |
| 165 | result.bottom = b - lp.bottomMargin; |
| 166 | result.top = b - th + lp.topMargin; |
| 167 | break; |
| 168 | case 90: |
| 169 | // phone landscape: below anchor on right |
| 170 | result.right = r - lp.rightMargin; |
| 171 | result.left = r - tw + lp.leftMargin; |
| 172 | result.top = anchor.bottom + lp.topMargin; |
| 173 | result.bottom = anchor.bottom + th - lp.bottomMargin; |
| 174 | break; |
| 175 | case 180: |
| 176 | // phone upside down: right of anchor at top |
| 177 | result.left = anchor.right + lp.leftMargin; |
| 178 | result.right = anchor.right + tw - lp.rightMargin; |
| 179 | result.top = t + lp.topMargin; |
| 180 | result.bottom = t + th - lp.bottomMargin; |
| 181 | break; |
| 182 | case 270: |
| 183 | // reverse landscape: above anchor on left |
| 184 | result.left = l + lp.leftMargin; |
| 185 | result.right = l + tw - lp.rightMargin; |
| 186 | result.bottom = anchor.top - lp.bottomMargin; |
| 187 | result.top = anchor.top - th + lp.topMargin; |
| 188 | break; |
| 189 | } |
| 190 | v.layout(result.left, result.top, result.right, result.bottom); |
| 191 | } |
| 192 | |
| 193 | private void toRight(View v, int l, int t, int r, int b, int orientation, int rotation, Rect anchor) { |
| 194 | FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams(); |
| 195 | int tw = lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin; |
| 196 | int th = lp.topMargin + v.getMeasuredHeight() + lp.bottomMargin; |
| 197 | Rect result = new Rect(); |
| 198 | switch (rotation) { |
| 199 | case 0: |
| 200 | // portrait, right of anchor at bottom |
| 201 | result.left = anchor.right + lp.leftMargin; |
| 202 | result.right = anchor.right + tw - lp.rightMargin; |
| 203 | result.bottom = b - lp.bottomMargin; |
| 204 | result.top = b - th + lp.topMargin; |
| 205 | break; |
| 206 | case 90: |
| 207 | // phone landscape: above anchor on right |
| 208 | result.right = r - lp.rightMargin; |
Michael Kolb | ef87866 | 2013-04-29 14:50:41 -0700 | [diff] [blame] | 209 | result.left = r - tw + lp.leftMargin; |
Michael Kolb | b22b852 | 2013-04-22 10:22:21 -0700 | [diff] [blame] | 210 | result.bottom = anchor.top - lp.bottomMargin; |
| 211 | result.top = anchor.top - th + lp.topMargin; |
| 212 | break; |
| 213 | case 180: |
| 214 | // phone upside down: left of anchor at top |
| 215 | result.right = anchor.left - lp.rightMargin; |
| 216 | result.left = anchor.left - tw + lp.leftMargin; |
| 217 | result.top = t + lp.topMargin; |
| 218 | result.bottom = t + th - lp.bottomMargin; |
| 219 | break; |
| 220 | case 270: |
| 221 | // reverse landscape: below anchor on left |
| 222 | result.left = l + lp.leftMargin; |
| 223 | result.right = l + tw - lp.rightMargin; |
| 224 | result.top = anchor.bottom + lp.topMargin; |
| 225 | result.bottom = anchor.bottom + th - lp.bottomMargin; |
| 226 | break; |
| 227 | } |
| 228 | v.layout(result.left, result.top, result.right, result.bottom); |
Doris Liu | 048a61c | 2013-04-02 17:48:56 -0700 | [diff] [blame] | 229 | } |
| 230 | |
Michael Kolb | c43e5b6 | 2013-04-30 15:34:19 -0700 | [diff] [blame] | 231 | private void topRight(View v, int l, int t, int r, int b, int orientation, int rotation) { |
| 232 | // layout using the specific margins; the rotation code messes up the others |
| 233 | int mt = getContext().getResources().getDimensionPixelSize(R.dimen.capture_margin_top); |
| 234 | int mr = getContext().getResources().getDimensionPixelSize(R.dimen.capture_margin_right); |
| 235 | v.layout(r - v.getMeasuredWidth() - mr, t + mt, r - mr, t + mt + v.getMeasuredHeight()); |
| 236 | } |
| 237 | |
Doris Liu | 048a61c | 2013-04-02 17:48:56 -0700 | [diff] [blame] | 238 | // In reverse landscape and reverse portrait, camera controls will be laid out |
| 239 | // on the wrong side of the screen. We need to make adjustment to move the controls |
| 240 | // to the USB side |
| 241 | public void adjustControlsToRightPosition() { |
| 242 | Configuration config = getResources().getConfiguration(); |
| 243 | int orientation = Util.getDisplayRotation((Activity) getContext()); |
| 244 | if (orientation == 270 && config.orientation == Configuration.ORIENTATION_LANDSCAPE) { |
| 245 | flipChildren(); |
| 246 | } |
| 247 | if (orientation == 180 && config.orientation == Configuration.ORIENTATION_PORTRAIT) { |
| 248 | flipChildren(); |
| 249 | } |
| 250 | adjustBackground(); |
| 251 | } |
| 252 | |
| 253 | private void adjustBackground() { |
| 254 | // remove current drawable and reset rotation |
| 255 | mBackgroundView.setBackgroundDrawable(null); |
| 256 | mBackgroundView.setRotationX(0); |
| 257 | mBackgroundView.setRotationY(0); |
| 258 | // if the switcher background is top aligned we need to flip the background |
| 259 | // drawable vertically; if left aligned, flip horizontally |
| 260 | int gravity = ((LayoutParams) mBackgroundView.getLayoutParams()).gravity; |
| 261 | if ((gravity & Gravity.TOP) == Gravity.TOP) { |
| 262 | mBackgroundView.setRotationX(180); |
| 263 | } else if ((gravity & Gravity.LEFT) == Gravity.LEFT) { |
| 264 | mBackgroundView.setRotationY(180); |
| 265 | } |
| 266 | mBackgroundView.setBackgroundResource(R.drawable.switcher_bg); |
| 267 | } |
Michael Kolb | b22b852 | 2013-04-22 10:22:21 -0700 | [diff] [blame] | 268 | |
Doris Liu | 048a61c | 2013-04-02 17:48:56 -0700 | [diff] [blame] | 269 | } |