1 /*
2  * Copyright (C) 2010 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.annotation.TargetApi;
20 import android.content.Context;
21 import android.graphics.Canvas;
22 import android.graphics.Matrix;
23 import android.graphics.Rect;
24 import android.util.AttributeSet;
25 import android.view.MotionEvent;
26 import android.view.View;
27 import android.view.ViewGroup;
28 import android.view.ViewParent;
29 
30 import com.android.gallery3d.common.ApiHelper;
31 import com.android.gallery3d.util.MotionEventHelper;
32 
33 // A RotateLayout is designed to display a single item and provides the
34 // capabilities to rotate the item.
35 public class RotateLayout extends ViewGroup implements Rotatable {
36     @SuppressWarnings("unused")
37     private static final String TAG = "RotateLayout";
38     private int mOrientation;
39     private Matrix mMatrix = new Matrix();
40     protected View mChild;
41 
RotateLayout(Context context, AttributeSet attrs)42     public RotateLayout(Context context, AttributeSet attrs) {
43         super(context, attrs);
44         // The transparent background here is a workaround of the render issue
45         // happened when the view is rotated as the device's orientation
46         // changed. The view looks fine in landscape. After rotation, the view
47         // is invisible.
48         setBackgroundResource(android.R.color.transparent);
49     }
50 
51     @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB)
52     @Override
onFinishInflate()53     protected void onFinishInflate() {
54         mChild = getChildAt(0);
55         if (ApiHelper.HAS_VIEW_TRANSFORM_PROPERTIES) {
56             mChild.setPivotX(0);
57             mChild.setPivotY(0);
58         }
59     }
60 
61     @Override
onLayout( boolean change, int left, int top, int right, int bottom)62     protected void onLayout(
63             boolean change, int left, int top, int right, int bottom) {
64         int width = right - left;
65         int height = bottom - top;
66         switch (mOrientation) {
67             case 0:
68             case 180:
69                 mChild.layout(0, 0, width, height);
70                 break;
71             case 90:
72             case 270:
73                 mChild.layout(0, 0, height, width);
74                 break;
75         }
76     }
77 
78     @Override
dispatchTouchEvent(MotionEvent event)79     public boolean dispatchTouchEvent(MotionEvent event) {
80         if (!ApiHelper.HAS_VIEW_TRANSFORM_PROPERTIES) {
81             final int w = getMeasuredWidth();
82             final int h = getMeasuredHeight();
83             switch (mOrientation) {
84                 case 0:
85                     mMatrix.setTranslate(0, 0);
86                     break;
87                 case 90:
88                     mMatrix.setTranslate(0, -h);
89                     break;
90                 case 180:
91                     mMatrix.setTranslate(-w, -h);
92                     break;
93                 case 270:
94                     mMatrix.setTranslate(-w, 0);
95                     break;
96             }
97             mMatrix.postRotate(mOrientation);
98             event = MotionEventHelper.transformEvent(event, mMatrix);
99         }
100         return super.dispatchTouchEvent(event);
101     }
102 
103     @Override
dispatchDraw(Canvas canvas)104     protected void dispatchDraw(Canvas canvas) {
105         if (ApiHelper.HAS_VIEW_TRANSFORM_PROPERTIES) {
106             super.dispatchDraw(canvas);
107         } else {
108             canvas.save();
109             int w = getMeasuredWidth();
110             int h = getMeasuredHeight();
111             switch (mOrientation) {
112                 case 0:
113                     canvas.translate(0, 0);
114                     break;
115                 case 90:
116                     canvas.translate(0, h);
117                     break;
118                 case 180:
119                     canvas.translate(w, h);
120                     break;
121                 case 270:
122                     canvas.translate(w, 0);
123                     break;
124             }
125             canvas.rotate(-mOrientation, 0, 0);
126             super.dispatchDraw(canvas);
127             canvas.restore();
128         }
129     }
130 
131     @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB)
132     @Override
onMeasure(int widthSpec, int heightSpec)133     protected void onMeasure(int widthSpec, int heightSpec) {
134         int w = 0, h = 0;
135         switch(mOrientation) {
136             case 0:
137             case 180:
138                 measureChild(mChild, widthSpec, heightSpec);
139                 w = mChild.getMeasuredWidth();
140                 h = mChild.getMeasuredHeight();
141                 break;
142             case 90:
143             case 270:
144                 measureChild(mChild, heightSpec, widthSpec);
145                 w = mChild.getMeasuredHeight();
146                 h = mChild.getMeasuredWidth();
147                 break;
148         }
149         setMeasuredDimension(w, h);
150 
151         if (ApiHelper.HAS_VIEW_TRANSFORM_PROPERTIES) {
152             switch (mOrientation) {
153                 case 0:
154                     mChild.setTranslationX(0);
155                     mChild.setTranslationY(0);
156                     break;
157                 case 90:
158                     mChild.setTranslationX(0);
159                     mChild.setTranslationY(h);
160                     break;
161                 case 180:
162                     mChild.setTranslationX(w);
163                     mChild.setTranslationY(h);
164                     break;
165                 case 270:
166                     mChild.setTranslationX(w);
167                     mChild.setTranslationY(0);
168                     break;
169             }
170             mChild.setRotation(-mOrientation);
171         }
172     }
173 
174     @Override
shouldDelayChildPressedState()175     public boolean shouldDelayChildPressedState() {
176         return false;
177     }
178 
179     // Rotate the view counter-clockwise
180     @Override
setOrientation(int orientation, boolean animation)181     public void setOrientation(int orientation, boolean animation) {
182         orientation = orientation % 360;
183         if (mOrientation == orientation) return;
184         mOrientation = orientation;
185         requestLayout();
186     }
187 
getOrientation()188     public int getOrientation() {
189         return mOrientation;
190     }
191 
192     @Override
invalidateChildInParent(int[] location, Rect r)193     public ViewParent invalidateChildInParent(int[] location, Rect r) {
194         if (!ApiHelper.HAS_VIEW_TRANSFORM_PROPERTIES && mOrientation != 0) {
195             // The workaround invalidates the entire rotate layout. After
196             // rotation, the correct area to invalidate may be larger than the
197             // size of the child. Ex: ListView. There is no way to invalidate
198             // only the necessary area.
199             r.set(0, 0, getWidth(), getHeight());
200         }
201         return super.invalidateChildInParent(location, r);
202     }
203 }
204