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