diff options
author | nicolasroard <nicolasroard@google.com> | 2013-07-12 18:27:54 -0700 |
---|---|---|
committer | nicolasroard <nicolasroard@google.com> | 2013-07-16 17:07:33 -0700 |
commit | 24d6ec7beb37eb3a5449f1fa49b4adb123391d24 (patch) | |
tree | 4faa84bdb70e9284e9b1cf11c8cb4c1efd005a03 /src/com/android/gallery3d/filtershow/pipeline | |
parent | dda606af6d6a18c31b1da3f8885e06b51982b601 (diff) | |
download | android_packages_apps_Gallery2-24d6ec7beb37eb3a5449f1fa49b4adb123391d24.tar.gz android_packages_apps_Gallery2-24d6ec7beb37eb3a5449f1fa49b4adb123391d24.tar.bz2 android_packages_apps_Gallery2-24d6ec7beb37eb3a5449f1fa49b4adb123391d24.zip |
Add background processing service
bug:7298624
Change-Id: Ie79f88fd84fdf8f4dab6a8071f06a819e247b357
Diffstat (limited to 'src/com/android/gallery3d/filtershow/pipeline')
7 files changed, 514 insertions, 5 deletions
diff --git a/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java b/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java index a7580a835..535d02f0b 100644 --- a/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java +++ b/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java @@ -16,7 +16,7 @@ package com.android.gallery3d.filtershow.pipeline; -import android.app.Activity; +import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.support.v8.renderscript.Allocation; @@ -68,7 +68,7 @@ public class CachingPipeline implements PipelineInterface { return sRS; } - public static synchronized void createRenderscriptContext(Activity context) { + public static synchronized void createRenderscriptContext(Context context) { if (sRS != null) { Log.w(LOGTAG, "A prior RS context exists when calling setRenderScriptContext"); destroyRenderScriptContext(); diff --git a/src/com/android/gallery3d/filtershow/pipeline/FilteringPipeline.java b/src/com/android/gallery3d/filtershow/pipeline/FilteringPipeline.java index a302b1907..0e9b83d7f 100644 --- a/src/com/android/gallery3d/filtershow/pipeline/FilteringPipeline.java +++ b/src/com/android/gallery3d/filtershow/pipeline/FilteringPipeline.java @@ -21,7 +21,6 @@ import android.os.*; import android.os.Process; import android.util.Log; -import com.android.gallery3d.filtershow.cache.ImageLoader; import com.android.gallery3d.filtershow.filters.FiltersManager; import com.android.gallery3d.filtershow.imageshow.MasterImage; diff --git a/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java b/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java index 78a4d211d..2b9e3701f 100644 --- a/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java +++ b/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java @@ -55,7 +55,7 @@ public class ImagePreset { private boolean mPartialRendering = false; private Rect mPartialRenderingBounds; - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; public ImagePreset() { } @@ -607,7 +607,7 @@ public class ImagePreset { if (DEBUG) { Log.v(LOGTAG, "Serialization: " + sname); if (sname == null) { - Log.v(LOGTAG, "Serialization: " + filter); + Log.v(LOGTAG, "Serialization name null for filter: " + filter); } } writer.name(sname); diff --git a/src/com/android/gallery3d/filtershow/pipeline/ImageSavingTask.java b/src/com/android/gallery3d/filtershow/pipeline/ImageSavingTask.java new file mode 100644 index 000000000..e93ec1687 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/pipeline/ImageSavingTask.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.filtershow.pipeline; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.net.Uri; +import com.android.gallery3d.filtershow.cache.ImageLoader; +import com.android.gallery3d.filtershow.filters.FiltersManager; +import com.android.gallery3d.filtershow.tools.SaveImage; + +import java.io.File; + +public class ImageSavingTask extends ProcessingTask { + private ProcessingService mProcessingService; + + static class SaveRequest implements Request { + Uri sourceUri; + Uri selectedUri; + File destinationFile; + ImagePreset preset; + } + + static class UpdateBitmap implements Update { + Bitmap bitmap; + } + + static class UpdateProgress implements Update { + int max; + int current; + } + + static class URIResult implements Result { + Uri uri; + } + + public ImageSavingTask(ProcessingService service) { + mProcessingService = service; + } + + public void saveImage(Uri sourceUri, Uri selectedUri, + File destinationFile, ImagePreset preset) { + SaveRequest request = new SaveRequest(); + request.sourceUri = sourceUri; + request.selectedUri = selectedUri; + request.destinationFile = destinationFile; + request.preset = preset; + postRequest(request); + } + + public Result doInBackground(Request message) { + SaveRequest request = (SaveRequest) message; + Uri sourceUri = request.sourceUri; + Uri selectedUri = request.selectedUri; + File destinationFile = request.destinationFile; + ImagePreset preset = request.preset; + + // We create a small bitmap showing the result that we can + // give to the notification + UpdateBitmap updateBitmap = new UpdateBitmap(); + updateBitmap.bitmap = createNotificationBitmap(sourceUri, preset); + postUpdate(updateBitmap); + + SaveImage saveImage = new SaveImage(mProcessingService, sourceUri, + selectedUri, destinationFile, + new SaveImage.Callback() { + @Override + public void onProgress(int max, int current) { + UpdateProgress updateProgress = new UpdateProgress(); + updateProgress.max = max; + updateProgress.current = current; + postUpdate(updateProgress); + } + }); + + Uri uri = saveImage.processAndSaveImage(preset); + URIResult result = new URIResult(); + result.uri = uri; + return result; + } + + @Override + public void onResult(Result message) { + URIResult result = (URIResult) message; + mProcessingService.completeSaveImage(result.uri); + } + + @Override + public void onUpdate(Update message) { + if (message instanceof UpdateBitmap) { + Bitmap bitmap = ((UpdateBitmap) message).bitmap; + mProcessingService.updateNotificationWithBitmap(bitmap); + } + if (message instanceof UpdateProgress) { + UpdateProgress progress = (UpdateProgress) message; + mProcessingService.updateProgress(progress.max, progress.current); + } + } + + private Bitmap createNotificationBitmap(Uri sourceUri, ImagePreset preset) { + int notificationBitmapSize = Resources.getSystem().getDimensionPixelSize( + android.R.dimen.notification_large_icon_width); + Bitmap bitmap = ImageLoader.loadConstrainedBitmap(sourceUri, getContext(), + notificationBitmapSize, null, true); + CachingPipeline pipeline = new CachingPipeline(FiltersManager.getManager(), "Thumb"); + return pipeline.renderFinalImage(bitmap, preset); + } + +} diff --git a/src/com/android/gallery3d/filtershow/pipeline/ProcessingService.java b/src/com/android/gallery3d/filtershow/pipeline/ProcessingService.java new file mode 100644 index 000000000..032024746 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/pipeline/ProcessingService.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.filtershow.pipeline; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Binder; +import android.os.IBinder; +import android.util.Log; +import com.android.gallery3d.R; +import com.android.gallery3d.filtershow.FilterShowActivity; +import com.android.gallery3d.filtershow.filters.FiltersManager; +import com.android.gallery3d.filtershow.filters.ImageFilter; +import com.android.gallery3d.filtershow.tools.SaveImage; + +import java.io.File; + +public class ProcessingService extends Service { + private static final String LOGTAG = "ProcessingService"; + private static final boolean SHOW_IMAGE = false; + private int mNotificationId; + private NotificationManager mNotifyMgr = null; + private Notification.Builder mBuilder = null; + + private static final String PRESET = "preset"; + private static final String SOURCE_URI = "sourceUri"; + private static final String SELECTED_URI = "selectedUri"; + private static final String DESTINATION_FILE = "destinationFile"; + private static final String SAVING = "saving"; + + private ProcessingTaskController mProcessingTaskController; + private ImageSavingTask mImageSavingTask; + + private final IBinder mBinder = new LocalBinder(); + private FilterShowActivity mFiltershowActivity; + + private boolean mSaving = false; + private boolean mNeedsAlive = false; + + public void setFiltershowActivity(FilterShowActivity filtershowActivity) { + mFiltershowActivity = filtershowActivity; + } + + public class LocalBinder extends Binder { + public ProcessingService getService() { + return ProcessingService.this; + } + } + + public static Intent getSaveIntent(Context context, ImagePreset preset, File destination, + Uri selectedImageUri, Uri sourceImageUri) { + Intent processIntent = new Intent(context, ProcessingService.class); + processIntent.putExtra(ProcessingService.SOURCE_URI, + sourceImageUri.toString()); + processIntent.putExtra(ProcessingService.SELECTED_URI, + selectedImageUri.toString()); + if (destination != null) { + processIntent.putExtra(ProcessingService.DESTINATION_FILE, destination.toString()); + } + processIntent.putExtra(ProcessingService.PRESET, + preset.getJsonString(context.getString(R.string.saved))); + processIntent.putExtra(ProcessingService.SAVING, true); + return processIntent; + } + + + @Override + public void onCreate() { + mProcessingTaskController = new ProcessingTaskController(this); + mImageSavingTask = new ImageSavingTask(this); + mProcessingTaskController.add(mImageSavingTask); + setupPipeline(); + } + + @Override + public void onDestroy() { + tearDownPipeline(); + mProcessingTaskController.quit(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + mNeedsAlive = true; + if (intent != null && intent.getBooleanExtra(SAVING, false)) { + // we save using an intent to keep the service around after the + // activity has been destroyed. + String presetJson = intent.getStringExtra(PRESET); + String source = intent.getStringExtra(SOURCE_URI); + String selected = intent.getStringExtra(SELECTED_URI); + String destination = intent.getStringExtra(DESTINATION_FILE); + Uri sourceUri = Uri.parse(source); + Uri selectedUri = null; + if (selected != null) { + selectedUri = Uri.parse(selected); + } + File destinationFile = null; + if (destination != null) { + destinationFile = new File(destination); + } + ImagePreset preset = new ImagePreset(); + preset.readJsonFromString(presetJson); + mNeedsAlive = false; + mSaving = true; + handleSaveRequest(sourceUri, selectedUri, destinationFile, preset); + } + return START_REDELIVER_INTENT; + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + public void onStart() { + mNeedsAlive = true; + if (!mSaving && mFiltershowActivity != null) { + mFiltershowActivity.updateUIAfterServiceStarted(); + } + } + + public void handleSaveRequest(Uri sourceUri, Uri selectedUri, + File destinationFile, ImagePreset preset) { + mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + + mNotificationId++; + + mBuilder = + new Notification.Builder(this) + .setSmallIcon(R.drawable.filtershow_button_fx) + .setContentTitle(getString(R.string.filtershow_notification_label)) + .setContentText(getString(R.string.filtershow_notification_message)); + + startForeground(mNotificationId, mBuilder.build()); + + updateProgress(SaveImage.MAX_PROCESSING_STEPS, 0); + + // Process the image + + mImageSavingTask.saveImage(sourceUri, selectedUri, destinationFile, preset); + } + + public void updateNotificationWithBitmap(Bitmap bitmap) { + mBuilder.setLargeIcon(bitmap); + mNotifyMgr.notify(mNotificationId, mBuilder.build()); + } + + public void updateProgress(int max, int current) { + mBuilder.setProgress(max, current, false); + mNotifyMgr.notify(mNotificationId, mBuilder.build()); + } + + public void completeSaveImage(Uri result) { + if (SHOW_IMAGE) { + // TODO: we should update the existing image in Gallery instead + Intent viewImage = new Intent(Intent.ACTION_VIEW, result); + viewImage.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(viewImage); + } + stopForeground(true); + stopSelf(); + if (mNeedsAlive) { + // If the app has been restarted while we were saving... + mFiltershowActivity.updateUIAfterServiceStarted(); + } else if (mFiltershowActivity.isSimpleEditAction()) { + // terminate now + mFiltershowActivity.completeSaveImage(result); + } + } + + private void setupPipeline() { + Resources res = getResources(); + FiltersManager.setResources(res); + CachingPipeline.createRenderscriptContext(this); + + FiltersManager filtersManager = FiltersManager.getManager(); + filtersManager.addLooks(this); + filtersManager.addBorders(this); + filtersManager.addTools(this); + filtersManager.addEffects(); + } + + private void tearDownPipeline() { + FilteringPipeline.getPipeline().turnOnPipeline(false); + FilteringPipeline.reset(); + ImageFilter.resetStatics(); + FiltersManager.getPreviewManager().freeRSFilterScripts(); + FiltersManager.getManager().freeRSFilterScripts(); + FiltersManager.getHighresManager().freeRSFilterScripts(); + FiltersManager.reset(); + CachingPipeline.destroyRenderScriptContext(); + } + + static { + System.loadLibrary("jni_filtershow_filters"); + } +} diff --git a/src/com/android/gallery3d/filtershow/pipeline/ProcessingTask.java b/src/com/android/gallery3d/filtershow/pipeline/ProcessingTask.java new file mode 100644 index 000000000..c3687ee6a --- /dev/null +++ b/src/com/android/gallery3d/filtershow/pipeline/ProcessingTask.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.filtershow.pipeline; + +import android.content.Context; +import android.os.Handler; +import android.os.Message; + +public abstract class ProcessingTask { + private ProcessingTaskController mTaskController; + private Handler mProcessingHandler; + private Handler mResultHandler; + private int mType; + + static interface Request {} + static interface Update {} + static interface Result {} + + public void postRequest(Request message) { + Message msg = mProcessingHandler.obtainMessage(mType); + msg.obj = message; + mProcessingHandler.sendMessage(msg); + } + + public void postUpdate(Update message) { + Message msg = mResultHandler.obtainMessage(mType); + msg.obj = message; + msg.arg1 = ProcessingTaskController.UPDATE; + mResultHandler.sendMessage(msg); + } + + public void processRequest(Request message) { + Object result = doInBackground(message); + Message msg = mResultHandler.obtainMessage(mType); + msg.obj = result; + msg.arg1 = ProcessingTaskController.RESULT; + mResultHandler.sendMessage(msg); + } + + public void added(ProcessingTaskController taskController) { + mTaskController = taskController; + mResultHandler = taskController.getResultHandler(); + mProcessingHandler = taskController.getProcessingHandler(); + mType = taskController.getReservedType(); + } + + public int getType() { + return mType; + } + + public Context getContext() { + return mTaskController.getContext(); + } + + public abstract Result doInBackground(Request message); + public abstract void onResult(Result message); + public void onUpdate(Update message) {} +} diff --git a/src/com/android/gallery3d/filtershow/pipeline/ProcessingTaskController.java b/src/com/android/gallery3d/filtershow/pipeline/ProcessingTaskController.java new file mode 100644 index 000000000..218ea630a --- /dev/null +++ b/src/com/android/gallery3d/filtershow/pipeline/ProcessingTaskController.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.filtershow.pipeline; + +import android.content.Context; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Message; +import android.util.Log; + +import java.util.HashMap; + +public class ProcessingTaskController implements Handler.Callback { + private static final String LOGTAG = "ProcessingTaskController"; + + private Context mContext; + private HandlerThread mHandlerThread = null; + private Handler mProcessingHandler = null; + private int mCurrentType; + private HashMap<Integer, ProcessingTask> mTasks = new HashMap<Integer, ProcessingTask>(); + + public final static int RESULT = 1; + public final static int UPDATE = 2; + + private final Handler mResultHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + ProcessingTask task = mTasks.get(msg.what); + if (task != null) { + if (msg.arg1 == RESULT) { + task.onResult((ProcessingTask.Result) msg.obj); + } else if (msg.arg1 == UPDATE) { + task.onUpdate((ProcessingTask.Update) msg.obj); + } else { + Log.w(LOGTAG, "received unknown message! " + msg.arg1); + } + } + } + }; + + @Override + public boolean handleMessage(Message msg) { + ProcessingTask task = mTasks.get(msg.what); + if (task != null) { + task.processRequest((ProcessingTask.Request) msg.obj); + return true; + } + return false; + } + + public ProcessingTaskController(Context context) { + mContext = context; + mHandlerThread = new HandlerThread("ProcessingTaskController", + android.os.Process.THREAD_PRIORITY_FOREGROUND); + mHandlerThread.start(); + mProcessingHandler = new Handler(mHandlerThread.getLooper(), this); + } + + public Handler getProcessingHandler() { + return mProcessingHandler; + } + + public Handler getResultHandler() { + return mResultHandler; + } + + public int getReservedType() { + return mCurrentType++; + } + + public Context getContext() { + return mContext; + } + + public void add(ProcessingTask task) { + task.added(this); + mTasks.put(task.getType(), task); + } + + public void quit() { + mHandlerThread.quit(); + } +} |