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
17package com.android.gallery3d.filtershow.pipeline;
18
19import android.app.Notification;
20import android.app.NotificationManager;
21import android.app.Service;
22import android.content.Context;
23import android.content.Intent;
24import android.content.res.Resources;
25import android.graphics.Bitmap;
26import android.graphics.Rect;
27import android.net.Uri;
28import android.os.Binder;
29import android.os.IBinder;
30import android.util.Log;
31import com.android.gallery3d.R;
32import com.android.gallery3d.filtershow.FilterShowActivity;
33import com.android.gallery3d.filtershow.filters.FiltersManager;
34import com.android.gallery3d.filtershow.filters.ImageFilter;
35import com.android.gallery3d.filtershow.imageshow.MasterImage;
36import com.android.gallery3d.filtershow.tools.SaveImage;
37
38import java.io.File;
39
40public 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
70    public void setFiltershowActivity(FilterShowActivity filtershowActivity) {
71        mFiltershowActivity = filtershowActivity;
72    }
73
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
84    public void updatePreviewBuffer() {
85        mHighresRenderingRequestTask.stop();
86        mFullresRenderingRequestTask.stop();
87        mUpdatePreviewTask.updatePreview();
88    }
89
90    public void postRenderingRequest(RenderingRequest request) {
91        mRenderingRequestTask.postRenderingRequest(request);
92    }
93
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
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
123    public void setHighresPreviewScaleFactor(float highResPreviewScale) {
124        mHighresRenderingRequestTask.setHighresPreviewScaleFactor(highResPreviewScale);
125    }
126
127    public void setPreviewScaleFactor(float previewScale) {
128        mHighresRenderingRequestTask.setPreviewScaleFactor(previewScale);
129        mFullresRenderingRequestTask.setPreviewScaleFactor(previewScale);
130        mRenderingRequestTask.setPreviewScaleFactor(previewScale);
131    }
132
133    public void setOriginalBitmapHighres(Bitmap originalHires) {
134        mHighresRenderingRequestTask.setOriginalBitmapHighres(originalHires);
135    }
136
137    public class LocalBinder extends Binder {
138        public ProcessingService getService() {
139            return ProcessingService.this;
140        }
141    }
142
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
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
184    public void onDestroy() {
185        tearDownPipeline();
186        mProcessingTaskController.quit();
187    }
188
189    @Override
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
224    public IBinder onBind(Intent intent) {
225        return mBinder;
226    }
227
228    public void onStart() {
229        mNeedsAlive = true;
230        if (!mSaving && mFiltershowActivity != null) {
231            mFiltershowActivity.updateUIAfterServiceStarted();
232        }
233    }
234
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
257    public void updateNotificationWithBitmap(Bitmap bitmap) {
258        mBuilder.setLargeIcon(bitmap);
259        mNotifyMgr.notify(mNotificationId, mBuilder.build());
260    }
261
262    public void updateProgress(int max, int current) {
263        mBuilder.setProgress(max, current, false);
264        mNotifyMgr.notify(mNotificationId, mBuilder.build());
265    }
266
267    public void completePreviewSaveImage(Uri result, boolean exit) {
268        if (exit && !mNeedsAlive && !mFiltershowActivity.isSimpleEditAction()) {
269            mFiltershowActivity.completeSaveImage(result);
270        }
271    }
272
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
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
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