ActivityBase.java revision 9833a3328025552a3a176dc4d054cde63ef11885
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.Activity;
20import android.app.KeyguardManager;
21import android.content.ContentResolver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.ActivityInfo;
25import android.hardware.Camera.Parameters;
26import android.os.AsyncTask;
27import android.os.Bundle;
28import android.util.Log;
29import android.view.KeyEvent;
30import android.view.View;
31import android.view.WindowManager;
32
33import com.android.camera.ui.PopupManager;
34import com.android.camera.ui.RotateImageView;
35
36import java.io.File;
37
38/**
39 * Superclass of Camera and VideoCamera activities.
40 */
41abstract public class ActivityBase extends Activity {
42    private static final String TAG = "ActivityBase";
43    private static boolean LOGV = false;
44    private int mResultCodeForTesting;
45    private boolean mOnResumePending;
46    private Intent mResultDataForTesting;
47    private OnScreenHint mStorageHint;
48    // The bitmap of the last captured picture thumbnail and the URI of the
49    // original picture.
50    protected Thumbnail mThumbnail;
51    // An imageview showing showing the last captured picture thumbnail.
52    protected RotateImageView mThumbnailView;
53    protected int mThumbnailViewWidth; // layout width of the thumbnail
54    protected AsyncTask<Void, Void, Thumbnail> mLoadThumbnailTask;
55    protected boolean mOpenCameraFail;
56    protected boolean mCameraDisabled;
57    protected CameraManager.CameraProxy mCameraDevice;
58    protected Parameters mParameters;
59    protected boolean mPaused; // The activity is paused.
60
61    // multiple cameras support
62    protected int mNumberOfCameras;
63    protected int mCameraId;
64
65    protected class CameraOpenThread extends Thread {
66        @Override
67        public void run() {
68            try {
69                mCameraDevice = Util.openCamera(ActivityBase.this, mCameraId);
70                mParameters = mCameraDevice.getParameters();
71            } catch (CameraHardwareException e) {
72                mOpenCameraFail = true;
73            } catch (CameraDisabledException e) {
74                mCameraDisabled = true;
75            }
76        }
77    }
78
79    @Override
80    public void onCreate(Bundle icicle) {
81        if (Util.isTabletUI()) {
82            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
83        } else {
84            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
85        }
86        getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
87        super.onCreate(icicle);
88    }
89
90    @Override
91    public void onWindowFocusChanged(boolean hasFocus) {
92        if (LOGV) Log.v(TAG, "onWindowFocusChanged.hasFocus=" + hasFocus
93                + ".mOnResumePending=" + mOnResumePending);
94        if (hasFocus && mOnResumePending) {
95            mPaused = false;
96            doOnResume();
97            mOnResumePending = false;
98        }
99    }
100
101    @Override
102    protected void onResume() {
103        super.onResume();
104        // Don't grab the camera if in use by lockscreen. For example, face
105        // unlock may be using the camera. Camera may be already opened in
106        // onCreate. doOnResume should continue if mCameraDevice != null.
107        // Suppose camera app is in the foreground. If users turn off and turn
108        // on the screen very fast, camera app can still have the focus when the
109        // lock screen shows up. The keyguard takes input focus, so the camera
110        // app will lose focus when it is displayed.
111        if (LOGV) Log.v(TAG, "onResume. hasWindowFocus()=" + hasWindowFocus());
112        if (mCameraDevice == null && isKeyguardLocked()) {
113            if (LOGV) Log.v(TAG, "onResume. mOnResumePending=true");
114            mOnResumePending = true;
115        } else {
116            if (LOGV) Log.v(TAG, "onResume. mOnResumePending=false");
117            mPaused = false;
118            doOnResume();
119            mOnResumePending = false;
120        }
121    }
122
123    @Override
124    protected void onPause() {
125        mPaused = true;
126        if (LOGV) Log.v(TAG, "onPause");
127        saveThumbnailToFile();
128        super.onPause();
129
130        if (mLoadThumbnailTask != null) {
131            mLoadThumbnailTask.cancel(true);
132            mLoadThumbnailTask = null;
133        }
134
135        if (mStorageHint != null) {
136            mStorageHint.cancel();
137            mStorageHint = null;
138        }
139
140        mOnResumePending = false;
141    }
142
143    // Put the code of onResume in this method.
144    abstract protected void doOnResume();
145
146    @Override
147    public boolean onSearchRequested() {
148        return false;
149    }
150
151    @Override
152    public boolean onKeyDown(int keyCode, KeyEvent event) {
153        // Prevent software keyboard or voice search from showing up.
154        if (keyCode == KeyEvent.KEYCODE_SEARCH
155                || keyCode == KeyEvent.KEYCODE_MENU) {
156            if (event.isLongPress()) return true;
157        }
158
159        return super.onKeyDown(keyCode, event);
160    }
161
162    protected void setResultEx(int resultCode) {
163        mResultCodeForTesting = resultCode;
164        setResult(resultCode);
165    }
166
167    protected void setResultEx(int resultCode, Intent data) {
168        mResultCodeForTesting = resultCode;
169        mResultDataForTesting = data;
170        setResult(resultCode, data);
171    }
172
173    public int getResultCode() {
174        return mResultCodeForTesting;
175    }
176
177    public Intent getResultData() {
178        return mResultDataForTesting;
179    }
180
181    @Override
182    protected void onDestroy() {
183        PopupManager.removeInstance(this);
184        super.onDestroy();
185    }
186
187    private boolean isKeyguardLocked() {
188        KeyguardManager kgm = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
189        if (LOGV) {
190            if (kgm != null) {
191                Log.v(TAG, "kgm.isKeyguardLocked()="+kgm.isKeyguardLocked()
192                        + ". kgm.isKeyguardSecure()="+kgm.isKeyguardSecure());
193            }
194        }
195        // isKeyguardSecure excludes the slide lock case.
196        return (kgm != null) && kgm.isKeyguardLocked() && kgm.isKeyguardSecure();
197    }
198
199    protected void updateStorageHint(long storageSpace) {
200        String message = null;
201        if (storageSpace == Storage.UNAVAILABLE) {
202            message = getString(R.string.no_storage);
203        } else if (storageSpace == Storage.PREPARING) {
204            message = getString(R.string.preparing_sd);
205        } else if (storageSpace == Storage.UNKNOWN_SIZE) {
206            message = getString(R.string.access_sd_fail);
207        } else if (storageSpace < Storage.LOW_STORAGE_THRESHOLD) {
208            message = getString(R.string.spaceIsLow_content);
209        }
210
211        if (message != null) {
212            if (mStorageHint == null) {
213                mStorageHint = OnScreenHint.makeText(this, message);
214            } else {
215                mStorageHint.setText(message);
216            }
217            mStorageHint.show();
218        } else if (mStorageHint != null) {
219            mStorageHint.cancel();
220            mStorageHint = null;
221        }
222    }
223
224    private void updateThumbnailView() {
225        if (mThumbnail != null) {
226            mThumbnailView.setBitmap(mThumbnail.getBitmap());
227            mThumbnailView.setVisibility(View.VISIBLE);
228        } else {
229            mThumbnailView.setBitmap(null);
230            mThumbnailView.setVisibility(View.GONE);
231        }
232    }
233
234    protected void getLastThumbnail() {
235        mThumbnail = ThumbnailHolder.getLastThumbnail(getContentResolver());
236        // Suppose users tap the thumbnail view, go to the gallery, delete the
237        // image, and coming back to the camera. Thumbnail file will be invalid.
238        // Since the new thumbnail will be loaded in another thread later, the
239        // view should be set to gone to prevent from opening the invalid image.
240        updateThumbnailView();
241        if (mThumbnail == null) {
242            mLoadThumbnailTask = new LoadThumbnailTask().execute();
243        }
244    }
245
246    private class LoadThumbnailTask extends AsyncTask<Void, Void, Thumbnail> {
247        @Override
248        protected Thumbnail doInBackground(Void... params) {
249            // Load the thumbnail from the file.
250            ContentResolver resolver = getContentResolver();
251            Thumbnail t = Thumbnail.getLastThumbnailFromFile(getFilesDir(), resolver);
252
253            if (isCancelled()) return null;
254
255            if (t == null) {
256                // Load the thumbnail from the media provider.
257                t = Thumbnail.getLastThumbnailFromContentResolver(resolver);
258            }
259            return t;
260        }
261
262        @Override
263        protected void onPostExecute(Thumbnail thumbnail) {
264            mThumbnail = thumbnail;
265            updateThumbnailView();
266        }
267    }
268
269    protected void gotoGallery() {
270        Util.viewUri(mThumbnail.getUri(), this);
271    }
272
273    protected void saveThumbnailToFile() {
274        if (mThumbnail != null && !mThumbnail.fromFile()) {
275            new SaveThumbnailTask().execute(mThumbnail);
276        }
277    }
278
279    private class SaveThumbnailTask extends AsyncTask<Thumbnail, Void, Void> {
280        @Override
281        protected Void doInBackground(Thumbnail... params) {
282            final int n = params.length;
283            final File filesDir = getFilesDir();
284            for (int i = 0; i < n; i++) {
285                params[i].saveLastThumbnailToFile(filesDir);
286            }
287            return null;
288        }
289    }
290}
291