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