Home | History | Annotate | Download | only in ui
      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.util.AttributeSet;
     23 import android.view.Gravity;
     24 import android.view.View;
     25 import android.view.ViewGroup;
     26 import android.widget.FrameLayout;
     27 
     28 import com.android.camera.util.CameraUtil;
     29 
     30 /* RotatableLayout rotates itself as well as all its children when orientation
     31  * changes. Specifically, when going from portrait to landscape, camera
     32  * controls move from the bottom of the screen to right side of the screen
     33  * (i.e. counter clockwise). Similarly, when the screen changes to portrait, we
     34  * need to move the controls from right side to the bottom of the screen, which
     35  * is a clockwise rotation.
     36  */
     37 
     38 public class RotatableLayout extends FrameLayout {
     39 
     40     private static final String TAG = "RotatableLayout";
     41     private static final int UNKOWN_ORIENTATION = -1;
     42     // Initial orientation of the layout (ORIENTATION_PORTRAIT, or ORIENTATION_LANDSCAPE)
     43     private int mInitialOrientation;
     44     private int mPrevRotation = UNKOWN_ORIENTATION;
     45     private boolean mIsDefaultToPortrait = false;
     46 
     47     public RotatableLayout(Context context, AttributeSet attrs, int defStyle) {
     48         super(context, attrs, defStyle);
     49         init();
     50     }
     51 
     52     public RotatableLayout(Context context, AttributeSet attrs) {
     53         super(context, attrs);
     54         init();
     55     }
     56 
     57     public RotatableLayout(Context context) {
     58         super(context);
     59         init();
     60     }
     61 
     62     private void init() {
     63         mInitialOrientation = getResources().getConfiguration().orientation;
     64     }
     65 
     66     @Override
     67     public void onAttachedToWindow() {
     68         // Before the first time this view is attached to window, device rotation
     69         // will not trigger onConfigurationChanged callback. So in the first run
     70         // we need to rotate the view if necessary. After that, onConfigurationChanged
     71         // call will track all the subsequent device rotation.
     72         if (mPrevRotation == UNKOWN_ORIENTATION) {
     73             mIsDefaultToPortrait = CameraUtil.isDefaultToPortrait((Activity) getContext());
     74             if (mIsDefaultToPortrait) {
     75                 // Natural orientation for tablet is landscape
     76                 mPrevRotation =  mInitialOrientation == Configuration.ORIENTATION_PORTRAIT ?
     77                         0 : 90;
     78             } else {
     79                 // When tablet orientation is 0 or 270 (i.e. getUnifiedOrientation
     80                 // = 0 or 90), we load the layout resource without any rotation.
     81                 mPrevRotation =  mInitialOrientation == Configuration.ORIENTATION_LANDSCAPE ?
     82                         0 : 270;
     83             }
     84 
     85             // check if there is any rotation before the view is attached to window
     86             rotateIfNeeded();
     87         }
     88     }
     89 
     90     private void rotateIfNeeded() {
     91         if (mPrevRotation == UNKOWN_ORIENTATION) {
     92             return;
     93         }
     94         int rotation = CameraUtil.getDisplayRotation((Activity) getContext());
     95         int diff = (rotation - mPrevRotation + 360) % 360;
     96         if ( diff == 0) {
     97             // No rotation
     98             return;
     99         } else if (diff == 180) {
    100             // 180-degree rotation
    101             mPrevRotation = rotation;
    102             flipChildren();
    103             return;
    104         }
    105         // 90 or 270-degree rotation
    106         boolean clockwise = isClockWiseRotation(mPrevRotation, rotation);
    107         mPrevRotation = rotation;
    108         rotateLayout(clockwise);
    109     }
    110 
    111     protected int getUnifiedRotation() {
    112         // all the layout code assumes camera device orientation to be portrait
    113         // adjust rotation for landscape
    114         int rotation = CameraUtil.getDisplayRotation((Activity) getContext());
    115         if (!mIsDefaultToPortrait) {
    116             return (rotation + 90) % 360;
    117         }
    118         return rotation;
    119     }
    120 
    121     public void checkLayoutFlip() {
    122         int currentRotation = CameraUtil.getDisplayRotation((Activity) getContext());
    123         if ((currentRotation - mPrevRotation + 360) % 360 == 180) {
    124             mPrevRotation = currentRotation;
    125             flipChildren();
    126             requestLayout();
    127         }
    128     }
    129 
    130     @Override
    131     public void onWindowVisibilityChanged(int visibility) {
    132         if (visibility == View.VISIBLE) {
    133             // Make sure when coming back from onPause, the layout is rotated correctly
    134             checkLayoutFlip();
    135         }
    136     }
    137 
    138     @Override
    139     public void onConfigurationChanged(Configuration config) {
    140         super.onConfigurationChanged(config);
    141         rotateIfNeeded();
    142     }
    143 
    144     protected void rotateLayout(boolean clockwise) {
    145         // Change the size of the layout
    146         ViewGroup.LayoutParams lp = getLayoutParams();
    147         int width = lp.width;
    148         int height = lp.height;
    149         lp.height = width;
    150         lp.width = height;
    151         setLayoutParams(lp);
    152 
    153         // rotate all the children
    154         rotateChildren(clockwise);
    155     }
    156 
    157     protected void rotateChildren(boolean clockwise) {
    158         int childCount = getChildCount();
    159         for (int i = 0; i < childCount; i++) {
    160             View child = getChildAt(i);
    161             rotate(child, clockwise);
    162         }
    163     }
    164 
    165     protected void flipChildren() {
    166         int childCount = getChildCount();
    167         for (int i = 0; i < childCount; i++) {
    168             View child = getChildAt(i);
    169             flip(child);
    170         }
    171     }
    172 
    173     public static boolean isClockWiseRotation(int prevRotation, int currentRotation) {
    174         if (prevRotation == (currentRotation + 90) % 360) {
    175             return true;
    176         }
    177         return false;
    178     }
    179 
    180     public static void rotate(View view, boolean isClockwise) {
    181         if (isClockwise) {
    182             rotateClockwise(view);
    183         } else {
    184             rotateCounterClockwise(view);
    185         }
    186     }
    187 
    188     private static boolean contains(int value, int mask) {
    189         return (value & mask) == mask;
    190     }
    191 
    192     public static void rotateClockwise(View view) {
    193         if (view == null) return;
    194         LayoutParams lp = (LayoutParams) view.getLayoutParams();
    195         int gravity = lp.gravity;
    196         int ngravity = 0;
    197         // rotate gravity
    198         if (contains(gravity, Gravity.LEFT)) {
    199             ngravity |= Gravity.TOP;
    200         }
    201         if (contains(gravity, Gravity.RIGHT)) {
    202             ngravity |= Gravity.BOTTOM;
    203         }
    204         if (contains(gravity, Gravity.TOP)) {
    205             ngravity |= Gravity.RIGHT;
    206         }
    207         if (contains(gravity, Gravity.BOTTOM)) {
    208             ngravity |= Gravity.LEFT;
    209         }
    210         if (contains(gravity, Gravity.CENTER)) {
    211             ngravity |= Gravity.CENTER;
    212         }
    213         if (contains(gravity, Gravity.CENTER_HORIZONTAL)) {
    214             ngravity |= Gravity.CENTER_VERTICAL;
    215         }
    216         if (contains(gravity, Gravity.CENTER_VERTICAL)) {
    217             ngravity |= Gravity.CENTER_HORIZONTAL;
    218         }
    219         lp.gravity = ngravity;
    220         int ml = lp.leftMargin;
    221         int mr = lp.rightMargin;
    222         int mt = lp.topMargin;
    223         int mb = lp.bottomMargin;
    224         lp.leftMargin = mb;
    225         lp.rightMargin = mt;
    226         lp.topMargin = ml;
    227         lp.bottomMargin = mr;
    228         int width = lp.width;
    229         int height = lp.height;
    230         lp.width = height;
    231         lp.height = width;
    232         view.setLayoutParams(lp);
    233     }
    234 
    235     public static void rotateCounterClockwise(View view) {
    236         if (view == null) return;
    237         LayoutParams lp = (LayoutParams) view.getLayoutParams();
    238         int gravity = lp.gravity;
    239         int ngravity = 0;
    240         // change gravity
    241         if (contains(gravity, Gravity.RIGHT)) {
    242             ngravity |= Gravity.TOP;
    243         }
    244         if (contains(gravity, Gravity.LEFT)) {
    245             ngravity |= Gravity.BOTTOM;
    246         }
    247         if (contains(gravity, Gravity.TOP)) {
    248             ngravity |= Gravity.LEFT;
    249         }
    250         if (contains(gravity, Gravity.BOTTOM)) {
    251             ngravity |= Gravity.RIGHT;
    252         }
    253         if (contains(gravity, Gravity.CENTER)) {
    254             ngravity |= Gravity.CENTER;
    255         }
    256         if (contains(gravity, Gravity.CENTER_HORIZONTAL)) {
    257             ngravity |= Gravity.CENTER_VERTICAL;
    258         }
    259         if (contains(gravity, Gravity.CENTER_VERTICAL)) {
    260             ngravity |= Gravity.CENTER_HORIZONTAL;
    261         }
    262         lp.gravity = ngravity;
    263         int ml = lp.leftMargin;
    264         int mr = lp.rightMargin;
    265         int mt = lp.topMargin;
    266         int mb = lp.bottomMargin;
    267         lp.leftMargin = mt;
    268         lp.rightMargin = mb;
    269         lp.topMargin = mr;
    270         lp.bottomMargin = ml;
    271         int width = lp.width;
    272         int height = lp.height;
    273         lp.width = height;
    274         lp.height = width;
    275         view.setLayoutParams(lp);
    276     }
    277 
    278     // Rotate a given view 180 degrees
    279     public static void flip(View view) {
    280         rotateClockwise(view);
    281         rotateClockwise(view);
    282     }
    283 }