17c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala/*
27c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala * Copyright (C) 2014 The Android Open Source Project
37c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala *
47c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala * Licensed under the Apache License, Version 2.0 (the "License");
57c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala * you may not use this file except in compliance with the License.
67c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala * You may obtain a copy of the License at
77c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala *
87c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala *      http://www.apache.org/licenses/LICENSE-2.0
97c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala *
107c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala * Unless required by applicable law or agreed to in writing, software
117c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala * distributed under the License is distributed on an "AS IS" BASIS,
127c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala * See the License for the specific language governing permissions and
147c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala * limitations under the License.
157c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala */
167c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
177c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvalapackage com.android.testingcamera2;
187c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
197c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvalaimport java.util.ArrayList;
207c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvalaimport java.util.List;
217c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
227c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvalaimport android.content.Context;
237c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvalaimport android.hardware.camera2.CameraCharacteristics;
24684d44a36b4ac32e2e162caf9ce4e625bd13b555Igor Murashkinimport android.hardware.camera2.params.StreamConfigurationMap;
25e82937211409e869f32c8398ee16ce6bb77bfce6Igor Murashkinimport android.util.Size;
267c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvalaimport android.util.AttributeSet;
270b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvalaimport android.util.Size;
287c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvalaimport android.view.LayoutInflater;
297c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvalaimport android.view.Surface;
307c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvalaimport android.view.SurfaceHolder;
317c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvalaimport android.view.SurfaceView;
327c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvalaimport android.view.View;
337c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvalaimport android.widget.AdapterView;
347c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvalaimport android.widget.ArrayAdapter;
357c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvalaimport android.widget.LinearLayout;
367c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvalaimport android.widget.Spinner;
377c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvalaimport android.widget.AdapterView.OnItemSelectedListener;
387c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
397c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvalapublic class SurfaceViewSubPane extends TargetSubPane implements SurfaceHolder.Callback {
407c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
417c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    private static final int NO_SIZE = -1;
420b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala    private final FixedAspectSurfaceView mFixedSurfaceView;
437c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    private Surface mSurface;
447c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
45684d44a36b4ac32e2e162caf9ce4e625bd13b555Igor Murashkin    private final Spinner mSizeSpinner;
467c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    private Size[] mSizes;
470b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala    private int mCurrentCameraOrientation = 0;
480b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala    private int mCurrentUiOrientation = 0;
490b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala
507c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    private int mCurrentSizeId = NO_SIZE;
517c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    private CameraControlPane mCurrentCamera;
527c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
530b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala    private float mAspectRatio = DEFAULT_ASPECT_RATIO;
540b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala
550b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala    private static final float DEFAULT_ASPECT_RATIO = 1.5f;
560b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala
577c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    public SurfaceViewSubPane(Context context, AttributeSet attrs) {
587c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        super(context, attrs);
597c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
607c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        LayoutInflater inflater =
617c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
627c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
637c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        inflater.inflate(R.layout.surfaceview_target_subpane, this);
647c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        this.setOrientation(VERTICAL);
657c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
660b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala        mFixedSurfaceView = (FixedAspectSurfaceView) this.findViewById(R.id.target_subpane_surface_view_view);
670b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala        mFixedSurfaceView.getHolder().addCallback(this);
687c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        mSizeSpinner = (Spinner) this.findViewById(R.id.target_subpane_surface_view_size_spinner);
697c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        mSizeSpinner.setOnItemSelectedListener(mSizeSpinnerListener);
707c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    }
717c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
727c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    @Override
737c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    public void setTargetCameraPane(CameraControlPane target) {
747c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        if (target != null) {
757c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            Size oldSize = null;
767c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            if (mCurrentSizeId != NO_SIZE) {
777c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala                oldSize = mSizes[mCurrentSizeId];
787c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            }
797c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
800b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala            CameraCharacteristics info = target.getCharacteristics();
81e82937211409e869f32c8398ee16ce6bb77bfce6Igor Murashkin            {
82e82937211409e869f32c8398ee16ce6bb77bfce6Igor Murashkin                StreamConfigurationMap streamConfigMap =
83e82937211409e869f32c8398ee16ce6bb77bfce6Igor Murashkin                        info.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
84e82937211409e869f32c8398ee16ce6bb77bfce6Igor Murashkin                mSizes = streamConfigMap.getOutputSizes(SurfaceHolder.class);
857c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            }
86684d44a36b4ac32e2e162caf9ce4e625bd13b555Igor Murashkin
877c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            int newSelectionId = 0;
887c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            for (int i = 0; i < mSizes.length; i++) {
897c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala                if (mSizes[i].equals(oldSize)) {
907c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala                    newSelectionId = i;
917c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala                    break;
927c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala                }
937c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            }
947c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            String[] outputSizeItems = new String[mSizes.length];
957c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            for (int i = 0; i < outputSizeItems.length; i++) {
967c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala                outputSizeItems[i] = mSizes[i].toString();
977c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            }
987c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
997c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            mSizeSpinner.setAdapter(new ArrayAdapter<String>(getContext(), R.layout.spinner_item,
1007c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala                    outputSizeItems));
1017c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            mSizeSpinner.setSelection(newSelectionId);
1020b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala
1030b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala            // Map sensor orientation to Surface.ROTATE_* constants
1040b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala            final int SENSOR_ORIENTATION_TO_SURFACE_ROTATE = 90;
1050b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala            mCurrentCameraOrientation = info.get(CameraCharacteristics.SENSOR_ORIENTATION) /
1060b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala                    SENSOR_ORIENTATION_TO_SURFACE_ROTATE;
1070b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala
1080b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala            updateAspectRatio();
1097c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        } else {
1107c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            mSizeSpinner.setAdapter(null);
1117c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            mCurrentSizeId = NO_SIZE;
1127c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        }
1137c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    }
1147c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
1150b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala    @Override
1160b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala    public void setUiOrientation(int orientation) {
1170b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala        mCurrentUiOrientation = orientation;
1180b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala        updateAspectRatio();
1190b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala    }
1200b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala
1217c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    private void updateSizes() {
1227c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        if (mCurrentSizeId != NO_SIZE) {
1237c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            Size s = mSizes[mCurrentSizeId];
1240b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala            mFixedSurfaceView.getHolder().setFixedSize(s.getWidth(), s.getHeight());
1250b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala            mAspectRatio = ((float) s.getWidth()) / s.getHeight();
1267c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        } else {
1277c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            // Make sure the view has some reasonable size even when there's no
1287c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            // target camera for aspect-ratio correct sizing
1290b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala            mAspectRatio = DEFAULT_ASPECT_RATIO;
1300b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala        }
1310b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala        updateAspectRatio();
1320b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala    }
1330b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala
1340b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala    private void updateAspectRatio() {
1350b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala        // Swap aspect ratios when the UI orientation and the camera orientation don't line up
1360b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala        boolean swapAspect = Math.abs(mCurrentUiOrientation - mCurrentCameraOrientation) % 2 == 1;
1370b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala        if (swapAspect) {
1380b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala            mFixedSurfaceView.setAspectRatio(1/mAspectRatio);
1390b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala        } else {
1400b631a014c90c9053a8e17be141351abab66d30eEino-Ville Talvala            mFixedSurfaceView.setAspectRatio(mAspectRatio);
1417c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        }
1427c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    }
1437c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
1447c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    @Override
1457c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    public Surface getOutputSurface() {
1467c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        return mSurface;
1477c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    }
1487c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
149684d44a36b4ac32e2e162caf9ce4e625bd13b555Igor Murashkin    private final OnItemSelectedListener mSizeSpinnerListener = new OnItemSelectedListener() {
1507c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        @Override
1517c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
1527c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            mCurrentSizeId = pos;
1537c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            updateSizes();
1547c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        };
1557c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
1567c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        @Override
1577c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        public void onNothingSelected(AdapterView<?> parent) {
1587c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala            mCurrentSizeId = NO_SIZE;
1597c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        };
1607c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    };
1617c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
1627c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    @Override
1637c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
1647c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala        mSurface = holder.getSurface();
1657c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    }
1667c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
1677c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    @Override
1687c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    public void surfaceCreated(SurfaceHolder holder) {
1697c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
1707c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    }
1717c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
1727c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    @Override
1737c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    public void surfaceDestroyed(SurfaceHolder holder) {
1747c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
1757c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala    }
1767c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala
1777c5f2935442e974ce30158e35d142f7e2c3ee1a2Eino-Ville Talvala}
178