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