Home | History | Annotate | Download | only in state
      1 /*
      2  * Copyright (C) 2015 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 
     17 package com.android.camera.captureintent.state;
     18 
     19 import android.graphics.Bitmap;
     20 import android.net.Uri;
     21 
     22 import com.android.camera.async.RefCountBase;
     23 import com.android.camera.captureintent.CaptureIntentConfig;
     24 import com.android.camera.captureintent.PictureDecoder;
     25 import com.android.camera.captureintent.event.EventOnTextureViewLayoutChanged;
     26 import com.android.camera.captureintent.event.EventPause;
     27 import com.android.camera.captureintent.event.EventPictureCompressed;
     28 import com.android.camera.captureintent.event.EventPictureDecoded;
     29 import com.android.camera.captureintent.event.EventTapOnCancelIntentButton;
     30 import com.android.camera.captureintent.event.EventTapOnConfirmPhotoButton;
     31 import com.android.camera.captureintent.event.EventTapOnRetakePhotoButton;
     32 import com.android.camera.captureintent.resource.ResourceCaptureTools;
     33 import com.android.camera.captureintent.resource.ResourceConstructed;
     34 import com.android.camera.captureintent.stateful.EventHandler;
     35 import com.android.camera.captureintent.stateful.State;
     36 import com.android.camera.captureintent.stateful.StateImpl;
     37 import com.android.camera.debug.Log;
     38 import com.android.camera.session.CaptureSessionManager;
     39 import com.google.common.base.Optional;
     40 
     41 /**
     42  * A state that shows the taken picture for review. The Cancel, Done or
     43  * Take buttons are presented. The state handles 3 events:
     44  * - OnCancelButtonClicked
     45  * - OnRetakeButtonClicked
     46  * - OnDoneButtonClicked
     47  */
     48 public class StateReviewingPicture extends StateImpl {
     49     private static final Log.Tag TAG = new Log.Tag("StateReviewPic");
     50 
     51     private final RefCountBase<ResourceCaptureTools> mResourceCaptureTools;
     52 
     53     /** The picture bitmap to be shown. */
     54     private Bitmap mPictureBitmap;
     55 
     56     /** The compressed picture byte array. */
     57     private Optional<byte[]> mPictureData;
     58 
     59     private boolean mIsReviewingThumbnail;
     60     private boolean mShouldFinishWhenReceivePictureData;
     61 
     62     public static StateReviewingPicture from(
     63             StateReadyForCapture readyForCapture,
     64             RefCountBase<ResourceCaptureTools> resourceCaptureTools,
     65             Bitmap pictureBitmap,
     66             Optional<byte[]> pictureData) {
     67         return new StateReviewingPicture(
     68                 readyForCapture, resourceCaptureTools, pictureBitmap, pictureData);
     69     }
     70 
     71     private StateReviewingPicture(
     72             State previousState,
     73             RefCountBase<ResourceCaptureTools> resourceCaptureTools,
     74             Bitmap pictureBitmap,
     75             Optional<byte[]> pictureData) {
     76         super(previousState);
     77         mResourceCaptureTools = resourceCaptureTools;
     78         mResourceCaptureTools.addRef();  // Will be balanced in onLeave().
     79         mPictureBitmap = pictureBitmap;
     80         mPictureData = pictureData;
     81         mIsReviewingThumbnail = !pictureData.isPresent();
     82         mShouldFinishWhenReceivePictureData = false;
     83         registerEventHandlers();
     84     }
     85 
     86     private void registerEventHandlers() {
     87         /** Handles EventPause. */
     88         EventHandler<EventPause> pauseHandler = new EventHandler<EventPause>() {
     89             @Override
     90             public Optional<State> processEvent(EventPause event) {
     91                 return Optional.of((State) StateBackgroundWithSurfaceTexture.from(
     92                         StateReviewingPicture.this,
     93                         mResourceCaptureTools.get().getResourceConstructed(),
     94                         mResourceCaptureTools.get().getResourceSurfaceTexture()));
     95             }
     96         };
     97         setEventHandler(EventPause.class, pauseHandler);
     98 
     99         /** Handles EventOnTextureViewLayoutChanged. */
    100         EventHandler<EventOnTextureViewLayoutChanged> onTextureViewLayoutChangedHandler =
    101                 new EventHandler<EventOnTextureViewLayoutChanged>() {
    102                     @Override
    103                     public Optional<State> processEvent(EventOnTextureViewLayoutChanged event) {
    104                         mResourceCaptureTools.get().getResourceSurfaceTexture().get()
    105                                 .setPreviewLayoutSize(event.getLayoutSize());
    106                         return NO_CHANGE;
    107                     }
    108                 };
    109         setEventHandler(EventOnTextureViewLayoutChanged.class, onTextureViewLayoutChangedHandler);
    110 
    111         /** Handles EventTapOnCancelIntentButton. */
    112         EventHandler<EventTapOnCancelIntentButton> tapOnCancelIntentButtonHandler =
    113                 new EventHandler<EventTapOnCancelIntentButton>() {
    114                     @Override
    115                     public Optional<State> processEvent(EventTapOnCancelIntentButton event) {
    116                         return Optional.of((State) StateIntentCompleted.from(
    117                                 StateReviewingPicture.this,
    118                                 mResourceCaptureTools.get().getResourceConstructed()));
    119                     }
    120                 };
    121         setEventHandler(EventTapOnCancelIntentButton.class, tapOnCancelIntentButtonHandler);
    122 
    123         /** Handles EventTapOnConfirmPhotoButton. */
    124         EventHandler<EventTapOnConfirmPhotoButton> tapOnConfirmPhotoButtonHandler =
    125                 new EventHandler<EventTapOnConfirmPhotoButton>() {
    126                     @Override
    127                     public Optional<State> processEvent(EventTapOnConfirmPhotoButton event) {
    128                         // If the compressed data is not available, need to wait until it arrives.
    129                         if (!mPictureData.isPresent()) {
    130                             mShouldFinishWhenReceivePictureData = true;
    131                             return NO_CHANGE;
    132                         }
    133 
    134                         // If the compressed data is available, just saving the picture and finish.
    135                         return Optional.of((State) StateSavingPicture.from(
    136                                 StateReviewingPicture.this,
    137                                 mResourceCaptureTools.get().getResourceConstructed(),
    138                                 mPictureData.get()));
    139                     }
    140                 };
    141         setEventHandler(EventTapOnConfirmPhotoButton.class, tapOnConfirmPhotoButtonHandler);
    142 
    143         /** Handles EventTapOnRetakePhotoButton. */
    144         EventHandler<EventTapOnRetakePhotoButton> tapOnRetakePhotoButtonHandler =
    145                 new EventHandler<EventTapOnRetakePhotoButton>() {
    146                     @Override
    147                     public Optional<State> processEvent(EventTapOnRetakePhotoButton event) {
    148                         return Optional.of((State) StateReadyForCapture.from(
    149                                 StateReviewingPicture.this, mResourceCaptureTools));
    150                     }
    151                 };
    152         setEventHandler(EventTapOnRetakePhotoButton.class, tapOnRetakePhotoButtonHandler);
    153 
    154         /** Handles EventPictureCompressed. */
    155         EventHandler<EventPictureCompressed> pictureCompressedHandler =
    156                 new EventHandler<EventPictureCompressed>() {
    157                     @Override
    158                     public Optional<State> processEvent(EventPictureCompressed event) {
    159                         // Users have clicked the done button, save the data and finish now.
    160                         if (mShouldFinishWhenReceivePictureData) {
    161                             return Optional.of((State) StateSavingPicture.from(
    162                                     StateReviewingPicture.this,
    163                                     mResourceCaptureTools.get().getResourceConstructed(),
    164                                     event.getPictureData()));
    165                         }
    166 
    167                         if (mIsReviewingThumbnail) {
    168                             final byte[] pictureData = event.getPictureData();
    169                             final int pictureOrientation = event.getOrientation();
    170                             ResourceConstructed resourceConstructed =
    171                                     mResourceCaptureTools.get().getResourceConstructed().get();
    172                             resourceConstructed.getCameraHandler().post(
    173                                     new Runnable() {
    174                                         @Override
    175                                         public void run() {
    176                                             final Bitmap pictureBitmap = PictureDecoder.decode(
    177                                                     pictureData,
    178                                                     CaptureIntentConfig.DOWN_SAMPLE_FACTOR,
    179                                                     pictureOrientation,
    180                                                     false);
    181                                             getStateMachine().processEvent(
    182                                                     new EventPictureDecoded(pictureBitmap, pictureData));
    183                                         }
    184                                     });
    185                         }
    186                         // Wait until the picture got decoded.
    187                         return NO_CHANGE;
    188                     }
    189                 };
    190         setEventHandler(EventPictureCompressed.class, pictureCompressedHandler);
    191 
    192         /** Handles EventPictureDecoded. */
    193         EventHandler<EventPictureDecoded> pictureDecodedHandler =
    194                 new EventHandler<EventPictureDecoded>() {
    195                     @Override
    196                     public Optional<State> processEvent(EventPictureDecoded event) {
    197                         mPictureData = Optional.of(event.getPictureData());
    198                         showPicture(event.getPictureBitmap());
    199                         return NO_CHANGE;
    200                     }
    201                 };
    202         setEventHandler(EventPictureDecoded.class, pictureDecodedHandler);
    203     }
    204 
    205     @Override
    206     public Optional<State> onEnter() {
    207         mResourceCaptureTools.get().getCaptureSessionManager()
    208                 .addSessionListener(mCaptureSessionListener);  // Will be balanced in onLeave().
    209         showPicture(mPictureBitmap);
    210         return NO_CHANGE;
    211     }
    212 
    213     @Override
    214     public void onLeave() {
    215         mResourceCaptureTools.close();
    216         mResourceCaptureTools.get().getCaptureSessionManager()
    217                 .removeSessionListener(mCaptureSessionListener);
    218     }
    219 
    220     private void showPicture(final Bitmap bitmap) {
    221         mPictureBitmap = bitmap;
    222         mResourceCaptureTools.get().getMainThread().execute(new Runnable() {
    223             @Override
    224             public void run() {
    225                 mResourceCaptureTools.get().getModuleUI().showPictureReviewUI(mPictureBitmap);
    226             }
    227         });
    228     }
    229 
    230     private final CaptureSessionManager.SessionListener mCaptureSessionListener =
    231             new CaptureSessionManager.SessionListener() {
    232                 @Override
    233                 public void onSessionThumbnailUpdate(final Bitmap thumbnailBitmap) {
    234                     // Not waiting for thumbnail anymore.
    235                 }
    236 
    237                 @Override
    238                 public void onSessionPictureDataUpdate(byte[] pictureData, int orientation) {
    239                     getStateMachine().processEvent(
    240                             new EventPictureCompressed(pictureData, orientation));
    241                 }
    242 
    243                 @Override
    244                 public void onSessionQueued(Uri sessionUri) {
    245                 }
    246 
    247                 @Override
    248                 public void onSessionUpdated(Uri sessionUri) {
    249                 }
    250 
    251                 @Override
    252                 public void onSessionCaptureIndicatorUpdate(Bitmap bitmap, int rotationDegrees) {
    253                 }
    254 
    255                 @Override
    256                 public void onSessionDone(Uri sessionUri) {
    257                 }
    258 
    259                 @Override
    260                 public void onSessionFailed(Uri sessionUri, int failureMessageId,
    261                         boolean removeFromFilmstrip) {
    262                 }
    263 
    264                 @Override
    265                 public void onSessionCanceled(Uri mediaUri) {
    266                 }
    267 
    268                 @Override
    269                 public void onSessionProgress(Uri sessionUri, int progress) {
    270                 }
    271 
    272                 @Override
    273                 public void onSessionProgressText(Uri sessionUri, int messageId) {
    274                 }
    275             };
    276 }