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
198c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucherimport android.content.res.Configuration;
20274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphaelimport android.graphics.Bitmap;
2170da918464276b110c43868caa272c97baadb89eDoris Liuimport android.graphics.Matrix;
2270da918464276b110c43868caa272c97baadb89eDoris Liuimport android.graphics.RectF;
2370da918464276b110c43868caa272c97baadb89eDoris Liuimport android.graphics.SurfaceTexture;
2470da918464276b110c43868caa272c97baadb89eDoris Liuimport android.view.TextureView;
2570da918464276b110c43868caa272c97baadb89eDoris Liuimport android.view.View;
2670da918464276b110c43868caa272c97baadb89eDoris Liuimport android.view.View.OnLayoutChangeListener;
2770da918464276b110c43868caa272c97baadb89eDoris Liu
288c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucherimport com.android.camera.app.CameraProvider;
292bca210e5fc8a77685775ffb403096167b017dceAngus Kongimport com.android.camera.debug.Log;
308c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucherimport com.android.camera.settings.Keys;
318c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucherimport com.android.camera.settings.ResolutionUtil;
328c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucherimport com.android.camera.settings.SettingsManager;
338c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucherimport com.android.camera.settings.SettingsUtil;
3470da918464276b110c43868caa272c97baadb89eDoris Liuimport com.android.camera.ui.PreviewStatusListener;
35344320c90639f823451ddac42e8f39b74727787cDoris Liuimport com.android.camera.util.CameraUtil;
368c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucherimport com.android.ex.camera2.portability.CameraDeviceInfo;
378c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucherimport com.android.ex.camera2.portability.Size;
3870da918464276b110c43868caa272c97baadb89eDoris Liu
39482de029dc20e0a577388a602985fb31c3200309Doris Liuimport java.util.ArrayList;
40fe60979d7d7ce78de503404da8e91e118242071aAngus Kongimport java.util.List;
41482de029dc20e0a577388a602985fb31c3200309Doris Liu
4270da918464276b110c43868caa272c97baadb89eDoris Liu/**
4370da918464276b110c43868caa272c97baadb89eDoris Liu * This class aims to automate TextureView transform change and notify listeners
4470da918464276b110c43868caa272c97baadb89eDoris Liu * (e.g. bottom bar) of the preview size change.
4570da918464276b110c43868caa272c97baadb89eDoris Liu */
4670da918464276b110c43868caa272c97baadb89eDoris Liupublic class TextureViewHelper implements TextureView.SurfaceTextureListener,
4770da918464276b110c43868caa272c97baadb89eDoris Liu        OnLayoutChangeListener {
4870da918464276b110c43868caa272c97baadb89eDoris Liu
492bca210e5fc8a77685775ffb403096167b017dceAngus Kong    private static final Log.Tag TAG = new Log.Tag("TexViewHelper");
5028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    public static final float MATCH_SCREEN = 0f;
51344320c90639f823451ddac42e8f39b74727787cDoris Liu    private static final int UNSET = -1;
52ee36c5f591ac181a10692b2ec2b96de5251c4abbSascha Haeberling    private final TextureView mPreview;
538c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher    private final CameraProvider mCameraProvider;
5470da918464276b110c43868caa272c97baadb89eDoris Liu    private int mWidth = 0;
5570da918464276b110c43868caa272c97baadb89eDoris Liu    private int mHeight = 0;
56a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu    private RectF mPreviewArea = new RectF();
5728a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private float mAspectRatio = MATCH_SCREEN;
5870da918464276b110c43868caa272c97baadb89eDoris Liu    private boolean mAutoAdjustTransform = true;
5970da918464276b110c43868caa272c97baadb89eDoris Liu    private TextureView.SurfaceTextureListener mSurfaceTextureListener;
6028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
6128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private final ArrayList<PreviewStatusListener.PreviewAspectRatioChangedListener>
6228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            mAspectRatioChangedListeners =
6328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            new ArrayList<PreviewStatusListener.PreviewAspectRatioChangedListener>();
6470da918464276b110c43868caa272c97baadb89eDoris Liu
652bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong    private final ArrayList<PreviewStatusListener.PreviewAreaChangedListener>
66482de029dc20e0a577388a602985fb31c3200309Doris Liu            mPreviewSizeChangedListeners =
672bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong            new ArrayList<PreviewStatusListener.PreviewAreaChangedListener>();
6870da918464276b110c43868caa272c97baadb89eDoris Liu    private OnLayoutChangeListener mOnLayoutChangeListener = null;
6928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private CaptureLayoutHelper mCaptureLayoutHelper = null;
70344320c90639f823451ddac42e8f39b74727787cDoris Liu    private int mOrientation = UNSET;
7170da918464276b110c43868caa272c97baadb89eDoris Liu
728c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher    public TextureViewHelper(TextureView preview, CaptureLayoutHelper helper,
738c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher                             CameraProvider cameraProvider) {
7470da918464276b110c43868caa272c97baadb89eDoris Liu        mPreview = preview;
758c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        mCameraProvider = cameraProvider;
7670da918464276b110c43868caa272c97baadb89eDoris Liu        mPreview.addOnLayoutChangeListener(this);
7770da918464276b110c43868caa272c97baadb89eDoris Liu        mPreview.setSurfaceTextureListener(this);
7828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        mCaptureLayoutHelper = helper;
7970da918464276b110c43868caa272c97baadb89eDoris Liu    }
8070da918464276b110c43868caa272c97baadb89eDoris Liu
8170da918464276b110c43868caa272c97baadb89eDoris Liu    /**
8270da918464276b110c43868caa272c97baadb89eDoris Liu     * If auto adjust transform is enabled, when there is a layout change, the
8370da918464276b110c43868caa272c97baadb89eDoris Liu     * transform matrix will be automatically adjusted based on the preview stream
8470da918464276b110c43868caa272c97baadb89eDoris Liu     * aspect ratio in the new layout.
8570da918464276b110c43868caa272c97baadb89eDoris Liu     *
8670da918464276b110c43868caa272c97baadb89eDoris Liu     * @param enable whether or not auto adjustment should be enabled
8770da918464276b110c43868caa272c97baadb89eDoris Liu     */
8870da918464276b110c43868caa272c97baadb89eDoris Liu    public void setAutoAdjustTransform(boolean enable) {
8970da918464276b110c43868caa272c97baadb89eDoris Liu        mAutoAdjustTransform = enable;
9070da918464276b110c43868caa272c97baadb89eDoris Liu    }
9170da918464276b110c43868caa272c97baadb89eDoris Liu
9270da918464276b110c43868caa272c97baadb89eDoris Liu    @Override
9370da918464276b110c43868caa272c97baadb89eDoris Liu    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
9470da918464276b110c43868caa272c97baadb89eDoris Liu                               int oldTop, int oldRight, int oldBottom) {
957351415ddfaf7f563a4a12c9204e6feb20fdf49dAlan Newberger        Log.v(TAG, "onLayoutChange");
9670da918464276b110c43868caa272c97baadb89eDoris Liu        int width = right - left;
9770da918464276b110c43868caa272c97baadb89eDoris Liu        int height = bottom - top;
98344320c90639f823451ddac42e8f39b74727787cDoris Liu        int rotation = CameraUtil.getDisplayRotation(mPreview.getContext());
99344320c90639f823451ddac42e8f39b74727787cDoris Liu        if (mWidth != width || mHeight != height || mOrientation != rotation) {
10070da918464276b110c43868caa272c97baadb89eDoris Liu            mWidth = width;
10170da918464276b110c43868caa272c97baadb89eDoris Liu            mHeight = height;
102344320c90639f823451ddac42e8f39b74727787cDoris Liu            mOrientation = rotation;
1038c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher            if (!updateTransform()) {
104d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren                clearTransform();
10570da918464276b110c43868caa272c97baadb89eDoris Liu            }
10670da918464276b110c43868caa272c97baadb89eDoris Liu        }
10770da918464276b110c43868caa272c97baadb89eDoris Liu        if (mOnLayoutChangeListener != null) {
10870da918464276b110c43868caa272c97baadb89eDoris Liu            mOnLayoutChangeListener.onLayoutChange(v, left, top, right, bottom, oldLeft, oldTop,
10970da918464276b110c43868caa272c97baadb89eDoris Liu                    oldRight, oldBottom);
11070da918464276b110c43868caa272c97baadb89eDoris Liu        }
11170da918464276b110c43868caa272c97baadb89eDoris Liu    }
11270da918464276b110c43868caa272c97baadb89eDoris Liu
113d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren    /**
114d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren     * Transforms the preview with the identity matrix, ensuring there
115d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren     * is no scaling on the preview.  It also calls onPreviewSizeChanged, to
116d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren     * trigger any necessary preview size changing callbacks.
117d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren     */
118d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren    public void clearTransform() {
119d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren        mPreview.setTransform(new Matrix());
120d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren        mPreviewArea.set(0, 0, mWidth, mHeight);
1212bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong        onPreviewAreaChanged(mPreviewArea);
12228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        setAspectRatio(MATCH_SCREEN);
123d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren    }
124d8de077f72b04cee1bdf26e5ca1678ac5297bd71Erin Dahlgren
12570da918464276b110c43868caa272c97baadb89eDoris Liu    public void updateAspectRatio(float aspectRatio) {
1267351415ddfaf7f563a4a12c9204e6feb20fdf49dAlan Newberger        Log.v(TAG, "updateAspectRatio");
12770da918464276b110c43868caa272c97baadb89eDoris Liu        if (aspectRatio <= 0) {
12870da918464276b110c43868caa272c97baadb89eDoris Liu            Log.e(TAG, "Invalid aspect ratio: " + aspectRatio);
12970da918464276b110c43868caa272c97baadb89eDoris Liu            return;
13070da918464276b110c43868caa272c97baadb89eDoris Liu        }
13170da918464276b110c43868caa272c97baadb89eDoris Liu        if (aspectRatio < 1f) {
13270da918464276b110c43868caa272c97baadb89eDoris Liu            aspectRatio = 1f / aspectRatio;
13370da918464276b110c43868caa272c97baadb89eDoris Liu        }
13428a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        setAspectRatio(aspectRatio);
13570da918464276b110c43868caa272c97baadb89eDoris Liu        updateTransform();
13670da918464276b110c43868caa272c97baadb89eDoris Liu    }
13770da918464276b110c43868caa272c97baadb89eDoris Liu
13828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private void setAspectRatio(float aspectRatio) {
1397351415ddfaf7f563a4a12c9204e6feb20fdf49dAlan Newberger        Log.v(TAG, "setAspectRatio: " + aspectRatio);
14028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        if (mAspectRatio != aspectRatio) {
1417351415ddfaf7f563a4a12c9204e6feb20fdf49dAlan Newberger            Log.v(TAG, "aspect ratio changed from: " + mAspectRatio);
14228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            mAspectRatio = aspectRatio;
14328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            onAspectRatioChanged();
14428a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        }
14528a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    }
14628a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
14728a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    private void onAspectRatioChanged() {
14828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        mCaptureLayoutHelper.onPreviewAspectRatioChanged(mAspectRatio);
14928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        for (PreviewStatusListener.PreviewAspectRatioChangedListener listener
15028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                : mAspectRatioChangedListeners) {
15128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            listener.onPreviewAspectRatioChanged(mAspectRatio);
15228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        }
15328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    }
15428a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
15528a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    public void addAspectRatioChangedListener(
15628a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            PreviewStatusListener.PreviewAspectRatioChangedListener listener) {
15728a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        if (listener != null && !mAspectRatioChangedListeners.contains(listener)) {
15828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            mAspectRatioChangedListeners.add(listener);
15928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        }
16028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu    }
16128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
16202c129a598b6a165885fe950275675d62993d2eeSeth Raphael
16302c129a598b6a165885fe950275675d62993d2eeSeth Raphael    /**
16402c129a598b6a165885fe950275675d62993d2eeSeth Raphael     * This returns the rect that is available to display the preview, and
16502c129a598b6a165885fe950275675d62993d2eeSeth Raphael     * capture buttons
16602c129a598b6a165885fe950275675d62993d2eeSeth Raphael     *
16702c129a598b6a165885fe950275675d62993d2eeSeth Raphael     * @return the rect.
16802c129a598b6a165885fe950275675d62993d2eeSeth Raphael     */
16902c129a598b6a165885fe950275675d62993d2eeSeth Raphael    public RectF getFullscreenRect() {
17002c129a598b6a165885fe950275675d62993d2eeSeth Raphael        return mCaptureLayoutHelper.getFullscreenRect();
17102c129a598b6a165885fe950275675d62993d2eeSeth Raphael    }
17202c129a598b6a165885fe950275675d62993d2eeSeth Raphael
17302c129a598b6a165885fe950275675d62993d2eeSeth Raphael    /**
17402c129a598b6a165885fe950275675d62993d2eeSeth Raphael     * This takes a matrix to apply to the texture view and uses the screen
17502c129a598b6a165885fe950275675d62993d2eeSeth Raphael     * aspect ratio as the target aspect ratio
176b4a2222950f627a39267636fba19649974cb8734Spike Sprague     *
17702c129a598b6a165885fe950275675d62993d2eeSeth Raphael     * @param matrix the matrix to apply
1786382c70a769b9ec94a3b0372ee27c0cd075e8be8Seth Raphael     * @param aspectRatio the aspectRatio that the preview should be
17902c129a598b6a165885fe950275675d62993d2eeSeth Raphael     */
1806382c70a769b9ec94a3b0372ee27c0cd075e8be8Seth Raphael    public void updateTransformFullScreen(Matrix matrix, float aspectRatio) {
18102c129a598b6a165885fe950275675d62993d2eeSeth Raphael        aspectRatio = aspectRatio < 1 ? 1 / aspectRatio : aspectRatio;
18202c129a598b6a165885fe950275675d62993d2eeSeth Raphael        if (aspectRatio != mAspectRatio) {
18302c129a598b6a165885fe950275675d62993d2eeSeth Raphael            setAspectRatio(aspectRatio);
18402c129a598b6a165885fe950275675d62993d2eeSeth Raphael        }
18502c129a598b6a165885fe950275675d62993d2eeSeth Raphael
18602c129a598b6a165885fe950275675d62993d2eeSeth Raphael        mPreview.setTransform(matrix);
187274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        mPreviewArea = mCaptureLayoutHelper.getPreviewRect();
188274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        onPreviewAreaChanged(mPreviewArea);
189274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael
19002c129a598b6a165885fe950275675d62993d2eeSeth Raphael    }
19102c129a598b6a165885fe950275675d62993d2eeSeth Raphael
19270da918464276b110c43868caa272c97baadb89eDoris Liu    public void updateTransform(Matrix matrix) {
19370da918464276b110c43868caa272c97baadb89eDoris Liu        RectF previewRect = new RectF(0, 0, mWidth, mHeight);
19470da918464276b110c43868caa272c97baadb89eDoris Liu        matrix.mapRect(previewRect);
19570da918464276b110c43868caa272c97baadb89eDoris Liu
19670da918464276b110c43868caa272c97baadb89eDoris Liu        float previewWidth = previewRect.width();
19770da918464276b110c43868caa272c97baadb89eDoris Liu        float previewHeight = previewRect.height();
19870da918464276b110c43868caa272c97baadb89eDoris Liu        if (previewHeight == 0 || previewWidth == 0) {
19970da918464276b110c43868caa272c97baadb89eDoris Liu            Log.e(TAG, "Invalid preview size: " + previewWidth + " x " + previewHeight);
20070da918464276b110c43868caa272c97baadb89eDoris Liu            return;
20170da918464276b110c43868caa272c97baadb89eDoris Liu        }
20228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        float aspectRatio = previewWidth / previewHeight;
20328a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        aspectRatio = aspectRatio < 1 ? 1 / aspectRatio : aspectRatio;
20428a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        if (aspectRatio != mAspectRatio) {
20528a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu            setAspectRatio(aspectRatio);
20628a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        }
20728a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu
20828a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        RectF previewAreaBasedOnAspectRatio = mCaptureLayoutHelper.getPreviewRect();
20928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        Matrix addtionalTransform = new Matrix();
21028a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        addtionalTransform.setRectToRect(previewRect, previewAreaBasedOnAspectRatio,
21128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu                Matrix.ScaleToFit.CENTER);
21228a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        matrix.postConcat(addtionalTransform);
21370da918464276b110c43868caa272c97baadb89eDoris Liu        mPreview.setTransform(matrix);
214a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu        updatePreviewArea(matrix);
215a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu    }
21670da918464276b110c43868caa272c97baadb89eDoris Liu
217a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu    /**
218a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu     * Calculates and updates the preview area rect using the latest transform matrix.
219a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu     */
220a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu    private void updatePreviewArea(Matrix matrix) {
221a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu        mPreviewArea.set(0, 0, mWidth, mHeight);
222a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu        matrix.mapRect(mPreviewArea);
2232bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong        onPreviewAreaChanged(mPreviewArea);
22470da918464276b110c43868caa272c97baadb89eDoris Liu    }
22570da918464276b110c43868caa272c97baadb89eDoris Liu
22670da918464276b110c43868caa272c97baadb89eDoris Liu    public void setOnLayoutChangeListener(OnLayoutChangeListener listener) {
22770da918464276b110c43868caa272c97baadb89eDoris Liu        mOnLayoutChangeListener = listener;
22870da918464276b110c43868caa272c97baadb89eDoris Liu    }
22970da918464276b110c43868caa272c97baadb89eDoris Liu
23070da918464276b110c43868caa272c97baadb89eDoris Liu    public void setSurfaceTextureListener(TextureView.SurfaceTextureListener listener) {
23170da918464276b110c43868caa272c97baadb89eDoris Liu        mSurfaceTextureListener = listener;
23270da918464276b110c43868caa272c97baadb89eDoris Liu    }
23370da918464276b110c43868caa272c97baadb89eDoris Liu
23458b9b03f2bedc522bde5b4199121ea2e559c79a9Angus Kong    /**
23570da918464276b110c43868caa272c97baadb89eDoris Liu     * Updates the transform matrix based current width and height of TextureView
23670da918464276b110c43868caa272c97baadb89eDoris Liu     * and preview stream aspect ratio.
2378c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher     *
2388c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher     * <p>If not {@code mAutoAdjustTransform}, this does nothing except return
2398c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher     * {@code false}. In all other cases, it returns {@code true}, regardless of
2408c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher     * whether the transform was changed.</p>
2418c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher     *
2428c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher     * @return Whether {@code mAutoAdjustTransform}.
24370da918464276b110c43868caa272c97baadb89eDoris Liu     */
2448c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher    private boolean updateTransform() {
2457351415ddfaf7f563a4a12c9204e6feb20fdf49dAlan Newberger        Log.v(TAG, "updateTransform");
2468c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        if (!mAutoAdjustTransform) {
2478c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher            return false;
2488c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        }
24928a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu        if (mAspectRatio == MATCH_SCREEN || mAspectRatio < 0 || mWidth == 0 || mHeight == 0) {
2508c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher            return true;
2518c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        }
2528c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher
2538c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        Matrix matrix;
2548c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        int cameraId = mCameraProvider.getCurrentCameraId();
2558c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        if (cameraId >= 0) {
2568c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher            CameraDeviceInfo.Characteristics info = mCameraProvider.getCharacteristics(cameraId);
2578c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher            matrix = info.getPreviewTransform(mOrientation, new RectF(0, 0, mWidth, mHeight),
2588c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher                    mCaptureLayoutHelper.getPreviewRect());
2598c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        } else {
2608c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher            Log.w(TAG, "Unable to find current camera... defaulting to identity matrix");
2618c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher            matrix = new Matrix();
26270da918464276b110c43868caa272c97baadb89eDoris Liu        }
26370da918464276b110c43868caa272c97baadb89eDoris Liu
26470da918464276b110c43868caa272c97baadb89eDoris Liu        mPreview.setTransform(matrix);
265a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu        updatePreviewArea(matrix);
2668c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        return true;
26770da918464276b110c43868caa272c97baadb89eDoris Liu    }
26870da918464276b110c43868caa272c97baadb89eDoris Liu
2692bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong    private void onPreviewAreaChanged(final RectF previewArea) {
2702bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong        // Notify listeners of preview area change
2712bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong        final List<PreviewStatusListener.PreviewAreaChangedListener> listeners =
2722bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong                new ArrayList<PreviewStatusListener.PreviewAreaChangedListener>(
273fe60979d7d7ce78de503404da8e91e118242071aAngus Kong                        mPreviewSizeChangedListeners);
274fe60979d7d7ce78de503404da8e91e118242071aAngus Kong        // This method can be called during layout pass. We post a Runnable so
275fe60979d7d7ce78de503404da8e91e118242071aAngus Kong        // that the callbacks won't happen during the layout pass.
276fe60979d7d7ce78de503404da8e91e118242071aAngus Kong        mPreview.post(new Runnable() {
277fe60979d7d7ce78de503404da8e91e118242071aAngus Kong            @Override
278fe60979d7d7ce78de503404da8e91e118242071aAngus Kong            public void run() {
2792bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong                for (PreviewStatusListener.PreviewAreaChangedListener listener : listeners) {
2802bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong                    listener.onPreviewAreaChanged(previewArea);
281fe60979d7d7ce78de503404da8e91e118242071aAngus Kong                }
282fe60979d7d7ce78de503404da8e91e118242071aAngus Kong            }
283fe60979d7d7ce78de503404da8e91e118242071aAngus Kong        });
28470da918464276b110c43868caa272c97baadb89eDoris Liu    }
28570da918464276b110c43868caa272c97baadb89eDoris Liu
28670da918464276b110c43868caa272c97baadb89eDoris Liu    /**
287213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     * Returns a new copy of the preview area, to avoid internal data being modified
288213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     * from outside of the class.
289213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu     */
290213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    public RectF getPreviewArea() {
291213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu        return new RectF(mPreviewArea);
292213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    }
293213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu
294213a4a086b54904cee543adf60b16fc1a61efe38Doris Liu    /**
295274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael     * Returns a copy of the area of the whole preview, including bits clipped
296274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael     * by the view
297274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael     */
298274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael    public RectF getTextureArea() {
299274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael
300274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        if (mPreview == null) {
301274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael            return new RectF();
302274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        }
303274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        Matrix matrix = new Matrix();
304274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        RectF area = new RectF(0, 0, mWidth, mHeight);
305274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        mPreview.getTransform(matrix).mapRect(area);
306274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        return area;
307274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael    }
308274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael
309274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael    public Bitmap getPreviewBitmap(int downsample) {
310274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael        RectF textureArea = getTextureArea();
3118c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        int width = (int) textureArea.width() / downsample;
3128c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        int height = (int) textureArea.height() / downsample;
3138c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        Bitmap preview = mPreview.getBitmap(width, height);
3148c8fb114ad4e121c4c1f3d726632dffbb32bda13Sol Boucher        return Bitmap.createBitmap(preview, 0, 0, width, height, mPreview.getTransform(null), true);
315274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael    }
316274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael
317274f6e962e9e7f94cfc4ceb9cad3fa8dc0a80abbSeth Raphael    /**
3182bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong     * Adds a listener that will get notified when the preview area changed. This
31970da918464276b110c43868caa272c97baadb89eDoris Liu     * can be useful for UI elements or focus overlay to adjust themselves according
3202bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong     * to the preview area change.
32128a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu     * <p/>
32215b9961c5ca49fc6ac9d036e7a967797987e46eeDoris Liu     * Note that a listener will only be added once. A newly added listener will receive
3232bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong     * a notification of current preview area immediately after being added.
32428a2950939614a0c1c83787960ce1a218fb69a5eDoris Liu     * <p/>
32515b9961c5ca49fc6ac9d036e7a967797987e46eeDoris Liu     * This function should be called on the UI thread and listeners will be notified
32615b9961c5ca49fc6ac9d036e7a967797987e46eeDoris Liu     * on the UI thread.
32715b9961c5ca49fc6ac9d036e7a967797987e46eeDoris Liu     *
3282bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong     * @param listener the listener that will get notified of preview area change
32970da918464276b110c43868caa272c97baadb89eDoris Liu     */
330482de029dc20e0a577388a602985fb31c3200309Doris Liu    public void addPreviewAreaSizeChangedListener(
3312bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong            PreviewStatusListener.PreviewAreaChangedListener listener) {
332482de029dc20e0a577388a602985fb31c3200309Doris Liu        if (listener != null && !mPreviewSizeChangedListeners.contains(listener)) {
333482de029dc20e0a577388a602985fb31c3200309Doris Liu            mPreviewSizeChangedListeners.add(listener);
334a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu            if (mPreviewArea.width() == 0 || mPreviewArea.height() == 0) {
3352bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong                listener.onPreviewAreaChanged(new RectF(0, 0, mWidth, mHeight));
336a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu            } else {
3372bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong                listener.onPreviewAreaChanged(new RectF(mPreviewArea));
338a1ec04a9f9526418f5cb17a5afbfc48aca1e02d0Doris Liu            }
339482de029dc20e0a577388a602985fb31c3200309Doris Liu        }
340482de029dc20e0a577388a602985fb31c3200309Doris Liu    }
341482de029dc20e0a577388a602985fb31c3200309Doris Liu
342482de029dc20e0a577388a602985fb31c3200309Doris Liu    /**
3432bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong     * Removes a listener that gets notified when the preview area changed.
344482de029dc20e0a577388a602985fb31c3200309Doris Liu     *
3452bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong     * @param listener the listener that gets notified of preview area change
346482de029dc20e0a577388a602985fb31c3200309Doris Liu     */
347482de029dc20e0a577388a602985fb31c3200309Doris Liu    public void removePreviewAreaSizeChangedListener(
3482bacca795a1b0adb0daf515c43c48234b44bbba5Angus Kong            PreviewStatusListener.PreviewAreaChangedListener listener) {
349482de029dc20e0a577388a602985fb31c3200309Doris Liu        if (listener != null && mPreviewSizeChangedListeners.contains(listener)) {
350482de029dc20e0a577388a602985fb31c3200309Doris Liu            mPreviewSizeChangedListeners.remove(listener);
351482de029dc20e0a577388a602985fb31c3200309Doris Liu        }
35270da918464276b110c43868caa272c97baadb89eDoris Liu    }
35370da918464276b110c43868caa272c97baadb89eDoris Liu
35470da918464276b110c43868caa272c97baadb89eDoris Liu    @Override
35570da918464276b110c43868caa272c97baadb89eDoris Liu    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
35670da918464276b110c43868caa272c97baadb89eDoris Liu        // Workaround for b/11168275, see b/10981460 for more details
35770da918464276b110c43868caa272c97baadb89eDoris Liu        if (mWidth != 0 && mHeight != 0) {
35870da918464276b110c43868caa272c97baadb89eDoris Liu            // Re-apply transform matrix for new surface texture
35970da918464276b110c43868caa272c97baadb89eDoris Liu            updateTransform();
36070da918464276b110c43868caa272c97baadb89eDoris Liu        }
36170da918464276b110c43868caa272c97baadb89eDoris Liu        if (mSurfaceTextureListener != null) {
36270da918464276b110c43868caa272c97baadb89eDoris Liu            mSurfaceTextureListener.onSurfaceTextureAvailable(surface, width, height);
36370da918464276b110c43868caa272c97baadb89eDoris Liu        }
36470da918464276b110c43868caa272c97baadb89eDoris Liu    }
36570da918464276b110c43868caa272c97baadb89eDoris Liu
36670da918464276b110c43868caa272c97baadb89eDoris Liu    @Override
36770da918464276b110c43868caa272c97baadb89eDoris Liu    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
36870da918464276b110c43868caa272c97baadb89eDoris Liu        if (mSurfaceTextureListener != null) {
36970da918464276b110c43868caa272c97baadb89eDoris Liu            mSurfaceTextureListener.onSurfaceTextureSizeChanged(surface, width, height);
37070da918464276b110c43868caa272c97baadb89eDoris Liu        }
37170da918464276b110c43868caa272c97baadb89eDoris Liu    }
37270da918464276b110c43868caa272c97baadb89eDoris Liu
37370da918464276b110c43868caa272c97baadb89eDoris Liu    @Override
37470da918464276b110c43868caa272c97baadb89eDoris Liu    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
37570da918464276b110c43868caa272c97baadb89eDoris Liu        if (mSurfaceTextureListener != null) {
37670da918464276b110c43868caa272c97baadb89eDoris Liu            mSurfaceTextureListener.onSurfaceTextureDestroyed(surface);
37770da918464276b110c43868caa272c97baadb89eDoris Liu        }
37870da918464276b110c43868caa272c97baadb89eDoris Liu        return false;
37970da918464276b110c43868caa272c97baadb89eDoris Liu    }
38070da918464276b110c43868caa272c97baadb89eDoris Liu
38170da918464276b110c43868caa272c97baadb89eDoris Liu    @Override
38270da918464276b110c43868caa272c97baadb89eDoris Liu    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
38370da918464276b110c43868caa272c97baadb89eDoris Liu        if (mSurfaceTextureListener != null) {
38470da918464276b110c43868caa272c97baadb89eDoris Liu            mSurfaceTextureListener.onSurfaceTextureUpdated(surface);
38570da918464276b110c43868caa272c97baadb89eDoris Liu        }
38670da918464276b110c43868caa272c97baadb89eDoris Liu
38770da918464276b110c43868caa272c97baadb89eDoris Liu    }
38870da918464276b110c43868caa272c97baadb89eDoris Liu}
389