Home | History | Annotate | Download | only in camera
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.camera;
     18 
     19 import android.app.AlertDialog;
     20 import android.app.ProgressDialog;
     21 import android.content.Context;
     22 import android.content.DialogInterface;
     23 import android.content.res.Configuration;
     24 import android.content.res.Resources;
     25 import android.graphics.Bitmap;
     26 import android.graphics.Canvas;
     27 import android.graphics.Matrix;
     28 import android.graphics.Point;
     29 import android.graphics.Rect;
     30 import android.graphics.SurfaceTexture;
     31 import android.graphics.drawable.BitmapDrawable;
     32 import android.graphics.drawable.Drawable;
     33 import android.util.Log;
     34 import android.view.LayoutInflater;
     35 import android.view.TextureView;
     36 import android.view.View;
     37 import android.view.ViewGroup;
     38 import android.widget.FrameLayout;
     39 import android.widget.ImageView;
     40 import android.widget.LinearLayout;
     41 import android.widget.TextView;
     42 
     43 import com.android.camera.ui.CameraControls;
     44 import com.android.camera.ui.CameraRootView;
     45 import com.android.camera.ui.ModuleSwitcher;
     46 import com.android.camera.util.CameraUtil;
     47 import com.android.camera2.R;
     48 
     49 /**
     50  * The UI of {@link WideAnglePanoramaModule}.
     51  */
     52 public class WideAnglePanoramaUI implements
     53         TextureView.SurfaceTextureListener,
     54         ShutterButton.OnShutterButtonListener,
     55         CameraRootView.MyDisplayListener,
     56         View.OnLayoutChangeListener {
     57 
     58     @SuppressWarnings("unused")
     59     private static final String TAG = "CAM_WidePanoramaUI";
     60 
     61     private CameraActivity mActivity;
     62     private WideAnglePanoramaController mController;
     63 
     64     private ViewGroup mRootView;
     65     private ModuleSwitcher mSwitcher;
     66     private FrameLayout mCaptureLayout;
     67     private View mReviewLayout;
     68     private ImageView mReview;
     69     private View mPreviewBorder;
     70     private View mLeftIndicator;
     71     private View mRightIndicator;
     72     private View mCaptureIndicator;
     73     private PanoProgressBar mCaptureProgressBar;
     74     private PanoProgressBar mSavingProgressBar;
     75     private TextView mTooFastPrompt;
     76     private View mPreviewLayout;
     77     private ViewGroup mReviewControl;
     78     private TextureView mTextureView;
     79     private ShutterButton mShutterButton;
     80     private CameraControls mCameraControls;
     81 
     82     private Matrix mProgressDirectionMatrix = new Matrix();
     83     private float[] mProgressAngle = new float[2];
     84 
     85     private DialogHelper mDialogHelper;
     86 
     87     // Color definitions.
     88     private int mIndicatorColor;
     89     private int mIndicatorColorFast;
     90     private int mReviewBackground;
     91     private SurfaceTexture mSurfaceTexture;
     92     private View mPreviewCover;
     93 
     94     /** Constructor. */
     95     public WideAnglePanoramaUI(
     96             CameraActivity activity,
     97             WideAnglePanoramaController controller,
     98             ViewGroup root) {
     99         mActivity = activity;
    100         mController = controller;
    101         mRootView = root;
    102 
    103         createContentView();
    104         mSwitcher = (ModuleSwitcher) mRootView.findViewById(R.id.camera_switcher);
    105         mSwitcher.setCurrentIndex(ModuleSwitcher.WIDE_ANGLE_PANO_MODULE_INDEX);
    106         mSwitcher.setSwitchListener(mActivity);
    107     }
    108 
    109     public void onStartCapture() {
    110         hideSwitcher();
    111         mShutterButton.setImageResource(R.drawable.btn_shutter_recording);
    112         mCaptureIndicator.setVisibility(View.VISIBLE);
    113         showDirectionIndicators(PanoProgressBar.DIRECTION_NONE);
    114     }
    115 
    116     public void showPreviewUI() {
    117         mCaptureLayout.setVisibility(View.VISIBLE);
    118         showUI();
    119     }
    120 
    121     public void onStopCapture() {
    122         mCaptureIndicator.setVisibility(View.INVISIBLE);
    123         hideTooFastIndication();
    124         hideDirectionIndicators();
    125     }
    126 
    127     public void hideSwitcher() {
    128         mSwitcher.closePopup();
    129         mSwitcher.setVisibility(View.INVISIBLE);
    130     }
    131 
    132     public void hideUI() {
    133         hideSwitcher();
    134         mCameraControls.setVisibility(View.INVISIBLE);
    135     }
    136 
    137     public void showUI() {
    138         showSwitcher();
    139         mCameraControls.setVisibility(View.VISIBLE);
    140     }
    141 
    142     public void onPreviewFocusChanged(boolean previewFocused) {
    143         if (previewFocused) {
    144             showUI();
    145         } else {
    146             hideUI();
    147         }
    148     }
    149 
    150     public boolean arePreviewControlsVisible() {
    151         return (mCameraControls.getVisibility() == View.VISIBLE);
    152     }
    153 
    154     public void showSwitcher() {
    155         mSwitcher.setVisibility(View.VISIBLE);
    156     }
    157 
    158     public void setCaptureProgressOnDirectionChangeListener(
    159             PanoProgressBar.OnDirectionChangeListener listener) {
    160         mCaptureProgressBar.setOnDirectionChangeListener(listener);
    161     }
    162 
    163     public void resetCaptureProgress() {
    164         mCaptureProgressBar.reset();
    165     }
    166 
    167     public void setMaxCaptureProgress(int max) {
    168         mCaptureProgressBar.setMaxProgress(max);
    169     }
    170 
    171     public void showCaptureProgress() {
    172         mCaptureProgressBar.setVisibility(View.VISIBLE);
    173     }
    174 
    175     public void updateCaptureProgress(
    176             float panningRateXInDegree, float panningRateYInDegree,
    177             float progressHorizontalAngle, float progressVerticalAngle,
    178             float maxPanningSpeed) {
    179 
    180         if ((Math.abs(panningRateXInDegree) > maxPanningSpeed)
    181                 || (Math.abs(panningRateYInDegree) > maxPanningSpeed)) {
    182             showTooFastIndication();
    183         } else {
    184             hideTooFastIndication();
    185         }
    186 
    187         // progressHorizontalAngle and progressVerticalAngle are relative to the
    188         // camera. Convert them to UI direction.
    189         mProgressAngle[0] = progressHorizontalAngle;
    190         mProgressAngle[1] = progressVerticalAngle;
    191         mProgressDirectionMatrix.mapPoints(mProgressAngle);
    192 
    193         int angleInMajorDirection =
    194                 (Math.abs(mProgressAngle[0]) > Math.abs(mProgressAngle[1]))
    195                         ? (int) mProgressAngle[0]
    196                         : (int) mProgressAngle[1];
    197         mCaptureProgressBar.setProgress((angleInMajorDirection));
    198     }
    199 
    200     public void setProgressOrientation(int orientation) {
    201         mProgressDirectionMatrix.reset();
    202         mProgressDirectionMatrix.postRotate(orientation);
    203     }
    204 
    205     public void showDirectionIndicators(int direction) {
    206         switch (direction) {
    207             case PanoProgressBar.DIRECTION_NONE:
    208                 mLeftIndicator.setVisibility(View.VISIBLE);
    209                 mRightIndicator.setVisibility(View.VISIBLE);
    210                 break;
    211             case PanoProgressBar.DIRECTION_LEFT:
    212                 mLeftIndicator.setVisibility(View.VISIBLE);
    213                 mRightIndicator.setVisibility(View.INVISIBLE);
    214                 break;
    215             case PanoProgressBar.DIRECTION_RIGHT:
    216                 mLeftIndicator.setVisibility(View.INVISIBLE);
    217                 mRightIndicator.setVisibility(View.VISIBLE);
    218                 break;
    219         }
    220     }
    221 
    222     public SurfaceTexture getSurfaceTexture() {
    223         return mSurfaceTexture;
    224     }
    225 
    226     @Override
    227     public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i2) {
    228         mSurfaceTexture = surfaceTexture;
    229         mController.onPreviewUIReady();
    230     }
    231 
    232     @Override
    233     public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i2) {
    234 
    235     }
    236 
    237     @Override
    238     public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
    239         mController.onPreviewUIDestroyed();
    240         mSurfaceTexture = null;
    241         Log.d(TAG, "surfaceTexture is destroyed");
    242         return true;
    243     }
    244 
    245     @Override
    246     public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
    247         // Make sure preview cover is hidden if preview data is available.
    248         if (mPreviewCover.getVisibility() != View.GONE) {
    249             mPreviewCover.setVisibility(View.GONE);
    250         }
    251     }
    252 
    253     private void hideDirectionIndicators() {
    254         mLeftIndicator.setVisibility(View.INVISIBLE);
    255         mRightIndicator.setVisibility(View.INVISIBLE);
    256     }
    257 
    258     public Point getPreviewAreaSize() {
    259         return new Point(
    260                 mTextureView.getWidth(), mTextureView.getHeight());
    261     }
    262 
    263     public void reset() {
    264         mShutterButton.setImageResource(R.drawable.btn_new_shutter);
    265         mReviewLayout.setVisibility(View.GONE);
    266         mCaptureProgressBar.setVisibility(View.INVISIBLE);
    267     }
    268 
    269     public void showFinalMosaic(Bitmap bitmap, int orientation) {
    270         if (bitmap != null && orientation != 0) {
    271             Matrix rotateMatrix = new Matrix();
    272             rotateMatrix.setRotate(orientation);
    273             bitmap = Bitmap.createBitmap(
    274                     bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
    275                     rotateMatrix, false);
    276         }
    277 
    278         mReview.setImageBitmap(bitmap);
    279         mCaptureLayout.setVisibility(View.GONE);
    280         mReviewLayout.setVisibility(View.VISIBLE);
    281     }
    282 
    283     public void onConfigurationChanged(
    284             Configuration newConfig, boolean threadRunning) {
    285         Drawable lowResReview = null;
    286         if (threadRunning) lowResReview = mReview.getDrawable();
    287 
    288         // Change layout in response to configuration change
    289         LayoutInflater inflater = (LayoutInflater)
    290                 mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    291 
    292         mReviewControl.removeAllViews();
    293         inflater.inflate(R.layout.pano_review_control, mReviewControl, true);
    294 
    295         mRootView.bringChildToFront(mCameraControls);
    296         setViews(mActivity.getResources());
    297         if (threadRunning) {
    298             mReview.setImageDrawable(lowResReview);
    299             mCaptureLayout.setVisibility(View.GONE);
    300             mReviewLayout.setVisibility(View.VISIBLE);
    301         }
    302     }
    303 
    304     public void resetSavingProgress() {
    305         mSavingProgressBar.reset();
    306         mSavingProgressBar.setRightIncreasing(true);
    307     }
    308 
    309     public void updateSavingProgress(int progress) {
    310         mSavingProgressBar.setProgress(progress);
    311     }
    312 
    313     @Override
    314     public void onShutterButtonFocus(boolean pressed) {
    315         // Do nothing.
    316     }
    317 
    318     @Override
    319     public void onShutterButtonClick() {
    320         mController.onShutterButtonClick();
    321     }
    322 
    323     @Override
    324     public void onLayoutChange(
    325             View v, int l, int t, int r, int b,
    326             int oldl, int oldt, int oldr, int oldb) {
    327         mController.onPreviewUILayoutChange(l, t, r, b);
    328     }
    329 
    330     public void showAlertDialog(
    331             String title, String failedString,
    332             String OKString, Runnable runnable) {
    333         mDialogHelper.showAlertDialog(title, failedString, OKString, runnable);
    334     }
    335 
    336     public void showWaitingDialog(String title) {
    337         mDialogHelper.showWaitingDialog(title);
    338     }
    339 
    340     public void dismissAllDialogs() {
    341         mDialogHelper.dismissAll();
    342     }
    343 
    344     private void createContentView() {
    345         LayoutInflater inflator = (LayoutInflater) mActivity
    346                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    347         inflator.inflate(R.layout.panorama_module, mRootView, true);
    348 
    349         Resources appRes = mActivity.getResources();
    350         mIndicatorColor = appRes.getColor(R.color.pano_progress_indication);
    351         mReviewBackground = appRes.getColor(R.color.review_background);
    352         mIndicatorColorFast = appRes.getColor(R.color.pano_progress_indication_fast);
    353 
    354         mPreviewCover = mRootView.findViewById(R.id.preview_cover);
    355         mPreviewLayout = mRootView.findViewById(R.id.pano_preview_layout);
    356         mReviewControl = (ViewGroup) mRootView.findViewById(R.id.pano_review_control);
    357         mReviewLayout = mRootView.findViewById(R.id.pano_review_layout);
    358         mReview = (ImageView) mRootView.findViewById(R.id.pano_reviewarea);
    359         mCaptureLayout = (FrameLayout) mRootView.findViewById(R.id.panorama_capture_layout);
    360         mCaptureProgressBar = (PanoProgressBar) mRootView.findViewById(R.id.pano_pan_progress_bar);
    361         mCaptureProgressBar.setBackgroundColor(appRes.getColor(R.color.pano_progress_empty));
    362         mCaptureProgressBar.setDoneColor(appRes.getColor(R.color.pano_progress_done));
    363         mCaptureProgressBar.setIndicatorColor(mIndicatorColor);
    364         mCaptureProgressBar.setIndicatorWidth(20);
    365 
    366         mPreviewBorder = mCaptureLayout.findViewById(R.id.pano_preview_area_border);
    367 
    368         mLeftIndicator = mRootView.findViewById(R.id.pano_pan_left_indicator);
    369         mRightIndicator = mRootView.findViewById(R.id.pano_pan_right_indicator);
    370         mLeftIndicator.setEnabled(false);
    371         mRightIndicator.setEnabled(false);
    372         mTooFastPrompt = (TextView) mRootView.findViewById(R.id.pano_capture_too_fast_textview);
    373         mCaptureIndicator = mRootView.findViewById(R.id.pano_capture_indicator);
    374 
    375         mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button);
    376         mShutterButton.setImageResource(R.drawable.btn_new_shutter);
    377         mShutterButton.setOnShutterButtonListener(this);
    378         // Hide menu and indicators.
    379         mRootView.findViewById(R.id.menu).setVisibility(View.GONE);
    380         mRootView.findViewById(R.id.on_screen_indicators).setVisibility(View.GONE);
    381         mReview.setBackgroundColor(mReviewBackground);
    382 
    383         // TODO: set display change listener properly.
    384         ((CameraRootView) mRootView).setDisplayChangeListener(null);
    385         mTextureView = (TextureView) mRootView.findViewById(R.id.pano_preview_textureview);
    386         mTextureView.setSurfaceTextureListener(this);
    387         mTextureView.addOnLayoutChangeListener(this);
    388         mCameraControls = (CameraControls) mRootView.findViewById(R.id.camera_controls);
    389 
    390         mDialogHelper = new DialogHelper();
    391         setViews(appRes);
    392     }
    393 
    394     private void setViews(Resources appRes) {
    395         int weight = appRes.getInteger(R.integer.SRI_pano_layout_weight);
    396 
    397         LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mPreviewLayout.getLayoutParams();
    398         lp.weight = weight;
    399         mPreviewLayout.setLayoutParams(lp);
    400 
    401         lp = (LinearLayout.LayoutParams) mReview.getLayoutParams();
    402         lp.weight = weight;
    403         mPreviewLayout.setLayoutParams(lp);
    404 
    405         mSavingProgressBar = (PanoProgressBar) mRootView.findViewById(R.id.pano_saving_progress_bar);
    406         mSavingProgressBar.setIndicatorWidth(0);
    407         mSavingProgressBar.setMaxProgress(100);
    408         mSavingProgressBar.setBackgroundColor(appRes.getColor(R.color.pano_progress_empty));
    409         mSavingProgressBar.setDoneColor(appRes.getColor(R.color.pano_progress_indication));
    410 
    411         View cancelButton = mRootView.findViewById(R.id.pano_review_cancel_button);
    412         cancelButton.setOnClickListener(new View.OnClickListener() {
    413             @Override
    414             public void onClick(View arg0) {
    415                 mController.cancelHighResStitching();
    416             }
    417         });
    418 
    419 
    420     }
    421 
    422     private void showTooFastIndication() {
    423         mTooFastPrompt.setVisibility(View.VISIBLE);
    424         // The PreviewArea also contains the border for "too fast" indication.
    425         mPreviewBorder.setVisibility(View.VISIBLE);
    426         mCaptureProgressBar.setIndicatorColor(mIndicatorColorFast);
    427         mLeftIndicator.setEnabled(true);
    428         mRightIndicator.setEnabled(true);
    429     }
    430 
    431     private void hideTooFastIndication() {
    432         mTooFastPrompt.setVisibility(View.GONE);
    433         mPreviewBorder.setVisibility(View.INVISIBLE);
    434         mCaptureProgressBar.setIndicatorColor(mIndicatorColor);
    435         mLeftIndicator.setEnabled(false);
    436         mRightIndicator.setEnabled(false);
    437     }
    438 
    439     public void flipPreviewIfNeeded() {
    440         // Rotation needed to display image correctly clockwise
    441         int cameraOrientation = mController.getCameraOrientation();
    442         // Display rotated counter-clockwise
    443         int displayRotation = CameraUtil.getDisplayRotation(mActivity);
    444         // Rotation needed to display image correctly on current display
    445         int rotation = (cameraOrientation - displayRotation + 360) % 360;
    446         if (rotation >= 180) {
    447             mTextureView.setRotation(180);
    448         } else {
    449             mTextureView.setRotation(0);
    450         }
    451     }
    452 
    453     @Override
    454     public void onDisplayChanged() {
    455         mCameraControls.checkLayoutFlip();
    456         flipPreviewIfNeeded();
    457     }
    458 
    459     public void initDisplayChangeListener() {
    460         ((CameraRootView) mRootView).setDisplayChangeListener(this);
    461     }
    462 
    463     public void removeDisplayChangeListener() {
    464         ((CameraRootView) mRootView).removeDisplayChangeListener();
    465     }
    466 
    467     public void showPreviewCover() {
    468         mPreviewCover.setVisibility(View.VISIBLE);
    469     }
    470 
    471     private class DialogHelper {
    472         private ProgressDialog mProgressDialog;
    473         private AlertDialog mAlertDialog;
    474 
    475         DialogHelper() {
    476             mProgressDialog = null;
    477             mAlertDialog = null;
    478         }
    479 
    480         public void dismissAll() {
    481             if (mAlertDialog != null) {
    482                 mAlertDialog.dismiss();
    483                 mAlertDialog = null;
    484             }
    485             if (mProgressDialog != null) {
    486                 mProgressDialog.dismiss();
    487                 mProgressDialog = null;
    488             }
    489         }
    490 
    491         public void showAlertDialog(
    492                 CharSequence title, CharSequence message,
    493                 CharSequence buttonMessage, final Runnable buttonRunnable) {
    494             dismissAll();
    495             mAlertDialog = (new AlertDialog.Builder(mActivity))
    496                     .setTitle(title)
    497                     .setMessage(message)
    498                     .setNeutralButton(buttonMessage, new DialogInterface.OnClickListener() {
    499                         @Override
    500                         public void onClick(DialogInterface dialogInterface, int i) {
    501                             buttonRunnable.run();
    502                         }
    503                     })
    504                     .show();
    505         }
    506 
    507         public void showWaitingDialog(CharSequence message) {
    508             dismissAll();
    509             mProgressDialog = ProgressDialog.show(mActivity, null, message, true, false);
    510         }
    511     }
    512 
    513     private static class FlipBitmapDrawable extends BitmapDrawable {
    514 
    515         public FlipBitmapDrawable(Resources res, Bitmap bitmap) {
    516             super(res, bitmap);
    517         }
    518 
    519         @Override
    520         public void draw(Canvas canvas) {
    521             Rect bounds = getBounds();
    522             int cx = bounds.centerX();
    523             int cy = bounds.centerY();
    524             canvas.save(Canvas.MATRIX_SAVE_FLAG);
    525             canvas.rotate(180, cx, cy);
    526             super.draw(canvas);
    527             canvas.restore();
    528         }
    529     }
    530 }
    531