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; 22 import android.graphics.Rect; 23 import android.hardware.display.DisplayManager; 24 import android.hardware.display.DisplayManager.DisplayListener; 25 import android.util.AttributeSet; 26 import android.view.View; 27 import android.widget.FrameLayout; 28 29 import com.android.camera.Util; 30 import com.android.gallery3d.R; 31 import com.android.gallery3d.common.ApiHelper; 32 33 public class CameraControls extends RotatableLayout { 34 35 private static final String TAG = "CAM_Controls"; 36 37 private View mBackgroundView; 38 private View mShutter; 39 private View mSwitcher; 40 private View mMenu; 41 private View mIndicators; 42 private View mPreview; 43 private Object mDisplayListener = null; 44 private int mLastRotation = 0; 45 46 public CameraControls(Context context, AttributeSet attrs) { 47 super(context, attrs); 48 initDisplayListener(); 49 } 50 51 public CameraControls(Context context) { 52 super(context); 53 initDisplayListener(); 54 } 55 56 public void initDisplayListener() { 57 if (ApiHelper.HAS_DISPLAY_LISTENER) { 58 mDisplayListener = new DisplayListener() { 59 60 @Override 61 public void onDisplayAdded(int arg0) {} 62 63 @Override 64 public void onDisplayChanged(int arg0) { 65 checkLayoutFlip(); 66 } 67 68 @Override 69 public void onDisplayRemoved(int arg0) {} 70 }; 71 } 72 } 73 74 private void checkLayoutFlip() { 75 int currentRotation = Util.getDisplayRotation((Activity) getContext()); 76 if ((currentRotation - mLastRotation + 360) % 360 == 180) { 77 mLastRotation = currentRotation; 78 flipChildren(); 79 getParent().requestLayout(); 80 } 81 } 82 83 @Override 84 public void onFinishInflate() { 85 super.onFinishInflate(); 86 mBackgroundView = findViewById(R.id.blocker); 87 mSwitcher = findViewById(R.id.camera_switcher); 88 mShutter = findViewById(R.id.shutter_button); 89 mMenu = findViewById(R.id.menu); 90 mIndicators = findViewById(R.id.on_screen_indicators); 91 mPreview = findViewById(R.id.preview_thumb); 92 } 93 94 @Override 95 public void onAttachedToWindow() { 96 super.onAttachedToWindow(); 97 adjustControlsToRightPosition(); 98 mLastRotation = Util.getDisplayRotation((Activity) getContext()); 99 if (ApiHelper.HAS_DISPLAY_LISTENER) { 100 ((DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE)) 101 .registerDisplayListener((DisplayListener) mDisplayListener, null); 102 } 103 } 104 105 @Override 106 public void onWindowVisibilityChanged(int visibility) { 107 if (visibility == View.VISIBLE) { 108 // Make sure when coming back from onPause, the layout is rotated correctly 109 checkLayoutFlip(); 110 } 111 } 112 113 @Override 114 public void onDetachedFromWindow () { 115 super.onDetachedFromWindow(); 116 if (ApiHelper.HAS_DISPLAY_LISTENER) { 117 ((DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE)) 118 .unregisterDisplayListener((DisplayListener) mDisplayListener); 119 } 120 } 121 122 @Override 123 public void onLayout(boolean changed, int l, int t, int r, int b) { 124 mLastRotation = Util.getDisplayRotation((Activity) getContext()); 125 int orientation = getResources().getConfiguration().orientation; 126 int size = getResources().getDimensionPixelSize(R.dimen.camera_controls_size); 127 int rotation = getUnifiedRotation(); 128 adjustBackground(); 129 // As l,t,r,b are positions relative to parents, we need to convert them 130 // to child's coordinates 131 r = r - l; 132 b = b - t; 133 l = 0; 134 t = 0; 135 for (int i = 0; i < getChildCount(); i++) { 136 View v = getChildAt(i); 137 v.layout(l, t, r, b); 138 } 139 Rect shutter = new Rect(); 140 topRight(mPreview, l, t, r, b); 141 if (size > 0) { 142 // restrict controls to size 143 switch (rotation) { 144 case 0: 145 case 180: 146 l = (l + r - size) / 2; 147 r = l + size; 148 break; 149 case 90: 150 case 270: 151 t = (t + b - size) / 2; 152 b = t + size; 153 break; 154 } 155 } 156 center(mShutter, l, t, r, b, orientation, rotation, shutter); 157 center(mBackgroundView, l, t, r, b, orientation, rotation, new Rect()); 158 toLeft(mSwitcher, shutter, rotation); 159 toRight(mMenu, shutter, rotation); 160 toRight(mIndicators, shutter, rotation); 161 View retake = findViewById(R.id.btn_retake); 162 if (retake != null) { 163 center(retake, shutter, rotation); 164 View cancel = findViewById(R.id.btn_cancel); 165 toLeft(cancel, shutter, rotation); 166 View done = findViewById(R.id.btn_done); 167 toRight(done, shutter, rotation); 168 } 169 } 170 171 private int getUnifiedRotation() { 172 // all the layout code assumes camera device orientation to be portrait 173 // adjust rotation for landscape 174 int orientation = getResources().getConfiguration().orientation; 175 int rotation = Util.getDisplayRotation((Activity) getContext()); 176 int camOrientation = (rotation % 180 == 0) ? Configuration.ORIENTATION_PORTRAIT 177 : Configuration.ORIENTATION_LANDSCAPE; 178 if (camOrientation != orientation) { 179 return (rotation + 90) % 360; 180 } 181 return rotation; 182 } 183 184 private void center(View v, int l, int t, int r, int b, int orientation, int rotation, Rect result) { 185 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams(); 186 int tw = lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin; 187 int th = lp.topMargin + v.getMeasuredHeight() + lp.bottomMargin; 188 switch (rotation) { 189 case 0: 190 // phone portrait; controls bottom 191 result.left = (r + l) / 2 - tw / 2 + lp.leftMargin; 192 result.right = (r + l) / 2 + tw / 2 - lp.rightMargin; 193 result.bottom = b - lp.bottomMargin; 194 result.top = b - th + lp.topMargin; 195 break; 196 case 90: 197 // phone landscape: controls right 198 result.right = r - lp.rightMargin; 199 result.left = r - tw + lp.leftMargin; 200 result.top = (b + t) / 2 - th / 2 + lp.topMargin; 201 result.bottom = (b + t) / 2 + th / 2 - lp.bottomMargin; 202 break; 203 case 180: 204 // phone upside down: controls top 205 result.left = (r + l) / 2 - tw / 2 + lp.leftMargin; 206 result.right = (r + l) / 2 + tw / 2 - lp.rightMargin; 207 result.top = t + lp.topMargin; 208 result.bottom = t + th - lp.bottomMargin; 209 break; 210 case 270: 211 // reverse landscape: controls left 212 result.left = l + lp.leftMargin; 213 result.right = l + tw - lp.rightMargin; 214 result.top = (b + t) / 2 - th / 2 + lp.topMargin; 215 result.bottom = (b + t) / 2 + th / 2 - lp.bottomMargin; 216 break; 217 } 218 v.layout(result.left, result.top, result.right, result.bottom); 219 } 220 221 private void center(View v, Rect other, int rotation) { 222 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams(); 223 int tw = lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin; 224 int th = lp.topMargin + v.getMeasuredHeight() + lp.bottomMargin; 225 int cx = (other.left + other.right) / 2; 226 int cy = (other.top + other.bottom) / 2; 227 v.layout(cx - tw / 2 + lp.leftMargin, 228 cy - th / 2 + lp.topMargin, 229 cx + tw / 2 - lp.rightMargin, 230 cy + th / 2 - lp.bottomMargin); 231 } 232 233 private void toLeft(View v, Rect other, int rotation) { 234 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams(); 235 int tw = lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin; 236 int th = lp.topMargin + v.getMeasuredHeight() + lp.bottomMargin; 237 int cx = (other.left + other.right) / 2; 238 int cy = (other.top + other.bottom) / 2; 239 int l = 0, r = 0, t = 0, b = 0; 240 switch (rotation) { 241 case 0: 242 // portrait, to left of anchor at bottom 243 l = other.left - tw + lp.leftMargin; 244 r = other.left - lp.rightMargin; 245 t = cy - th / 2 + lp.topMargin; 246 b = cy + th / 2 - lp.bottomMargin; 247 break; 248 case 90: 249 // phone landscape: below anchor on right 250 l = cx - tw / 2 + lp.leftMargin; 251 r = cx + tw / 2 - lp.rightMargin; 252 t = other.bottom + lp.topMargin; 253 b = other.bottom + th - lp.bottomMargin; 254 break; 255 case 180: 256 // phone upside down: right of anchor at top 257 l = other.right + lp.leftMargin; 258 r = other.right + tw - lp.rightMargin; 259 t = cy - th / 2 + lp.topMargin; 260 b = cy + th / 2 - lp.bottomMargin; 261 break; 262 case 270: 263 // reverse landscape: above anchor on left 264 l = cx - tw / 2 + lp.leftMargin; 265 r = cx + tw / 2 - lp.rightMargin; 266 t = other.top - th + lp.topMargin; 267 b = other.top - lp.bottomMargin; 268 break; 269 } 270 v.layout(l, t, r, b); 271 } 272 273 private void toRight(View v, Rect other, int rotation) { 274 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams(); 275 int tw = lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin; 276 int th = lp.topMargin + v.getMeasuredHeight() + lp.bottomMargin; 277 int cx = (other.left + other.right) / 2; 278 int cy = (other.top + other.bottom) / 2; 279 int l = 0, r = 0, t = 0, b = 0; 280 switch (rotation) { 281 case 0: 282 l = other.right + lp.leftMargin; 283 r = other.right + tw - lp.rightMargin; 284 t = cy - th / 2 + lp.topMargin; 285 b = cy + th / 2 - lp.bottomMargin; 286 break; 287 case 90: 288 l = cx - tw / 2 + lp.leftMargin; 289 r = cx + tw / 2 - lp.rightMargin; 290 t = other.top - th + lp.topMargin; 291 b = other.top - lp.bottomMargin; 292 break; 293 case 180: 294 l = other.left - tw + lp.leftMargin; 295 r = other.left - lp.rightMargin; 296 t = cy - th / 2 + lp.topMargin; 297 b = cy + th / 2 - lp.bottomMargin; 298 break; 299 case 270: 300 l = cx - tw / 2 + lp.leftMargin; 301 r = cx + tw / 2 - lp.rightMargin; 302 t = other.bottom + lp.topMargin; 303 b = other.bottom + th - lp.bottomMargin; 304 break; 305 } 306 v.layout(l, t, r, b); 307 } 308 309 private void topRight(View v, int l, int t, int r, int b) { 310 // layout using the specific margins; the rotation code messes up the others 311 int mt = getContext().getResources().getDimensionPixelSize(R.dimen.capture_margin_top); 312 int mr = getContext().getResources().getDimensionPixelSize(R.dimen.capture_margin_right); 313 v.layout(r - v.getMeasuredWidth() - mr, t + mt, r - mr, t + mt + v.getMeasuredHeight()); 314 } 315 316 // In reverse landscape and reverse portrait, camera controls will be laid out 317 // on the wrong side of the screen. We need to make adjustment to move the controls 318 // to the USB side 319 public void adjustControlsToRightPosition() { 320 int orientation = getUnifiedRotation(); 321 if (orientation >= 180) { 322 flipChildren(); 323 } 324 } 325 326 private void adjustBackground() { 327 int rotation = getUnifiedRotation(); 328 // remove current drawable and reset rotation 329 mBackgroundView.setBackgroundDrawable(null); 330 mBackgroundView.setRotationX(0); 331 mBackgroundView.setRotationY(0); 332 // if the switcher background is top aligned we need to flip the background 333 // drawable vertically; if left aligned, flip horizontally 334 switch (rotation) { 335 case 180: 336 mBackgroundView.setRotationX(180); 337 break; 338 case 270: 339 mBackgroundView.setRotationY(180); 340 break; 341 default: 342 break; 343 } 344 mBackgroundView.setBackgroundResource(R.drawable.switcher_bg); 345 } 346 347 } 348