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 Listener { 41 42 void onClick(); 43 44 void onDone(); 45 } 46 47 protected EffectToolFactory factory; 48 49 private Listener listener; 50 private Toast tooltip; 51 private FilterStack filterStack; 52 private boolean pushedFilter; 53 private FilterChangedCallback lastFilterChangedCallback; 54 55 public EffectAction(Context context, AttributeSet attrs) { 56 super(context, attrs); 57 } 58 59 public void setListener(Listener l) { 60 listener = l; 61 findViewById(R.id.effect_button).setOnClickListener( 62 (listener == null) ? null : new View.OnClickListener() { 63 64 @Override 65 public void onClick(View v) { 66 listener.onClick(); 67 } 68 }); 69 } 70 71 public CharSequence name() { 72 return ((TextView) findViewById(R.id.effect_label)).getText(); 73 } 74 75 public void begin(FilterStack filterStack, EffectToolFactory factory) { 76 // This view is already detached from UI view hierarchy by reaching here; findViewById() 77 // could only access its own child views from here. 78 this.filterStack = filterStack; 79 this.factory = factory; 80 81 // Shows the tooltip if it's available. 82 if (getTag() != null) { 83 tooltip = Toast.makeText(getContext(), (String) getTag(), Toast.LENGTH_SHORT); 84 tooltip.setGravity(Gravity.CENTER, 0, 0); 85 tooltip.show(); 86 } 87 doBegin(); 88 } 89 90 /** 91 * Ends the effect and then executes the runnable after the effect is finished. 92 */ 93 public void end(final Runnable runnableOnODone) { 94 doEnd(); 95 96 // Wait till last output callback is done before finishing. 97 if ((lastFilterChangedCallback == null) || lastFilterChangedCallback.done) { 98 finish(runnableOnODone); 99 } else { 100 lastFilterChangedCallback.runnableOnReady = new Runnable() { 101 102 @Override 103 public void run() { 104 finish(runnableOnODone); 105 } 106 }; 107 } 108 } 109 110 private void finish(Runnable runnableOnDone) { 111 // Close the tooltip if it's still showing. 112 if ((tooltip != null) && (tooltip.getView().getParent() != null)) { 113 tooltip.cancel(); 114 tooltip = null; 115 } 116 pushedFilter = false; 117 lastFilterChangedCallback = null; 118 119 runnableOnDone.run(); 120 } 121 122 protected void notifyDone() { 123 if (listener != null) { 124 listener.onDone(); 125 } 126 } 127 128 protected void notifyFilterChanged(Filter filter, boolean output) { 129 if (!pushedFilter && filter.isValid()) { 130 filterStack.pushFilter(filter); 131 pushedFilter = true; 132 } 133 if (pushedFilter && output) { 134 // Notify the stack to execute the changed top filter and output the results. 135 lastFilterChangedCallback = new FilterChangedCallback(); 136 filterStack.topFilterChanged(lastFilterChangedCallback); 137 } 138 } 139 140 /** 141 * Subclasses should creates a specific filter and binds the filter to necessary UI controls 142 * here when the action is about to begin. 143 */ 144 protected abstract void doBegin(); 145 146 /** 147 * Subclasses could do specific ending operations here when the action is about to end. 148 */ 149 protected abstract void doEnd(); 150 151 /** 152 * Done callback for executing top filter changes. 153 */ 154 private class FilterChangedCallback implements OnDoneCallback { 155 156 private boolean done; 157 private Runnable runnableOnReady; 158 159 @Override 160 public void onDone() { 161 done = true; 162 163 if (runnableOnReady != null) { 164 runnableOnReady.run(); 165 } 166 } 167 } 168 } 169