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
17package com.android.camera.ui;
18
19import android.annotation.TargetApi;
20import android.content.Context;
21import android.graphics.Canvas;
22import android.graphics.Matrix;
23import android.graphics.Rect;
24import android.util.AttributeSet;
25import android.view.MotionEvent;
26import android.view.View;
27import android.view.ViewGroup;
28import android.view.ViewParent;
29
30import com.android.gallery3d.common.ApiHelper;
31import 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.
35public 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
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
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
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
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
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
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
175    public boolean shouldDelayChildPressedState() {
176        return false;
177    }
178
179    // Rotate the view counter-clockwise
180    @Override
181    public void setOrientation(int orientation, boolean animation) {
182        orientation = orientation % 360;
183        if (mOrientation == orientation) return;
184        mOrientation = orientation;
185        requestLayout();
186    }
187
188    public int getOrientation() {
189        return mOrientation;
190    }
191
192    @Override
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