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 }