170da918464276b110c43868caa272c97baadb89eDoris Liu/*
270da918464276b110c43868caa272c97baadb89eDoris Liu * Copyright (C) 2013 The Android Open Source Project
370da918464276b110c43868caa272c97baadb89eDoris Liu *
470da918464276b110c43868caa272c97baadb89eDoris Liu * Licensed under the Apache License, Version 2.0 (the "License");
570da918464276b110c43868caa272c97baadb89eDoris Liu * you may not use this file except in compliance with the License.
670da918464276b110c43868caa272c97baadb89eDoris Liu * You may obtain a copy of the License at
770da918464276b110c43868caa272c97baadb89eDoris Liu *
870da918464276b110c43868caa272c97baadb89eDoris Liu *      http://www.apache.org/licenses/LICENSE-2.0
970da918464276b110c43868caa272c97baadb89eDoris Liu *
1070da918464276b110c43868caa272c97baadb89eDoris Liu * Unless required by applicable law or agreed to in writing, software
1170da918464276b110c43868caa272c97baadb89eDoris Liu * distributed under the License is distributed on an "AS IS" BASIS,
1270da918464276b110c43868caa272c97baadb89eDoris Liu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1370da918464276b110c43868caa272c97baadb89eDoris Liu * See the License for the specific language governing permissions and
1470da918464276b110c43868caa272c97baadb89eDoris Liu * limitations under the License.
1570da918464276b110c43868caa272c97baadb89eDoris Liu */
1670da918464276b110c43868caa272c97baadb89eDoris Liu
1770da918464276b110c43868caa272c97baadb89eDoris Liupackage com.android.camera;
1870da918464276b110c43868caa272c97baadb89eDoris Liu
19274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphaelimport android.graphics.Bitmap;
2070da918464276b110c43868caa272c97baadb89eDoris Liuimport android.graphics.Matrix;
2170da918464276b110c43868caa272c97baadb89eDoris Liuimport android.graphics.RectF;
2270da918464276b110c43868caa272c97baadb89eDoris Liuimport android.graphics.SurfaceTexture;
2370da918464276b110c43868caa272c97baadb89eDoris Liuimport android.view.TextureView;
2470da918464276b110c43868caa272c97baadb89eDoris Liuimport android.view.View;
2570da918464276b110c43868caa272c97baadb89eDoris Liuimport android.view.View.OnLayoutChangeListener;
2670da918464276b110c43868caa272c97baadb89eDoris Liu
27f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Linimport com.android.camera.app.AppController;
288c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucherimport com.android.camera.app.CameraProvider;
291d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Linimport com.android.camera.app.OrientationManager;
302bca210e5fc8a77685775ffb403096167b017dceAngus Kongimport com.android.camera.debug.Log;
311d84d7107686aa428ee2eeb1a8caf0ea3e43b1dfPaul Rohdeimport com.android.camera.device.CameraId;
3270da918464276b110c43868caa272c97baadb89eDoris Liuimport com.android.camera.ui.PreviewStatusListener;
33e2529657eeb168588b9a47f5dc3bc87eb54f503dSascha Haeberlingimport com.android.camera.util.ApiHelper;
34344320c90639f823451ddac42e8f39b74727787cDoris Liuimport com.android.camera.util.CameraUtil;
35f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Linimport com.android.camera2.R;
368c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucherimport com.android.ex.camera2.portability.CameraDeviceInfo;
3770da918464276b110c43868caa272c97baadb89eDoris Liu
38482de029dc20e0a577388a602985fb31c3200309Doris Liuimport java.util.ArrayList;
39fe60979d7d7ce78de503404da8e91e118242071aAngus Kongimport java.util.List;
40482de029dc20e0a577388a602985fb31c3200309Doris Liu
4170da918464276b110c43868caa272c97baadb89eDoris Liu/**
4270da918464276b110c43868caa272c97baadb89eDoris Liu * This class aims to automate TextureView transform change and notify listeners
4370da918464276b110c43868caa272c97baadb89eDoris Liu * (e.g. bottom bar) of the preview size change.
4470da918464276b110c43868caa272c97baadb89eDoris Liu */
4570da918464276b110c43868caa272c97baadb89eDoris Liupublic class TextureViewHelper implements TextureView.SurfaceTextureListener,
4670da918464276b110c43868caa272c97baadb89eDoris Liu        OnLayoutChangeListener {
4770da918464276b110c43868caa272c97baadb89eDoris Liu
482bca210e5fc8a77685775ffb403096167b017dceAngus Kong    private static final Log.Tag TAG = new Log.Tag("TexViewHelper");
4928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    public static final float MATCH_SCREEN = 0f;
50344320c90639f823451ddac42e8f39b74727787cDoris Liu    private static final int UNSET = -1;
51ee36c5f591ac181a10692b2ec2b96de5251c4abbSascha Haeberling    private final TextureView mPreview;
528c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher    private final CameraProvider mCameraProvider;
5370da918464276b110c43868caa272c97baadb89eDoris Liu    private int mWidth = 0;
5470da918464276b110c43868caa272c97baadb89eDoris Liu    private int mHeight = 0;
55a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu    private RectF mPreviewArea = new RectF();
5628a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private float mAspectRatio = MATCH_SCREEN;
5770da918464276b110c43868caa272c97baadb89eDoris Liu    private boolean mAutoAdjustTransform = true;
5870da918464276b110c43868caa272c97baadb89eDoris Liu    private TextureView.SurfaceTextureListener mSurfaceTextureListener;
5928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
6028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private final ArrayList<PreviewStatusListener.PreviewAspectRatioChangedListener>
6128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            mAspectRatioChangedListeners =
6228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            new ArrayList<PreviewStatusListener.PreviewAspectRatioChangedListener>();
6370da918464276b110c43868caa272c97baadb89eDoris Liu
642bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong    private final ArrayList<PreviewStatusListener.PreviewAreaChangedListener>
65482de029dc20e0a577388a602985fb31c3200309Doris Liu            mPreviewSizeChangedListeners =
662bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong            new ArrayList<PreviewStatusListener.PreviewAreaChangedListener>();
6770da918464276b110c43868caa272c97baadb89eDoris Liu    private OnLayoutChangeListener mOnLayoutChangeListener = null;
6828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private CaptureLayoutHelper mCaptureLayoutHelper = null;
69344320c90639f823451ddac42e8f39b74727787cDoris Liu    private int mOrientation = UNSET;
7070da918464276b110c43868caa272c97baadb89eDoris Liu
71f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Lin    // Hack to allow to know which module is running for b/20694189
72f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Lin    private final AppController mAppController;
73436aba83744b104d000ae34fead732c5271d3144I-Jong Lin    private final int mCameraModeId;
74436aba83744b104d000ae34fead732c5271d3144I-Jong Lin    private final int mCaptureIntentModeId;
75f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Lin
768c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher    public TextureViewHelper(TextureView preview, CaptureLayoutHelper helper,
77f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Lin            CameraProvider cameraProvider, AppController appController) {
7870da918464276b110c43868caa272c97baadb89eDoris Liu        mPreview = preview;
798c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        mCameraProvider = cameraProvider;
8070da918464276b110c43868caa272c97baadb89eDoris Liu        mPreview.addOnLayoutChangeListener(this);
8170da918464276b110c43868caa272c97baadb89eDoris Liu        mPreview.setSurfaceTextureListener(this);
8228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        mCaptureLayoutHelper = helper;
83f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Lin        mAppController = appController;
84436aba83744b104d000ae34fead732c5271d3144I-Jong Lin        mCameraModeId = appController.getAndroidContext().getResources()
85f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Lin                .getInteger(R.integer.camera_mode_photo);
86436aba83744b104d000ae34fead732c5271d3144I-Jong Lin        mCaptureIntentModeId = appController.getAndroidContext().getResources()
87436aba83744b104d000ae34fead732c5271d3144I-Jong Lin                .getInteger(R.integer.camera_mode_capture_intent);
8870da918464276b110c43868caa272c97baadb89eDoris Liu    }
8970da918464276b110c43868caa272c97baadb89eDoris Liu
9070da918464276b110c43868caa272c97baadb89eDoris Liu    /**
9170da918464276b110c43868caa272c97baadb89eDoris Liu     * If auto adjust transform is enabled, when there is a layout change, the
921d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * transform matrix will be automatically adjusted based on the preview
931d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * stream aspect ratio in the new layout.
9470da918464276b110c43868caa272c97baadb89eDoris Liu     *
9570da918464276b110c43868caa272c97baadb89eDoris Liu     * @param enable whether or not auto adjustment should be enabled
9670da918464276b110c43868caa272c97baadb89eDoris Liu     */
9770da918464276b110c43868caa272c97baadb89eDoris Liu    public void setAutoAdjustTransform(boolean enable) {
9870da918464276b110c43868caa272c97baadb89eDoris Liu        mAutoAdjustTransform = enable;
9970da918464276b110c43868caa272c97baadb89eDoris Liu    }
10070da918464276b110c43868caa272c97baadb89eDoris Liu
10170da918464276b110c43868caa272c97baadb89eDoris Liu    @Override
10270da918464276b110c43868caa272c97baadb89eDoris Liu    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
1031d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin            int oldTop, int oldRight, int oldBottom) {
1047351415ddfaf7f563a4a12c9204e6feb20fdf49dAlan Newberger        Log.v(TAG, "onLayoutChange");
10570da918464276b110c43868caa272c97baadb89eDoris Liu        int width = right - left;
10670da918464276b110c43868caa272c97baadb89eDoris Liu        int height = bottom - top;
1078be316c7a8caf962cf3fcf5e49d332fb2718319fPaul Rohde        int rotation = CameraUtil.getDisplayRotation();
108344320c90639f823451ddac42e8f39b74727787cDoris Liu        if (mWidth != width || mHeight != height || mOrientation != rotation) {
10970da918464276b110c43868caa272c97baadb89eDoris Liu            mWidth = width;
11070da918464276b110c43868caa272c97baadb89eDoris Liu            mHeight = height;
111344320c90639f823451ddac42e8f39b74727787cDoris Liu            mOrientation = rotation;
1128c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher            if (!updateTransform()) {
113d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren                clearTransform();
11470da918464276b110c43868caa272c97baadb89eDoris Liu            }
11570da918464276b110c43868caa272c97baadb89eDoris Liu        }
11670da918464276b110c43868caa272c97baadb89eDoris Liu        if (mOnLayoutChangeListener != null) {
11770da918464276b110c43868caa272c97baadb89eDoris Liu            mOnLayoutChangeListener.onLayoutChange(v, left, top, right, bottom, oldLeft, oldTop,
11870da918464276b110c43868caa272c97baadb89eDoris Liu                    oldRight, oldBottom);
11970da918464276b110c43868caa272c97baadb89eDoris Liu        }
12070da918464276b110c43868caa272c97baadb89eDoris Liu    }
12170da918464276b110c43868caa272c97baadb89eDoris Liu
122d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren    /**
1231d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * Transforms the preview with the identity matrix, ensuring there is no
1241d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * scaling on the preview. It also calls onPreviewSizeChanged, to trigger
1251d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * any necessary preview size changing callbacks.
126d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren     */
127d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren    public void clearTransform() {
128d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren        mPreview.setTransform(new Matrix());
129d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren        mPreviewArea.set(0, 0, mWidth, mHeight);
1302bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong        onPreviewAreaChanged(mPreviewArea);
13128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        setAspectRatio(MATCH_SCREEN);
132d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren    }
133d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren
13470da918464276b110c43868caa272c97baadb89eDoris Liu    public void updateAspectRatio(float aspectRatio) {
1351d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin        Log.v(TAG, "updateAspectRatio " + aspectRatio);
13670da918464276b110c43868caa272c97baadb89eDoris Liu        if (aspectRatio <= 0) {
13770da918464276b110c43868caa272c97baadb89eDoris Liu            Log.e(TAG, "Invalid aspect ratio: " + aspectRatio);
13870da918464276b110c43868caa272c97baadb89eDoris Liu            return;
13970da918464276b110c43868caa272c97baadb89eDoris Liu        }
14070da918464276b110c43868caa272c97baadb89eDoris Liu        if (aspectRatio < 1f) {
14170da918464276b110c43868caa272c97baadb89eDoris Liu            aspectRatio = 1f / aspectRatio;
14270da918464276b110c43868caa272c97baadb89eDoris Liu        }
14328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        setAspectRatio(aspectRatio);
14470da918464276b110c43868caa272c97baadb89eDoris Liu        updateTransform();
14570da918464276b110c43868caa272c97baadb89eDoris Liu    }
14670da918464276b110c43868caa272c97baadb89eDoris Liu
14728a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private void setAspectRatio(float aspectRatio) {
1487351415ddfaf7f563a4a12c9204e6feb20fdf49dAlan Newberger        Log.v(TAG, "setAspectRatio: " + aspectRatio);
14928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        if (mAspectRatio != aspectRatio) {
1507351415ddfaf7f563a4a12c9204e6feb20fdf49dAlan Newberger            Log.v(TAG, "aspect ratio changed from: " + mAspectRatio);
15128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            mAspectRatio = aspectRatio;
15228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            onAspectRatioChanged();
15328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        }
15428a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    }
15528a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
15628a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private void onAspectRatioChanged() {
15728a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        mCaptureLayoutHelper.onPreviewAspectRatioChanged(mAspectRatio);
15828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        for (PreviewStatusListener.PreviewAspectRatioChangedListener listener
15928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                : mAspectRatioChangedListeners) {
16028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            listener.onPreviewAspectRatioChanged(mAspectRatio);
16128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        }
16228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    }
16328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
16428a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    public void addAspectRatioChangedListener(
16528a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            PreviewStatusListener.PreviewAspectRatioChangedListener listener) {
16628a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        if (listener != null && !mAspectRatioChangedListeners.contains(listener)) {
16728a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            mAspectRatioChangedListeners.add(listener);
16828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        }
16928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    }
17028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
17102c129a598b6a165885fe950275675d62993d2eeSeth Raphael    /**
17202c129a598b6a165885fe950275675d62993d2eeSeth Raphael     * This returns the rect that is available to display the preview, and
17302c129a598b6a165885fe950275675d62993d2eeSeth Raphael     * capture buttons
17402c129a598b6a165885fe950275675d62993d2eeSeth Raphael     *
17502c129a598b6a165885fe950275675d62993d2eeSeth Raphael     * @return the rect.
17602c129a598b6a165885fe950275675d62993d2eeSeth Raphael     */
17702c129a598b6a165885fe950275675d62993d2eeSeth Raphael    public RectF getFullscreenRect() {
17802c129a598b6a165885fe950275675d62993d2eeSeth Raphael        return mCaptureLayoutHelper.getFullscreenRect();
17902c129a598b6a165885fe950275675d62993d2eeSeth Raphael    }
18002c129a598b6a165885fe950275675d62993d2eeSeth Raphael
18102c129a598b6a165885fe950275675d62993d2eeSeth Raphael    /**
18202c129a598b6a165885fe950275675d62993d2eeSeth Raphael     * This takes a matrix to apply to the texture view and uses the screen
18302c129a598b6a165885fe950275675d62993d2eeSeth Raphael     * aspect ratio as the target aspect ratio
184b4a2222950f627a39267636fba19649974cb8734Spike Sprague     *
18502c129a598b6a165885fe950275675d62993d2eeSeth Raphael     * @param matrix the matrix to apply
1866382c70a769b9ec94a3b0372ee27c0cd075e8be8Seth Raphael     * @param aspectRatio the aspectRatio that the preview should be
18702c129a598b6a165885fe950275675d62993d2eeSeth Raphael     */
1886382c70a769b9ec94a3b0372ee27c0cd075e8be8Seth Raphael    public void updateTransformFullScreen(Matrix matrix, float aspectRatio) {
18902c129a598b6a165885fe950275675d62993d2eeSeth Raphael        aspectRatio = aspectRatio < 1 ? 1 / aspectRatio : aspectRatio;
19002c129a598b6a165885fe950275675d62993d2eeSeth Raphael        if (aspectRatio != mAspectRatio) {
19102c129a598b6a165885fe950275675d62993d2eeSeth Raphael            setAspectRatio(aspectRatio);
19202c129a598b6a165885fe950275675d62993d2eeSeth Raphael        }
19302c129a598b6a165885fe950275675d62993d2eeSeth Raphael
19402c129a598b6a165885fe950275675d62993d2eeSeth Raphael        mPreview.setTransform(matrix);
195274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        mPreviewArea = mCaptureLayoutHelper.getPreviewRect();
196274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        onPreviewAreaChanged(mPreviewArea);
197274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael
19802c129a598b6a165885fe950275675d62993d2eeSeth Raphael    }
19902c129a598b6a165885fe950275675d62993d2eeSeth Raphael
20070da918464276b110c43868caa272c97baadb89eDoris Liu    public void updateTransform(Matrix matrix) {
20170da918464276b110c43868caa272c97baadb89eDoris Liu        RectF previewRect = new RectF(0, 0, mWidth, mHeight);
20270da918464276b110c43868caa272c97baadb89eDoris Liu        matrix.mapRect(previewRect);
20370da918464276b110c43868caa272c97baadb89eDoris Liu
20470da918464276b110c43868caa272c97baadb89eDoris Liu        float previewWidth = previewRect.width();
20570da918464276b110c43868caa272c97baadb89eDoris Liu        float previewHeight = previewRect.height();
20670da918464276b110c43868caa272c97baadb89eDoris Liu        if (previewHeight == 0 || previewWidth == 0) {
20770da918464276b110c43868caa272c97baadb89eDoris Liu            Log.e(TAG, "Invalid preview size: " + previewWidth + " x " + previewHeight);
20870da918464276b110c43868caa272c97baadb89eDoris Liu            return;
20970da918464276b110c43868caa272c97baadb89eDoris Liu        }
21028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        float aspectRatio = previewWidth / previewHeight;
21128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        aspectRatio = aspectRatio < 1 ? 1 / aspectRatio : aspectRatio;
21228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        if (aspectRatio != mAspectRatio) {
21328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            setAspectRatio(aspectRatio);
21428a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        }
21528a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
21628a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        RectF previewAreaBasedOnAspectRatio = mCaptureLayoutHelper.getPreviewRect();
21728a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        Matrix addtionalTransform = new Matrix();
21828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        addtionalTransform.setRectToRect(previewRect, previewAreaBasedOnAspectRatio,
21928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                Matrix.ScaleToFit.CENTER);
22028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        matrix.postConcat(addtionalTransform);
22170da918464276b110c43868caa272c97baadb89eDoris Liu        mPreview.setTransform(matrix);
222a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu        updatePreviewArea(matrix);
223a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu    }
22470da918464276b110c43868caa272c97baadb89eDoris Liu
225a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu    /**
2261d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * Calculates and updates the preview area rect using the latest transform
2271d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * matrix.
228a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu     */
229a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu    private void updatePreviewArea(Matrix matrix) {
230a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu        mPreviewArea.set(0, 0, mWidth, mHeight);
231a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu        matrix.mapRect(mPreviewArea);
2322bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong        onPreviewAreaChanged(mPreviewArea);
23370da918464276b110c43868caa272c97baadb89eDoris Liu    }
23470da918464276b110c43868caa272c97baadb89eDoris Liu
23570da918464276b110c43868caa272c97baadb89eDoris Liu    public void setOnLayoutChangeListener(OnLayoutChangeListener listener) {
23670da918464276b110c43868caa272c97baadb89eDoris Liu        mOnLayoutChangeListener = listener;
23770da918464276b110c43868caa272c97baadb89eDoris Liu    }
23870da918464276b110c43868caa272c97baadb89eDoris Liu
23970da918464276b110c43868caa272c97baadb89eDoris Liu    public void setSurfaceTextureListener(TextureView.SurfaceTextureListener listener) {
24070da918464276b110c43868caa272c97baadb89eDoris Liu        mSurfaceTextureListener = listener;
24170da918464276b110c43868caa272c97baadb89eDoris Liu    }
24270da918464276b110c43868caa272c97baadb89eDoris Liu
24358b9b03f2bedc522bde5b4199121ea2e559c79a9Angus Kong    /**
2441d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * Returns a transformation matrix that implements rotation that is
2451d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * consistent with CaptureLayoutHelper and TextureViewHelper. The magical
2461d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * invariant for CaptureLayoutHelper and TextureViewHelper that must be
2471d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * obeyed is that the bounding box of the view must be EXACTLY the bounding
2481d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * box of the surfaceDimensions AFTER the transformation has been applied.
2498c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher     *
2501d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * @param currentDisplayOrientation The current display orientation,
2511d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     *            measured counterclockwise from to the device's natural
2521d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     *            orientation (in degrees, always a multiple of 90, and between
2531d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     *            0 and 270, inclusive).
2541d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * @param surfaceDimensions The dimensions of the
2551d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     *            {@link android.view.Surface} on which the preview image is
2561d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     *            being rendered. It usually only makes sense for the upper-left
2571d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     *            corner to be at the origin.
2581d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * @param desiredBounds The boundaries within the
2591d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     *            {@link android.view.Surface} where the final image should
2601d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     *            appear. These can be used to translate and scale the output,
2611d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     *            but note that the image will be stretched to fit, possibly
2621d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     *            changing its aspect ratio.
2631d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * @return The transform matrix that should be applied to the
2641d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     *         {@link android.view.Surface} in order for the image to display
2651d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     *         properly in the device's current orientation.
2661d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     */
2671d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin    public Matrix getPreviewRotationalTransform(int currentDisplayOrientation,
2681d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin            RectF surfaceDimensions,
2691d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin            RectF desiredBounds) {
2701d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin        if (surfaceDimensions.equals(desiredBounds)) {
2711d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin            return new Matrix();
2721d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin        }
2731d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin
2741d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin        Matrix transform = new Matrix();
2751d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin        transform.setRectToRect(surfaceDimensions, desiredBounds, Matrix.ScaleToFit.FILL);
2761d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin
2771d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin        RectF normalRect = surfaceDimensions;
2781d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin        // Bounding box of 90 or 270 degree rotation.
2791d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin        RectF rotatedRect = new RectF(normalRect.width() / 2 - normalRect.height() / 2,
2801d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin                normalRect.height() / 2 - normalRect.width() / 2,
2811d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin                normalRect.width() / 2 + normalRect.height() / 2,
2821d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin                normalRect.height() / 2 + normalRect.width() / 2);
2831d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin
2841d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin        OrientationManager.DeviceOrientation deviceOrientation =
2851d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin                OrientationManager.DeviceOrientation.from(currentDisplayOrientation);
2861d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin
28755e498578b46ef9e7b3aa70f30a68ea9bd788d63I-Jong Lin        // This rotation code assumes that the aspect ratio of the content
28855e498578b46ef9e7b3aa70f30a68ea9bd788d63I-Jong Lin        // (not of necessarily the surface) equals the aspect ratio of view that is receiving
28955e498578b46ef9e7b3aa70f30a68ea9bd788d63I-Jong Lin        // the preview.  So, a 4:3 surface that contains 16:9 data will look correct as
29055e498578b46ef9e7b3aa70f30a68ea9bd788d63I-Jong Lin        // long as the view is also 16:9.
2911d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin        switch (deviceOrientation) {
2921d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin            case CLOCKWISE_90:
2931d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin                transform.setRectToRect(rotatedRect, desiredBounds, Matrix.ScaleToFit.FILL);
2941d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin                transform.preRotate(270, mWidth / 2, mHeight / 2);
2951d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin                break;
2961d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin            case CLOCKWISE_180:
2971d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin                transform.setRectToRect(normalRect, desiredBounds, Matrix.ScaleToFit.FILL);
2981d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin                transform.preRotate(180, mWidth / 2, mHeight / 2);
2991d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin                break;
3001d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin            case CLOCKWISE_270:
3011d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin                transform.setRectToRect(rotatedRect, desiredBounds, Matrix.ScaleToFit.FILL);
3021d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin                transform.preRotate(90, mWidth / 2, mHeight / 2);
3031d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin                break;
3041d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin            case CLOCKWISE_0:
3051d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin            default:
3061d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin                transform.setRectToRect(normalRect, desiredBounds, Matrix.ScaleToFit.FILL);
3071d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin                break;
3081d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin        }
3091d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin
3101d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin        return transform;
3111d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin    }
3121d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin
3131d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin    /**
3141d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * Updates the transform matrix based current width and height of
3151d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * TextureView and preview stream aspect ratio.
3161d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * <p>
3171d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * If not {@code mAutoAdjustTransform}, this does nothing except return
3188c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher     * {@code false}. In all other cases, it returns {@code true}, regardless of
3191d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * whether the transform was changed.
3201d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * </p>
3211d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * In {@code mAutoAdjustTransform} and the CameraProvder is invalid, it is assumed
3221d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * that the CaptureModule/PhotoModule is Camera2 API-based and must implements its
3231d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * rotation via matrix transformation implemented in getPreviewRotationalTransform.
3248c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher     *
3258c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher     * @return Whether {@code mAutoAdjustTransform}.
32670da918464276b110c43868caa272c97baadb89eDoris Liu     */
3278c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher    private boolean updateTransform() {
3287351415ddfaf7f563a4a12c9204e6feb20fdf49dAlan Newberger        Log.v(TAG, "updateTransform");
3298c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        if (!mAutoAdjustTransform) {
3308c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher            return false;
3318c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        }
3321d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin
33328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        if (mAspectRatio == MATCH_SCREEN || mAspectRatio < 0 || mWidth == 0 || mHeight == 0) {
3348c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher            return true;
3358c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        }
3368c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher
337f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Lin        Matrix matrix = new Matrix();
3381d84d7107686aa428ee2eeb1a8caf0ea3e43b1dfPaul Rohde        CameraId cameraKey = mCameraProvider.getCurrentCameraId();
3391d84d7107686aa428ee2eeb1a8caf0ea3e43b1dfPaul Rohde        int cameraId = -1;
3401d84d7107686aa428ee2eeb1a8caf0ea3e43b1dfPaul Rohde
3411d84d7107686aa428ee2eeb1a8caf0ea3e43b1dfPaul Rohde        try {
3421d84d7107686aa428ee2eeb1a8caf0ea3e43b1dfPaul Rohde            cameraId = cameraKey.getLegacyValue();
3431d84d7107686aa428ee2eeb1a8caf0ea3e43b1dfPaul Rohde        } catch (UnsupportedOperationException ignored) {
3441d84d7107686aa428ee2eeb1a8caf0ea3e43b1dfPaul Rohde            Log.e(TAG, "TransformViewHelper does not support Camera API2");
3451d84d7107686aa428ee2eeb1a8caf0ea3e43b1dfPaul Rohde        }
3461d84d7107686aa428ee2eeb1a8caf0ea3e43b1dfPaul Rohde
347f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Lin
348f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Lin        // Only apply this fix when Current Active Module is Photo module AND
349f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Lin        // Phone is Nexus4 The enhancement fix b/20694189 to original fix to
350f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Lin        // b/19271661 ensures that the fix should only be applied when:
351f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Lin        // 1) the phone is a Nexus4 which requires the specific workaround
352436aba83744b104d000ae34fead732c5271d3144I-Jong Lin        // 2) CaptureModule is enabled.
353436aba83744b104d000ae34fead732c5271d3144I-Jong Lin        // 3) the Camera Photo Mode Or Capture Intent Photo Mode is active
354436aba83744b104d000ae34fead732c5271d3144I-Jong Lin        if (ApiHelper.IS_NEXUS_4 && mAppController.getCameraFeatureConfig().isUsingCaptureModule()
355436aba83744b104d000ae34fead732c5271d3144I-Jong Lin                && (mAppController.getCurrentModuleIndex() == mCameraModeId ||
356436aba83744b104d000ae34fead732c5271d3144I-Jong Lin                mAppController.getCurrentModuleIndex() == mCaptureIntentModeId)) {
357436aba83744b104d000ae34fead732c5271d3144I-Jong Lin            Log.v(TAG, "Applying Photo Mode, Capture Module, Nexus-4 specific fix for b/19271661");
3581d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin            mOrientation = CameraUtil.getDisplayRotation();
359f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Lin            matrix = getPreviewRotationalTransform(mOrientation,
360f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Lin                    new RectF(0, 0, mWidth, mHeight),
361f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Lin                    mCaptureLayoutHelper.getPreviewRect());
362436aba83744b104d000ae34fead732c5271d3144I-Jong Lin        } else if (cameraId >= 0) {
363436aba83744b104d000ae34fead732c5271d3144I-Jong Lin            // Otherwise, do the default, legacy action.
364f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Lin            CameraDeviceInfo.Characteristics info = mCameraProvider.getCharacteristics(cameraId);
365f018bce18af5626bf67e6dcb37ea60b0cc6a53cdI-Jong Lin            matrix = info.getPreviewTransform(mOrientation, new RectF(0, 0, mWidth, mHeight),
3661d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin                    mCaptureLayoutHelper.getPreviewRect());
367436aba83744b104d000ae34fead732c5271d3144I-Jong Lin        } else {
368436aba83744b104d000ae34fead732c5271d3144I-Jong Lin            // Do Nothing
36970da918464276b110c43868caa272c97baadb89eDoris Liu        }
37070da918464276b110c43868caa272c97baadb89eDoris Liu
37170da918464276b110c43868caa272c97baadb89eDoris Liu        mPreview.setTransform(matrix);
372a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu        updatePreviewArea(matrix);
3738c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        return true;
37470da918464276b110c43868caa272c97baadb89eDoris Liu    }
37570da918464276b110c43868caa272c97baadb89eDoris Liu
3762bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong    private void onPreviewAreaChanged(final RectF previewArea) {
3772bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong        // Notify listeners of preview area change
3782bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong        final List<PreviewStatusListener.PreviewAreaChangedListener> listeners =
3792bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong                new ArrayList<PreviewStatusListener.PreviewAreaChangedListener>(
380fe60979d7d7ce78de503404da8e91e118242071aAngus Kong                        mPreviewSizeChangedListeners);
381fe60979d7d7ce78de503404da8e91e118242071aAngus Kong        // This method can be called during layout pass. We post a Runnable so
382fe60979d7d7ce78de503404da8e91e118242071aAngus Kong        // that the callbacks won't happen during the layout pass.
383fe60979d7d7ce78de503404da8e91e118242071aAngus Kong        mPreview.post(new Runnable() {
384fe60979d7d7ce78de503404da8e91e118242071aAngus Kong            @Override
385fe60979d7d7ce78de503404da8e91e118242071aAngus Kong            public void run() {
3862bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong                for (PreviewStatusListener.PreviewAreaChangedListener listener : listeners) {
3872bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong                    listener.onPreviewAreaChanged(previewArea);
388fe60979d7d7ce78de503404da8e91e118242071aAngus Kong                }
389fe60979d7d7ce78de503404da8e91e118242071aAngus Kong            }
390fe60979d7d7ce78de503404da8e91e118242071aAngus Kong        });
39170da918464276b110c43868caa272c97baadb89eDoris Liu    }
39270da918464276b110c43868caa272c97baadb89eDoris Liu
39370da918464276b110c43868caa272c97baadb89eDoris Liu    /**
3941d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * Returns a new copy of the preview area, to avoid internal data being
3951d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * modified from outside of the class.
396213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     */
397213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    public RectF getPreviewArea() {
398213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        return new RectF(mPreviewArea);
399213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    }
400213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
401213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    /**
402274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael     * Returns a copy of the area of the whole preview, including bits clipped
403274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael     * by the view
404274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael     */
405274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael    public RectF getTextureArea() {
406274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael
407274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        if (mPreview == null) {
408274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael            return new RectF();
409274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        }
410274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        Matrix matrix = new Matrix();
411274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        RectF area = new RectF(0, 0, mWidth, mHeight);
412274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        mPreview.getTransform(matrix).mapRect(area);
413274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        return area;
414274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael    }
415274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael
416274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael    public Bitmap getPreviewBitmap(int downsample) {
417274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        RectF textureArea = getTextureArea();
4188c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        int width = (int) textureArea.width() / downsample;
4198c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        int height = (int) textureArea.height() / downsample;
4208c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        Bitmap preview = mPreview.getBitmap(width, height);
4218c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        return Bitmap.createBitmap(preview, 0, 0, width, height, mPreview.getTransform(null), true);
422274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael    }
423274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael
424274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael    /**
4251d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * Adds a listener that will get notified when the preview area changed.
4261d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * This can be useful for UI elements or focus overlay to adjust themselves
4271d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * according to the preview area change.
42828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu     * <p/>
4291d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * Note that a listener will only be added once. A newly added listener will
4301d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * receive a notification of current preview area immediately after being
4311d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * added.
43228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu     * <p/>
4331d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * This function should be called on the UI thread and listeners will be
4341d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * notified on the UI thread.
43515b9961c5ca49fc6ac9d036e7a967797987e46eeDoris Liu     *
4361d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     * @param listener the listener that will get notified of preview area
4371d0dd50db5d3d02d1209b4c0a3e6d4a71d2c8acfI-Jong Lin     *            change
43870da918464276b110c43868caa272c97baadb89eDoris Liu     */
439482de029dc20e0a577388a602985fb31c3200309Doris Liu    public void addPreviewAreaSizeChangedListener(
4402bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong            PreviewStatusListener.PreviewAreaChangedListener listener) {
441482de029dc20e0a577388a602985fb31c3200309Doris Liu        if (listener != null && !mPreviewSizeChangedListeners.contains(listener)) {
442482de029dc20e0a577388a602985fb31c3200309Doris Liu            mPreviewSizeChangedListeners.add(listener);
443a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu            if (mPreviewArea.width() == 0 || mPreviewArea.height() == 0) {
4442bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong                listener.onPreviewAreaChanged(new RectF(0, 0, mWidth, mHeight));
445a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu            } else {
4462bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong                listener.onPreviewAreaChanged(new RectF(mPreviewArea));
447a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu            }
448482de029dc20e0a577388a602985fb31c3200309Doris Liu        }
449482de029dc20e0a577388a602985fb31c3200309Doris Liu    }
450482de029dc20e0a577388a602985fb31c3200309Doris Liu
451482de029dc20e0a577388a602985fb31c3200309Doris Liu    /**
4522bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong     * Removes a listener that gets notified when the preview area changed.
453482de029dc20e0a577388a602985fb31c3200309Doris Liu     *
4542bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong     * @param listener the listener that gets notified of preview area change
455482de029dc20e0a577388a602985fb31c3200309Doris Liu     */
456482de029dc20e0a577388a602985fb31c3200309Doris Liu    public void removePreviewAreaSizeChangedListener(
4572bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong            PreviewStatusListener.PreviewAreaChangedListener listener) {
458482de029dc20e0a577388a602985fb31c3200309Doris Liu        if (listener != null && mPreviewSizeChangedListeners.contains(listener)) {
459482de029dc20e0a577388a602985fb31c3200309Doris Liu            mPreviewSizeChangedListeners.remove(listener);
460482de029dc20e0a577388a602985fb31c3200309Doris Liu        }
46170da918464276b110c43868caa272c97baadb89eDoris Liu    }
46270da918464276b110c43868caa272c97baadb89eDoris Liu
46370da918464276b110c43868caa272c97baadb89eDoris Liu    @Override
46470da918464276b110c43868caa272c97baadb89eDoris Liu    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
46570da918464276b110c43868caa272c97baadb89eDoris Liu        // Workaround for b/11168275, see b/10981460 for more details
46670da918464276b110c43868caa272c97baadb89eDoris Liu        if (mWidth != 0 && mHeight != 0) {
46770da918464276b110c43868caa272c97baadb89eDoris Liu            // Re-apply transform matrix for new surface texture
46870da918464276b110c43868caa272c97baadb89eDoris Liu            updateTransform();
46970da918464276b110c43868caa272c97baadb89eDoris Liu        }
47070da918464276b110c43868caa272c97baadb89eDoris Liu        if (mSurfaceTextureListener != null) {
47170da918464276b110c43868caa272c97baadb89eDoris Liu            mSurfaceTextureListener.onSurfaceTextureAvailable(surface, width, height);
47270da918464276b110c43868caa272c97baadb89eDoris Liu        }
47370da918464276b110c43868caa272c97baadb89eDoris Liu    }
47470da918464276b110c43868caa272c97baadb89eDoris Liu
47570da918464276b110c43868caa272c97baadb89eDoris Liu    @Override
47670da918464276b110c43868caa272c97baadb89eDoris Liu    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
47770da918464276b110c43868caa272c97baadb89eDoris Liu        if (mSurfaceTextureListener != null) {
47870da918464276b110c43868caa272c97baadb89eDoris Liu            mSurfaceTextureListener.onSurfaceTextureSizeChanged(surface, width, height);
47970da918464276b110c43868caa272c97baadb89eDoris Liu        }
48070da918464276b110c43868caa272c97baadb89eDoris Liu    }
48170da918464276b110c43868caa272c97baadb89eDoris Liu
48270da918464276b110c43868caa272c97baadb89eDoris Liu    @Override
48370da918464276b110c43868caa272c97baadb89eDoris Liu    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
48470da918464276b110c43868caa272c97baadb89eDoris Liu        if (mSurfaceTextureListener != null) {
48570da918464276b110c43868caa272c97baadb89eDoris Liu            mSurfaceTextureListener.onSurfaceTextureDestroyed(surface);
48670da918464276b110c43868caa272c97baadb89eDoris Liu        }
48770da918464276b110c43868caa272c97baadb89eDoris Liu        return false;
48870da918464276b110c43868caa272c97baadb89eDoris Liu    }
48970da918464276b110c43868caa272c97baadb89eDoris Liu
49070da918464276b110c43868caa272c97baadb89eDoris Liu    @Override
49170da918464276b110c43868caa272c97baadb89eDoris Liu    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
49270da918464276b110c43868caa272c97baadb89eDoris Liu        if (mSurfaceTextureListener != null) {
49370da918464276b110c43868caa272c97baadb89eDoris Liu            mSurfaceTextureListener.onSurfaceTextureUpdated(surface);
49470da918464276b110c43868caa272c97baadb89eDoris Liu        }
49570da918464276b110c43868caa272c97baadb89eDoris Liu
49670da918464276b110c43868caa272c97baadb89eDoris Liu    }
49770da918464276b110c43868caa272c97baadb89eDoris Liu}
498