1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php 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 package com.android.ide.eclipse.adt.internal.editors.layout.gle2; 17 18 import static com.android.SdkConstants.FD_RESOURCES; 19 import static com.android.SdkConstants.FD_RES_ANIMATOR; 20 import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP; 21 22 import com.android.ide.common.rendering.api.Capability; 23 import com.android.ide.common.rendering.api.IAnimationListener; 24 import com.android.ide.common.rendering.api.RenderSession; 25 import com.android.ide.common.rendering.api.Result; 26 import com.android.ide.eclipse.adt.AdtPlugin; 27 import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; 28 import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileWizard; 29 import com.android.resources.ResourceType; 30 import com.android.utils.Pair; 31 32 import org.eclipse.core.resources.IProject; 33 import org.eclipse.jface.action.Action; 34 import org.eclipse.jface.action.ActionContributionItem; 35 import org.eclipse.jface.action.IAction; 36 import org.eclipse.jface.action.Separator; 37 import org.eclipse.jface.viewers.IStructuredSelection; 38 import org.eclipse.jface.viewers.StructuredSelection; 39 import org.eclipse.jface.wizard.WizardDialog; 40 import org.eclipse.swt.widgets.Menu; 41 import org.eclipse.swt.widgets.Shell; 42 import org.eclipse.ui.IWorkbench; 43 import org.eclipse.ui.IWorkbenchWindow; 44 45 import java.util.ArrayList; 46 import java.util.Collection; 47 import java.util.Collections; 48 import java.util.List; 49 50 /** 51 * "Play Animation" context menu which lists available animations in the project and in 52 * the framework, as well as a "Create Animation" shortcut, and allows the animation to be 53 * run on the selection 54 * <p/> 55 * TODO: Add transport controls for play/rewind/pause/loop, and (if possible) scrubbing 56 */ 57 public class PlayAnimationMenu extends SubmenuAction { 58 /** Associated canvas */ 59 private final LayoutCanvas mCanvas; 60 /** Whether this menu is showing local animations or framework animations */ 61 private boolean mFramework; 62 63 /** 64 * Creates a "Play Animation" menu 65 * 66 * @param canvas associated canvas 67 */ 68 public PlayAnimationMenu(LayoutCanvas canvas) { 69 this(canvas, "Play Animation", false); 70 } 71 72 /** 73 * Creates an animation menu; this can be used either for the outer Play animation 74 * menu, or the inner frameworks-animations list 75 * 76 * @param canvas the associated canvas 77 * @param title menu item name 78 * @param framework true to show the framework animations, false for the project (and 79 * nested framework-animation-menu) animations 80 */ 81 private PlayAnimationMenu(LayoutCanvas canvas, String title, boolean framework) { 82 super(title); 83 mCanvas = canvas; 84 mFramework = framework; 85 } 86 87 @Override 88 protected void addMenuItems(Menu menu) { 89 SelectionManager selectionManager = mCanvas.getSelectionManager(); 90 List<SelectionItem> selection = selectionManager.getSelections(); 91 if (selection.size() != 1) { 92 addDisabledMessageItem("Select exactly one widget"); 93 return; 94 } 95 96 GraphicalEditorPart graphicalEditor = mCanvas.getEditorDelegate().getGraphicalEditor(); 97 if (graphicalEditor.renderingSupports(Capability.PLAY_ANIMATION)) { 98 // List of animations 99 Collection<String> animationNames = graphicalEditor.getResourceNames(mFramework, 100 ResourceType.ANIMATOR); 101 if (animationNames.size() > 0) { 102 // Sort alphabetically 103 List<String> sortedNames = new ArrayList<String>(animationNames); 104 Collections.sort(sortedNames); 105 106 for (String animation : sortedNames) { 107 String title = animation; 108 IAction action = new PlayAnimationAction(title, animation, mFramework); 109 new ActionContributionItem(action).fill(menu, -1); 110 } 111 112 new Separator().fill(menu, -1); 113 } 114 115 if (!mFramework) { 116 // Not in the framework submenu: include recent list and create new actions 117 118 // "Create New" action 119 new ActionContributionItem(new CreateAnimationAction()).fill(menu, -1); 120 121 // Framework resources submenu 122 new Separator().fill(menu, -1); 123 PlayAnimationMenu sub = new PlayAnimationMenu(mCanvas, "Android Builtin", true); 124 new ActionContributionItem(sub).fill(menu, -1); 125 } 126 } else { 127 addDisabledMessageItem( 128 "Not supported for this SDK version; try changing the Render Target"); 129 } 130 } 131 132 private class PlayAnimationAction extends Action { 133 private final String mAnimationName; 134 private final boolean mIsFrameworkAnim; 135 136 public PlayAnimationAction(String title, String animationName, boolean isFrameworkAnim) { 137 super(title, IAction.AS_PUSH_BUTTON); 138 mAnimationName = animationName; 139 mIsFrameworkAnim = isFrameworkAnim; 140 } 141 142 @Override 143 public void run() { 144 SelectionManager selectionManager = mCanvas.getSelectionManager(); 145 List<SelectionItem> selection = selectionManager.getSelections(); 146 SelectionItem canvasSelection = selection.get(0); 147 CanvasViewInfo info = canvasSelection.getViewInfo(); 148 149 Object viewObject = info.getViewObject(); 150 if (viewObject != null) { 151 ViewHierarchy viewHierarchy = mCanvas.getViewHierarchy(); 152 RenderSession session = viewHierarchy.getSession(); 153 Result r = session.animate(viewObject, mAnimationName, mIsFrameworkAnim, 154 new IAnimationListener() { 155 private boolean mPendingDrawing = false; 156 157 @Override 158 public void onNewFrame(RenderSession s) { 159 SelectionOverlay selectionOverlay = mCanvas.getSelectionOverlay(); 160 if (!selectionOverlay.isHiding()) { 161 selectionOverlay.setHiding(true); 162 } 163 HoverOverlay hoverOverlay = mCanvas.getHoverOverlay(); 164 if (!hoverOverlay.isHiding()) { 165 hoverOverlay.setHiding(true); 166 } 167 168 ImageOverlay imageOverlay = mCanvas.getImageOverlay(); 169 imageOverlay.setImage(s.getImage(), s.isAlphaChannelImage()); 170 synchronized (this) { 171 if (mPendingDrawing == false) { 172 mCanvas.getDisplay().asyncExec(new Runnable() { 173 @Override 174 public void run() { 175 synchronized (this) { 176 mPendingDrawing = false; 177 } 178 mCanvas.redraw(); 179 } 180 }); 181 mPendingDrawing = true; 182 } 183 } 184 } 185 186 @Override 187 public boolean isCanceled() { 188 return false; 189 } 190 191 @Override 192 public void done(Result result) { 193 SelectionOverlay selectionOverlay = mCanvas.getSelectionOverlay(); 194 selectionOverlay.setHiding(false); 195 HoverOverlay hoverOverlay = mCanvas.getHoverOverlay(); 196 hoverOverlay.setHiding(false); 197 198 // Must refresh view hierarchy to force objects back to 199 // their original positions in case animations have left 200 // them elsewhere 201 mCanvas.getDisplay().asyncExec(new Runnable() { 202 @Override 203 public void run() { 204 GraphicalEditorPart graphicalEditor = mCanvas 205 .getEditorDelegate().getGraphicalEditor(); 206 graphicalEditor.recomputeLayout(); 207 } 208 }); 209 } 210 }); 211 212 if (!r.isSuccess()) { 213 if (r.getErrorMessage() != null) { 214 AdtPlugin.log(r.getException(), r.getErrorMessage()); 215 } 216 } 217 } 218 } 219 } 220 221 /** 222 * Action which brings up the "Create new XML File" wizard, pre-selected with the 223 * animation category 224 */ 225 private class CreateAnimationAction extends Action { 226 public CreateAnimationAction() { 227 super("Create...", IAction.AS_PUSH_BUTTON); 228 } 229 230 @Override 231 public void run() { 232 Shell parent = mCanvas.getShell(); 233 NewXmlFileWizard wizard = new NewXmlFileWizard(); 234 LayoutEditorDelegate editor = mCanvas.getEditorDelegate(); 235 IWorkbenchWindow workbenchWindow = 236 editor.getEditor().getEditorSite().getWorkbenchWindow(); 237 IWorkbench workbench = workbenchWindow.getWorkbench(); 238 String animationDir = FD_RESOURCES + WS_SEP + FD_RES_ANIMATOR; 239 Pair<IProject, String> pair = Pair.of(editor.getEditor().getProject(), animationDir); 240 IStructuredSelection selection = new StructuredSelection(pair); 241 wizard.init(workbench, selection); 242 WizardDialog dialog = new WizardDialog(parent, wizard); 243 dialog.create(); 244 dialog.open(); 245 } 246 } 247 } 248