1 /* 2 * Copyright (C) 2010 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.photoeditor.actions; 18 19 import android.content.Context; 20 import android.util.AttributeSet; 21 import android.view.Gravity; 22 import android.view.View; 23 import android.widget.LinearLayout; 24 import android.widget.TextView; 25 import android.widget.Toast; 26 27 import com.android.gallery3d.R; 28 import com.android.gallery3d.photoeditor.FilterStack; 29 import com.android.gallery3d.photoeditor.OnDoneCallback; 30 import com.android.gallery3d.photoeditor.filters.Filter; 31 32 /** 33 * An action binding UI controls and effect operation for editing photo. 34 */ 35 public abstract class EffectAction extends LinearLayout { 36 37 /** 38 * Listener of effect action. 39 */ 40 public interface ActionListener { 41 42 /** 43 * Invoked when the action is okayed (effect is applied and completed). 44 */ onOk()45 void onOk(); 46 } 47 48 protected EffectToolKit toolKit; 49 private Toast tooltip; 50 private FilterStack filterStack; 51 private boolean pushedFilter; 52 private boolean disableFilterOutput; 53 private FilterChangedCallback lastFilterChangedCallback; 54 private ActionListener listener; 55 EffectAction(Context context, AttributeSet attrs)56 public EffectAction(Context context, AttributeSet attrs) { 57 super(context, attrs); 58 } 59 begin(View root, FilterStack filterStack, ActionListener listener)60 public void begin(View root, FilterStack filterStack, ActionListener listener) { 61 // This view is already detached from UI view hierarchy by reaching here; findViewById() 62 // could only access its own child views from here. 63 toolKit = new EffectToolKit(root, ((TextView) findViewById(R.id.effect_label)).getText()); 64 this.filterStack = filterStack; 65 this.listener = listener; 66 67 // Shows the tooltip if it's available. 68 if (getTag() != null) { 69 tooltip = Toast.makeText(getContext(), (String) getTag(), Toast.LENGTH_SHORT); 70 tooltip.setGravity(Gravity.CENTER, 0, 0); 71 tooltip.show(); 72 } 73 prepare(); 74 } 75 76 /** 77 * Subclasses should create a specific filter and bind the filter to necessary UI controls here 78 * when the action is about to begin. 79 */ prepare()80 protected abstract void prepare(); 81 82 /** 83 * Ends the effect and then executes the runnable after the effect is finished. 84 */ end(final Runnable runnableOnODone)85 public void end(final Runnable runnableOnODone) { 86 // Cancel the tooltip if it's still showing. 87 if ((tooltip != null) && (tooltip.getView().getParent() != null)) { 88 tooltip.cancel(); 89 tooltip = null; 90 } 91 // End tool editing by canceling unfinished touch events. 92 toolKit.cancel(); 93 // Output the pushed filter if it wasn't outputted. 94 if (pushedFilter && disableFilterOutput) { 95 outputFilter(); 96 } 97 98 // Wait till last output callback is done before finishing. 99 if ((lastFilterChangedCallback == null) || lastFilterChangedCallback.done) { 100 finish(runnableOnODone); 101 } else { 102 lastFilterChangedCallback.runnableOnReady = new Runnable() { 103 104 @Override 105 public void run() { 106 finish(runnableOnODone); 107 } 108 }; 109 } 110 } 111 finish(Runnable runnableOnDone)112 private void finish(Runnable runnableOnDone) { 113 toolKit.close(); 114 pushedFilter = false; 115 disableFilterOutput = false; 116 lastFilterChangedCallback = null; 117 118 runnableOnDone.run(); 119 } 120 disableFilterOutput()121 protected void disableFilterOutput() { 122 // Filter output won't be outputted until this effect has done editing its filter. 123 disableFilterOutput = true; 124 } 125 outputFilter()126 protected void outputFilter() { 127 // Notify the stack to execute the changed top filter and output the results. 128 lastFilterChangedCallback = new FilterChangedCallback(); 129 filterStack.topFilterChanged(lastFilterChangedCallback); 130 } 131 notifyChanged(Filter filter)132 protected void notifyChanged(Filter filter) { 133 if (!pushedFilter) { 134 filterStack.pushFilter(filter); 135 pushedFilter = true; 136 } 137 if (pushedFilter && !disableFilterOutput) { 138 outputFilter(); 139 } 140 } 141 notifyOk()142 protected void notifyOk() { 143 listener.onOk(); 144 } 145 146 /** 147 * Done callback for executing top filter changes. 148 */ 149 private class FilterChangedCallback implements OnDoneCallback { 150 151 private boolean done; 152 private Runnable runnableOnReady; 153 154 @Override onDone()155 public void onDone() { 156 done = true; 157 158 if (runnableOnReady != null) { 159 runnableOnReady.run(); 160 } 161 } 162 } 163 } 164