CameraControls.java revision fa1c3a528de2ec357b8f43aa133feae444899668
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
17package com.android.camera.ui;
18
19import android.app.Activity;
20import android.content.Context;
21import android.content.res.Configuration;
22import android.graphics.Rect;
23import android.hardware.display.DisplayManager;
24import android.hardware.display.DisplayManager.DisplayListener;
25import android.util.AttributeSet;
26import android.view.View;
27import android.widget.FrameLayout;
28
29import com.android.camera.Util;
30import com.android.gallery3d.R;
31import com.android.gallery3d.common.ApiHelper;
32
33public 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        super.onLayout(changed, l, t, r, b);
130        // As l,t,r,b are positions relative to parents, we need to convert them
131        // to child's coordinates
132        r = r - l;
133        b = b - t;
134        l = 0;
135        t = 0;
136        Rect shutter = new Rect();
137        topRight(mPreview, l, t, r, b, orientation, rotation);
138        if (size > 0) {
139            // restrict controls to size
140            switch (rotation) {
141            case 0:
142            case 180:
143                l = (l + r - size) / 2;
144                r = l + size;
145                break;
146            case 90:
147            case 270:
148                t = (t + b - size) / 2;
149                b = t + size;
150                break;
151            }
152        }
153        center(mShutter, l, t, r, b, orientation, rotation, shutter);
154        center(mBackgroundView, l, t, r, b, orientation, rotation, new Rect());
155        toLeft(mSwitcher, l, t, r, b, orientation, rotation, shutter);
156        toRight(mMenu, l, t, r, b, orientation, rotation, shutter);
157        toRight(mIndicators, l, t, r, b, orientation, rotation, shutter);
158        View retake = findViewById(R.id.btn_retake);
159        if (retake != null) {
160            Rect retakeRect = new Rect();
161            center(retake, l, t, r, b, orientation, rotation, retakeRect);
162            View cancel = findViewById(R.id.btn_cancel);
163            toLeft(cancel, l, t, r, b, orientation, rotation, shutter);
164            View done = findViewById(R.id.btn_done);
165            toRight(done, l, t, r, b, orientation, rotation, shutter);
166        }
167    }
168
169    private int getUnifiedRotation() {
170        // all the layout code assumes camera device orientation to be portrait
171        // adjust rotation for landscape
172        int orientation = getResources().getConfiguration().orientation;
173        int rotation = Util.getDisplayRotation((Activity) getContext());
174        int camOrientation = (rotation % 180 == 0) ? Configuration.ORIENTATION_PORTRAIT
175                : Configuration.ORIENTATION_LANDSCAPE;
176        if (camOrientation != orientation) {
177            return (rotation + 90) % 360;
178        }
179        return rotation;
180    }
181
182    private void center(View v, int l, int t, int r, int b, int orientation, int rotation, Rect result) {
183        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams();
184        int tw = lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin;
185        int th = lp.topMargin + v.getMeasuredHeight() + lp.bottomMargin;
186        switch (rotation) {
187        case 0:
188            // phone portrait; controls bottom
189            result.left = (r + l) / 2 - tw / 2 + lp.leftMargin;
190            result.right = (r + l) / 2 + tw / 2 - lp.rightMargin;
191            result.bottom = b - lp.bottomMargin;
192            result.top = b - th + lp.topMargin;
193            break;
194        case 90:
195            // phone landscape: controls right
196            result.right = r - lp.rightMargin;
197            result.left = r - tw + lp.leftMargin;
198            result.top = (b + t) / 2 - th / 2 + lp.topMargin;
199            result.bottom = (b + t) / 2 + th / 2 - lp.bottomMargin;
200            break;
201        case 180:
202            // phone upside down: controls top
203            result.left = (r + l) / 2 - tw / 2 + lp.leftMargin;
204            result.right = (r + l) / 2 + tw / 2 - lp.rightMargin;
205            result.top = t + lp.topMargin;
206            result.bottom = t + th - lp.bottomMargin;
207            break;
208        case 270:
209            // reverse landscape: controls left
210            result.left = l + lp.leftMargin;
211            result.right = l + tw - lp.rightMargin;
212            result.top = (b + t) / 2 - th / 2 + lp.topMargin;
213            result.bottom = (b + t) / 2 + th / 2 - lp.bottomMargin;
214            break;
215        }
216        v.layout(result.left, result.top, result.right, result.bottom);
217    }
218
219    private void toLeft(View v, int l, int t, int r, int b, int orientation, int rotation, Rect anchor) {
220        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams();
221        int tw = lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin;
222        int th = lp.topMargin + v.getMeasuredHeight() + lp.bottomMargin;
223        Rect result = new Rect();
224        switch (rotation) {
225        case 0:
226            // portrait, to left of anchor at bottom
227            result.right = anchor.left - lp.rightMargin;
228            result.left = anchor.left - tw + lp.leftMargin;
229            result.bottom = b - lp.bottomMargin;
230            result.top = b - th + lp.topMargin;
231            break;
232        case 90:
233            // phone landscape: below anchor on right
234            result.right = r - lp.rightMargin;
235            result.left = r - tw + lp.leftMargin;
236            result.top = anchor.bottom + lp.topMargin;
237            result.bottom = anchor.bottom + th - lp.bottomMargin;
238            break;
239        case 180:
240            // phone upside down: right of anchor at top
241            result.left = anchor.right + lp.leftMargin;
242            result.right = anchor.right + tw - lp.rightMargin;
243            result.top = t + lp.topMargin;
244            result.bottom = t + th - lp.bottomMargin;
245            break;
246        case 270:
247            // reverse landscape: above anchor on left
248            result.left = l + lp.leftMargin;
249            result.right = l + tw - lp.rightMargin;
250            result.bottom = anchor.top - lp.bottomMargin;
251            result.top = anchor.top - th + lp.topMargin;
252            break;
253        }
254        v.layout(result.left, result.top, result.right, result.bottom);
255    }
256
257    private void toRight(View v, int l, int t, int r, int b, int orientation, int rotation, Rect anchor) {
258        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams();
259        int tw = lp.leftMargin + v.getMeasuredWidth() + lp.rightMargin;
260        int th = lp.topMargin + v.getMeasuredHeight() + lp.bottomMargin;
261        Rect result = new Rect();
262        switch (rotation) {
263        case 0:
264            // portrait, right of anchor at bottom
265            result.left = anchor.right + lp.leftMargin;
266            result.right = anchor.right + tw - lp.rightMargin;
267            result.bottom = b - lp.bottomMargin;
268            result.top = b - th + lp.topMargin;
269            break;
270        case 90:
271            // phone landscape: above anchor on right
272            result.right = r - lp.rightMargin;
273            result.left = r - tw + lp.leftMargin;
274            result.bottom = anchor.top - lp.bottomMargin;
275            result.top = anchor.top - th + lp.topMargin;
276            break;
277        case 180:
278            // phone upside down: left of anchor at top
279            result.right = anchor.left - lp.rightMargin;
280            result.left = anchor.left - tw + lp.leftMargin;
281            result.top = t + lp.topMargin;
282            result.bottom = t + th - lp.bottomMargin;
283            break;
284        case 270:
285            // reverse landscape: below anchor on left
286            result.left = l + lp.leftMargin;
287            result.right = l + tw - lp.rightMargin;
288            result.top = anchor.bottom + lp.topMargin;
289            result.bottom = anchor.bottom + th - lp.bottomMargin;
290            break;
291        }
292        v.layout(result.left, result.top, result.right, result.bottom);
293    }
294
295    private void topRight(View v, int l, int t, int r, int b, int orientation, int rotation) {
296        // layout using the specific margins; the rotation code messes up the others
297        int mt = getContext().getResources().getDimensionPixelSize(R.dimen.capture_margin_top);
298        int mr = getContext().getResources().getDimensionPixelSize(R.dimen.capture_margin_right);
299        v.layout(r - v.getMeasuredWidth() - mr, t + mt, r - mr, t + mt + v.getMeasuredHeight());
300    }
301
302    // In reverse landscape and reverse portrait, camera controls will be laid out
303    // on the wrong side of the screen. We need to make adjustment to move the controls
304    // to the USB side
305    public void adjustControlsToRightPosition() {
306        Configuration config = getResources().getConfiguration();
307        int orientation = Util.getDisplayRotation((Activity) getContext());
308        if (orientation >= 180) {
309            flipChildren();
310        }
311    }
312
313    private void adjustBackground() {
314        int rotation = getUnifiedRotation();
315        // remove current drawable and reset rotation
316        mBackgroundView.setBackgroundDrawable(null);
317        mBackgroundView.setRotationX(0);
318        mBackgroundView.setRotationY(0);
319        // if the switcher background is top aligned we need to flip the background
320        // drawable vertically; if left aligned, flip horizontally
321        switch (rotation) {
322            case 180:
323                mBackgroundView.setRotationX(180);
324                break;
325            case 270:
326                mBackgroundView.setRotationY(180);
327                break;
328            default:
329                break;
330        }
331        mBackgroundView.setBackgroundResource(R.drawable.switcher_bg);
332    }
333
334}
335