1/*
2 * Copyright (C) 2011 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
17package android.view;
18
19import android.content.Context;
20import android.util.Log;
21
22/**
23 * An ActionProvider defines rich menu interaction in a single component.
24 * ActionProvider can generate action views for use in the action bar,
25 * dynamically populate submenus of a MenuItem, and handle default menu
26 * item invocations.
27 *
28 * <p>An ActionProvider can be optionally specified for a {@link MenuItem} and will be
29 * responsible for creating the action view that appears in the {@link android.app.ActionBar}
30 * in place of a simple button in the bar. When the menu item is presented in a way that
31 * does not allow custom action views, (e.g. in an overflow menu,) the ActionProvider
32 * can perform a default action.</p>
33 *
34 * <p>There are two ways to use an action provider:
35 * <ul>
36 * <li>
37 * Set the action provider on a {@link MenuItem} directly by calling
38 * {@link MenuItem#setActionProvider(ActionProvider)}.
39 * </li>
40 * <li>
41 * Declare the action provider in an XML menu resource. For example:
42 * <pre>
43 * <code>
44 *   &lt;item android:id="@+id/my_menu_item"
45 *     android:title="Title"
46 *     android:icon="@drawable/my_menu_item_icon"
47 *     android:showAsAction="ifRoom"
48 *     android:actionProviderClass="foo.bar.SomeActionProvider" /&gt;
49 * </code>
50 * </pre>
51 * </li>
52 * </ul>
53 * </p>
54 *
55 * @see MenuItem#setActionProvider(ActionProvider)
56 * @see MenuItem#getActionProvider()
57 */
58public abstract class ActionProvider {
59    private static final String TAG = "ActionProvider";
60    private SubUiVisibilityListener mSubUiVisibilityListener;
61    private VisibilityListener mVisibilityListener;
62
63    /**
64     * Creates a new instance. ActionProvider classes should always implement a
65     * constructor that takes a single Context parameter for inflating from menu XML.
66     *
67     * @param context Context for accessing resources.
68     */
69    public ActionProvider(Context context) {
70    }
71
72    /**
73     * Factory method called by the Android framework to create new action views.
74     *
75     * <p>This method has been deprecated in favor of {@link #onCreateActionView(MenuItem)}.
76     * Newer apps that wish to support platform versions prior to API 16 should also
77     * implement this method to return a valid action view.</p>
78     *
79     * @return A new action view.
80     *
81     * @deprecated use {@link #onCreateActionView(MenuItem)}
82     */
83    public abstract View onCreateActionView();
84
85    /**
86     * Factory method called by the Android framework to create new action views.
87     * This method returns a new action view for the given MenuItem.
88     *
89     * <p>If your ActionProvider implementation overrides the deprecated no-argument overload
90     * {@link #onCreateActionView()}, overriding this method for devices running API 16 or later
91     * is recommended but optional. The default implementation calls {@link #onCreateActionView()}
92     * for compatibility with applications written for older platform versions.</p>
93     *
94     * @param forItem MenuItem to create the action view for
95     * @return the new action view
96     */
97    public View onCreateActionView(MenuItem forItem) {
98        return onCreateActionView();
99    }
100
101    /**
102     * The result of this method determines whether or not {@link #isVisible()} will be used
103     * by the {@link MenuItem} this ActionProvider is bound to help determine its visibility.
104     *
105     * @return true if this ActionProvider overrides the visibility of the MenuItem
106     *         it is bound to, false otherwise. The default implementation returns false.
107     * @see #isVisible()
108     */
109    public boolean overridesItemVisibility() {
110        return false;
111    }
112
113    /**
114     * If {@link #overridesItemVisibility()} returns true, the return value of this method
115     * will help determine the visibility of the {@link MenuItem} this ActionProvider is bound to.
116     *
117     * <p>If the MenuItem's visibility is explicitly set to false by the application,
118     * the MenuItem will not be shown, even if this method returns true.</p>
119     *
120     * @return true if the MenuItem this ActionProvider is bound to is visible, false if
121     *         it is invisible. The default implementation returns true.
122     */
123    public boolean isVisible() {
124        return true;
125    }
126
127    /**
128     * If this ActionProvider is associated with an item in a menu,
129     * refresh the visibility of the item based on {@link #overridesItemVisibility()} and
130     * {@link #isVisible()}. If {@link #overridesItemVisibility()} returns false, this call
131     * will have no effect.
132     */
133    public void refreshVisibility() {
134        if (mVisibilityListener != null && overridesItemVisibility()) {
135            mVisibilityListener.onActionProviderVisibilityChanged(isVisible());
136        }
137    }
138
139    /**
140     * Performs an optional default action.
141     * <p>
142     * For the case of an action provider placed in a menu item not shown as an action this
143     * method is invoked if previous callbacks for processing menu selection has handled
144     * the event.
145     * </p>
146     * <p>
147     * A menu item selection is processed in the following order:
148     * <ul>
149     * <li>
150     * Receiving a call to {@link MenuItem.OnMenuItemClickListener#onMenuItemClick
151     *  MenuItem.OnMenuItemClickListener.onMenuItemClick}.
152     * </li>
153     * <li>
154     * Receiving a call to {@link android.app.Activity#onOptionsItemSelected(MenuItem)
155     *  Activity.onOptionsItemSelected(MenuItem)}
156     * </li>
157     * <li>
158     * Receiving a call to {@link android.app.Fragment#onOptionsItemSelected(MenuItem)
159     *  Fragment.onOptionsItemSelected(MenuItem)}
160     * </li>
161     * <li>
162     * Launching the {@link android.content.Intent} set via
163     * {@link MenuItem#setIntent(android.content.Intent) MenuItem.setIntent(android.content.Intent)}
164     * </li>
165     * <li>
166     * Invoking this method.
167     * </li>
168     * </ul>
169     * </p>
170     * <p>
171     * The default implementation does not perform any action and returns false.
172     * </p>
173     */
174    public boolean onPerformDefaultAction() {
175        return false;
176    }
177
178    /**
179     * Determines if this ActionProvider has a submenu associated with it.
180     *
181     * <p>Associated submenus will be shown when an action view is not. This
182     * provider instance will receive a call to {@link #onPrepareSubMenu(SubMenu)}
183     * after the call to {@link #onPerformDefaultAction()} and before a submenu is
184     * displayed to the user.
185     *
186     * @return true if the item backed by this provider should have an associated submenu
187     */
188    public boolean hasSubMenu() {
189        return false;
190    }
191
192    /**
193     * Called to prepare an associated submenu for the menu item backed by this ActionProvider.
194     *
195     * <p>if {@link #hasSubMenu()} returns true, this method will be called when the
196     * menu item is selected to prepare the submenu for presentation to the user. Apps
197     * may use this to create or alter submenu content right before display.
198     *
199     * @param subMenu Submenu that will be displayed
200     */
201    public void onPrepareSubMenu(SubMenu subMenu) {
202    }
203
204    /**
205     * Notify the system that the visibility of an action view's sub-UI such as
206     * an anchored popup has changed. This will affect how other system
207     * visibility notifications occur.
208     *
209     * @hide Pending future API approval
210     */
211    public void subUiVisibilityChanged(boolean isVisible) {
212        if (mSubUiVisibilityListener != null) {
213            mSubUiVisibilityListener.onSubUiVisibilityChanged(isVisible);
214        }
215    }
216
217    /**
218     * @hide Internal use only
219     */
220    public void setSubUiVisibilityListener(SubUiVisibilityListener listener) {
221        mSubUiVisibilityListener = listener;
222    }
223
224    /**
225     * Set a listener to be notified when this ActionProvider's overridden visibility changes.
226     * This should only be used by MenuItem implementations.
227     *
228     * @param listener listener to set
229     */
230    public void setVisibilityListener(VisibilityListener listener) {
231        if (mVisibilityListener != null) {
232            Log.w(TAG, "setVisibilityListener: Setting a new ActionProvider.VisibilityListener " +
233                    "when one is already set. Are you reusing this " + getClass().getSimpleName() +
234                    " instance while it is still in use somewhere else?");
235        }
236        mVisibilityListener = listener;
237    }
238
239    /**
240     * @hide
241     */
242    public void reset() {
243        mVisibilityListener = null;
244        mSubUiVisibilityListener = null;
245    }
246
247    /**
248     * @hide Internal use only
249     */
250    public interface SubUiVisibilityListener {
251        public void onSubUiVisibilityChanged(boolean isVisible);
252    }
253
254    /**
255     * Listens to changes in visibility as reported by {@link ActionProvider#refreshVisibility()}.
256     *
257     * @see ActionProvider#overridesItemVisibility()
258     * @see ActionProvider#isVisible()
259     */
260    public interface VisibilityListener {
261        public void onActionProviderVisibilityChanged(boolean isVisible);
262    }
263}
264