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