ActivityBase.java revision 27e6caa9dfdb349adb676b57716ab93f5bf7494f
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.content.BroadcastReceiver;
20import android.content.ContentResolver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.graphics.Bitmap;
25import android.graphics.BitmapFactory;
26import android.graphics.Rect;
27import android.hardware.Camera.Parameters;
28import android.net.Uri;
29import android.os.AsyncTask;
30import android.os.Bundle;
31import android.os.Handler;
32import android.os.Message;
33import android.support.v4.content.LocalBroadcastManager;
34import android.util.Log;
35import android.view.KeyEvent;
36import android.view.View;
37import android.view.Window;
38import android.view.WindowManager;
39import android.view.animation.AlphaAnimation;
40import android.view.animation.Animation;
41import android.view.animation.DecelerateInterpolator;
42
43import com.android.camera.ui.CameraPicker;
44import com.android.camera.ui.LayoutChangeNotifier;
45import com.android.camera.ui.PopupManager;
46import com.android.camera.ui.RotateImageView;
47import com.android.gallery3d.app.AbstractGalleryActivity;
48import com.android.gallery3d.app.AppBridge;
49import com.android.gallery3d.app.GalleryActionBar;
50import com.android.gallery3d.app.PhotoPage;
51import com.android.gallery3d.common.ApiHelper;
52import com.android.gallery3d.ui.ScreenNail;
53import com.android.gallery3d.util.MediaSetUtils;
54
55import java.io.File;
56
57/**
58 * Superclass of Camera and VideoCamera activities.
59 */
60public abstract class ActivityBase extends AbstractGalleryActivity
61        implements LayoutChangeNotifier.Listener {
62
63    private static final String TAG = "ActivityBase";
64    private static final int CAMERA_APP_VIEW_TOGGLE_TIME = 100;  // milliseconds
65    private static final String ACTION_DELETE_PICTURE =
66            "com.android.gallery3d.action.DELETE_PICTURE";
67    private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE =
68            "android.media.action.STILL_IMAGE_CAMERA_SECURE";
69    public static final String ACTION_IMAGE_CAPTURE_SECURE =
70            "android.media.action.IMAGE_CAPTURE_SECURE";
71    // The intent extra for camera from secure lock screen. True if the gallery
72    // should only show newly captured pictures. sSecureAlbumId does not
73    // increment. This is used when switching between camera, camcorder, and
74    // panorama. If the extra is not set, it is in the normal camera mode.
75    public static final String SECURE_CAMERA_EXTRA = "secure_camera";
76
77    private int mResultCodeForTesting;
78    private Intent mResultDataForTesting;
79    private OnScreenHint mStorageHint;
80    private View mSingleTapArea;
81
82    // The bitmap of the last captured picture thumbnail and the URI of the
83    // original picture.
84    protected Thumbnail mThumbnail;
85    protected int mThumbnailViewWidth; // layout width of the thumbnail
86    protected AsyncTask<Void, Void, Thumbnail> mLoadThumbnailTask;
87    // An imageview showing the last captured picture thumbnail.
88    protected RotateImageView mThumbnailView;
89    protected CameraPicker mCameraPicker;
90
91    protected boolean mOpenCameraFail;
92    protected boolean mCameraDisabled;
93    protected CameraManager.CameraProxy mCameraDevice;
94    protected Parameters mParameters;
95    // The activity is paused. The classes that extend this class should set
96    // mPaused the first thing in onResume/onPause.
97    protected boolean mPaused;
98    protected GalleryActionBar mActionBar;
99
100    // multiple cameras support
101    protected int mNumberOfCameras;
102    protected int mCameraId;
103    // The activity is going to switch to the specified camera id. This is
104    // needed because texture copy is done in GL thread. -1 means camera is not
105    // switching.
106    protected int mPendingSwitchCameraId = -1;
107
108    protected MyAppBridge mAppBridge;
109    protected ScreenNail mCameraScreenNail; // This shows camera preview.
110    // The view containing only camera related widgets like control panel,
111    // indicator bar, focus indicator and etc.
112    protected View mCameraAppView;
113    protected boolean mShowCameraAppView = true;
114    private Animation mCameraAppViewFadeIn;
115    private Animation mCameraAppViewFadeOut;
116    // Secure album id. This should be incremented every time the camera is
117    // launched from the secure lock screen. The id should be the same when
118    // switching between camera, camcorder, and panorama.
119    protected static int sSecureAlbumId;
120    // True if the camera is started from secure lock screen.
121    protected boolean mSecureCamera;
122    private static boolean sFirstStartAfterScreenOn = true;
123
124    private long mStorageSpace = Storage.LOW_STORAGE_THRESHOLD;
125    private static final int UPDATE_STORAGE_HINT = 0;
126    private final Handler mHandler = new Handler() {
127            @Override
128            public void handleMessage(Message msg) {
129                switch (msg.what) {
130                    case UPDATE_STORAGE_HINT:
131                        updateStorageHint();
132                        return;
133                }
134            }
135    };
136
137    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
138        @Override
139        public void onReceive(Context context, Intent intent) {
140            String action = intent.getAction();
141            if (action.equals(Intent.ACTION_MEDIA_MOUNTED)
142                    || action.equals(Intent.ACTION_MEDIA_UNMOUNTED)
143                    || action.equals(Intent.ACTION_MEDIA_CHECKING)
144                    || action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) {
145                updateStorageSpaceAndHint();
146            }
147        }
148    };
149
150    private boolean mUpdateThumbnailDelayed;
151    private IntentFilter mDeletePictureFilter =
152            new IntentFilter(ACTION_DELETE_PICTURE);
153    private BroadcastReceiver mDeletePictureReceiver =
154            new BroadcastReceiver() {
155                @Override
156                public void onReceive(Context context, Intent intent) {
157                    if (mShowCameraAppView) {
158                        getLastThumbnailUncached();
159                    } else {
160                        mUpdateThumbnailDelayed = true;
161                    }
162                }
163            };
164
165    // close activity when screen turns off
166    private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
167        @Override
168        public void onReceive(Context context, Intent intent) {
169            finish();
170        }
171    };
172
173    private static BroadcastReceiver sScreenOffReceiver;
174    private static class ScreenOffReceiver extends BroadcastReceiver {
175        @Override
176        public void onReceive(Context context, Intent intent) {
177            sFirstStartAfterScreenOn = true;
178        }
179    }
180
181    public static boolean isFirstStartAfterScreenOn() {
182        return sFirstStartAfterScreenOn;
183    }
184
185    public static void resetFirstStartAfterScreenOn() {
186        sFirstStartAfterScreenOn = false;
187    }
188
189    protected class CameraOpenThread extends Thread {
190        @Override
191        public void run() {
192            try {
193                mCameraDevice = Util.openCamera(ActivityBase.this, mCameraId);
194                mParameters = mCameraDevice.getParameters();
195            } catch (CameraHardwareException e) {
196                mOpenCameraFail = true;
197            } catch (CameraDisabledException e) {
198                mCameraDisabled = true;
199            }
200        }
201    }
202
203    @Override
204    public void onCreate(Bundle icicle) {
205        super.disableToggleStatusBar();
206        // Set a theme with action bar. It is not specified in manifest because
207        // we want to hide it by default. setTheme must happen before
208        // setContentView.
209        //
210        // This must be set before we call super.onCreate(), where the window's
211        // background is removed.
212        setTheme(R.style.Theme_Gallery);
213        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
214        if (ApiHelper.HAS_ACTION_BAR) {
215            requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
216        } else {
217            requestWindowFeature(Window.FEATURE_NO_TITLE);
218        }
219
220        // Check if this is in the secure camera mode.
221        Intent intent = getIntent();
222        String action = intent.getAction();
223        if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) {
224            mSecureCamera = true;
225            // Use a new album when this is started from the lock screen.
226            sSecureAlbumId++;
227        } else if (ACTION_IMAGE_CAPTURE_SECURE.equals(action)) {
228            mSecureCamera = true;
229        } else {
230            mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false);
231        }
232        if (mSecureCamera) {
233            IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
234            registerReceiver(mScreenOffReceiver, filter);
235            if (sScreenOffReceiver == null) {
236                sScreenOffReceiver = new ScreenOffReceiver();
237                getApplicationContext().registerReceiver(sScreenOffReceiver, filter);
238            }
239        }
240        super.onCreate(icicle);
241    }
242
243    public boolean isPanoramaActivity() {
244        return false;
245    }
246
247    @Override
248    protected void onResume() {
249        super.onResume();
250        LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
251        manager.registerReceiver(mDeletePictureReceiver, mDeletePictureFilter);
252
253        installIntentFilter();
254        if(updateStorageHintOnResume()) {
255            updateStorageSpace();
256            mHandler.sendEmptyMessageDelayed(UPDATE_STORAGE_HINT, 200);
257        }
258    }
259
260    @Override
261    protected void onPause() {
262        super.onPause();
263        LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
264        manager.unregisterReceiver(mDeletePictureReceiver);
265
266        saveThumbnailToFile();
267
268        if (mLoadThumbnailTask != null) {
269            mLoadThumbnailTask.cancel(true);
270            mLoadThumbnailTask = null;
271        }
272
273        if (mStorageHint != null) {
274            mStorageHint.cancel();
275            mStorageHint = null;
276        }
277
278        unregisterReceiver(mReceiver);
279    }
280
281    @Override
282    public void setContentView(int layoutResID) {
283        super.setContentView(layoutResID);
284        // getActionBar() should be after setContentView
285        mActionBar = new GalleryActionBar(this);
286        mActionBar.hide();
287    }
288
289    @Override
290    public boolean onSearchRequested() {
291        return false;
292    }
293
294    @Override
295    public boolean onKeyDown(int keyCode, KeyEvent event) {
296        // Prevent software keyboard or voice search from showing up.
297        if (keyCode == KeyEvent.KEYCODE_SEARCH
298                || keyCode == KeyEvent.KEYCODE_MENU) {
299            if (event.isLongPress()) return true;
300        }
301        if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraAppView) {
302            return true;
303        }
304
305        return super.onKeyDown(keyCode, event);
306    }
307
308    @Override
309    public boolean onKeyUp(int keyCode, KeyEvent event) {
310        if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraAppView) {
311            return true;
312        }
313        return super.onKeyUp(keyCode, event);
314    }
315
316    protected void setResultEx(int resultCode) {
317        mResultCodeForTesting = resultCode;
318        setResult(resultCode);
319    }
320
321    protected void setResultEx(int resultCode, Intent data) {
322        mResultCodeForTesting = resultCode;
323        mResultDataForTesting = data;
324        setResult(resultCode, data);
325    }
326
327    public int getResultCode() {
328        return mResultCodeForTesting;
329    }
330
331    public Intent getResultData() {
332        return mResultDataForTesting;
333    }
334
335    @Override
336    protected void onDestroy() {
337        PopupManager.removeInstance(this);
338        if (mSecureCamera) unregisterReceiver(mScreenOffReceiver);
339        super.onDestroy();
340    }
341
342    protected void installIntentFilter() {
343        // install an intent filter to receive SD card related events.
344        IntentFilter intentFilter =
345                new IntentFilter(Intent.ACTION_MEDIA_MOUNTED);
346        intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
347        intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
348        intentFilter.addAction(Intent.ACTION_MEDIA_CHECKING);
349        intentFilter.addDataScheme("file");
350        registerReceiver(mReceiver, intentFilter);
351    }
352
353    protected void updateStorageSpace() {
354        mStorageSpace = Storage.getAvailableSpace();
355    }
356
357    protected long getStorageSpace() {
358        return mStorageSpace;
359    }
360
361    protected void updateStorageSpaceAndHint() {
362        updateStorageSpace();
363        updateStorageHint(mStorageSpace);
364    }
365
366    protected void updateStorageHint() {
367        updateStorageHint(mStorageSpace);
368    }
369
370    protected boolean updateStorageHintOnResume() {
371        return true;
372    }
373
374    protected void updateStorageHint(long storageSpace) {
375        String message = null;
376        if (storageSpace == Storage.UNAVAILABLE) {
377            message = getString(R.string.no_storage);
378        } else if (storageSpace == Storage.PREPARING) {
379            message = getString(R.string.preparing_sd);
380        } else if (storageSpace == Storage.UNKNOWN_SIZE) {
381            message = getString(R.string.access_sd_fail);
382        } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD) {
383            message = getString(R.string.spaceIsLow_content);
384        }
385
386        if (message != null) {
387            if (mStorageHint == null) {
388                mStorageHint = OnScreenHint.makeText(this, message);
389            } else {
390                mStorageHint.setText(message);
391            }
392            mStorageHint.show();
393        } else if (mStorageHint != null) {
394            mStorageHint.cancel();
395            mStorageHint = null;
396        }
397    }
398
399    protected void updateThumbnailView() {
400        if (mThumbnail != null && mThumbnailView != null) {
401            mThumbnailView.setBitmap(mThumbnail.getBitmap());
402            mThumbnailView.setVisibility(View.VISIBLE);
403        } else if (mThumbnailView != null) {
404            mThumbnailView.setBitmap(null);
405            mThumbnailView.setVisibility(View.GONE);
406        }
407    }
408
409    protected void getLastThumbnail() {
410        mThumbnail = ThumbnailHolder.getLastThumbnail(getContentResolver());
411        // Suppose users tap the thumbnail view, go to the gallery, delete the
412        // image, and coming back to the camera. Thumbnail file will be invalid.
413        // Since the new thumbnail will be loaded in another thread later, the
414        // view should be set to gone to prevent from opening the invalid image.
415        updateThumbnailView();
416        if (mThumbnail == null && !mSecureCamera) {
417            mLoadThumbnailTask = new LoadThumbnailTask(true).execute();
418        }
419    }
420
421    protected void getLastThumbnailUncached() {
422        if (mSecureCamera) {
423            // Check if the thumbnail is valid.
424            if (mThumbnail != null && !Util.isUriValid(
425                    mThumbnail.getUri(), getContentResolver())) {
426                mThumbnail = null;
427                updateThumbnailView();
428            }
429        } else {
430            if (mLoadThumbnailTask != null) mLoadThumbnailTask.cancel(true);
431            mLoadThumbnailTask = new LoadThumbnailTask(false).execute();
432        }
433    }
434
435    private class LoadThumbnailTask extends AsyncTask<Void, Void, Thumbnail> {
436        private boolean mLookAtCache;
437
438        public LoadThumbnailTask(boolean lookAtCache) {
439            mLookAtCache = lookAtCache;
440        }
441
442        @Override
443        protected Thumbnail doInBackground(Void... params) {
444            // Load the thumbnail from the file.
445            ContentResolver resolver = getContentResolver();
446            Thumbnail t = null;
447            if (mLookAtCache) {
448                t = Thumbnail.getLastThumbnailFromFile(getFilesDir(), resolver);
449            }
450
451            if (isCancelled()) return null;
452
453            if (t == null) {
454                Thumbnail result[] = new Thumbnail[1];
455                // Load the thumbnail from the media provider.
456                int code = Thumbnail.getLastThumbnailFromContentResolver(
457                        resolver, result);
458                switch (code) {
459                    case Thumbnail.THUMBNAIL_FOUND:
460                        return result[0];
461                    case Thumbnail.THUMBNAIL_NOT_FOUND:
462                        return null;
463                    case Thumbnail.THUMBNAIL_DELETED:
464                        cancel(true);
465                        return null;
466                }
467            }
468            return t;
469        }
470
471        @Override
472        protected void onPostExecute(Thumbnail thumbnail) {
473            if (isCancelled()) return;
474            mThumbnail = thumbnail;
475            updateThumbnailView();
476        }
477    }
478
479    protected void gotoGallery() {
480        // Move the next picture with capture animation. "1" means next.
481        mAppBridge.switchWithCaptureAnimation(1);
482    }
483
484    protected void saveThumbnailToFile() {
485        if (mThumbnail != null && !mThumbnail.fromFile()) {
486            new SaveThumbnailTask().execute(mThumbnail);
487        }
488    }
489
490    private class SaveThumbnailTask extends AsyncTask<Thumbnail, Void, Void> {
491        @Override
492        protected Void doInBackground(Thumbnail... params) {
493            final int n = params.length;
494            final File filesDir = getFilesDir();
495            for (int i = 0; i < n; i++) {
496                params[i].saveLastThumbnailToFile(filesDir);
497            }
498            return null;
499        }
500    }
501
502    // Call this after setContentView.
503    public ScreenNail createCameraScreenNail(boolean getPictures) {
504        mCameraAppView = findViewById(R.id.camera_app_root);
505        Bundle data = new Bundle();
506        String path;
507        if (getPictures) {
508            if (mSecureCamera) {
509                path = "/secure/all/" + sSecureAlbumId;
510            } else {
511                path = "/local/all/" + MediaSetUtils.CAMERA_BUCKET_ID;
512            }
513        } else {
514            path = "/local/all/0"; // Use 0 so gallery does not show anything.
515        }
516        data.putString(PhotoPage.KEY_MEDIA_SET_PATH, path);
517        data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, path);
518        data.putBoolean(PhotoPage.KEY_SHOW_WHEN_LOCKED, mSecureCamera);
519
520        // Send an AppBridge to gallery to enable the camera preview.
521        if (mAppBridge != null) {
522            mCameraScreenNail.recycle();
523        }
524        mAppBridge = new MyAppBridge();
525        data.putParcelable(PhotoPage.KEY_APP_BRIDGE, mAppBridge);
526        if (getStateManager().getStateCount() == 0) {
527            getStateManager().startState(PhotoPage.class, data);
528        } else {
529            getStateManager().switchState(getStateManager().getTopState(),
530                    PhotoPage.class, data);
531        }
532        mCameraScreenNail = mAppBridge.getCameraScreenNail();
533        return mCameraScreenNail;
534    }
535
536    // Call this after setContentView.
537    protected ScreenNail reuseCameraScreenNail(boolean getPictures) {
538        mCameraAppView = findViewById(R.id.camera_app_root);
539        Bundle data = new Bundle();
540        String path;
541        if (getPictures) {
542            if (mSecureCamera) {
543                path = "/secure/all/" + sSecureAlbumId;
544            } else {
545                path = "/local/all/" + MediaSetUtils.CAMERA_BUCKET_ID;
546            }
547        } else {
548            path = "/local/all/0"; // Use 0 so gallery does not show anything.
549        }
550        data.putString(PhotoPage.KEY_MEDIA_SET_PATH, path);
551        data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, path);
552        data.putBoolean(PhotoPage.KEY_SHOW_WHEN_LOCKED, mSecureCamera);
553
554        // Send an AppBridge to gallery to enable the camera preview.
555        if (mAppBridge == null) {
556            mAppBridge = new MyAppBridge();
557        }
558        data.putParcelable(PhotoPage.KEY_APP_BRIDGE, mAppBridge);
559        if (getStateManager().getStateCount() == 0) {
560            getStateManager().startState(PhotoPage.class, data);
561        }
562        mCameraScreenNail = mAppBridge.getCameraScreenNail();
563        return mCameraScreenNail;
564    }
565
566    private class HideCameraAppView implements Animation.AnimationListener {
567        @Override
568        public void onAnimationEnd(Animation animation) {
569            // We cannot set this as GONE because we want to receive the
570            // onLayoutChange() callback even when we are invisible.
571            mCameraAppView.setVisibility(View.INVISIBLE);
572        }
573
574        @Override
575        public void onAnimationRepeat(Animation animation) {
576        }
577
578        @Override
579        public void onAnimationStart(Animation animation) {
580        }
581    }
582
583    protected void updateCameraAppView() {
584        // Initialize the animation.
585        if (mCameraAppViewFadeIn == null) {
586            mCameraAppViewFadeIn = new AlphaAnimation(0f, 1f);
587            mCameraAppViewFadeIn.setDuration(CAMERA_APP_VIEW_TOGGLE_TIME);
588            mCameraAppViewFadeIn.setInterpolator(new DecelerateInterpolator());
589
590            mCameraAppViewFadeOut = new AlphaAnimation(1f, 0f);
591            mCameraAppViewFadeOut.setDuration(CAMERA_APP_VIEW_TOGGLE_TIME);
592            mCameraAppViewFadeOut.setInterpolator(new DecelerateInterpolator());
593            mCameraAppViewFadeOut.setAnimationListener(new HideCameraAppView());
594        }
595
596        if (mShowCameraAppView) {
597            mCameraAppView.setVisibility(View.VISIBLE);
598            // The "transparent region" is not recomputed when a sibling of
599            // SurfaceView changes visibility (unless it involves GONE). It's
600            // been broken since 1.0. Call requestLayout to work around it.
601            mCameraAppView.requestLayout();
602            mCameraAppView.startAnimation(mCameraAppViewFadeIn);
603        } else {
604            mCameraAppView.startAnimation(mCameraAppViewFadeOut);
605        }
606    }
607
608    protected void onFullScreenChanged(boolean full) {
609        if (mShowCameraAppView == full) return;
610        mShowCameraAppView = full;
611        if (mPaused || isFinishing()) return;
612        updateCameraAppView();
613
614        // If we received DELETE_PICTURE broadcasts while the Camera UI is
615        // hidden, we update the thumbnail now.
616        if (full && mUpdateThumbnailDelayed) {
617            getLastThumbnailUncached();
618            mUpdateThumbnailDelayed = false;
619        }
620    }
621
622    @Override
623    public GalleryActionBar getGalleryActionBar() {
624        return mActionBar;
625    }
626
627    // Preview frame layout has changed.
628    @Override
629    public void onLayoutChange(View v, int left, int top, int right, int bottom) {
630        if (mAppBridge == null) return;
631
632        int width = right - left;
633        int height = bottom - top;
634        if (ApiHelper.HAS_SURFACE_TEXTURE) {
635            CameraScreenNail screenNail = (CameraScreenNail) mCameraScreenNail;
636            if (Util.getDisplayRotation(this) % 180 == 0) {
637                screenNail.setPreviewFrameLayoutSize(width, height);
638            } else {
639                // Swap the width and height. Camera screen nail draw() is based on
640                // natural orientation, not the view system orientation.
641                screenNail.setPreviewFrameLayoutSize(height, width);
642            }
643        }
644
645        // Find out the coordinates of the preview frame relative to GL
646        // root view.
647        View root = (View) getGLRoot();
648        int[] rootLocation = new int[2];
649        int[] viewLocation = new int[2];
650        root.getLocationInWindow(rootLocation);
651        v.getLocationInWindow(viewLocation);
652
653        int l = viewLocation[0] - rootLocation[0];
654        int t = viewLocation[1] - rootLocation[1];
655        int r = l + width;
656        int b = t + height;
657        Rect frame = new Rect(l, t, r, b);
658        Log.d(TAG, "set CameraRelativeFrame as " + frame);
659        mAppBridge.setCameraRelativeFrame(frame);
660        getGLRoot().requestLayoutContentPane();
661    }
662
663    protected void setSingleTapUpListener(View singleTapArea) {
664        mSingleTapArea = singleTapArea;
665    }
666
667    private boolean onSingleTapUp(int x, int y) {
668        // Ignore if listener is null or the camera control is invisible.
669        if (mSingleTapArea == null || !mShowCameraAppView) return false;
670
671        int[] relativeLocation = Util.getRelativeLocation((View) getGLRoot(),
672                mSingleTapArea);
673        x -= relativeLocation[0];
674        y -= relativeLocation[1];
675        if (x >= 0 && x < mSingleTapArea.getWidth() && y >= 0
676                && y < mSingleTapArea.getHeight()) {
677            onSingleTapUp(mSingleTapArea, x, y);
678            return true;
679        }
680        return false;
681    }
682
683    protected void onSingleTapUp(View view, int x, int y) {
684    }
685
686    public void setSwipingEnabled(boolean enabled) {
687        mAppBridge.setSwipingEnabled(enabled);
688    }
689
690    public void notifyScreenNailChanged() {
691        mAppBridge.notifyScreenNailChanged();
692    }
693
694    protected void onPreviewTextureCopied() {
695    }
696
697    protected void onCaptureTextureCopied() {
698    }
699
700    protected void addSecureAlbumItemIfNeeded(boolean isVideo, Uri uri) {
701        if (mSecureCamera) {
702            int id = Integer.parseInt(uri.getLastPathSegment());
703            mAppBridge.addSecureAlbumItem(isVideo, id);
704        }
705    }
706
707    public boolean isSecureCamera() {
708        return mSecureCamera;
709    }
710
711    //////////////////////////////////////////////////////////////////////////
712    //  The is the communication interface between the Camera Application and
713    //  the Gallery PhotoPage.
714    //////////////////////////////////////////////////////////////////////////
715
716    class MyAppBridge extends AppBridge implements CameraScreenNail.Listener {
717        @SuppressWarnings("hiding")
718        private ScreenNail mCameraScreenNail;
719        private Server mServer;
720
721        @Override
722        public ScreenNail attachScreenNail() {
723            if (mCameraScreenNail == null) {
724                if (ApiHelper.HAS_SURFACE_TEXTURE) {
725                    mCameraScreenNail = new CameraScreenNail(this);
726                } else {
727                    Bitmap b = BitmapFactory.decodeResource(getResources(),
728                            R.drawable.wallpaper_picker_preview);
729                    mCameraScreenNail = new StaticBitmapScreenNail(b);
730                }
731            }
732            return mCameraScreenNail;
733        }
734
735        @Override
736        public void detachScreenNail() {
737            mCameraScreenNail = null;
738        }
739
740        public ScreenNail getCameraScreenNail() {
741            return mCameraScreenNail;
742        }
743
744        // Return true if the tap is consumed.
745        @Override
746        public boolean onSingleTapUp(int x, int y) {
747            return ActivityBase.this.onSingleTapUp(x, y);
748        }
749
750        // This is used to notify that the screen nail will be drawn in full screen
751        // or not in next draw() call.
752        @Override
753        public void onFullScreenChanged(boolean full) {
754            ActivityBase.this.onFullScreenChanged(full);
755        }
756
757        @Override
758        public void requestRender() {
759            getGLRoot().requestRender();
760        }
761
762        @Override
763        public void onPreviewTextureCopied() {
764            ActivityBase.this.onPreviewTextureCopied();
765        }
766
767        @Override
768        public void onCaptureTextureCopied() {
769            ActivityBase.this.onCaptureTextureCopied();
770        }
771
772        @Override
773        public void setServer(Server s) {
774            mServer = s;
775        }
776
777        @Override
778        public boolean isPanorama() {
779            return ActivityBase.this.isPanoramaActivity();
780        }
781
782        @Override
783        public boolean isStaticCamera() {
784            return !ApiHelper.HAS_SURFACE_TEXTURE;
785        }
786
787        public void addSecureAlbumItem(boolean isVideo, int id) {
788            if (mServer != null) mServer.addSecureAlbumItem(isVideo, id);
789        }
790
791        private void setCameraRelativeFrame(Rect frame) {
792            if (mServer != null) mServer.setCameraRelativeFrame(frame);
793        }
794
795        private void switchWithCaptureAnimation(int offset) {
796            if (mServer != null) mServer.switchWithCaptureAnimation(offset);
797        }
798
799        private void setSwipingEnabled(boolean enabled) {
800            if (mServer != null) mServer.setSwipingEnabled(enabled);
801        }
802
803        private void notifyScreenNailChanged() {
804            if (mServer != null) mServer.notifyScreenNailChanged();
805        }
806    }
807}
808