blob: 85948c67e58294f3ba6537404712953bd750900c [file] [log] [blame]
Doris Liu6a0de792013-02-26 10:54:25 -08001/*
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
17package com.android.camera.ui;
18
Doris Liu48239f42013-03-04 22:19:10 -080019import android.app.Activity;
Doris Liu6a0de792013-02-26 10:54:25 -080020import android.content.Context;
21import android.content.res.Configuration;
22import android.util.AttributeSet;
23import android.view.Gravity;
24import android.view.View;
Doris Liu48239f42013-03-04 22:19:10 -080025import android.view.ViewGroup;
Doris Liu6a0de792013-02-26 10:54:25 -080026import android.widget.FrameLayout;
27
Angus Kong2bca2102014-03-11 16:27:30 -070028import com.android.camera.debug.Log;
Angus Kongb50b5cb2013-08-09 14:55:20 -070029import com.android.camera.util.CameraUtil;
Doris Liu48239f42013-03-04 22:19:10 -080030
Doris Liu6a0de792013-02-26 10:54:25 -080031/* RotatableLayout rotates itself as well as all its children when orientation
32 * changes. Specifically, when going from portrait to landscape, camera
33 * controls move from the bottom of the screen to right side of the screen
34 * (i.e. counter clockwise). Similarly, when the screen changes to portrait, we
35 * need to move the controls from right side to the bottom of the screen, which
36 * is a clockwise rotation.
37 */
38
39public class RotatableLayout extends FrameLayout {
40
Angus Kong2bca2102014-03-11 16:27:30 -070041 private static final Log.Tag TAG = new Log.Tag("RotatableLayout");
Doris Liu8cecf832013-09-17 15:07:31 -070042 private static final int UNKOWN_ORIENTATION = -1;
Doris Liubec9cf02013-04-20 11:30:35 -070043 // Initial orientation of the layout (ORIENTATION_PORTRAIT, or ORIENTATION_LANDSCAPE)
44 private int mInitialOrientation;
Doris Liu8cecf832013-09-17 15:07:31 -070045 private int mPrevRotation = UNKOWN_ORIENTATION;
Doris Liu3068c522013-10-13 14:46:57 -070046 private boolean mIsDefaultToPortrait = false;
Doris Liu8cecf832013-09-17 15:07:31 -070047
Doris Liu6a0de792013-02-26 10:54:25 -080048 public RotatableLayout(Context context, AttributeSet attrs, int defStyle) {
49 super(context, attrs, defStyle);
Doris Liu86ad8432013-10-11 19:01:30 -070050 init();
Doris Liu6a0de792013-02-26 10:54:25 -080051 }
52
53 public RotatableLayout(Context context, AttributeSet attrs) {
54 super(context, attrs);
Doris Liu86ad8432013-10-11 19:01:30 -070055 init();
Doris Liu6a0de792013-02-26 10:54:25 -080056 }
57
58 public RotatableLayout(Context context) {
59 super(context);
Doris Liu86ad8432013-10-11 19:01:30 -070060 init();
61 }
62
63 private void init() {
Doris Liubec9cf02013-04-20 11:30:35 -070064 mInitialOrientation = getResources().getConfiguration().orientation;
Doris Liu6a0de792013-02-26 10:54:25 -080065 }
66
67 @Override
Doris Liubec9cf02013-04-20 11:30:35 -070068 public void onAttachedToWindow() {
Doris Liu8cecf832013-09-17 15:07:31 -070069 // Before the first time this view is attached to window, device rotation
70 // will not trigger onConfigurationChanged callback. So in the first run
71 // we need to rotate the view if necessary. After that, onConfigurationChanged
72 // call will track all the subsequent device rotation.
73 if (mPrevRotation == UNKOWN_ORIENTATION) {
Doris Liu0ba8eaa2013-10-16 12:51:02 -070074 mIsDefaultToPortrait = CameraUtil.isDefaultToPortrait((Activity) getContext());
Doris Liu3068c522013-10-13 14:46:57 -070075 if (mIsDefaultToPortrait) {
Doris Liu86ad8432013-10-11 19:01:30 -070076 // Natural orientation for tablet is landscape
Doris Liu3068c522013-10-13 14:46:57 -070077 mPrevRotation = mInitialOrientation == Configuration.ORIENTATION_PORTRAIT ?
Doris Liu86ad8432013-10-11 19:01:30 -070078 0 : 90;
79 } else {
Doris Liu0ba8eaa2013-10-16 12:51:02 -070080 // When tablet orientation is 0 or 270 (i.e. getUnifiedOrientation
81 // = 0 or 90), we load the layout resource without any rotation.
Doris Liu3068c522013-10-13 14:46:57 -070082 mPrevRotation = mInitialOrientation == Configuration.ORIENTATION_LANDSCAPE ?
Doris Liu0ba8eaa2013-10-16 12:51:02 -070083 0 : 270;
Doris Liu86ad8432013-10-11 19:01:30 -070084 }
85
Doris Liu8cecf832013-09-17 15:07:31 -070086 // check if there is any rotation before the view is attached to window
Doris Liu86ad8432013-10-11 19:01:30 -070087 rotateIfNeeded();
Doris Liu44ab1472013-06-07 18:04:57 -070088 }
89 }
90
Doris Liu86ad8432013-10-11 19:01:30 -070091 private void rotateIfNeeded() {
Doris Liu8cecf832013-09-17 15:07:31 -070092 if (mPrevRotation == UNKOWN_ORIENTATION) {
93 return;
94 }
Angus Kongb50b5cb2013-08-09 14:55:20 -070095 int rotation = CameraUtil.getDisplayRotation((Activity) getContext());
Doris Liu44ab1472013-06-07 18:04:57 -070096 int diff = (rotation - mPrevRotation + 360) % 360;
97 if ( diff == 0) {
98 // No rotation
99 return;
100 } else if (diff == 180) {
101 // 180-degree rotation
Doris Liufa1c3a52013-05-03 14:33:57 -0700102 mPrevRotation = rotation;
Doris Liu44ab1472013-06-07 18:04:57 -0700103 flipChildren();
Doris Liubec9cf02013-04-20 11:30:35 -0700104 return;
105 }
Doris Liu44ab1472013-06-07 18:04:57 -0700106 // 90 or 270-degree rotation
Doris Liubbee3bf2013-04-02 17:48:56 -0700107 boolean clockwise = isClockWiseRotation(mPrevRotation, rotation);
Doris Liubec9cf02013-04-20 11:30:35 -0700108 mPrevRotation = rotation;
Doris Liu44ab1472013-06-07 18:04:57 -0700109 rotateLayout(clockwise);
Doris Liubec9cf02013-04-20 11:30:35 -0700110 }
111
Doris Liu86ad8432013-10-11 19:01:30 -0700112 protected int getUnifiedRotation() {
113 // all the layout code assumes camera device orientation to be portrait
114 // adjust rotation for landscape
115 int rotation = CameraUtil.getDisplayRotation((Activity) getContext());
Doris Liu3068c522013-10-13 14:46:57 -0700116 if (!mIsDefaultToPortrait) {
Doris Liu86ad8432013-10-11 19:01:30 -0700117 return (rotation + 90) % 360;
118 }
119 return rotation;
120 }
121
122 public void checkLayoutFlip() {
123 int currentRotation = CameraUtil.getDisplayRotation((Activity) getContext());
124 if ((currentRotation - mPrevRotation + 360) % 360 == 180) {
125 mPrevRotation = currentRotation;
126 flipChildren();
127 requestLayout();
128 }
129 }
130
131 @Override
132 public void onWindowVisibilityChanged(int visibility) {
133 if (visibility == View.VISIBLE) {
134 // Make sure when coming back from onPause, the layout is rotated correctly
135 checkLayoutFlip();
136 }
137 }
138
139 @Override
140 public void onConfigurationChanged(Configuration config) {
141 super.onConfigurationChanged(config);
142 rotateIfNeeded();
143 }
144
Doris Liubec9cf02013-04-20 11:30:35 -0700145 protected void rotateLayout(boolean clockwise) {
Doris Liu48239f42013-03-04 22:19:10 -0800146 // Change the size of the layout
147 ViewGroup.LayoutParams lp = getLayoutParams();
148 int width = lp.width;
149 int height = lp.height;
150 lp.height = width;
151 lp.width = height;
152 setLayoutParams(lp);
Doris Liubbee3bf2013-04-02 17:48:56 -0700153
Doris Liu48239f42013-03-04 22:19:10 -0800154 // rotate all the children
Doris Liubbee3bf2013-04-02 17:48:56 -0700155 rotateChildren(clockwise);
156 }
157
158 protected void rotateChildren(boolean clockwise) {
Doris Liu6a0de792013-02-26 10:54:25 -0800159 int childCount = getChildCount();
160 for (int i = 0; i < childCount; i++) {
161 View child = getChildAt(i);
162 rotate(child, clockwise);
163 }
Doris Liubbee3bf2013-04-02 17:48:56 -0700164 }
165
166 protected void flipChildren() {
Doris Liubbee3bf2013-04-02 17:48:56 -0700167 int childCount = getChildCount();
168 for (int i = 0; i < childCount; i++) {
169 View child = getChildAt(i);
170 flip(child);
171 }
Doris Liu6a0de792013-02-26 10:54:25 -0800172 }
173
Doris Liu48239f42013-03-04 22:19:10 -0800174 public static boolean isClockWiseRotation(int prevRotation, int currentRotation) {
175 if (prevRotation == (currentRotation + 90) % 360) {
176 return true;
177 }
178 return false;
179 }
180
Doris Liu6a0de792013-02-26 10:54:25 -0800181 public static void rotate(View view, boolean isClockwise) {
182 if (isClockwise) {
183 rotateClockwise(view);
184 } else {
185 rotateCounterClockwise(view);
186 }
187 }
188
189 private static boolean contains(int value, int mask) {
190 return (value & mask) == mask;
191 }
192
193 public static void rotateClockwise(View view) {
194 if (view == null) return;
195 LayoutParams lp = (LayoutParams) view.getLayoutParams();
196 int gravity = lp.gravity;
197 int ngravity = 0;
198 // rotate gravity
199 if (contains(gravity, Gravity.LEFT)) {
200 ngravity |= Gravity.TOP;
201 }
202 if (contains(gravity, Gravity.RIGHT)) {
203 ngravity |= Gravity.BOTTOM;
204 }
205 if (contains(gravity, Gravity.TOP)) {
206 ngravity |= Gravity.RIGHT;
207 }
208 if (contains(gravity, Gravity.BOTTOM)) {
209 ngravity |= Gravity.LEFT;
210 }
211 if (contains(gravity, Gravity.CENTER)) {
212 ngravity |= Gravity.CENTER;
213 }
214 if (contains(gravity, Gravity.CENTER_HORIZONTAL)) {
215 ngravity |= Gravity.CENTER_VERTICAL;
216 }
217 if (contains(gravity, Gravity.CENTER_VERTICAL)) {
218 ngravity |= Gravity.CENTER_HORIZONTAL;
219 }
220 lp.gravity = ngravity;
221 int ml = lp.leftMargin;
222 int mr = lp.rightMargin;
223 int mt = lp.topMargin;
224 int mb = lp.bottomMargin;
225 lp.leftMargin = mb;
226 lp.rightMargin = mt;
227 lp.topMargin = ml;
228 lp.bottomMargin = mr;
229 int width = lp.width;
230 int height = lp.height;
231 lp.width = height;
232 lp.height = width;
233 view.setLayoutParams(lp);
234 }
235
236 public static void rotateCounterClockwise(View view) {
237 if (view == null) return;
238 LayoutParams lp = (LayoutParams) view.getLayoutParams();
239 int gravity = lp.gravity;
240 int ngravity = 0;
241 // change gravity
242 if (contains(gravity, Gravity.RIGHT)) {
243 ngravity |= Gravity.TOP;
244 }
245 if (contains(gravity, Gravity.LEFT)) {
246 ngravity |= Gravity.BOTTOM;
247 }
248 if (contains(gravity, Gravity.TOP)) {
249 ngravity |= Gravity.LEFT;
250 }
251 if (contains(gravity, Gravity.BOTTOM)) {
252 ngravity |= Gravity.RIGHT;
253 }
254 if (contains(gravity, Gravity.CENTER)) {
255 ngravity |= Gravity.CENTER;
256 }
257 if (contains(gravity, Gravity.CENTER_HORIZONTAL)) {
258 ngravity |= Gravity.CENTER_VERTICAL;
259 }
260 if (contains(gravity, Gravity.CENTER_VERTICAL)) {
261 ngravity |= Gravity.CENTER_HORIZONTAL;
262 }
263 lp.gravity = ngravity;
264 int ml = lp.leftMargin;
265 int mr = lp.rightMargin;
266 int mt = lp.topMargin;
267 int mb = lp.bottomMargin;
268 lp.leftMargin = mt;
269 lp.rightMargin = mb;
270 lp.topMargin = mr;
271 lp.bottomMargin = ml;
272 int width = lp.width;
273 int height = lp.height;
274 lp.width = height;
275 lp.height = width;
276 view.setLayoutParams(lp);
277 }
Doris Liubbee3bf2013-04-02 17:48:56 -0700278
279 // Rotate a given view 180 degrees
280 public static void flip(View view) {
281 rotateClockwise(view);
282 rotateClockwise(view);
283 }
Angus Kong2bca2102014-03-11 16:27:30 -0700284}