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