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 17package com.android.camera; 18 19import android.app.AlertDialog; 20import android.app.ProgressDialog; 21import android.content.Context; 22import android.content.DialogInterface; 23import android.content.res.Configuration; 24import android.content.res.Resources; 25import android.graphics.Bitmap; 26import android.graphics.Canvas; 27import android.graphics.Matrix; 28import android.graphics.Point; 29import android.graphics.Rect; 30import android.graphics.SurfaceTexture; 31import android.graphics.drawable.BitmapDrawable; 32import android.graphics.drawable.Drawable; 33import android.util.Log; 34import android.view.LayoutInflater; 35import android.view.TextureView; 36import android.view.View; 37import android.view.ViewGroup; 38import android.widget.FrameLayout; 39import android.widget.ImageView; 40import android.widget.LinearLayout; 41import android.widget.TextView; 42 43import com.android.camera.ui.CameraControls; 44import com.android.camera.ui.CameraRootView; 45import com.android.camera.ui.ModuleSwitcher; 46import com.android.camera.util.CameraUtil; 47import com.android.camera2.R; 48 49/** 50 * The UI of {@link WideAnglePanoramaModule}. 51 */ 52public 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