ActivityBase.java revision 72fe79a040df8f01b03a39bec2473f7b0de8228d
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.app.KeyguardManager; 20import android.content.ContentResolver; 21import android.content.Context; 22import android.content.Intent; 23import android.content.pm.ActivityInfo; 24import android.content.res.Configuration; 25import android.hardware.Camera.Parameters; 26import android.os.AsyncTask; 27import android.os.Bundle; 28import android.util.Log; 29import android.view.animation.DecelerateInterpolator; 30import android.view.KeyEvent; 31import android.view.Menu; 32import android.view.View; 33import android.view.Window; 34import android.view.WindowManager; 35 36import com.android.camera.ui.PopupManager; 37import com.android.camera.ui.RotateImageView; 38import com.android.gallery3d.app.AbstractGalleryActivity; 39import com.android.gallery3d.app.PhotoPage; 40import com.android.gallery3d.app.PhotoPage.PageTapListener; 41import com.android.gallery3d.app.GalleryActionBar; 42import com.android.gallery3d.app.StateManager; 43import com.android.gallery3d.util.MediaSetUtils; 44 45import java.io.File; 46 47/** 48 * Superclass of Camera and VideoCamera activities. 49 */ 50abstract public class ActivityBase extends AbstractGalleryActivity 51 implements CameraScreenNail.PositionChangedListener, 52 View.OnLayoutChangeListener, PageTapListener { 53 54 private static final String TAG = "ActivityBase"; 55 private static boolean LOGV = false; 56 private static final int CAMERA_APP_VIEW_TOGGLE_TIME = 100; // milliseconds 57 private int mResultCodeForTesting; 58 private Intent mResultDataForTesting; 59 private OnScreenHint mStorageHint; 60 private UpdateCameraAppView mUpdateCameraAppView; 61 private HideCameraAppView mHideCameraAppView; 62 private View mSingleTapArea; 63 64 // The bitmap of the last captured picture thumbnail and the URI of the 65 // original picture. 66 protected Thumbnail mThumbnail; 67 // An imageview showing showing the last captured picture thumbnail. 68 protected RotateImageView mThumbnailView; 69 protected int mThumbnailViewWidth; // layout width of the thumbnail 70 protected AsyncTask<Void, Void, Thumbnail> mLoadThumbnailTask; 71 protected boolean mOpenCameraFail; 72 protected boolean mCameraDisabled; 73 protected CameraManager.CameraProxy mCameraDevice; 74 protected Parameters mParameters; 75 // The activity is paused. The classes that extend this class should set 76 // mPaused the first thing in onResume/onPause. 77 protected boolean mPaused; 78 protected GalleryActionBar mActionBar; 79 80 // multiple cameras support 81 protected int mNumberOfCameras; 82 protected int mCameraId; 83 84 protected CameraScreenNail mCameraScreenNail; // This shows camera preview. 85 // The view containing only camera related widgets like control panel, 86 // indicator bar, focus indicator and etc. 87 protected View mCameraAppView; 88 protected boolean mShowCameraAppView = true; 89 90 protected class CameraOpenThread extends Thread { 91 @Override 92 public void run() { 93 try { 94 mCameraDevice = Util.openCamera(ActivityBase.this, mCameraId); 95 mParameters = mCameraDevice.getParameters(); 96 } catch (CameraHardwareException e) { 97 mOpenCameraFail = true; 98 } catch (CameraDisabledException e) { 99 mCameraDisabled = true; 100 } 101 } 102 } 103 104 @Override 105 public void onCreate(Bundle icicle) { 106 if (getResources().getConfiguration().orientation 107 == Configuration.ORIENTATION_LANDSCAPE) { 108 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); 109 } else { 110 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 111 } 112 getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); 113 super.disableToggleStatusBar(); 114 super.onCreate(icicle); 115 } 116 117 @Override 118 protected void onPause() { 119 super.onPause(); 120 if (LOGV) Log.v(TAG, "onPause"); 121 saveThumbnailToFile(); 122 123 if (mLoadThumbnailTask != null) { 124 mLoadThumbnailTask.cancel(true); 125 mLoadThumbnailTask = null; 126 } 127 128 if (mStorageHint != null) { 129 mStorageHint.cancel(); 130 mStorageHint = null; 131 } 132 } 133 134 @Override 135 public void setContentView(int layoutResID) { 136 // Set a theme with action bar. It is not specified in manifest because 137 // we want to hide it by default. setTheme must happen before 138 // setContentView. 139 setTheme(android.R.style.Theme_Holo); 140 getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); 141 requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY); 142 143 super.setContentView(layoutResID); 144 // getActionBar() should be after setContentView 145 mActionBar = new GalleryActionBar(this); 146 mActionBar.hide(); 147 } 148 149 @Override 150 public boolean onSearchRequested() { 151 return false; 152 } 153 154 @Override 155 public boolean onKeyDown(int keyCode, KeyEvent event) { 156 // Prevent software keyboard or voice search from showing up. 157 if (keyCode == KeyEvent.KEYCODE_SEARCH 158 || keyCode == KeyEvent.KEYCODE_MENU) { 159 if (event.isLongPress()) return true; 160 } 161 162 return super.onKeyDown(keyCode, event); 163 } 164 165 protected void setResultEx(int resultCode) { 166 mResultCodeForTesting = resultCode; 167 setResult(resultCode); 168 } 169 170 protected void setResultEx(int resultCode, Intent data) { 171 mResultCodeForTesting = resultCode; 172 mResultDataForTesting = data; 173 setResult(resultCode, data); 174 } 175 176 public int getResultCode() { 177 return mResultCodeForTesting; 178 } 179 180 public Intent getResultData() { 181 return mResultDataForTesting; 182 } 183 184 @Override 185 protected void onDestroy() { 186 PopupManager.removeInstance(this); 187 super.onDestroy(); 188 } 189 190 @Override 191 public boolean onCreateOptionsMenu(Menu menu) { 192 super.onCreateOptionsMenu(menu); 193 return getStateManager().createOptionsMenu(menu); 194 } 195 196 protected void updateStorageHint(long storageSpace) { 197 String message = null; 198 if (storageSpace == Storage.UNAVAILABLE) { 199 message = getString(R.string.no_storage); 200 } else if (storageSpace == Storage.PREPARING) { 201 message = getString(R.string.preparing_sd); 202 } else if (storageSpace == Storage.UNKNOWN_SIZE) { 203 message = getString(R.string.access_sd_fail); 204 } else if (storageSpace < Storage.LOW_STORAGE_THRESHOLD) { 205 message = getString(R.string.spaceIsLow_content); 206 } 207 208 if (message != null) { 209 if (mStorageHint == null) { 210 mStorageHint = OnScreenHint.makeText(this, message); 211 } else { 212 mStorageHint.setText(message); 213 } 214 mStorageHint.show(); 215 } else if (mStorageHint != null) { 216 mStorageHint.cancel(); 217 mStorageHint = null; 218 } 219 } 220 221 private void updateThumbnailView() { 222 if (mThumbnail != null) { 223 mThumbnailView.setBitmap(mThumbnail.getBitmap()); 224 mThumbnailView.setVisibility(View.VISIBLE); 225 } else { 226 mThumbnailView.setBitmap(null); 227 mThumbnailView.setVisibility(View.GONE); 228 } 229 } 230 231 protected void getLastThumbnail() { 232 mThumbnail = ThumbnailHolder.getLastThumbnail(getContentResolver()); 233 // Suppose users tap the thumbnail view, go to the gallery, delete the 234 // image, and coming back to the camera. Thumbnail file will be invalid. 235 // Since the new thumbnail will be loaded in another thread later, the 236 // view should be set to gone to prevent from opening the invalid image. 237 updateThumbnailView(); 238 if (mThumbnail == null) { 239 mLoadThumbnailTask = new LoadThumbnailTask().execute(); 240 } 241 } 242 243 private class LoadThumbnailTask extends AsyncTask<Void, Void, Thumbnail> { 244 @Override 245 protected Thumbnail doInBackground(Void... params) { 246 // Load the thumbnail from the file. 247 ContentResolver resolver = getContentResolver(); 248 Thumbnail t = Thumbnail.getLastThumbnailFromFile(getFilesDir(), resolver); 249 250 if (isCancelled()) return null; 251 252 if (t == null) { 253 // Load the thumbnail from the media provider. 254 t = Thumbnail.getLastThumbnailFromContentResolver(resolver); 255 } 256 return t; 257 } 258 259 @Override 260 protected void onPostExecute(Thumbnail thumbnail) { 261 mThumbnail = thumbnail; 262 updateThumbnailView(); 263 } 264 } 265 266 protected void gotoGallery() { 267 // TODO: remove this after panorama has swipe UI. 268 if (getStateManager().getStateCount() > 0) { 269 PhotoPage photoPage = (PhotoPage) getStateManager().getTopState(); 270 // Move the next picture with capture animation. "1" means next. 271 photoPage.switchWithCaptureAnimation(1); 272 } else { 273 Util.viewUri(mThumbnail.getUri(), this); 274 } 275 } 276 277 protected void saveThumbnailToFile() { 278 if (mThumbnail != null && !mThumbnail.fromFile()) { 279 new SaveThumbnailTask().execute(mThumbnail); 280 } 281 } 282 283 private class SaveThumbnailTask extends AsyncTask<Thumbnail, Void, Void> { 284 @Override 285 protected Void doInBackground(Thumbnail... params) { 286 final int n = params.length; 287 final File filesDir = getFilesDir(); 288 for (int i = 0; i < n; i++) { 289 params[i].saveLastThumbnailToFile(filesDir); 290 } 291 return null; 292 } 293 } 294 295 // Call this after setContentView. 296 protected void createCameraScreenNail(boolean getPictures) { 297 mCameraAppView = findViewById(R.id.camera_app_root); 298 Bundle data = new Bundle(); 299 String path = "/local/all/"; 300 // Intent mode does not show camera roll. Use 0 as a work around for 301 // invalid bucket id. 302 // TODO: add support of empty media set in gallery. 303 path += (getPictures ? MediaSetUtils.CAMERA_BUCKET_ID : "0"); 304 data.putString(PhotoPage.KEY_MEDIA_SET_PATH, path); 305 data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, path); 306 307 // Send a CameraScreenNail to gallery to enable the camera preview. 308 CameraScreenNailHolder holder = new CameraScreenNailHolder(this); 309 data.putParcelable(PhotoPage.KEY_SCREENNAIL_HOLDER, holder); 310 getStateManager().startState(PhotoPage.class, data); 311 mCameraScreenNail = holder.getCameraScreenNail(); 312 mCameraScreenNail.setPositionChangedListener(this); 313 } 314 315 private class HideCameraAppView implements Runnable { 316 @Override 317 public void run() { 318 mCameraAppView.setVisibility(View.GONE); 319 } 320 } 321 private class UpdateCameraAppView implements Runnable { 322 @Override 323 public void run() { 324 if (mShowCameraAppView) { 325 mCameraAppView.setVisibility(View.VISIBLE); 326 mCameraAppView.animate() 327 .setDuration(CAMERA_APP_VIEW_TOGGLE_TIME) 328 .withLayer().alpha(1); 329 } else { 330 mCameraAppView.animate() 331 .setDuration(CAMERA_APP_VIEW_TOGGLE_TIME) 332 .withLayer().alpha(0).withEndAction(mHideCameraAppView); 333 } 334 } 335 } 336 337 @Override 338 public void onPositionChanged(int x, int y, int width, int height, boolean visible) { 339 if (!mPaused && !isFinishing()) { 340 View rootView = (View) getGLRoot(); 341 int rootWidth = rootView.getWidth(); 342 int rootHeight = rootView.getHeight(); 343 boolean showCameraAppView; 344 // Check if the camera preview is in the center. 345 if (visible && (x == 0 && width == rootWidth) || 346 (y == 0 && height == rootHeight && Math.abs(x - (rootWidth - width) / 2) <= 1)) { 347 showCameraAppView = true; 348 } else { 349 showCameraAppView = false; 350 } 351 352 if (mShowCameraAppView != showCameraAppView) { 353 mShowCameraAppView = showCameraAppView; 354 // Initialize the animation. 355 if (mUpdateCameraAppView == null) { 356 mUpdateCameraAppView = new UpdateCameraAppView(); 357 mHideCameraAppView = new HideCameraAppView(); 358 mCameraAppView.animate() 359 .setInterpolator(new DecelerateInterpolator()); 360 } 361 runOnUiThread(mUpdateCameraAppView); 362 } 363 } 364 } 365 366 @Override 367 public GalleryActionBar getGalleryActionBar() { 368 return mActionBar; 369 } 370 371 // Preview frame layout has changed. Move the preview to the center of the 372 // layout. 373 @Override 374 public void onLayoutChange(View v, int left, int top, int right, int bottom, 375 int oldLeft, int oldTop, int oldRight, int oldBottom) { 376 // Find out the left and top of the preview frame layout relative to GL 377 // root view. 378 View root = (View) getGLRoot(); 379 int[] rootLocation = new int[2]; 380 int[] viewLocation = new int[2]; 381 root.getLocationInWindow(rootLocation); 382 v.getLocationInWindow(viewLocation); 383 int relativeLeft = viewLocation[0] - rootLocation[0]; 384 int relativeTop = viewLocation[1] - rootLocation[1]; 385 386 // Calculate the scale ratio between preview frame layout and GL root 387 // view. 388 int width = root.getWidth(); 389 int height = root.getHeight(); 390 float scale = Math.max((float) (right - left) / width, 391 (float) (bottom - top) / height); 392 float scalePx = width / 2f; 393 float scalePy = height / 2f; 394 395 // Calculate the translate distance. 396 float translateX = relativeLeft + (right - left - width) / 2f; 397 float translateY = relativeTop + (bottom - top - height) / 2f; 398 399 mCameraScreenNail.setMatrix(scale, scalePx, scalePy, translateX, translateY); 400 } 401 402 protected void setSingleTapUpListener(View singleTapArea) { 403 PhotoPage photoPage = (PhotoPage) getStateManager().getTopState(); 404 photoPage.setPageTapListener(this); 405 mSingleTapArea = singleTapArea; 406 } 407 408 // Single tap up from PhotoPage. 409 @Override 410 public boolean onSingleTapUp(int x, int y) { 411 // Camera control is invisible. Ignore. 412 if (!mShowCameraAppView) return false; 413 414 int[] relativeLocation = Util.getRelativeLocation((View) getGLRoot(), 415 mSingleTapArea); 416 x -= relativeLocation[0]; 417 y -= relativeLocation[1]; 418 if (x >= 0 && x < mSingleTapArea.getWidth() && y >= 0 419 && y < mSingleTapArea.getHeight()) { 420 onSingleTapUp(mSingleTapArea, x, y); 421 return true; 422 } 423 return false; 424 } 425 426 protected void onSingleTapUp(View view, int x, int y) { 427 } 428} 429