ActivityBase.java revision 1cc3a3466c8e9f30f13b81a88d8b2f31050c599c
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 49 private static final String TAG = "ActivityBase"; 50 private static boolean LOGV = false; 51 private int mResultCodeForTesting; 52 private boolean mOnResumePending; 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 public void onWindowFocusChanged(boolean hasFocus) { 115 if (LOGV) Log.v(TAG, "onWindowFocusChanged.hasFocus=" + hasFocus 116 + ".mOnResumePending=" + mOnResumePending); 117 if (hasFocus && mOnResumePending) { 118 mPaused = false; 119 doOnResume(); 120 mOnResumePending = false; 121 } 122 } 123 124 @Override 125 protected void onResume() { 126 super.onResume(); 127 // Don't grab the camera if in use by lockscreen. For example, face 128 // unlock may be using the camera. Camera may be already opened in 129 // onCreate. doOnResume should continue if mCameraDevice != null. 130 // Suppose camera app is in the foreground. If users turn off and turn 131 // on the screen very fast, camera app can still have the focus when the 132 // lock screen shows up. The keyguard takes input focus, so the camera 133 // app will lose focus when it is displayed. 134 if (LOGV) Log.v(TAG, "onResume. hasWindowFocus()=" + hasWindowFocus()); 135 if (mCameraDevice == null && isKeyguardLocked()) { 136 if (LOGV) Log.v(TAG, "onResume. mOnResumePending=true"); 137 mOnResumePending = true; 138 } else { 139 if (LOGV) Log.v(TAG, "onResume. mOnResumePending=false"); 140 mPaused = false; 141 doOnResume(); 142 mOnResumePending = false; 143 } 144 } 145 146 @Override 147 protected void onPause() { 148 mPaused = true; 149 if (LOGV) Log.v(TAG, "onPause"); 150 saveThumbnailToFile(); 151 super.onPause(); 152 153 if (mLoadThumbnailTask != null) { 154 mLoadThumbnailTask.cancel(true); 155 mLoadThumbnailTask = null; 156 } 157 158 if (mStorageHint != null) { 159 mStorageHint.cancel(); 160 mStorageHint = null; 161 } 162 163 mOnResumePending = false; 164 } 165 166 // Put the code of onResume in this method. 167 abstract protected void doOnResume(); 168 169 @Override 170 public boolean onSearchRequested() { 171 return false; 172 } 173 174 @Override 175 public boolean onKeyDown(int keyCode, KeyEvent event) { 176 // Prevent software keyboard or voice search from showing up. 177 if (keyCode == KeyEvent.KEYCODE_SEARCH 178 || keyCode == KeyEvent.KEYCODE_MENU) { 179 if (event.isLongPress()) return true; 180 } 181 182 return super.onKeyDown(keyCode, event); 183 } 184 185 protected void setResultEx(int resultCode) { 186 mResultCodeForTesting = resultCode; 187 setResult(resultCode); 188 } 189 190 protected void setResultEx(int resultCode, Intent data) { 191 mResultCodeForTesting = resultCode; 192 mResultDataForTesting = data; 193 setResult(resultCode, data); 194 } 195 196 public int getResultCode() { 197 return mResultCodeForTesting; 198 } 199 200 public Intent getResultData() { 201 return mResultDataForTesting; 202 } 203 204 @Override 205 protected void onDestroy() { 206 PopupManager.removeInstance(this); 207 super.onDestroy(); 208 } 209 210 private boolean isKeyguardLocked() { 211 KeyguardManager kgm = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); 212 if (LOGV) { 213 if (kgm != null) { 214 Log.v(TAG, "kgm.isKeyguardLocked()="+kgm.isKeyguardLocked() 215 + ". kgm.isKeyguardSecure()="+kgm.isKeyguardSecure()); 216 } 217 } 218 // isKeyguardSecure excludes the slide lock case. 219 return (kgm != null) && kgm.isKeyguardLocked() && kgm.isKeyguardSecure(); 220 } 221 222 protected void updateStorageHint(long storageSpace) { 223 String message = null; 224 if (storageSpace == Storage.UNAVAILABLE) { 225 message = getString(R.string.no_storage); 226 } else if (storageSpace == Storage.PREPARING) { 227 message = getString(R.string.preparing_sd); 228 } else if (storageSpace == Storage.UNKNOWN_SIZE) { 229 message = getString(R.string.access_sd_fail); 230 } else if (storageSpace < Storage.LOW_STORAGE_THRESHOLD) { 231 message = getString(R.string.spaceIsLow_content); 232 } 233 234 if (message != null) { 235 if (mStorageHint == null) { 236 mStorageHint = OnScreenHint.makeText(this, message); 237 } else { 238 mStorageHint.setText(message); 239 } 240 mStorageHint.show(); 241 } else if (mStorageHint != null) { 242 mStorageHint.cancel(); 243 mStorageHint = null; 244 } 245 } 246 247 private void updateThumbnailView() { 248 if (mThumbnail != null) { 249 mThumbnailView.setBitmap(mThumbnail.getBitmap()); 250 mThumbnailView.setVisibility(View.VISIBLE); 251 } else { 252 mThumbnailView.setBitmap(null); 253 mThumbnailView.setVisibility(View.GONE); 254 } 255 } 256 257 protected void getLastThumbnail() { 258 mThumbnail = ThumbnailHolder.getLastThumbnail(getContentResolver()); 259 // Suppose users tap the thumbnail view, go to the gallery, delete the 260 // image, and coming back to the camera. Thumbnail file will be invalid. 261 // Since the new thumbnail will be loaded in another thread later, the 262 // view should be set to gone to prevent from opening the invalid image. 263 updateThumbnailView(); 264 if (mThumbnail == null) { 265 mLoadThumbnailTask = new LoadThumbnailTask().execute(); 266 } 267 } 268 269 private class LoadThumbnailTask extends AsyncTask<Void, Void, Thumbnail> { 270 @Override 271 protected Thumbnail doInBackground(Void... params) { 272 // Load the thumbnail from the file. 273 ContentResolver resolver = getContentResolver(); 274 Thumbnail t = Thumbnail.getLastThumbnailFromFile(getFilesDir(), resolver); 275 276 if (isCancelled()) return null; 277 278 if (t == null) { 279 // Load the thumbnail from the media provider. 280 t = Thumbnail.getLastThumbnailFromContentResolver(resolver); 281 } 282 return t; 283 } 284 285 @Override 286 protected void onPostExecute(Thumbnail thumbnail) { 287 mThumbnail = thumbnail; 288 updateThumbnailView(); 289 } 290 } 291 292 protected void gotoGallery() { 293 Util.viewUri(mThumbnail.getUri(), this); 294 } 295 296 protected void saveThumbnailToFile() { 297 if (mThumbnail != null && !mThumbnail.fromFile()) { 298 new SaveThumbnailTask().execute(mThumbnail); 299 } 300 } 301 302 private class SaveThumbnailTask extends AsyncTask<Thumbnail, Void, Void> { 303 @Override 304 protected Void doInBackground(Thumbnail... params) { 305 final int n = params.length; 306 final File filesDir = getFilesDir(); 307 for (int i = 0; i < n; i++) { 308 params[i].saveLastThumbnailToFile(filesDir); 309 } 310 return null; 311 } 312 } 313 314 // Call this after setContentView. 315 protected void createCameraScreenNail(boolean getPictures) { 316 mCameraAppView = findViewById(R.id.camera_app_root); 317 Bundle data = new Bundle(); 318 String path = "/local/all/"; 319 // Intent mode does not show camera roll. Use 0 as a work around for 320 // invalid bucket id. 321 // TODO: add support of empty media set in gallery. 322 path += (getPictures ? MediaSetUtils.CAMERA_BUCKET_ID : "0"); 323 data.putString(PhotoPage.KEY_MEDIA_SET_PATH, path); 324 data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, path); 325 326 // Send a CameraScreenNail to gallery to enable the camera preview. 327 CameraScreenNailHolder holder = new CameraScreenNailHolder(this); 328 data.putParcelable(PhotoPage.KEY_SCREENNAIL_HOLDER, holder); 329 getStateManager().startState(PhotoPage.class, data); 330 mCameraScreenNail = holder.getCameraScreenNail(); 331 mCameraScreenNail.setPositionChangedListener(this); 332 } 333 334 private class HideCameraAppView implements Runnable { 335 @Override 336 public void run() { 337 mCameraAppView.setVisibility(View.GONE); 338 } 339 } 340 private class UpdateCameraAppView implements Runnable { 341 @Override 342 public void run() { 343 if (mShowCameraAppView) { 344 mCameraAppView.setVisibility(View.VISIBLE); 345 mCameraAppView.animate().withLayer().alpha(1); 346 } else { 347 mCameraAppView.animate().withLayer().alpha(0).withEndAction(mHideCameraAppView); 348 } 349 } 350 } 351 352 @Override 353 public void onPositionChanged(int x, int y, int width, int height, boolean visible) { 354 if (!mPaused && !isFinishing()) { 355 View rootView = (View) getGLRoot(); 356 int rootWidth = rootView.getWidth(); 357 int rootHeight = rootView.getHeight(); 358 boolean showCameraAppView; 359 // Check if the camera preview is in the center. 360 if (visible && (x == 0 && width == rootWidth) || 361 (y == 0 && height == rootHeight && Math.abs(x - (rootWidth - width) / 2) <= 1)) { 362 showCameraAppView = true; 363 } else { 364 showCameraAppView = false; 365 } 366 367 if (mShowCameraAppView != showCameraAppView) { 368 mShowCameraAppView = showCameraAppView; 369 // Initialize the animation. 370 if (mUpdateCameraAppView == null) { 371 mUpdateCameraAppView = new UpdateCameraAppView(); 372 mHideCameraAppView = new HideCameraAppView(); 373 mCameraAppView.animate().setDuration(300) 374 .setInterpolator(new DecelerateInterpolator()); 375 } 376 runOnUiThread(mUpdateCameraAppView); 377 } 378 } 379 } 380 381 @Override 382 public GalleryActionBar getGalleryActionBar() { 383 return mActionBar; 384 } 385} 386