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