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