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.photoeditor; 18 19 import android.content.Context; 20 import android.graphics.Bitmap; 21 import android.net.Uri; 22 import android.util.AttributeSet; 23 import android.view.MotionEvent; 24 import android.widget.RelativeLayout; 25 26 import com.android.photoeditor.animation.FadeAnimation; 27 import com.android.photoeditor.animation.Rotate3DAnimation; 28 import com.android.photoeditor.animation.Rotate3DAnimation.Rotate; 29 30 /** 31 * Toolbar that contains all tools and handles all operations for editing photo. 32 */ 33 public class Toolbar extends RelativeLayout { 34 35 private static final int UNDO_REDO_ANIMATION_DURATION = 100; 36 private static final int QUICKVIEW_ANIMATION_DURATION = 150; 37 38 private final FilterStack filterStack = new FilterStack(); 39 private ToolbarLayoutHandler layoutHandler; 40 private ToolbarIdleHandler idleHandler; 41 private EffectsBar effectsBar; 42 private ActionBar actionBar; 43 private PhotoView photoView; 44 private SpinnerProgressDialog progressDialog; 45 private Uri sourceUri; 46 Toolbar(Context context, AttributeSet attrs)47 public Toolbar(Context context, AttributeSet attrs) { 48 super(context, attrs); 49 } 50 51 @Override onInterceptTouchEvent(MotionEvent ev)52 public boolean onInterceptTouchEvent(MotionEvent ev) { 53 idleHandler.killIdle(); 54 return false; 55 } 56 57 @Override onLayout(boolean changed, int l, int t, int r, int b)58 protected void onLayout(boolean changed, int l, int t, int r, int b) { 59 super.onLayout(changed, l, t, r, b); 60 61 layoutHandler.extraLayout(l, t, r, b); 62 } 63 initialize()64 public void initialize() { 65 photoView = (PhotoView) findViewById(R.id.photo_view); 66 initializeEffectsBar(); 67 initializeActionBar(); 68 layoutHandler = new ToolbarLayoutHandler(this); 69 idleHandler = new ToolbarIdleHandler(this); 70 idleHandler.killIdle(); 71 } 72 initializeEffectsBar()73 private void initializeEffectsBar() { 74 effectsBar = (EffectsBar) findViewById(R.id.effects_bar); 75 effectsBar.initialize(filterStack, photoView, this); 76 } 77 initializeActionBar()78 private void initializeActionBar() { 79 final PhotoOutputCallback callback = new PhotoOutputCallback() { 80 81 @Override 82 public void onReady(Photo photo) { 83 photoView.setTransitionAnimations( 84 FadeAnimation.getFadeOutInAnimations(UNDO_REDO_ANIMATION_DURATION)); 85 photoView.update(photo); 86 progressDialog.dismiss(); 87 } 88 }; 89 90 actionBar = (ActionBar) findViewById(R.id.action_bar); 91 actionBar.initialize(new ActionBar.ActionBarListener() { 92 93 @Override 94 public void onUndo() { 95 effectsBar.effectsOff(new Runnable() { 96 97 @Override 98 public void run() { 99 progressDialog = SpinnerProgressDialog.show(Toolbar.this); 100 filterStack.undo(callback); 101 } 102 }); 103 } 104 105 @Override 106 public void onRedo() { 107 effectsBar.effectsOff(new Runnable() { 108 109 @Override 110 public void run() { 111 progressDialog = SpinnerProgressDialog.show(Toolbar.this); 112 filterStack.redo(callback); 113 } 114 }); 115 } 116 117 @Override 118 public void onQuickview(final boolean on) { 119 final PhotoOutputCallback callback = new PhotoOutputCallback() { 120 121 @Override 122 public void onReady(Photo photo) { 123 photoView.setTransitionAnimations(Rotate3DAnimation.getFlipAnimations( 124 on ? Rotate.RIGHT : Rotate.LEFT, QUICKVIEW_ANIMATION_DURATION)); 125 photoView.update(photo); 126 } 127 }; 128 129 if (on) { 130 effectsBar.effectsOff(new Runnable() { 131 132 @Override 133 public void run() { 134 effectsBar.setVisibility(INVISIBLE); 135 filterStack.getSourceCopy(callback); 136 } 137 }); 138 } else { 139 effectsBar.setVisibility(VISIBLE); 140 filterStack.getResultCopy(callback); 141 } 142 } 143 144 @Override 145 public void onSave() { 146 effectsBar.effectsOff(new Runnable() { 147 148 @Override 149 public void run() { 150 savePhoto(null); 151 } 152 }); 153 } 154 }); 155 } 156 openPhoto(Uri uri)157 public void openPhoto(Uri uri) { 158 sourceUri = uri; 159 filterStack.setStackListener(actionBar); 160 161 // clearPhotoSource() should be called before loading a new source photo to avoid OOM. 162 progressDialog = SpinnerProgressDialog.show(this); 163 filterStack.clearPhotoSource(); 164 new LoadScreennailTask(getContext(), new LoadScreennailTask.Callback() { 165 166 @Override 167 public void onComplete(Bitmap bitmap) { 168 filterStack.setPhotoSource(Photo.create(bitmap)); 169 filterStack.getResultCopy(new PhotoOutputCallback() { 170 171 @Override 172 public void onReady(Photo photo) { 173 photoView.update(photo); 174 progressDialog.dismiss(); 175 effectsBar.setEnabled(photo != null); 176 } 177 }); 178 } 179 }).execute(sourceUri); 180 } 181 182 /** 183 * Saves photo and executes runnable (if provided) after saving done. 184 */ savePhoto(final Runnable runnable)185 public void savePhoto(final Runnable runnable) { 186 progressDialog = SpinnerProgressDialog.show(this); 187 filterStack.getResultCopy(new PhotoOutputCallback() { 188 189 @Override 190 public void onReady(Photo photo) { 191 new SaveCopyTask(getContext(), sourceUri, new SaveCopyTask.Callback() { 192 193 @Override 194 public void onComplete(Uri uri) { 195 // TODO: Handle saving failure. 196 progressDialog.dismiss(); 197 actionBar.disableSave(); 198 if (runnable != null) { 199 runnable.run(); 200 } 201 } 202 }).execute(photo); 203 } 204 }); 205 } 206 } 207