CameraActivity.java revision 3f969c1735e2636bf22dfe44104d0e99924cca97
1/* 2 * Copyright (C) 2012 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.ActionBar; 20import android.app.Activity; 21import android.content.BroadcastReceiver; 22import android.content.ComponentName; 23import android.content.ContentResolver; 24import android.content.Context; 25import android.content.Intent; 26import android.content.IntentFilter; 27import android.content.ServiceConnection; 28import android.content.pm.ActivityInfo; 29import android.content.res.Configuration; 30import android.graphics.drawable.ColorDrawable; 31import android.net.Uri; 32import android.os.AsyncTask; 33import android.os.Bundle; 34import android.os.Handler; 35import android.os.IBinder; 36import android.provider.MediaStore; 37import android.provider.Settings; 38import android.util.Log; 39import android.view.KeyEvent; 40import android.view.LayoutInflater; 41import android.view.Menu; 42import android.view.MenuInflater; 43import android.view.MenuItem; 44import android.view.OrientationEventListener; 45import android.view.View; 46import android.view.ViewGroup; 47import android.view.Window; 48import android.view.WindowManager; 49import android.widget.FrameLayout; 50import android.widget.ImageView; 51import android.widget.ProgressBar; 52import android.widget.ShareActionProvider; 53 54import com.android.camera.data.CameraDataAdapter; 55import com.android.camera.data.CameraPreviewData; 56import com.android.camera.data.FixedFirstDataAdapter; 57import com.android.camera.data.FixedLastDataAdapter; 58import com.android.camera.data.LocalData; 59import com.android.camera.data.LocalDataAdapter; 60import com.android.camera.data.MediaDetails; 61import com.android.camera.data.SimpleViewData; 62import com.android.camera.ui.CameraSwitcher; 63import com.android.camera.ui.CameraSwitcher.CameraSwitchListener; 64import com.android.camera.ui.DetailsDialog; 65import com.android.camera.ui.FilmStripView; 66import com.android.camera.util.ApiHelper; 67import com.android.camera.util.CameraUtil; 68import com.android.camera.util.PhotoSphereHelper; 69import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper; 70import com.android.camera2.R; 71 72public class CameraActivity extends Activity 73 implements CameraSwitchListener { 74 75 private static final String TAG = "CAM_Activity"; 76 77 private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE = 78 "android.media.action.STILL_IMAGE_CAMERA_SECURE"; 79 public static final String ACTION_IMAGE_CAPTURE_SECURE = 80 "android.media.action.IMAGE_CAPTURE_SECURE"; 81 public static final String ACTION_TRIM_VIDEO = 82 "com.android.camera.action.TRIM"; 83 public static final String MEDIA_ITEM_PATH = "media-item-path"; 84 85 // The intent extra for camera from secure lock screen. True if the gallery 86 // should only show newly captured pictures. sSecureAlbumId does not 87 // increment. This is used when switching between camera, camcorder, and 88 // panorama. If the extra is not set, it is in the normal camera mode. 89 public static final String SECURE_CAMERA_EXTRA = "secure_camera"; 90 91 // Supported operations at FilmStripView. Different data has different 92 // set of supported operations. 93 private static final int SUPPORT_DELETE = 1 << 0; 94 private static final int SUPPORT_ROTATE = 1 << 1; 95 private static final int SUPPORT_INFO = 1 << 2; 96 private static final int SUPPORT_CROP = 1 << 3; 97 private static final int SUPPORT_SETAS = 1 << 4; 98 private static final int SUPPORT_EDIT = 1 << 5; 99 private static final int SUPPORT_TRIM = 1 << 6; 100 private static final int SUPPORT_SHARE = 1 << 7; 101 private static final int SUPPORT_SHARE_PANORAMA360 = 1 << 8; 102 private static final int SUPPORT_SHOW_ON_MAP = 1 << 9; 103 private static final int SUPPORT_ALL = 0xffffffff; 104 105 /** This data adapter is used by FilmStripView. */ 106 private LocalDataAdapter mDataAdapter; 107 /** This data adapter represents the real local camera data. */ 108 private LocalDataAdapter mWrappedDataAdapter; 109 110 private PanoramaStitchingManager mPanoramaManager; 111 private int mCurrentModuleIndex; 112 private CameraModule mCurrentModule; 113 private FrameLayout mLayoutRoot; 114 private FrameLayout mAboveFilmstripControlLayout; 115 private View mCameraModuleRootView; 116 private FilmStripView mFilmStripView; 117 private ProgressBar mBottomProgress; 118 private View mPanoStitchingPanel; 119 private int mResultCodeForTesting; 120 private Intent mResultDataForTesting; 121 private OnScreenHint mStorageHint; 122 private long mStorageSpace = Storage.LOW_STORAGE_THRESHOLD; 123 private boolean mAutoRotateScreen; 124 private boolean mSecureCamera; 125 // This is a hack to speed up the start of SecureCamera. 126 private static boolean sFirstStartAfterScreenOn = true; 127 private boolean mShowCameraPreview; 128 private int mLastRawOrientation; 129 private MyOrientationEventListener mOrientationListener; 130 private Handler mMainHandler; 131 private PanoramaViewHelper mPanoramaViewHelper; 132 private CameraPreviewData mCameraPreviewData; 133 private ActionBar mActionBar; 134 private Menu mActionBarMenu; 135 private ViewGroup mUndoDeletionBar; 136 137 private ShareActionProvider mStandardShareActionProvider; 138 private Intent mStandardShareIntent; 139 private ShareActionProvider mPanoramaShareActionProvider; 140 private Intent mPanoramaShareIntent; 141 142 public void gotoGallery() { 143 mFilmStripView.getController().goToNextItem(); 144 } 145 146 private class MyOrientationEventListener 147 extends OrientationEventListener { 148 public MyOrientationEventListener(Context context) { 149 super(context); 150 } 151 152 @Override 153 public void onOrientationChanged(int orientation) { 154 // We keep the last known orientation. So if the user first orient 155 // the camera then point the camera to floor or sky, we still have 156 // the correct orientation. 157 if (orientation == ORIENTATION_UNKNOWN) return; 158 mLastRawOrientation = orientation; 159 mCurrentModule.onOrientationChanged(orientation); 160 } 161 } 162 163 private MediaSaveService mMediaSaveService; 164 private ServiceConnection mConnection = new ServiceConnection() { 165 @Override 166 public void onServiceConnected(ComponentName className, IBinder b) { 167 mMediaSaveService = ((MediaSaveService.LocalBinder) b).getService(); 168 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); 169 } 170 @Override 171 public void onServiceDisconnected(ComponentName className) { 172 if (mMediaSaveService != null) { 173 mMediaSaveService.setListener(null); 174 mMediaSaveService = null; 175 } 176 }}; 177 178 // close activity when screen turns off 179 private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { 180 @Override 181 public void onReceive(Context context, Intent intent) { 182 finish(); 183 } 184 }; 185 186 private static BroadcastReceiver sScreenOffReceiver; 187 private static class ScreenOffReceiver extends BroadcastReceiver { 188 @Override 189 public void onReceive(Context context, Intent intent) { 190 sFirstStartAfterScreenOn = true; 191 } 192 } 193 194 public static boolean isFirstStartAfterScreenOn() { 195 return sFirstStartAfterScreenOn; 196 } 197 198 public static void resetFirstStartAfterScreenOn() { 199 sFirstStartAfterScreenOn = false; 200 } 201 202 private FilmStripView.Listener mFilmStripListener = 203 new FilmStripView.Listener() { 204 @Override 205 public void onDataPromoted(int dataID) { 206 removeData(dataID); 207 } 208 209 @Override 210 public void onDataDemoted(int dataID) { 211 removeData(dataID); 212 } 213 214 @Override 215 public void onDataFullScreenChange(int dataID, boolean full) { 216 } 217 218 @Override 219 public void onSwitchMode(boolean toCamera) { 220 mCurrentModule.onSwitchMode(toCamera); 221 if (toCamera) { 222 mActionBar.hide(); 223 } else { 224 mActionBar.show(); 225 } 226 } 227 228 @Override 229 public void onCurrentDataChanged(int dataID, boolean current) { 230 if (!current) { 231 hidePanoStitchingProgress(); 232 } else { 233 LocalData currentData = mDataAdapter.getLocalData(dataID); 234 if (currentData == null) { 235 Log.w(TAG, "Current data ID not found."); 236 hidePanoStitchingProgress(); 237 return; 238 } 239 updateActionBarMenu(dataID); 240 241 Uri contentUri = currentData.getContentUri(); 242 if (contentUri == null) { 243 hidePanoStitchingProgress(); 244 return; 245 } 246 int panoStitchingProgress = mPanoramaManager.getTaskProgress(contentUri); 247 if (panoStitchingProgress < 0) { 248 hidePanoStitchingProgress(); 249 return; 250 } 251 showPanoStitchingProgress(); 252 updateStitchingProgress(panoStitchingProgress); 253 } 254 } 255 256 @Override 257 public boolean onToggleActionBarVisibility() { 258 if (mActionBar.isShowing()) { 259 mActionBar.hide(); 260 } else { 261 mActionBar.show(); 262 } 263 return mActionBar.isShowing(); 264 } 265 }; 266 267 private void hidePanoStitchingProgress() { 268 mPanoStitchingPanel.setVisibility(View.GONE); 269 } 270 271 private void showPanoStitchingProgress() { 272 mPanoStitchingPanel.setVisibility(View.VISIBLE); 273 } 274 275 private void updateStitchingProgress(int progress) { 276 mBottomProgress.setProgress(progress); 277 } 278 279 private void setStandardShareIntent(Uri contentUri, String mimeType) { 280 if (mStandardShareIntent == null) { 281 mStandardShareIntent = new Intent(Intent.ACTION_SEND); 282 } 283 mStandardShareIntent.setType(mimeType); 284 mStandardShareIntent.putExtra(Intent.EXTRA_STREAM, contentUri); 285 if (mStandardShareActionProvider != null) { 286 mStandardShareActionProvider.setShareIntent(mStandardShareIntent); 287 } 288 } 289 290 private void setPanoramaShareIntent(Uri contentUri) { 291 if (mPanoramaShareIntent == null) { 292 mPanoramaShareIntent = new Intent(Intent.ACTION_SEND); 293 } 294 mPanoramaShareIntent.setType("application/vnd.google.panorama360+jpg"); 295 mPanoramaShareIntent.putExtra(Intent.EXTRA_STREAM, contentUri); 296 if (mPanoramaShareActionProvider != null) { 297 mPanoramaShareActionProvider.setShareIntent(mPanoramaShareIntent); 298 } 299 } 300 301 /** 302 * According to the data type, make the menu items for supported operations 303 * visible. 304 * @param dataID the data ID of the current item. 305 */ 306 private void updateActionBarMenu(int dataID) { 307 LocalData currentData = mDataAdapter.getLocalData(dataID); 308 int type = currentData.getLocalDataType(); 309 310 if (mActionBarMenu == null) { 311 return; 312 } 313 314 int supported = 0; 315 switch (type) { 316 case LocalData.LOCAL_IMAGE: 317 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO 318 | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT 319 | SUPPORT_SHARE | SUPPORT_SHOW_ON_MAP; 320 break; 321 case LocalData.LOCAL_VIDEO: 322 supported |= SUPPORT_DELETE | SUPPORT_INFO | SUPPORT_TRIM 323 | SUPPORT_SHARE; 324 break; 325 case LocalData.LOCAL_PHOTO_SPHERE: 326 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO 327 | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT 328 | SUPPORT_SHARE | SUPPORT_SHOW_ON_MAP; 329 break; 330 case LocalData.LOCAL_360_PHOTO_SPHERE: 331 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO 332 | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT 333 | SUPPORT_SHARE | SUPPORT_SHARE_PANORAMA360 334 | SUPPORT_SHOW_ON_MAP; 335 break; 336 default: 337 break; 338 } 339 340 setMenuItemVisible(mActionBarMenu, R.id.action_delete, 341 (supported & SUPPORT_DELETE) != 0); 342 setMenuItemVisible(mActionBarMenu, R.id.action_rotate_ccw, 343 (supported & SUPPORT_ROTATE) != 0); 344 setMenuItemVisible(mActionBarMenu, R.id.action_rotate_cw, 345 (supported & SUPPORT_ROTATE) != 0); 346 setMenuItemVisible(mActionBarMenu, R.id.action_details, 347 (supported & SUPPORT_INFO) != 0); 348 setMenuItemVisible(mActionBarMenu, R.id.action_crop, 349 (supported & SUPPORT_CROP) != 0); 350 setMenuItemVisible(mActionBarMenu, R.id.action_setas, 351 (supported & SUPPORT_SETAS) != 0); 352 setMenuItemVisible(mActionBarMenu, R.id.action_edit, 353 (supported & SUPPORT_EDIT) != 0); 354 setMenuItemVisible(mActionBarMenu, R.id.action_trim, 355 (supported & SUPPORT_TRIM) != 0); 356 357 if ((supported & SUPPORT_SHARE) != 0) { 358 setMenuItemVisible(mActionBarMenu, R.id.action_share, true); 359 setStandardShareIntent(currentData.getContentUri(), currentData.getMimeType()); 360 } 361 if ((supported & SUPPORT_SHARE_PANORAMA360) != 0) { 362 setMenuItemVisible(mActionBarMenu, R.id.action_share_panorama, true); 363 setPanoramaShareIntent(currentData.getContentUri()); 364 } 365 366 boolean itemHasLocation = currentData.getLatLong() != null; 367 setMenuItemVisible(mActionBarMenu, R.id.action_show_on_map, 368 itemHasLocation && (supported & SUPPORT_SHOW_ON_MAP) != 0); 369 } 370 371 private void setMenuItemVisible(Menu menu, int itemId, boolean visible) { 372 MenuItem item = menu.findItem(itemId); 373 if (item != null) 374 item.setVisible(visible); 375 } 376 377 private Runnable mDeletionRunnable = new Runnable() { 378 @Override 379 public void run() { 380 hideUndoDeletionBar(); 381 mDataAdapter.executeDeletion(CameraActivity.this); 382 } 383 }; 384 385 private ImageTaskManager.TaskListener mStitchingListener = 386 new ImageTaskManager.TaskListener() { 387 @Override 388 public void onTaskQueued(String filePath, final Uri imageUri) { 389 mMainHandler.post(new Runnable() { 390 @Override 391 public void run() { 392 notifyNewMedia(imageUri); 393 } 394 }); 395 } 396 397 @Override 398 public void onTaskDone(String filePath, final Uri imageUri) { 399 Log.v(TAG, "onTaskDone:" + filePath); 400 mMainHandler.post(new Runnable() { 401 @Override 402 public void run() { 403 int doneID = mDataAdapter.findDataByContentUri(imageUri); 404 int currentDataId = mFilmStripView.getCurrentId(); 405 406 if (currentDataId == doneID) { 407 hidePanoStitchingProgress(); 408 updateStitchingProgress(0); 409 } 410 411 mDataAdapter.refresh(getContentResolver(), imageUri); 412 } 413 }); 414 } 415 416 @Override 417 public void onTaskProgress( 418 String filePath, final Uri imageUri, final int progress) { 419 mMainHandler.post(new Runnable() { 420 @Override 421 public void run() { 422 int currentDataId = mFilmStripView.getCurrentId(); 423 if (currentDataId == -1) { 424 return; 425 } 426 if (imageUri.equals( 427 mDataAdapter.getLocalData(currentDataId).getContentUri())) { 428 updateStitchingProgress(progress); 429 } 430 } 431 }); 432 } 433 }; 434 435 public MediaSaveService getMediaSaveService() { 436 return mMediaSaveService; 437 } 438 439 public void notifyNewMedia(Uri uri) { 440 ContentResolver cr = getContentResolver(); 441 String mimeType = cr.getType(uri); 442 if (mimeType.startsWith("video/")) { 443 sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri)); 444 mDataAdapter.addNewVideo(cr, uri); 445 } else if (mimeType.startsWith("image/")) { 446 CameraUtil.broadcastNewPicture(this, uri); 447 mDataAdapter.addNewPhoto(cr, uri); 448 } else if (mimeType.startsWith("application/stitching-preview")) { 449 mDataAdapter.addNewPhoto(cr, uri); 450 } else { 451 android.util.Log.w(TAG, "Unknown new media with MIME type:" 452 + mimeType + ", uri:" + uri); 453 } 454 } 455 456 private void removeData(int dataID) { 457 mDataAdapter.removeData(CameraActivity.this, dataID); 458 showUndoDeletionBar(); 459 mMainHandler.removeCallbacks(mDeletionRunnable); 460 mMainHandler.postDelayed(mDeletionRunnable, 3000); 461 } 462 463 private void bindMediaSaveService() { 464 Intent intent = new Intent(this, MediaSaveService.class); 465 bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 466 } 467 468 private void unbindMediaSaveService() { 469 if (mConnection != null) { 470 unbindService(mConnection); 471 } 472 } 473 474 @Override 475 public boolean onCreateOptionsMenu(Menu menu) { 476 // Inflate the menu items for use in the action bar 477 MenuInflater inflater = getMenuInflater(); 478 inflater.inflate(R.menu.operations, menu); 479 mActionBarMenu = menu; 480 481 // Configure the standard share action provider 482 MenuItem item = menu.findItem(R.id.action_share); 483 mStandardShareActionProvider = (ShareActionProvider) item.getActionProvider(); 484 if (mStandardShareIntent != null) { 485 mStandardShareActionProvider.setShareIntent(mStandardShareIntent); 486 } 487 488 // Configure the panorama share action provider 489 item = menu.findItem(R.id.action_share_panorama); 490 mPanoramaShareActionProvider = (ShareActionProvider) item.getActionProvider(); 491 if (mPanoramaShareIntent != null) { 492 mPanoramaShareActionProvider.setShareIntent(mPanoramaShareIntent); 493 } 494 495 return super.onCreateOptionsMenu(menu); 496 } 497 498 @Override 499 public boolean onOptionsItemSelected(MenuItem item) { 500 int currentDataId = mFilmStripView.getCurrentId(); 501 if (currentDataId < 0) { 502 return false; 503 } 504 final LocalData localData = mDataAdapter.getLocalData(currentDataId); 505 506 // Handle presses on the action bar items 507 switch (item.getItemId()) { 508 case android.R.id.home: 509 // ActionBar's Up/Home button was clicked 510 mFilmStripView.getController().goToFirstItem(); 511 return true; 512 case R.id.action_delete: 513 removeData(currentDataId); 514 return true; 515 case R.id.action_edit: 516 launchEditor(localData); 517 return true; 518 case R.id.action_trim: { 519 // This is going to be handled by the Gallery app. 520 Intent intent = new Intent(ACTION_TRIM_VIDEO); 521 LocalData currentData = mDataAdapter.getLocalData( 522 mFilmStripView.getCurrentId()); 523 intent.setData(currentData.getContentUri()); 524 // We need the file path to wrap this into a RandomAccessFile. 525 intent.putExtra(MEDIA_ITEM_PATH, currentData.getPath()); 526 startActivity(intent); 527 return true; 528 } 529 case R.id.action_rotate_ccw: 530 localData.rotate90Degrees(this, mDataAdapter, currentDataId, false); 531 return true; 532 case R.id.action_rotate_cw: 533 localData.rotate90Degrees(this, mDataAdapter, currentDataId, true); 534 return true; 535 case R.id.action_crop: 536 // TODO: add the functionality. 537 return true; 538 case R.id.action_setas: { 539 Intent intent = new Intent(Intent.ACTION_ATTACH_DATA) 540 .setDataAndType(localData.getContentUri(), 541 localData.getMimeType()) 542 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 543 intent.putExtra("mimeType", intent.getType()); 544 startActivity(Intent.createChooser( 545 intent, getString(R.string.set_as))); 546 return true; 547 } 548 case R.id.action_details: 549 (new AsyncTask<Void, Void, MediaDetails>() { 550 @Override 551 protected MediaDetails doInBackground(Void... params) { 552 return localData.getMediaDetails(CameraActivity.this); 553 } 554 555 @Override 556 protected void onPostExecute(MediaDetails mediaDetails) { 557 DetailsDialog.create(CameraActivity.this, mediaDetails).show(); 558 } 559 }).execute(); 560 return true; 561 case R.id.action_show_on_map: 562 double[] latLong = localData.getLatLong(); 563 if (latLong != null) { 564 CameraUtil.showOnMap(this, latLong); 565 } 566 return true; 567 default: 568 return super.onOptionsItemSelected(item); 569 } 570 } 571 572 @Override 573 public void onCreate(Bundle state) { 574 super.onCreate(state); 575 getWindow().requestFeature(Window.FEATURE_ACTION_BAR); 576 setContentView(R.layout.camera_filmstrip); 577 mActionBar = getActionBar(); 578 // Hide action bar first since we are in full screen mode first. 579 mActionBar.hide(); 580 581 if (ApiHelper.HAS_ROTATION_ANIMATION) { 582 setRotationAnimation(); 583 } 584 // Check if this is in the secure camera mode. 585 Intent intent = getIntent(); 586 String action = intent.getAction(); 587 if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action) 588 || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) { 589 mSecureCamera = true; 590 } else { 591 mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false); 592 } 593 594 if (mSecureCamera) { 595 // Change the window flags so that secure camera can show when locked 596 Window win = getWindow(); 597 WindowManager.LayoutParams params = win.getAttributes(); 598 params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; 599 win.setAttributes(params); 600 601 // Filter for screen off so that we can finish secure camera activity 602 // when screen is off. 603 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 604 registerReceiver(mScreenOffReceiver, filter); 605 // TODO: This static screen off event receiver is a workaround to the 606 // double onResume() invocation (onResume->onPause->onResume). We should 607 // find a better solution to this. 608 if (sScreenOffReceiver == null) { 609 sScreenOffReceiver = new ScreenOffReceiver(); 610 registerReceiver(sScreenOffReceiver, filter); 611 } 612 } 613 mLayoutRoot = (FrameLayout) findViewById(R.id.camera_layout_root); 614 615 mAboveFilmstripControlLayout = 616 (FrameLayout) findViewById(R.id.camera_above_filmstrip_layout); 617 mAboveFilmstripControlLayout.setFitsSystemWindows(true); 618 mAboveFilmstripControlLayout.setSystemUiVisibility( 619 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 620 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); 621 mPanoramaManager = new PanoramaStitchingManager(CameraActivity.this); 622 mPanoramaManager.addTaskListener(mStitchingListener); 623 LayoutInflater inflater = getLayoutInflater(); 624 View rootLayout = inflater.inflate(R.layout.camera, null, false); 625 mCameraModuleRootView = rootLayout.findViewById(R.id.camera_app_root); 626 mPanoStitchingPanel = findViewById(R.id.pano_stitching_progress_panel); 627 mBottomProgress = (ProgressBar) findViewById(R.id.pano_stitching_progress_bar); 628 mCameraPreviewData = new CameraPreviewData(rootLayout, 629 FilmStripView.ImageData.SIZE_FULL, 630 FilmStripView.ImageData.SIZE_FULL); 631 // Put a CameraPreviewData at the first position. 632 mWrappedDataAdapter = new FixedFirstDataAdapter( 633 new CameraDataAdapter(new ColorDrawable( 634 getResources().getColor(R.color.photo_placeholder))), 635 mCameraPreviewData); 636 mFilmStripView = (FilmStripView) findViewById(R.id.filmstrip_view); 637 mFilmStripView.setViewGap( 638 getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap)); 639 mPanoramaViewHelper = new PanoramaViewHelper(this); 640 mPanoramaViewHelper.onCreate(); 641 mFilmStripView.setPanoramaViewHelper(mPanoramaViewHelper); 642 // Set up the camera preview first so the preview shows up ASAP. 643 mFilmStripView.setListener(mFilmStripListener); 644 if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction()) 645 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) { 646 mCurrentModule = new VideoModule(); 647 mCurrentModuleIndex = CameraSwitcher.VIDEO_MODULE_INDEX; 648 } else { 649 mCurrentModule = new PhotoModule(); 650 } 651 mCurrentModule.init(this, mCameraModuleRootView); 652 mOrientationListener = new MyOrientationEventListener(this); 653 mMainHandler = new Handler(getMainLooper()); 654 655 if (!mSecureCamera) { 656 mDataAdapter = mWrappedDataAdapter; 657 mFilmStripView.setDataAdapter(mDataAdapter); 658 mDataAdapter.requestLoad(getContentResolver()); 659 } else { 660 // Put a lock placeholder as the last image by setting its date to 0. 661 ImageView v = (ImageView) getLayoutInflater().inflate( 662 R.layout.secure_album_placeholder, null); 663 mDataAdapter = new FixedLastDataAdapter( 664 mWrappedDataAdapter, 665 new SimpleViewData( 666 v, 667 v.getDrawable().getIntrinsicWidth(), 668 v.getDrawable().getIntrinsicHeight(), 669 0, 0)); 670 // Flush out all the original data. 671 mDataAdapter.flush(); 672 mFilmStripView.setDataAdapter(mDataAdapter); 673 } 674 } 675 676 private void setRotationAnimation() { 677 int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; 678 rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; 679 Window win = getWindow(); 680 WindowManager.LayoutParams winParams = win.getAttributes(); 681 winParams.rotationAnimation = rotationAnimation; 682 win.setAttributes(winParams); 683 } 684 685 @Override 686 public void onUserInteraction() { 687 super.onUserInteraction(); 688 mCurrentModule.onUserInteraction(); 689 } 690 691 @Override 692 public void onPause() { 693 mOrientationListener.disable(); 694 mCurrentModule.onPauseBeforeSuper(); 695 super.onPause(); 696 mCurrentModule.onPauseAfterSuper(); 697 } 698 699 @Override 700 public void onResume() { 701 if (Settings.System.getInt(getContentResolver(), 702 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {// auto-rotate off 703 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 704 mAutoRotateScreen = false; 705 } else { 706 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); 707 mAutoRotateScreen = true; 708 } 709 mOrientationListener.enable(); 710 mCurrentModule.onResumeBeforeSuper(); 711 super.onResume(); 712 mCurrentModule.onResumeAfterSuper(); 713 714 setSwipingEnabled(true); 715 } 716 717 @Override 718 public void onStart() { 719 super.onStart(); 720 bindMediaSaveService(); 721 mPanoramaViewHelper.onStart(); 722 } 723 724 @Override 725 protected void onStop() { 726 super.onStop(); 727 mPanoramaViewHelper.onStop(); 728 unbindMediaSaveService(); 729 } 730 731 @Override 732 public void onDestroy() { 733 if (mSecureCamera) unregisterReceiver(mScreenOffReceiver); 734 super.onDestroy(); 735 } 736 737 @Override 738 public void onConfigurationChanged(Configuration config) { 739 super.onConfigurationChanged(config); 740 mCurrentModule.onConfigurationChanged(config); 741 } 742 743 @Override 744 public boolean onKeyDown(int keyCode, KeyEvent event) { 745 if (mCurrentModule.onKeyDown(keyCode, event)) return true; 746 // Prevent software keyboard or voice search from showing up. 747 if (keyCode == KeyEvent.KEYCODE_SEARCH 748 || keyCode == KeyEvent.KEYCODE_MENU) { 749 if (event.isLongPress()) return true; 750 } 751 if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraPreview) { 752 return true; 753 } 754 755 return super.onKeyDown(keyCode, event); 756 } 757 758 @Override 759 public boolean onKeyUp(int keyCode, KeyEvent event) { 760 if (mCurrentModule.onKeyUp(keyCode, event)) return true; 761 if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraPreview) { 762 return true; 763 } 764 return super.onKeyUp(keyCode, event); 765 } 766 767 public boolean isAutoRotateScreen() { 768 return mAutoRotateScreen; 769 } 770 771 protected void updateStorageSpace() { 772 mStorageSpace = Storage.getAvailableSpace(); 773 } 774 775 protected long getStorageSpace() { 776 return mStorageSpace; 777 } 778 779 protected void updateStorageSpaceAndHint() { 780 updateStorageSpace(); 781 updateStorageHint(mStorageSpace); 782 } 783 784 protected void updateStorageHint() { 785 updateStorageHint(mStorageSpace); 786 } 787 788 protected boolean updateStorageHintOnResume() { 789 return true; 790 } 791 792 protected void updateStorageHint(long storageSpace) { 793 String message = null; 794 if (storageSpace == Storage.UNAVAILABLE) { 795 message = getString(R.string.no_storage); 796 } else if (storageSpace == Storage.PREPARING) { 797 message = getString(R.string.preparing_sd); 798 } else if (storageSpace == Storage.UNKNOWN_SIZE) { 799 message = getString(R.string.access_sd_fail); 800 } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD) { 801 message = getString(R.string.spaceIsLow_content); 802 } 803 804 if (message != null) { 805 if (mStorageHint == null) { 806 mStorageHint = OnScreenHint.makeText(this, message); 807 } else { 808 mStorageHint.setText(message); 809 } 810 mStorageHint.show(); 811 } else if (mStorageHint != null) { 812 mStorageHint.cancel(); 813 mStorageHint = null; 814 } 815 } 816 817 protected void setResultEx(int resultCode) { 818 mResultCodeForTesting = resultCode; 819 setResult(resultCode); 820 } 821 822 protected void setResultEx(int resultCode, Intent data) { 823 mResultCodeForTesting = resultCode; 824 mResultDataForTesting = data; 825 setResult(resultCode, data); 826 } 827 828 public int getResultCode() { 829 return mResultCodeForTesting; 830 } 831 832 public Intent getResultData() { 833 return mResultDataForTesting; 834 } 835 836 public boolean isSecureCamera() { 837 return mSecureCamera; 838 } 839 840 @Override 841 public void onCameraSelected(int i) { 842 if (mCurrentModuleIndex == i) return; 843 844 CameraHolder.instance().keep(); 845 closeModule(mCurrentModule); 846 mCurrentModuleIndex = i; 847 switch (i) { 848 case CameraSwitcher.VIDEO_MODULE_INDEX: 849 mCurrentModule = new VideoModule(); 850 break; 851 case CameraSwitcher.PHOTO_MODULE_INDEX: 852 mCurrentModule = new PhotoModule(); 853 break; 854 case CameraSwitcher.LIGHTCYCLE_MODULE_INDEX: 855 mCurrentModule = PhotoSphereHelper.createPanoramaModule(); 856 break; 857 default: 858 break; 859 } 860 861 openModule(mCurrentModule); 862 mCurrentModule.onOrientationChanged(mLastRawOrientation); 863 if (mMediaSaveService != null) { 864 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); 865 } 866 } 867 868 /** 869 * Launches an ACTION_EDIT intent for the given local data item. 870 */ 871 public void launchEditor(LocalData data) { 872 Intent intent = new Intent(Intent.ACTION_EDIT) 873 .setDataAndType(data.getContentUri(), data.getMimeType()) 874 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 875 startActivity(Intent.createChooser(intent, null)); 876 } 877 878 private void openModule(CameraModule module) { 879 module.init(this, mCameraModuleRootView); 880 module.onResumeBeforeSuper(); 881 module.onResumeAfterSuper(); 882 } 883 884 private void closeModule(CameraModule module) { 885 module.onPauseBeforeSuper(); 886 module.onPauseAfterSuper(); 887 ((ViewGroup) mCameraModuleRootView).removeAllViews(); 888 } 889 890 private void showUndoDeletionBar() { 891 if (mUndoDeletionBar == null) { 892 Log.v(TAG, "showing undo bar"); 893 ViewGroup v = (ViewGroup) getLayoutInflater().inflate( 894 R.layout.undo_bar, mAboveFilmstripControlLayout, true); 895 mUndoDeletionBar = (ViewGroup) v.findViewById(R.id.camera_undo_deletion_bar); 896 View button = mUndoDeletionBar.findViewById(R.id.camera_undo_deletion_button); 897 button.setOnClickListener(new View.OnClickListener() { 898 @Override 899 public void onClick(View view) { 900 mDataAdapter.undoDataRemoval(); 901 mMainHandler.removeCallbacks(mDeletionRunnable); 902 hideUndoDeletionBar(); 903 } 904 }); 905 } 906 mUndoDeletionBar.setAlpha(0f); 907 mUndoDeletionBar.setVisibility(View.VISIBLE); 908 mUndoDeletionBar.animate().setDuration(200).alpha(1f).start(); 909 } 910 911 private void hideUndoDeletionBar() { 912 Log.v(TAG, "Hiding undo deletion bar"); 913 if (mUndoDeletionBar != null) { 914 mUndoDeletionBar.animate() 915 .setDuration(200) 916 .alpha(0f) 917 .withEndAction(new Runnable() { 918 @Override 919 public void run() { 920 mUndoDeletionBar.setVisibility(View.GONE); 921 } 922 }) 923 .start(); 924 } 925 } 926 927 @Override 928 public void onShowSwitcherPopup() { 929 } 930 931 public void setSwipingEnabled(boolean enable) { 932 mCameraPreviewData.lockPreview(!enable); 933 } 934 935 // Accessor methods for getting latency times used in performance testing 936 public long getAutoFocusTime() { 937 return (mCurrentModule instanceof PhotoModule) ? 938 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1; 939 } 940 941 public long getShutterLag() { 942 return (mCurrentModule instanceof PhotoModule) ? 943 ((PhotoModule) mCurrentModule).mShutterLag : -1; 944 } 945 946 public long getShutterToPictureDisplayedTime() { 947 return (mCurrentModule instanceof PhotoModule) ? 948 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1; 949 } 950 951 public long getPictureDisplayedToJpegCallbackTime() { 952 return (mCurrentModule instanceof PhotoModule) ? 953 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1; 954 } 955 956 public long getJpegCallbackFinishTime() { 957 return (mCurrentModule instanceof PhotoModule) ? 958 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1; 959 } 960 961 public long getCaptureStartTime() { 962 return (mCurrentModule instanceof PhotoModule) ? 963 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1; 964 } 965 966 public boolean isRecording() { 967 return (mCurrentModule instanceof VideoModule) ? 968 ((VideoModule) mCurrentModule).isRecording() : false; 969 } 970} 971