1 /*
2  * Copyright (C) 2013 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.gallery3d.filtershow.pipeline;
18 
19 import android.app.Notification;
20 import android.app.NotificationManager;
21 import android.app.Service;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.res.Resources;
25 import android.graphics.Bitmap;
26 import android.graphics.Rect;
27 import android.net.Uri;
28 import android.os.Binder;
29 import android.os.IBinder;
30 import android.util.Log;
31 import com.android.gallery3d.R;
32 import com.android.gallery3d.filtershow.FilterShowActivity;
33 import com.android.gallery3d.filtershow.filters.FiltersManager;
34 import com.android.gallery3d.filtershow.filters.ImageFilter;
35 import com.android.gallery3d.filtershow.imageshow.MasterImage;
36 import com.android.gallery3d.filtershow.tools.SaveImage;
37 
38 import java.io.File;
39 
40 public class ProcessingService extends Service {
41     private static final String LOGTAG = "ProcessingService";
42     private static final boolean SHOW_IMAGE = false;
43     private int mNotificationId;
44     private NotificationManager mNotifyMgr = null;
45     private Notification.Builder mBuilder = null;
46 
47     private static final String PRESET = "preset";
48     private static final String QUALITY = "quality";
49     private static final String SOURCE_URI = "sourceUri";
50     private static final String SELECTED_URI = "selectedUri";
51     private static final String DESTINATION_FILE = "destinationFile";
52     private static final String SAVING = "saving";
53     private static final String FLATTEN = "flatten";
54     private static final String SIZE_FACTOR = "sizeFactor";
55     private static final String EXIT = "exit";
56 
57     private ProcessingTaskController mProcessingTaskController;
58     private ImageSavingTask mImageSavingTask;
59     private UpdatePreviewTask mUpdatePreviewTask;
60     private HighresRenderingRequestTask mHighresRenderingRequestTask;
61     private FullresRenderingRequestTask mFullresRenderingRequestTask;
62     private RenderingRequestTask mRenderingRequestTask;
63 
64     private final IBinder mBinder = new LocalBinder();
65     private FilterShowActivity mFiltershowActivity;
66 
67     private boolean mSaving = false;
68     private boolean mNeedsAlive = false;
69 
setFiltershowActivity(FilterShowActivity filtershowActivity)70     public void setFiltershowActivity(FilterShowActivity filtershowActivity) {
71         mFiltershowActivity = filtershowActivity;
72     }
73 
setOriginalBitmap(Bitmap originalBitmap)74     public void setOriginalBitmap(Bitmap originalBitmap) {
75         if (mUpdatePreviewTask == null) {
76             return;
77         }
78         mUpdatePreviewTask.setOriginal(originalBitmap);
79         mHighresRenderingRequestTask.setOriginal(originalBitmap);
80         mFullresRenderingRequestTask.setOriginal(originalBitmap);
81         mRenderingRequestTask.setOriginal(originalBitmap);
82     }
83 
updatePreviewBuffer()84     public void updatePreviewBuffer() {
85         mHighresRenderingRequestTask.stop();
86         mFullresRenderingRequestTask.stop();
87         mUpdatePreviewTask.updatePreview();
88     }
89 
postRenderingRequest(RenderingRequest request)90     public void postRenderingRequest(RenderingRequest request) {
91         mRenderingRequestTask.postRenderingRequest(request);
92     }
93 
postHighresRenderingRequest(ImagePreset preset, float scaleFactor, RenderingRequestCaller caller)94     public void postHighresRenderingRequest(ImagePreset preset, float scaleFactor,
95                                             RenderingRequestCaller caller) {
96         RenderingRequest request = new RenderingRequest();
97         // TODO: use the triple buffer preset as UpdatePreviewTask does instead of creating a copy
98         ImagePreset passedPreset = new ImagePreset(preset);
99         request.setOriginalImagePreset(preset);
100         request.setScaleFactor(scaleFactor);
101         request.setImagePreset(passedPreset);
102         request.setType(RenderingRequest.HIGHRES_RENDERING);
103         request.setCaller(caller);
104         mHighresRenderingRequestTask.postRenderingRequest(request);
105     }
106 
postFullresRenderingRequest(ImagePreset preset, float scaleFactor, Rect bounds, Rect destination, RenderingRequestCaller caller)107     public void postFullresRenderingRequest(ImagePreset preset, float scaleFactor,
108                                             Rect bounds, Rect destination,
109                                             RenderingRequestCaller caller) {
110         RenderingRequest request = new RenderingRequest();
111         ImagePreset passedPreset = new ImagePreset(preset);
112         request.setOriginalImagePreset(preset);
113         request.setScaleFactor(scaleFactor);
114         request.setImagePreset(passedPreset);
115         request.setType(RenderingRequest.PARTIAL_RENDERING);
116         request.setCaller(caller);
117         request.setBounds(bounds);
118         request.setDestination(destination);
119         passedPreset.setPartialRendering(true, bounds);
120         mFullresRenderingRequestTask.postRenderingRequest(request);
121     }
122 
setHighresPreviewScaleFactor(float highResPreviewScale)123     public void setHighresPreviewScaleFactor(float highResPreviewScale) {
124         mHighresRenderingRequestTask.setHighresPreviewScaleFactor(highResPreviewScale);
125     }
126 
setPreviewScaleFactor(float previewScale)127     public void setPreviewScaleFactor(float previewScale) {
128         mHighresRenderingRequestTask.setPreviewScaleFactor(previewScale);
129         mFullresRenderingRequestTask.setPreviewScaleFactor(previewScale);
130         mRenderingRequestTask.setPreviewScaleFactor(previewScale);
131     }
132 
setOriginalBitmapHighres(Bitmap originalHires)133     public void setOriginalBitmapHighres(Bitmap originalHires) {
134         mHighresRenderingRequestTask.setOriginalBitmapHighres(originalHires);
135     }
136 
137     public class LocalBinder extends Binder {
getService()138         public ProcessingService getService() {
139             return ProcessingService.this;
140         }
141     }
142 
getSaveIntent(Context context, ImagePreset preset, File destination, Uri selectedImageUri, Uri sourceImageUri, boolean doFlatten, int quality, float sizeFactor, boolean needsExit)143     public static Intent getSaveIntent(Context context, ImagePreset preset, File destination,
144             Uri selectedImageUri, Uri sourceImageUri, boolean doFlatten, int quality,
145             float sizeFactor, boolean needsExit) {
146         Intent processIntent = new Intent(context, ProcessingService.class);
147         processIntent.putExtra(ProcessingService.SOURCE_URI,
148                 sourceImageUri.toString());
149         processIntent.putExtra(ProcessingService.SELECTED_URI,
150                 selectedImageUri.toString());
151         processIntent.putExtra(ProcessingService.QUALITY, quality);
152         processIntent.putExtra(ProcessingService.SIZE_FACTOR, sizeFactor);
153         if (destination != null) {
154             processIntent.putExtra(ProcessingService.DESTINATION_FILE, destination.toString());
155         }
156         processIntent.putExtra(ProcessingService.PRESET,
157                 preset.getJsonString(ImagePreset.JASON_SAVED));
158         processIntent.putExtra(ProcessingService.SAVING, true);
159         processIntent.putExtra(ProcessingService.EXIT, needsExit);
160         if (doFlatten) {
161             processIntent.putExtra(ProcessingService.FLATTEN, true);
162         }
163         return processIntent;
164     }
165 
166 
167     @Override
onCreate()168     public void onCreate() {
169         mProcessingTaskController = new ProcessingTaskController(this);
170         mImageSavingTask = new ImageSavingTask(this);
171         mUpdatePreviewTask = new UpdatePreviewTask();
172         mHighresRenderingRequestTask = new HighresRenderingRequestTask();
173         mFullresRenderingRequestTask = new FullresRenderingRequestTask();
174         mRenderingRequestTask = new RenderingRequestTask();
175         mProcessingTaskController.add(mImageSavingTask);
176         mProcessingTaskController.add(mUpdatePreviewTask);
177         mProcessingTaskController.add(mHighresRenderingRequestTask);
178         mProcessingTaskController.add(mFullresRenderingRequestTask);
179         mProcessingTaskController.add(mRenderingRequestTask);
180         setupPipeline();
181     }
182 
183     @Override
onDestroy()184     public void onDestroy() {
185         tearDownPipeline();
186         mProcessingTaskController.quit();
187     }
188 
189     @Override
onStartCommand(Intent intent, int flags, int startId)190     public int onStartCommand(Intent intent, int flags, int startId) {
191         mNeedsAlive = true;
192         if (intent != null && intent.getBooleanExtra(SAVING, false)) {
193             // we save using an intent to keep the service around after the
194             // activity has been destroyed.
195             String presetJson = intent.getStringExtra(PRESET);
196             String source = intent.getStringExtra(SOURCE_URI);
197             String selected = intent.getStringExtra(SELECTED_URI);
198             String destination = intent.getStringExtra(DESTINATION_FILE);
199             int quality = intent.getIntExtra(QUALITY, 100);
200             float sizeFactor = intent.getFloatExtra(SIZE_FACTOR, 1);
201             boolean flatten = intent.getBooleanExtra(FLATTEN, false);
202             boolean exit = intent.getBooleanExtra(EXIT, false);
203             Uri sourceUri = Uri.parse(source);
204             Uri selectedUri = null;
205             if (selected != null) {
206                 selectedUri = Uri.parse(selected);
207             }
208             File destinationFile = null;
209             if (destination != null) {
210                 destinationFile = new File(destination);
211             }
212             ImagePreset preset = new ImagePreset();
213             preset.readJsonFromString(presetJson);
214             mNeedsAlive = false;
215             mSaving = true;
216             handleSaveRequest(sourceUri, selectedUri, destinationFile, preset,
217                     MasterImage.getImage().getHighresImage(),
218                     flatten, quality, sizeFactor, exit);
219         }
220         return START_REDELIVER_INTENT;
221     }
222 
223     @Override
onBind(Intent intent)224     public IBinder onBind(Intent intent) {
225         return mBinder;
226     }
227 
onStart()228     public void onStart() {
229         mNeedsAlive = true;
230         if (!mSaving && mFiltershowActivity != null) {
231             mFiltershowActivity.updateUIAfterServiceStarted();
232         }
233     }
234 
handleSaveRequest(Uri sourceUri, Uri selectedUri, File destinationFile, ImagePreset preset, Bitmap previewImage, boolean flatten, int quality, float sizeFactor, boolean exit)235     public void handleSaveRequest(Uri sourceUri, Uri selectedUri,
236             File destinationFile, ImagePreset preset, Bitmap previewImage,
237             boolean flatten, int quality, float sizeFactor, boolean exit) {
238         mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
239         mNotifyMgr.cancelAll();
240 
241         mBuilder =
242                 new Notification.Builder(this)
243                         .setSmallIcon(R.drawable.filtershow_button_fx)
244                         .setContentTitle(getString(R.string.filtershow_notification_label))
245                         .setContentText(getString(R.string.filtershow_notification_message));
246 
247         startForeground(mNotificationId, mBuilder.build());
248 
249         updateProgress(SaveImage.MAX_PROCESSING_STEPS, 0);
250 
251         // Process the image
252 
253         mImageSavingTask.saveImage(sourceUri, selectedUri, destinationFile,
254                 preset, previewImage, flatten, quality, sizeFactor, exit);
255     }
256 
updateNotificationWithBitmap(Bitmap bitmap)257     public void updateNotificationWithBitmap(Bitmap bitmap) {
258         mBuilder.setLargeIcon(bitmap);
259         mNotifyMgr.notify(mNotificationId, mBuilder.build());
260     }
261 
updateProgress(int max, int current)262     public void updateProgress(int max, int current) {
263         mBuilder.setProgress(max, current, false);
264         mNotifyMgr.notify(mNotificationId, mBuilder.build());
265     }
266 
completePreviewSaveImage(Uri result, boolean exit)267     public void completePreviewSaveImage(Uri result, boolean exit) {
268         if (exit && !mNeedsAlive && !mFiltershowActivity.isSimpleEditAction()) {
269             mFiltershowActivity.completeSaveImage(result);
270         }
271     }
272 
completeSaveImage(Uri result, boolean exit)273     public void completeSaveImage(Uri result, boolean exit) {
274         if (SHOW_IMAGE) {
275             // TODO: we should update the existing image in Gallery instead
276             Intent viewImage = new Intent(Intent.ACTION_VIEW, result);
277             viewImage.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
278             startActivity(viewImage);
279         }
280         mNotifyMgr.cancel(mNotificationId);
281         if (!exit) {
282             stopForeground(true);
283             stopSelf();
284             return;
285         }
286         stopForeground(true);
287         stopSelf();
288         if (mNeedsAlive) {
289             // If the app has been restarted while we were saving...
290             mFiltershowActivity.updateUIAfterServiceStarted();
291         } else if (exit || mFiltershowActivity.isSimpleEditAction()) {
292             // terminate now
293             mFiltershowActivity.completeSaveImage(result);
294         }
295     }
296 
setupPipeline()297     private void setupPipeline() {
298         Resources res = getResources();
299         FiltersManager.setResources(res);
300         CachingPipeline.createRenderscriptContext(this);
301 
302         FiltersManager filtersManager = FiltersManager.getManager();
303         filtersManager.addLooks(this);
304         filtersManager.addBorders(this);
305         filtersManager.addTools(this);
306         filtersManager.addEffects();
307 
308         FiltersManager highresFiltersManager = FiltersManager.getHighresManager();
309         highresFiltersManager.addLooks(this);
310         highresFiltersManager.addBorders(this);
311         highresFiltersManager.addTools(this);
312         highresFiltersManager.addEffects();
313     }
314 
tearDownPipeline()315     private void tearDownPipeline() {
316         ImageFilter.resetStatics();
317         FiltersManager.getPreviewManager().freeRSFilterScripts();
318         FiltersManager.getManager().freeRSFilterScripts();
319         FiltersManager.getHighresManager().freeRSFilterScripts();
320         FiltersManager.reset();
321         CachingPipeline.destroyRenderScriptContext();
322     }
323 
324     static {
325         System.loadLibrary("jni_filtershow_filters");
326     }
327 }
328