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 
from( StateReadyForCapture readyForCapture, RefCountBase<ResourceCaptureTools> resourceCaptureTools, Bitmap pictureBitmap, Optional<byte[]> pictureData)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 
StateReviewingPicture( State previousState, RefCountBase<ResourceCaptureTools> resourceCaptureTools, Bitmap pictureBitmap, Optional<byte[]> pictureData)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 
registerEventHandlers()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
onEnter()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
onLeave()214     public void onLeave() {
215         mResourceCaptureTools.close();
216         mResourceCaptureTools.get().getCaptureSessionManager()
217                 .removeSessionListener(mCaptureSessionListener);
218     }
219 
showPicture(final Bitmap bitmap)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 }