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 */
16package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
17
18import static com.android.SdkConstants.FD_RESOURCES;
19import static com.android.SdkConstants.FD_RES_ANIMATOR;
20import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP;
21
22import com.android.ide.common.rendering.api.Capability;
23import com.android.ide.common.rendering.api.IAnimationListener;
24import com.android.ide.common.rendering.api.RenderSession;
25import com.android.ide.common.rendering.api.Result;
26import com.android.ide.eclipse.adt.AdtPlugin;
27import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
28import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileWizard;
29import com.android.resources.ResourceType;
30import com.android.utils.Pair;
31
32import org.eclipse.core.resources.IProject;
33import org.eclipse.jface.action.Action;
34import org.eclipse.jface.action.ActionContributionItem;
35import org.eclipse.jface.action.IAction;
36import org.eclipse.jface.action.Separator;
37import org.eclipse.jface.viewers.IStructuredSelection;
38import org.eclipse.jface.viewers.StructuredSelection;
39import org.eclipse.jface.wizard.WizardDialog;
40import org.eclipse.swt.widgets.Menu;
41import org.eclipse.swt.widgets.Shell;
42import org.eclipse.ui.IWorkbench;
43import org.eclipse.ui.IWorkbenchWindow;
44
45import java.util.ArrayList;
46import java.util.Collection;
47import java.util.Collections;
48import 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 */
57public 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