ActivityBase.java revision 27e6caa9dfdb349adb676b57716ab93f5bf7494f
1/* 2 * Copyright (C) 2009 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.content.BroadcastReceiver; 20import android.content.ContentResolver; 21import android.content.Context; 22import android.content.Intent; 23import android.content.IntentFilter; 24import android.graphics.Bitmap; 25import android.graphics.BitmapFactory; 26import android.graphics.Rect; 27import android.hardware.Camera.Parameters; 28import android.net.Uri; 29import android.os.AsyncTask; 30import android.os.Bundle; 31import android.os.Handler; 32import android.os.Message; 33import android.support.v4.content.LocalBroadcastManager; 34import android.util.Log; 35import android.view.KeyEvent; 36import android.view.View; 37import android.view.Window; 38import android.view.WindowManager; 39import android.view.animation.AlphaAnimation; 40import android.view.animation.Animation; 41import android.view.animation.DecelerateInterpolator; 42 43import com.android.camera.ui.CameraPicker; 44import com.android.camera.ui.LayoutChangeNotifier; 45import com.android.camera.ui.PopupManager; 46import com.android.camera.ui.RotateImageView; 47import com.android.gallery3d.app.AbstractGalleryActivity; 48import com.android.gallery3d.app.AppBridge; 49import com.android.gallery3d.app.GalleryActionBar; 50import com.android.gallery3d.app.PhotoPage; 51import com.android.gallery3d.common.ApiHelper; 52import com.android.gallery3d.ui.ScreenNail; 53import com.android.gallery3d.util.MediaSetUtils; 54 55import java.io.File; 56 57/** 58 * Superclass of Camera and VideoCamera activities. 59 */ 60public abstract class ActivityBase extends AbstractGalleryActivity 61 implements LayoutChangeNotifier.Listener { 62 63 private static final String TAG = "ActivityBase"; 64 private static final int CAMERA_APP_VIEW_TOGGLE_TIME = 100; // milliseconds 65 private static final String ACTION_DELETE_PICTURE = 66 "com.android.gallery3d.action.DELETE_PICTURE"; 67 private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE = 68 "android.media.action.STILL_IMAGE_CAMERA_SECURE"; 69 public static final String ACTION_IMAGE_CAPTURE_SECURE = 70 "android.media.action.IMAGE_CAPTURE_SECURE"; 71 // The intent extra for camera from secure lock screen. True if the gallery 72 // should only show newly captured pictures. sSecureAlbumId does not 73 // increment. This is used when switching between camera, camcorder, and 74 // panorama. If the extra is not set, it is in the normal camera mode. 75 public static final String SECURE_CAMERA_EXTRA = "secure_camera"; 76 77 private int mResultCodeForTesting; 78 private Intent mResultDataForTesting; 79 private OnScreenHint mStorageHint; 80 private View mSingleTapArea; 81 82 // The bitmap of the last captured picture thumbnail and the URI of the 83 // original picture. 84 protected Thumbnail mThumbnail; 85 protected int mThumbnailViewWidth; // layout width of the thumbnail 86 protected AsyncTask<Void, Void, Thumbnail> mLoadThumbnailTask; 87 // An imageview showing the last captured picture thumbnail. 88 protected RotateImageView mThumbnailView; 89 protected CameraPicker mCameraPicker; 90 91 protected boolean mOpenCameraFail; 92 protected boolean mCameraDisabled; 93 protected CameraManager.CameraProxy mCameraDevice; 94 protected Parameters mParameters; 95 // The activity is paused. The classes that extend this class should set 96 // mPaused the first thing in onResume/onPause. 97 protected boolean mPaused; 98 protected GalleryActionBar mActionBar; 99 100 // multiple cameras support 101 protected int mNumberOfCameras; 102 protected int mCameraId; 103 // The activity is going to switch to the specified camera id. This is 104 // needed because texture copy is done in GL thread. -1 means camera is not 105 // switching. 106 protected int mPendingSwitchCameraId = -1; 107 108 protected MyAppBridge mAppBridge; 109 protected ScreenNail mCameraScreenNail; // This shows camera preview. 110 // The view containing only camera related widgets like control panel, 111 // indicator bar, focus indicator and etc. 112 protected View mCameraAppView; 113 protected boolean mShowCameraAppView = true; 114 private Animation mCameraAppViewFadeIn; 115 private Animation mCameraAppViewFadeOut; 116 // Secure album id. This should be incremented every time the camera is 117 // launched from the secure lock screen. The id should be the same when 118 // switching between camera, camcorder, and panorama. 119 protected static int sSecureAlbumId; 120 // True if the camera is started from secure lock screen. 121 protected boolean mSecureCamera; 122 private static boolean sFirstStartAfterScreenOn = true; 123 124 private long mStorageSpace = Storage.LOW_STORAGE_THRESHOLD; 125 private static final int UPDATE_STORAGE_HINT = 0; 126 private final Handler mHandler = new Handler() { 127 @Override 128 public void handleMessage(Message msg) { 129 switch (msg.what) { 130 case UPDATE_STORAGE_HINT: 131 updateStorageHint(); 132 return; 133 } 134 } 135 }; 136 137 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 138 @Override 139 public void onReceive(Context context, Intent intent) { 140 String action = intent.getAction(); 141 if (action.equals(Intent.ACTION_MEDIA_MOUNTED) 142 || action.equals(Intent.ACTION_MEDIA_UNMOUNTED) 143 || action.equals(Intent.ACTION_MEDIA_CHECKING) 144 || action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) { 145 updateStorageSpaceAndHint(); 146 } 147 } 148 }; 149 150 private boolean mUpdateThumbnailDelayed; 151 private IntentFilter mDeletePictureFilter = 152 new IntentFilter(ACTION_DELETE_PICTURE); 153 private BroadcastReceiver mDeletePictureReceiver = 154 new BroadcastReceiver() { 155 @Override 156 public void onReceive(Context context, Intent intent) { 157 if (mShowCameraAppView) { 158 getLastThumbnailUncached(); 159 } else { 160 mUpdateThumbnailDelayed = true; 161 } 162 } 163 }; 164 165 // close activity when screen turns off 166 private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { 167 @Override 168 public void onReceive(Context context, Intent intent) { 169 finish(); 170 } 171 }; 172 173 private static BroadcastReceiver sScreenOffReceiver; 174 private static class ScreenOffReceiver extends BroadcastReceiver { 175 @Override 176 public void onReceive(Context context, Intent intent) { 177 sFirstStartAfterScreenOn = true; 178 } 179 } 180 181 public static boolean isFirstStartAfterScreenOn() { 182 return sFirstStartAfterScreenOn; 183 } 184 185 public static void resetFirstStartAfterScreenOn() { 186 sFirstStartAfterScreenOn = false; 187 } 188 189 protected class CameraOpenThread extends Thread { 190 @Override 191 public void run() { 192 try { 193 mCameraDevice = Util.openCamera(ActivityBase.this, mCameraId); 194 mParameters = mCameraDevice.getParameters(); 195 } catch (CameraHardwareException e) { 196 mOpenCameraFail = true; 197 } catch (CameraDisabledException e) { 198 mCameraDisabled = true; 199 } 200 } 201 } 202 203 @Override 204 public void onCreate(Bundle icicle) { 205 super.disableToggleStatusBar(); 206 // Set a theme with action bar. It is not specified in manifest because 207 // we want to hide it by default. setTheme must happen before 208 // setContentView. 209 // 210 // This must be set before we call super.onCreate(), where the window's 211 // background is removed. 212 setTheme(R.style.Theme_Gallery); 213 getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); 214 if (ApiHelper.HAS_ACTION_BAR) { 215 requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY); 216 } else { 217 requestWindowFeature(Window.FEATURE_NO_TITLE); 218 } 219 220 // Check if this is in the secure camera mode. 221 Intent intent = getIntent(); 222 String action = intent.getAction(); 223 if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) { 224 mSecureCamera = true; 225 // Use a new album when this is started from the lock screen. 226 sSecureAlbumId++; 227 } else if (ACTION_IMAGE_CAPTURE_SECURE.equals(action)) { 228 mSecureCamera = true; 229 } else { 230 mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false); 231 } 232 if (mSecureCamera) { 233 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 234 registerReceiver(mScreenOffReceiver, filter); 235 if (sScreenOffReceiver == null) { 236 sScreenOffReceiver = new ScreenOffReceiver(); 237 getApplicationContext().registerReceiver(sScreenOffReceiver, filter); 238 } 239 } 240 super.onCreate(icicle); 241 } 242 243 public boolean isPanoramaActivity() { 244 return false; 245 } 246 247 @Override 248 protected void onResume() { 249 super.onResume(); 250 LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this); 251 manager.registerReceiver(mDeletePictureReceiver, mDeletePictureFilter); 252 253 installIntentFilter(); 254 if(updateStorageHintOnResume()) { 255 updateStorageSpace(); 256 mHandler.sendEmptyMessageDelayed(UPDATE_STORAGE_HINT, 200); 257 } 258 } 259 260 @Override 261 protected void onPause() { 262 super.onPause(); 263 LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this); 264 manager.unregisterReceiver(mDeletePictureReceiver); 265 266 saveThumbnailToFile(); 267 268 if (mLoadThumbnailTask != null) { 269 mLoadThumbnailTask.cancel(true); 270 mLoadThumbnailTask = null; 271 } 272 273 if (mStorageHint != null) { 274 mStorageHint.cancel(); 275 mStorageHint = null; 276 } 277 278 unregisterReceiver(mReceiver); 279 } 280 281 @Override 282 public void setContentView(int layoutResID) { 283 super.setContentView(layoutResID); 284 // getActionBar() should be after setContentView 285 mActionBar = new GalleryActionBar(this); 286 mActionBar.hide(); 287 } 288 289 @Override 290 public boolean onSearchRequested() { 291 return false; 292 } 293 294 @Override 295 public boolean onKeyDown(int keyCode, KeyEvent event) { 296 // Prevent software keyboard or voice search from showing up. 297 if (keyCode == KeyEvent.KEYCODE_SEARCH 298 || keyCode == KeyEvent.KEYCODE_MENU) { 299 if (event.isLongPress()) return true; 300 } 301 if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraAppView) { 302 return true; 303 } 304 305 return super.onKeyDown(keyCode, event); 306 } 307 308 @Override 309 public boolean onKeyUp(int keyCode, KeyEvent event) { 310 if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraAppView) { 311 return true; 312 } 313 return super.onKeyUp(keyCode, event); 314 } 315 316 protected void setResultEx(int resultCode) { 317 mResultCodeForTesting = resultCode; 318 setResult(resultCode); 319 } 320 321 protected void setResultEx(int resultCode, Intent data) { 322 mResultCodeForTesting = resultCode; 323 mResultDataForTesting = data; 324 setResult(resultCode, data); 325 } 326 327 public int getResultCode() { 328 return mResultCodeForTesting; 329 } 330 331 public Intent getResultData() { 332 return mResultDataForTesting; 333 } 334 335 @Override 336 protected void onDestroy() { 337 PopupManager.removeInstance(this); 338 if (mSecureCamera) unregisterReceiver(mScreenOffReceiver); 339 super.onDestroy(); 340 } 341 342 protected void installIntentFilter() { 343 // install an intent filter to receive SD card related events. 344 IntentFilter intentFilter = 345 new IntentFilter(Intent.ACTION_MEDIA_MOUNTED); 346 intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); 347 intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED); 348 intentFilter.addAction(Intent.ACTION_MEDIA_CHECKING); 349 intentFilter.addDataScheme("file"); 350 registerReceiver(mReceiver, intentFilter); 351 } 352 353 protected void updateStorageSpace() { 354 mStorageSpace = Storage.getAvailableSpace(); 355 } 356 357 protected long getStorageSpace() { 358 return mStorageSpace; 359 } 360 361 protected void updateStorageSpaceAndHint() { 362 updateStorageSpace(); 363 updateStorageHint(mStorageSpace); 364 } 365 366 protected void updateStorageHint() { 367 updateStorageHint(mStorageSpace); 368 } 369 370 protected boolean updateStorageHintOnResume() { 371 return true; 372 } 373 374 protected void updateStorageHint(long storageSpace) { 375 String message = null; 376 if (storageSpace == Storage.UNAVAILABLE) { 377 message = getString(R.string.no_storage); 378 } else if (storageSpace == Storage.PREPARING) { 379 message = getString(R.string.preparing_sd); 380 } else if (storageSpace == Storage.UNKNOWN_SIZE) { 381 message = getString(R.string.access_sd_fail); 382 } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD) { 383 message = getString(R.string.spaceIsLow_content); 384 } 385 386 if (message != null) { 387 if (mStorageHint == null) { 388 mStorageHint = OnScreenHint.makeText(this, message); 389 } else { 390 mStorageHint.setText(message); 391 } 392 mStorageHint.show(); 393 } else if (mStorageHint != null) { 394 mStorageHint.cancel(); 395 mStorageHint = null; 396 } 397 } 398 399 protected void updateThumbnailView() { 400 if (mThumbnail != null && mThumbnailView != null) { 401 mThumbnailView.setBitmap(mThumbnail.getBitmap()); 402 mThumbnailView.setVisibility(View.VISIBLE); 403 } else if (mThumbnailView != null) { 404 mThumbnailView.setBitmap(null); 405 mThumbnailView.setVisibility(View.GONE); 406 } 407 } 408 409 protected void getLastThumbnail() { 410 mThumbnail = ThumbnailHolder.getLastThumbnail(getContentResolver()); 411 // Suppose users tap the thumbnail view, go to the gallery, delete the 412 // image, and coming back to the camera. Thumbnail file will be invalid. 413 // Since the new thumbnail will be loaded in another thread later, the 414 // view should be set to gone to prevent from opening the invalid image. 415 updateThumbnailView(); 416 if (mThumbnail == null && !mSecureCamera) { 417 mLoadThumbnailTask = new LoadThumbnailTask(true).execute(); 418 } 419 } 420 421 protected void getLastThumbnailUncached() { 422 if (mSecureCamera) { 423 // Check if the thumbnail is valid. 424 if (mThumbnail != null && !Util.isUriValid( 425 mThumbnail.getUri(), getContentResolver())) { 426 mThumbnail = null; 427 updateThumbnailView(); 428 } 429 } else { 430 if (mLoadThumbnailTask != null) mLoadThumbnailTask.cancel(true); 431 mLoadThumbnailTask = new LoadThumbnailTask(false).execute(); 432 } 433 } 434 435 private class LoadThumbnailTask extends AsyncTask<Void, Void, Thumbnail> { 436 private boolean mLookAtCache; 437 438 public LoadThumbnailTask(boolean lookAtCache) { 439 mLookAtCache = lookAtCache; 440 } 441 442 @Override 443 protected Thumbnail doInBackground(Void... params) { 444 // Load the thumbnail from the file. 445 ContentResolver resolver = getContentResolver(); 446 Thumbnail t = null; 447 if (mLookAtCache) { 448 t = Thumbnail.getLastThumbnailFromFile(getFilesDir(), resolver); 449 } 450 451 if (isCancelled()) return null; 452 453 if (t == null) { 454 Thumbnail result[] = new Thumbnail[1]; 455 // Load the thumbnail from the media provider. 456 int code = Thumbnail.getLastThumbnailFromContentResolver( 457 resolver, result); 458 switch (code) { 459 case Thumbnail.THUMBNAIL_FOUND: 460 return result[0]; 461 case Thumbnail.THUMBNAIL_NOT_FOUND: 462 return null; 463 case Thumbnail.THUMBNAIL_DELETED: 464 cancel(true); 465 return null; 466 } 467 } 468 return t; 469 } 470 471 @Override 472 protected void onPostExecute(Thumbnail thumbnail) { 473 if (isCancelled()) return; 474 mThumbnail = thumbnail; 475 updateThumbnailView(); 476 } 477 } 478 479 protected void gotoGallery() { 480 // Move the next picture with capture animation. "1" means next. 481 mAppBridge.switchWithCaptureAnimation(1); 482 } 483 484 protected void saveThumbnailToFile() { 485 if (mThumbnail != null && !mThumbnail.fromFile()) { 486 new SaveThumbnailTask().execute(mThumbnail); 487 } 488 } 489 490 private class SaveThumbnailTask extends AsyncTask<Thumbnail, Void, Void> { 491 @Override 492 protected Void doInBackground(Thumbnail... params) { 493 final int n = params.length; 494 final File filesDir = getFilesDir(); 495 for (int i = 0; i < n; i++) { 496 params[i].saveLastThumbnailToFile(filesDir); 497 } 498 return null; 499 } 500 } 501 502 // Call this after setContentView. 503 public ScreenNail createCameraScreenNail(boolean getPictures) { 504 mCameraAppView = findViewById(R.id.camera_app_root); 505 Bundle data = new Bundle(); 506 String path; 507 if (getPictures) { 508 if (mSecureCamera) { 509 path = "/secure/all/" + sSecureAlbumId; 510 } else { 511 path = "/local/all/" + MediaSetUtils.CAMERA_BUCKET_ID; 512 } 513 } else { 514 path = "/local/all/0"; // Use 0 so gallery does not show anything. 515 } 516 data.putString(PhotoPage.KEY_MEDIA_SET_PATH, path); 517 data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, path); 518 data.putBoolean(PhotoPage.KEY_SHOW_WHEN_LOCKED, mSecureCamera); 519 520 // Send an AppBridge to gallery to enable the camera preview. 521 if (mAppBridge != null) { 522 mCameraScreenNail.recycle(); 523 } 524 mAppBridge = new MyAppBridge(); 525 data.putParcelable(PhotoPage.KEY_APP_BRIDGE, mAppBridge); 526 if (getStateManager().getStateCount() == 0) { 527 getStateManager().startState(PhotoPage.class, data); 528 } else { 529 getStateManager().switchState(getStateManager().getTopState(), 530 PhotoPage.class, data); 531 } 532 mCameraScreenNail = mAppBridge.getCameraScreenNail(); 533 return mCameraScreenNail; 534 } 535 536 // Call this after setContentView. 537 protected ScreenNail reuseCameraScreenNail(boolean getPictures) { 538 mCameraAppView = findViewById(R.id.camera_app_root); 539 Bundle data = new Bundle(); 540 String path; 541 if (getPictures) { 542 if (mSecureCamera) { 543 path = "/secure/all/" + sSecureAlbumId; 544 } else { 545 path = "/local/all/" + MediaSetUtils.CAMERA_BUCKET_ID; 546 } 547 } else { 548 path = "/local/all/0"; // Use 0 so gallery does not show anything. 549 } 550 data.putString(PhotoPage.KEY_MEDIA_SET_PATH, path); 551 data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, path); 552 data.putBoolean(PhotoPage.KEY_SHOW_WHEN_LOCKED, mSecureCamera); 553 554 // Send an AppBridge to gallery to enable the camera preview. 555 if (mAppBridge == null) { 556 mAppBridge = new MyAppBridge(); 557 } 558 data.putParcelable(PhotoPage.KEY_APP_BRIDGE, mAppBridge); 559 if (getStateManager().getStateCount() == 0) { 560 getStateManager().startState(PhotoPage.class, data); 561 } 562 mCameraScreenNail = mAppBridge.getCameraScreenNail(); 563 return mCameraScreenNail; 564 } 565 566 private class HideCameraAppView implements Animation.AnimationListener { 567 @Override 568 public void onAnimationEnd(Animation animation) { 569 // We cannot set this as GONE because we want to receive the 570 // onLayoutChange() callback even when we are invisible. 571 mCameraAppView.setVisibility(View.INVISIBLE); 572 } 573 574 @Override 575 public void onAnimationRepeat(Animation animation) { 576 } 577 578 @Override 579 public void onAnimationStart(Animation animation) { 580 } 581 } 582 583 protected void updateCameraAppView() { 584 // Initialize the animation. 585 if (mCameraAppViewFadeIn == null) { 586 mCameraAppViewFadeIn = new AlphaAnimation(0f, 1f); 587 mCameraAppViewFadeIn.setDuration(CAMERA_APP_VIEW_TOGGLE_TIME); 588 mCameraAppViewFadeIn.setInterpolator(new DecelerateInterpolator()); 589 590 mCameraAppViewFadeOut = new AlphaAnimation(1f, 0f); 591 mCameraAppViewFadeOut.setDuration(CAMERA_APP_VIEW_TOGGLE_TIME); 592 mCameraAppViewFadeOut.setInterpolator(new DecelerateInterpolator()); 593 mCameraAppViewFadeOut.setAnimationListener(new HideCameraAppView()); 594 } 595 596 if (mShowCameraAppView) { 597 mCameraAppView.setVisibility(View.VISIBLE); 598 // The "transparent region" is not recomputed when a sibling of 599 // SurfaceView changes visibility (unless it involves GONE). It's 600 // been broken since 1.0. Call requestLayout to work around it. 601 mCameraAppView.requestLayout(); 602 mCameraAppView.startAnimation(mCameraAppViewFadeIn); 603 } else { 604 mCameraAppView.startAnimation(mCameraAppViewFadeOut); 605 } 606 } 607 608 protected void onFullScreenChanged(boolean full) { 609 if (mShowCameraAppView == full) return; 610 mShowCameraAppView = full; 611 if (mPaused || isFinishing()) return; 612 updateCameraAppView(); 613 614 // If we received DELETE_PICTURE broadcasts while the Camera UI is 615 // hidden, we update the thumbnail now. 616 if (full && mUpdateThumbnailDelayed) { 617 getLastThumbnailUncached(); 618 mUpdateThumbnailDelayed = false; 619 } 620 } 621 622 @Override 623 public GalleryActionBar getGalleryActionBar() { 624 return mActionBar; 625 } 626 627 // Preview frame layout has changed. 628 @Override 629 public void onLayoutChange(View v, int left, int top, int right, int bottom) { 630 if (mAppBridge == null) return; 631 632 int width = right - left; 633 int height = bottom - top; 634 if (ApiHelper.HAS_SURFACE_TEXTURE) { 635 CameraScreenNail screenNail = (CameraScreenNail) mCameraScreenNail; 636 if (Util.getDisplayRotation(this) % 180 == 0) { 637 screenNail.setPreviewFrameLayoutSize(width, height); 638 } else { 639 // Swap the width and height. Camera screen nail draw() is based on 640 // natural orientation, not the view system orientation. 641 screenNail.setPreviewFrameLayoutSize(height, width); 642 } 643 } 644 645 // Find out the coordinates of the preview frame relative to GL 646 // root view. 647 View root = (View) getGLRoot(); 648 int[] rootLocation = new int[2]; 649 int[] viewLocation = new int[2]; 650 root.getLocationInWindow(rootLocation); 651 v.getLocationInWindow(viewLocation); 652 653 int l = viewLocation[0] - rootLocation[0]; 654 int t = viewLocation[1] - rootLocation[1]; 655 int r = l + width; 656 int b = t + height; 657 Rect frame = new Rect(l, t, r, b); 658 Log.d(TAG, "set CameraRelativeFrame as " + frame); 659 mAppBridge.setCameraRelativeFrame(frame); 660 getGLRoot().requestLayoutContentPane(); 661 } 662 663 protected void setSingleTapUpListener(View singleTapArea) { 664 mSingleTapArea = singleTapArea; 665 } 666 667 private boolean onSingleTapUp(int x, int y) { 668 // Ignore if listener is null or the camera control is invisible. 669 if (mSingleTapArea == null || !mShowCameraAppView) return false; 670 671 int[] relativeLocation = Util.getRelativeLocation((View) getGLRoot(), 672 mSingleTapArea); 673 x -= relativeLocation[0]; 674 y -= relativeLocation[1]; 675 if (x >= 0 && x < mSingleTapArea.getWidth() && y >= 0 676 && y < mSingleTapArea.getHeight()) { 677 onSingleTapUp(mSingleTapArea, x, y); 678 return true; 679 } 680 return false; 681 } 682 683 protected void onSingleTapUp(View view, int x, int y) { 684 } 685 686 public void setSwipingEnabled(boolean enabled) { 687 mAppBridge.setSwipingEnabled(enabled); 688 } 689 690 public void notifyScreenNailChanged() { 691 mAppBridge.notifyScreenNailChanged(); 692 } 693 694 protected void onPreviewTextureCopied() { 695 } 696 697 protected void onCaptureTextureCopied() { 698 } 699 700 protected void addSecureAlbumItemIfNeeded(boolean isVideo, Uri uri) { 701 if (mSecureCamera) { 702 int id = Integer.parseInt(uri.getLastPathSegment()); 703 mAppBridge.addSecureAlbumItem(isVideo, id); 704 } 705 } 706 707 public boolean isSecureCamera() { 708 return mSecureCamera; 709 } 710 711 ////////////////////////////////////////////////////////////////////////// 712 // The is the communication interface between the Camera Application and 713 // the Gallery PhotoPage. 714 ////////////////////////////////////////////////////////////////////////// 715 716 class MyAppBridge extends AppBridge implements CameraScreenNail.Listener { 717 @SuppressWarnings("hiding") 718 private ScreenNail mCameraScreenNail; 719 private Server mServer; 720 721 @Override 722 public ScreenNail attachScreenNail() { 723 if (mCameraScreenNail == null) { 724 if (ApiHelper.HAS_SURFACE_TEXTURE) { 725 mCameraScreenNail = new CameraScreenNail(this); 726 } else { 727 Bitmap b = BitmapFactory.decodeResource(getResources(), 728 R.drawable.wallpaper_picker_preview); 729 mCameraScreenNail = new StaticBitmapScreenNail(b); 730 } 731 } 732 return mCameraScreenNail; 733 } 734 735 @Override 736 public void detachScreenNail() { 737 mCameraScreenNail = null; 738 } 739 740 public ScreenNail getCameraScreenNail() { 741 return mCameraScreenNail; 742 } 743 744 // Return true if the tap is consumed. 745 @Override 746 public boolean onSingleTapUp(int x, int y) { 747 return ActivityBase.this.onSingleTapUp(x, y); 748 } 749 750 // This is used to notify that the screen nail will be drawn in full screen 751 // or not in next draw() call. 752 @Override 753 public void onFullScreenChanged(boolean full) { 754 ActivityBase.this.onFullScreenChanged(full); 755 } 756 757 @Override 758 public void requestRender() { 759 getGLRoot().requestRender(); 760 } 761 762 @Override 763 public void onPreviewTextureCopied() { 764 ActivityBase.this.onPreviewTextureCopied(); 765 } 766 767 @Override 768 public void onCaptureTextureCopied() { 769 ActivityBase.this.onCaptureTextureCopied(); 770 } 771 772 @Override 773 public void setServer(Server s) { 774 mServer = s; 775 } 776 777 @Override 778 public boolean isPanorama() { 779 return ActivityBase.this.isPanoramaActivity(); 780 } 781 782 @Override 783 public boolean isStaticCamera() { 784 return !ApiHelper.HAS_SURFACE_TEXTURE; 785 } 786 787 public void addSecureAlbumItem(boolean isVideo, int id) { 788 if (mServer != null) mServer.addSecureAlbumItem(isVideo, id); 789 } 790 791 private void setCameraRelativeFrame(Rect frame) { 792 if (mServer != null) mServer.setCameraRelativeFrame(frame); 793 } 794 795 private void switchWithCaptureAnimation(int offset) { 796 if (mServer != null) mServer.switchWithCaptureAnimation(offset); 797 } 798 799 private void setSwipingEnabled(boolean enabled) { 800 if (mServer != null) mServer.setSwipingEnabled(enabled); 801 } 802 803 private void notifyScreenNailChanged() { 804 if (mServer != null) mServer.notifyScreenNailChanged(); 805 } 806 } 807} 808