TextureViewHelper.java revision 15b9961c5ca49fc6ac9d036e7a967797987e46ee
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
1970da918464276b110c43868caa272c97baadb89eDoris Liuimport android.graphics.Matrix;
2070da918464276b110c43868caa272c97baadb89eDoris Liuimport android.graphics.RectF;
2170da918464276b110c43868caa272c97baadb89eDoris Liuimport android.graphics.SurfaceTexture;
2270da918464276b110c43868caa272c97baadb89eDoris Liuimport android.util.Log;
2370da918464276b110c43868caa272c97baadb89eDoris Liuimport android.view.TextureView;
2470da918464276b110c43868caa272c97baadb89eDoris Liuimport android.view.View;
2570da918464276b110c43868caa272c97baadb89eDoris Liuimport android.view.View.OnLayoutChangeListener;
2670da918464276b110c43868caa272c97baadb89eDoris Liu
2770da918464276b110c43868caa272c97baadb89eDoris Liuimport com.android.camera.ui.PreviewStatusListener;
2870da918464276b110c43868caa272c97baadb89eDoris Liu
29482de029dc20e0a577388a602985fb31c3200309Doris Liuimport java.util.ArrayList;
30482de029dc20e0a577388a602985fb31c3200309Doris Liu
3170da918464276b110c43868caa272c97baadb89eDoris Liu/**
3270da918464276b110c43868caa272c97baadb89eDoris Liu * This class aims to automate TextureView transform change and notify listeners
3370da918464276b110c43868caa272c97baadb89eDoris Liu * (e.g. bottom bar) of the preview size change.
3470da918464276b110c43868caa272c97baadb89eDoris Liu */
3570da918464276b110c43868caa272c97baadb89eDoris Liupublic class TextureViewHelper implements TextureView.SurfaceTextureListener,
3670da918464276b110c43868caa272c97baadb89eDoris Liu        OnLayoutChangeListener {
3770da918464276b110c43868caa272c97baadb89eDoris Liu
3870da918464276b110c43868caa272c97baadb89eDoris Liu    private static final String TAG = "TextureViewHelper";
3970da918464276b110c43868caa272c97baadb89eDoris Liu    private static final float UNSET = 0f;
4070da918464276b110c43868caa272c97baadb89eDoris Liu    private TextureView mPreview;
4170da918464276b110c43868caa272c97baadb89eDoris Liu    private int mWidth = 0;
4270da918464276b110c43868caa272c97baadb89eDoris Liu    private int mHeight = 0;
4370da918464276b110c43868caa272c97baadb89eDoris Liu    private float mAspectRatio = UNSET;
4470da918464276b110c43868caa272c97baadb89eDoris Liu    private boolean mAutoAdjustTransform = true;
4570da918464276b110c43868caa272c97baadb89eDoris Liu    private TextureView.SurfaceTextureListener mSurfaceTextureListener;
4670da918464276b110c43868caa272c97baadb89eDoris Liu
47482de029dc20e0a577388a602985fb31c3200309Doris Liu    private final ArrayList<PreviewStatusListener.PreviewAreaSizeChangedListener>
48482de029dc20e0a577388a602985fb31c3200309Doris Liu            mPreviewSizeChangedListeners =
49482de029dc20e0a577388a602985fb31c3200309Doris Liu            new ArrayList<PreviewStatusListener.PreviewAreaSizeChangedListener>();
5070da918464276b110c43868caa272c97baadb89eDoris Liu    private OnLayoutChangeListener mOnLayoutChangeListener = null;
5170da918464276b110c43868caa272c97baadb89eDoris Liu
5270da918464276b110c43868caa272c97baadb89eDoris Liu    public TextureViewHelper(TextureView preview) {
5370da918464276b110c43868caa272c97baadb89eDoris Liu        mPreview = preview;
5470da918464276b110c43868caa272c97baadb89eDoris Liu        mPreview.addOnLayoutChangeListener(this);
5570da918464276b110c43868caa272c97baadb89eDoris Liu        mPreview.setSurfaceTextureListener(this);
5670da918464276b110c43868caa272c97baadb89eDoris Liu    }
5770da918464276b110c43868caa272c97baadb89eDoris Liu
5870da918464276b110c43868caa272c97baadb89eDoris Liu    /**
5970da918464276b110c43868caa272c97baadb89eDoris Liu     * If auto adjust transform is enabled, when there is a layout change, the
6070da918464276b110c43868caa272c97baadb89eDoris Liu     * transform matrix will be automatically adjusted based on the preview stream
6170da918464276b110c43868caa272c97baadb89eDoris Liu     * aspect ratio in the new layout.
6270da918464276b110c43868caa272c97baadb89eDoris Liu     *
6370da918464276b110c43868caa272c97baadb89eDoris Liu     * @param enable whether or not auto adjustment should be enabled
6470da918464276b110c43868caa272c97baadb89eDoris Liu     */
6570da918464276b110c43868caa272c97baadb89eDoris Liu    public void setAutoAdjustTransform(boolean enable) {
6670da918464276b110c43868caa272c97baadb89eDoris Liu        mAutoAdjustTransform = enable;
6770da918464276b110c43868caa272c97baadb89eDoris Liu    }
6870da918464276b110c43868caa272c97baadb89eDoris Liu
6970da918464276b110c43868caa272c97baadb89eDoris Liu    @Override
7070da918464276b110c43868caa272c97baadb89eDoris Liu    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
7170da918464276b110c43868caa272c97baadb89eDoris Liu                               int oldTop, int oldRight, int oldBottom) {
7270da918464276b110c43868caa272c97baadb89eDoris Liu        int width = right - left;
7370da918464276b110c43868caa272c97baadb89eDoris Liu        int height = bottom - top;
7470da918464276b110c43868caa272c97baadb89eDoris Liu        if (mWidth != width || mHeight != height) {
7570da918464276b110c43868caa272c97baadb89eDoris Liu            mWidth = width;
7670da918464276b110c43868caa272c97baadb89eDoris Liu            mHeight = height;
7770da918464276b110c43868caa272c97baadb89eDoris Liu            if (mAutoAdjustTransform) {
7870da918464276b110c43868caa272c97baadb89eDoris Liu                updateTransform();
7970da918464276b110c43868caa272c97baadb89eDoris Liu            }
8070da918464276b110c43868caa272c97baadb89eDoris Liu        }
8170da918464276b110c43868caa272c97baadb89eDoris Liu        if (mOnLayoutChangeListener != null) {
8270da918464276b110c43868caa272c97baadb89eDoris Liu            mOnLayoutChangeListener.onLayoutChange(v, left, top, right, bottom, oldLeft, oldTop,
8370da918464276b110c43868caa272c97baadb89eDoris Liu                    oldRight, oldBottom);
8470da918464276b110c43868caa272c97baadb89eDoris Liu        }
8570da918464276b110c43868caa272c97baadb89eDoris Liu    }
8670da918464276b110c43868caa272c97baadb89eDoris Liu
8770da918464276b110c43868caa272c97baadb89eDoris Liu    public void updateAspectRatio(float aspectRatio) {
8870da918464276b110c43868caa272c97baadb89eDoris Liu        if (aspectRatio <= 0) {
8970da918464276b110c43868caa272c97baadb89eDoris Liu            Log.e(TAG, "Invalid aspect ratio: " + aspectRatio);
9070da918464276b110c43868caa272c97baadb89eDoris Liu            return;
9170da918464276b110c43868caa272c97baadb89eDoris Liu        }
9270da918464276b110c43868caa272c97baadb89eDoris Liu        if (aspectRatio < 1f) {
9370da918464276b110c43868caa272c97baadb89eDoris Liu            aspectRatio = 1f / aspectRatio;
9470da918464276b110c43868caa272c97baadb89eDoris Liu        }
9570da918464276b110c43868caa272c97baadb89eDoris Liu        mAspectRatio = aspectRatio;
9670da918464276b110c43868caa272c97baadb89eDoris Liu        updateTransform();
9770da918464276b110c43868caa272c97baadb89eDoris Liu    }
9870da918464276b110c43868caa272c97baadb89eDoris Liu
9970da918464276b110c43868caa272c97baadb89eDoris Liu    public void updateTransform(Matrix matrix) {
10070da918464276b110c43868caa272c97baadb89eDoris Liu        RectF previewRect = new RectF(0, 0, mWidth, mHeight);
10170da918464276b110c43868caa272c97baadb89eDoris Liu        matrix.mapRect(previewRect);
10270da918464276b110c43868caa272c97baadb89eDoris Liu
10370da918464276b110c43868caa272c97baadb89eDoris Liu        float previewWidth = previewRect.width();
10470da918464276b110c43868caa272c97baadb89eDoris Liu        float previewHeight = previewRect.height();
10570da918464276b110c43868caa272c97baadb89eDoris Liu        if (previewHeight == 0 || previewWidth == 0) {
10670da918464276b110c43868caa272c97baadb89eDoris Liu            Log.e(TAG, "Invalid preview size: " + previewWidth + " x " + previewHeight);
10770da918464276b110c43868caa272c97baadb89eDoris Liu            return;
10870da918464276b110c43868caa272c97baadb89eDoris Liu        }
10970da918464276b110c43868caa272c97baadb89eDoris Liu        mPreview.setTransform(matrix);
11070da918464276b110c43868caa272c97baadb89eDoris Liu
11170da918464276b110c43868caa272c97baadb89eDoris Liu        onPreviewSizeChanged(previewWidth, previewHeight);
11270da918464276b110c43868caa272c97baadb89eDoris Liu    }
11370da918464276b110c43868caa272c97baadb89eDoris Liu
11470da918464276b110c43868caa272c97baadb89eDoris Liu    public void setOnLayoutChangeListener(OnLayoutChangeListener listener) {
11570da918464276b110c43868caa272c97baadb89eDoris Liu        mOnLayoutChangeListener = listener;
11670da918464276b110c43868caa272c97baadb89eDoris Liu    }
11770da918464276b110c43868caa272c97baadb89eDoris Liu
11870da918464276b110c43868caa272c97baadb89eDoris Liu    public void setSurfaceTextureListener(TextureView.SurfaceTextureListener listener) {
11970da918464276b110c43868caa272c97baadb89eDoris Liu        mSurfaceTextureListener = listener;
12070da918464276b110c43868caa272c97baadb89eDoris Liu    }
12170da918464276b110c43868caa272c97baadb89eDoris Liu
12270da918464276b110c43868caa272c97baadb89eDoris Liu    /**
12370da918464276b110c43868caa272c97baadb89eDoris Liu     * Updates the transform matrix based current width and height of TextureView
12470da918464276b110c43868caa272c97baadb89eDoris Liu     * and preview stream aspect ratio.
12570da918464276b110c43868caa272c97baadb89eDoris Liu     */
12670da918464276b110c43868caa272c97baadb89eDoris Liu    private void updateTransform() {
12770da918464276b110c43868caa272c97baadb89eDoris Liu        if (mAspectRatio == UNSET || mAspectRatio < 0 || mWidth == 0 || mHeight == 0) {
12870da918464276b110c43868caa272c97baadb89eDoris Liu            return;
12970da918464276b110c43868caa272c97baadb89eDoris Liu        }
13070da918464276b110c43868caa272c97baadb89eDoris Liu
13170da918464276b110c43868caa272c97baadb89eDoris Liu        Matrix matrix = mPreview.getTransform(null);
13270da918464276b110c43868caa272c97baadb89eDoris Liu        float scaledTextureWidth, scaledTextureHeight;
13370da918464276b110c43868caa272c97baadb89eDoris Liu        if (mWidth > mHeight) {
13470da918464276b110c43868caa272c97baadb89eDoris Liu            scaledTextureWidth = Math.min(mWidth,
13570da918464276b110c43868caa272c97baadb89eDoris Liu                    (int) (mHeight * mAspectRatio));
13670da918464276b110c43868caa272c97baadb89eDoris Liu            scaledTextureHeight = Math.min(mHeight,
13770da918464276b110c43868caa272c97baadb89eDoris Liu                    (int) (mWidth / mAspectRatio));
13870da918464276b110c43868caa272c97baadb89eDoris Liu        } else {
13970da918464276b110c43868caa272c97baadb89eDoris Liu            scaledTextureWidth = Math.min(mWidth,
14070da918464276b110c43868caa272c97baadb89eDoris Liu                    (int) (mHeight / mAspectRatio));
14170da918464276b110c43868caa272c97baadb89eDoris Liu            scaledTextureHeight = Math.min(mHeight,
14270da918464276b110c43868caa272c97baadb89eDoris Liu                    (int) (mWidth * mAspectRatio));
14370da918464276b110c43868caa272c97baadb89eDoris Liu        }
14470da918464276b110c43868caa272c97baadb89eDoris Liu
14570da918464276b110c43868caa272c97baadb89eDoris Liu        float scaleX = scaledTextureWidth / mWidth;
14670da918464276b110c43868caa272c97baadb89eDoris Liu        float scaleY = scaledTextureHeight / mHeight;
14770da918464276b110c43868caa272c97baadb89eDoris Liu
14870da918464276b110c43868caa272c97baadb89eDoris Liu        boolean landscape = mWidth > mHeight;
14970da918464276b110c43868caa272c97baadb89eDoris Liu        if (landscape) {
15070da918464276b110c43868caa272c97baadb89eDoris Liu            matrix.setScale(scaleX, scaleY, 0f, (float) mHeight / 2);
15170da918464276b110c43868caa272c97baadb89eDoris Liu        } else {
15270da918464276b110c43868caa272c97baadb89eDoris Liu            matrix.setScale(scaleX, scaleY, (float) mWidth / 2, 0.0f);
15370da918464276b110c43868caa272c97baadb89eDoris Liu        }
15470da918464276b110c43868caa272c97baadb89eDoris Liu        mPreview.setTransform(matrix);
15570da918464276b110c43868caa272c97baadb89eDoris Liu        onPreviewSizeChanged(scaledTextureWidth, scaledTextureHeight);
15670da918464276b110c43868caa272c97baadb89eDoris Liu    }
15770da918464276b110c43868caa272c97baadb89eDoris Liu
15870da918464276b110c43868caa272c97baadb89eDoris Liu    private void onPreviewSizeChanged(float scaledTextureWidth, float scaledTextureHeight) {
15970da918464276b110c43868caa272c97baadb89eDoris Liu        // Notify listeners of preview size change
16015b9961c5ca49fc6ac9d036e7a967797987e46eeDoris Liu        for (PreviewStatusListener.PreviewAreaSizeChangedListener listener
16115b9961c5ca49fc6ac9d036e7a967797987e46eeDoris Liu                : mPreviewSizeChangedListeners) {
162482de029dc20e0a577388a602985fb31c3200309Doris Liu            listener.onPreviewAreaSizeChanged(scaledTextureWidth, scaledTextureHeight);
16370da918464276b110c43868caa272c97baadb89eDoris Liu        }
16470da918464276b110c43868caa272c97baadb89eDoris Liu    }
16570da918464276b110c43868caa272c97baadb89eDoris Liu
16670da918464276b110c43868caa272c97baadb89eDoris Liu    /**
167482de029dc20e0a577388a602985fb31c3200309Doris Liu     * Adds a listener that will get notified when the preview size changed. This
16870da918464276b110c43868caa272c97baadb89eDoris Liu     * can be useful for UI elements or focus overlay to adjust themselves according
16970da918464276b110c43868caa272c97baadb89eDoris Liu     * to the preview size change.
17070da918464276b110c43868caa272c97baadb89eDoris Liu     *
17115b9961c5ca49fc6ac9d036e7a967797987e46eeDoris Liu     * Note that a listener will only be added once. A newly added listener will receive
17215b9961c5ca49fc6ac9d036e7a967797987e46eeDoris Liu     * a notification of current preview size immediately after being added.
17315b9961c5ca49fc6ac9d036e7a967797987e46eeDoris Liu     *
17415b9961c5ca49fc6ac9d036e7a967797987e46eeDoris Liu     * This function should be called on the UI thread and listeners will be notified
17515b9961c5ca49fc6ac9d036e7a967797987e46eeDoris Liu     * on the UI thread.
17615b9961c5ca49fc6ac9d036e7a967797987e46eeDoris Liu     *
17770da918464276b110c43868caa272c97baadb89eDoris Liu     * @param listener the listener that will get notified of preview size change
17870da918464276b110c43868caa272c97baadb89eDoris Liu     */
179482de029dc20e0a577388a602985fb31c3200309Doris Liu    public void addPreviewAreaSizeChangedListener(
180482de029dc20e0a577388a602985fb31c3200309Doris Liu            PreviewStatusListener.PreviewAreaSizeChangedListener listener) {
181482de029dc20e0a577388a602985fb31c3200309Doris Liu        if (listener != null && !mPreviewSizeChangedListeners.contains(listener)) {
182482de029dc20e0a577388a602985fb31c3200309Doris Liu            mPreviewSizeChangedListeners.add(listener);
183482de029dc20e0a577388a602985fb31c3200309Doris Liu            listener.onPreviewAreaSizeChanged(mWidth, mHeight);
184482de029dc20e0a577388a602985fb31c3200309Doris Liu        }
185482de029dc20e0a577388a602985fb31c3200309Doris Liu    }
186482de029dc20e0a577388a602985fb31c3200309Doris Liu
187482de029dc20e0a577388a602985fb31c3200309Doris Liu    /**
188482de029dc20e0a577388a602985fb31c3200309Doris Liu     * Removes a listener that gets notified when the preview size changed.
189482de029dc20e0a577388a602985fb31c3200309Doris Liu     *
190482de029dc20e0a577388a602985fb31c3200309Doris Liu     * @param listener the listener that gets notified of preview size change
191482de029dc20e0a577388a602985fb31c3200309Doris Liu     */
192482de029dc20e0a577388a602985fb31c3200309Doris Liu    public void removePreviewAreaSizeChangedListener(
193482de029dc20e0a577388a602985fb31c3200309Doris Liu            PreviewStatusListener.PreviewAreaSizeChangedListener listener) {
194482de029dc20e0a577388a602985fb31c3200309Doris Liu        if (listener != null && mPreviewSizeChangedListeners.contains(listener)) {
195482de029dc20e0a577388a602985fb31c3200309Doris Liu            mPreviewSizeChangedListeners.remove(listener);
196482de029dc20e0a577388a602985fb31c3200309Doris Liu        }
19770da918464276b110c43868caa272c97baadb89eDoris Liu    }
19870da918464276b110c43868caa272c97baadb89eDoris Liu
19970da918464276b110c43868caa272c97baadb89eDoris Liu    @Override
20070da918464276b110c43868caa272c97baadb89eDoris Liu    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
20170da918464276b110c43868caa272c97baadb89eDoris Liu        // Workaround for b/11168275, see b/10981460 for more details
20270da918464276b110c43868caa272c97baadb89eDoris Liu        if (mWidth != 0 && mHeight != 0) {
20370da918464276b110c43868caa272c97baadb89eDoris Liu            // Re-apply transform matrix for new surface texture
20470da918464276b110c43868caa272c97baadb89eDoris Liu            updateTransform();
20570da918464276b110c43868caa272c97baadb89eDoris Liu        }
20670da918464276b110c43868caa272c97baadb89eDoris Liu        if (mSurfaceTextureListener != null) {
20770da918464276b110c43868caa272c97baadb89eDoris Liu            mSurfaceTextureListener.onSurfaceTextureAvailable(surface, width, height);
20870da918464276b110c43868caa272c97baadb89eDoris Liu        }
20970da918464276b110c43868caa272c97baadb89eDoris Liu    }
21070da918464276b110c43868caa272c97baadb89eDoris Liu
21170da918464276b110c43868caa272c97baadb89eDoris Liu    @Override
21270da918464276b110c43868caa272c97baadb89eDoris Liu    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
21370da918464276b110c43868caa272c97baadb89eDoris Liu        if (mSurfaceTextureListener != null) {
21470da918464276b110c43868caa272c97baadb89eDoris Liu            mSurfaceTextureListener.onSurfaceTextureSizeChanged(surface, width, height);
21570da918464276b110c43868caa272c97baadb89eDoris Liu        }
21670da918464276b110c43868caa272c97baadb89eDoris Liu    }
21770da918464276b110c43868caa272c97baadb89eDoris Liu
21870da918464276b110c43868caa272c97baadb89eDoris Liu    @Override
21970da918464276b110c43868caa272c97baadb89eDoris Liu    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
22070da918464276b110c43868caa272c97baadb89eDoris Liu        if (mSurfaceTextureListener != null) {
22170da918464276b110c43868caa272c97baadb89eDoris Liu            mSurfaceTextureListener.onSurfaceTextureDestroyed(surface);
22270da918464276b110c43868caa272c97baadb89eDoris Liu        }
22370da918464276b110c43868caa272c97baadb89eDoris Liu        return false;
22470da918464276b110c43868caa272c97baadb89eDoris Liu    }
22570da918464276b110c43868caa272c97baadb89eDoris Liu
22670da918464276b110c43868caa272c97baadb89eDoris Liu    @Override
22770da918464276b110c43868caa272c97baadb89eDoris Liu    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
22870da918464276b110c43868caa272c97baadb89eDoris Liu        if (mSurfaceTextureListener != null) {
22970da918464276b110c43868caa272c97baadb89eDoris Liu            mSurfaceTextureListener.onSurfaceTextureUpdated(surface);
23070da918464276b110c43868caa272c97baadb89eDoris Liu        }
23170da918464276b110c43868caa272c97baadb89eDoris Liu
23270da918464276b110c43868caa272c97baadb89eDoris Liu    }
23370da918464276b110c43868caa272c97baadb89eDoris Liu}
234