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    @Deprecated
84    public abstract View onCreateActionView();
85
86    /**
87     * Factory method called by the Android framework to create new action views.
88     * This method returns a new action view for the given MenuItem.
89     *
90     * <p>If your ActionProvider implementation overrides the deprecated no-argument overload
91     * {@link #onCreateActionView()}, overriding this method for devices running API 16 or later
92     * is recommended but optional. The default implementation calls {@link #onCreateActionView()}
93     * for compatibility with applications written for older platform versions.</p>
94     *
95     * @param forItem MenuItem to create the action view for
96     * @return the new action view
97     */
98    public View onCreateActionView(MenuItem forItem) {
99        return onCreateActionView();
100    }
101
102    /**
103     * The result of this method determines whether or not {@link #isVisible()} will be used
104     * by the {@link MenuItem} this ActionProvider is bound to help determine its visibility.
105     *
106     * @return true if this ActionProvider overrides the visibility of the MenuItem
107     *         it is bound to, false otherwise. The default implementation returns false.
108     * @see #isVisible()
109     */
110    public boolean overridesItemVisibility() {
111        return false;
112    }
113
114    /**
115     * If {@link #overridesItemVisibility()} returns true, the return value of this method
116     * will help determine the visibility of the {@link MenuItem} this ActionProvider is bound to.
117     *
118     * <p>If the MenuItem's visibility is explicitly set to false by the application,
119     * the MenuItem will not be shown, even if this method returns true.</p>
120     *
121     * @return true if the MenuItem this ActionProvider is bound to is visible, false if
122     *         it is invisible. The default implementation returns true.
123     */
124    public boolean isVisible() {
125        return true;
126    }
127
128    /**
129     * If this ActionProvider is associated with an item in a menu,
130     * refresh the visibility of the item based on {@link #overridesItemVisibility()} and
131     * {@link #isVisible()}. If {@link #overridesItemVisibility()} returns false, this call
132     * will have no effect.
133     */
134    public void refreshVisibility() {
135        if (mVisibilityListener != null && overridesItemVisibility()) {
136            mVisibilityListener.onActionProviderVisibilityChanged(isVisible());
137        }
138    }
139
140    /**
141     * Performs an optional default action.
142     * <p>
143     * For the case of an action provider placed in a menu item not shown as an action this
144     * method is invoked if previous callbacks for processing menu selection has handled
145     * the event.
146     * </p>
147     * <p>
148     * A menu item selection is processed in the following order:
149     * <ul>
150     * <li>
151     * Receiving a call to {@link MenuItem.OnMenuItemClickListener#onMenuItemClick
152     *  MenuItem.OnMenuItemClickListener.onMenuItemClick}.
153     * </li>
154     * <li>
155     * Receiving a call to {@link android.app.Activity#onOptionsItemSelected(MenuItem)
156     *  Activity.onOptionsItemSelected(MenuItem)}
157     * </li>
158     * <li>
159     * Receiving a call to {@link android.app.Fragment#onOptionsItemSelected(MenuItem)
160     *  Fragment.onOptionsItemSelected(MenuItem)}
161     * </li>
162     * <li>
163     * Launching the {@link android.content.Intent} set via
164     * {@link MenuItem#setIntent(android.content.Intent) MenuItem.setIntent(android.content.Intent)}
165     * </li>
166     * <li>
167     * Invoking this method.
168     * </li>
169     * </ul>
170     * </p>
171     * <p>
172     * The default implementation does not perform any action and returns false.
173     * </p>
174     */
175    public boolean onPerformDefaultAction() {
176        return false;
177    }
178
179    /**
180     * Determines if this ActionProvider has a submenu associated with it.
181     *
182     * <p>Associated submenus will be shown when an action view is not. This
183     * provider instance will receive a call to {@link #onPrepareSubMenu(SubMenu)}
184     * after the call to {@link #onPerformDefaultAction()} and before a submenu is
185     * displayed to the user.
186     *
187     * @return true if the item backed by this provider should have an associated submenu
188     */
189    public boolean hasSubMenu() {
190        return false;
191    }
192
193    /**
194     * Called to prepare an associated submenu for the menu item backed by this ActionProvider.
195     *
196     * <p>if {@link #hasSubMenu()} returns true, this method will be called when the
197     * menu item is selected to prepare the submenu for presentation to the user. Apps
198     * may use this to create or alter submenu content right before display.
199     *
200     * @param subMenu Submenu that will be displayed
201     */
202    public void onPrepareSubMenu(SubMenu subMenu) {
203    }
204
205    /**
206     * Notify the system that the visibility of an action view's sub-UI such as
207     * an anchored popup has changed. This will affect how other system
208     * visibility notifications occur.
209     *
210     * @hide Pending future API approval
211     */
212    public void subUiVisibilityChanged(boolean isVisible) {
213        if (mSubUiVisibilityListener != null) {
214            mSubUiVisibilityListener.onSubUiVisibilityChanged(isVisible);
215        }
216    }
217
218    /**
219     * @hide Internal use only
220     */
221    public void setSubUiVisibilityListener(SubUiVisibilityListener listener) {
222        mSubUiVisibilityListener = listener;
223    }
224
225    /**
226     * Set a listener to be notified when this ActionProvider's overridden visibility changes.
227     * This should only be used by MenuItem implementations.
228     *
229     * @param listener listener to set
230     */
231    public void setVisibilityListener(VisibilityListener listener) {
232        if (mVisibilityListener != null) {
233            Log.w(TAG, "setVisibilityListener: Setting a new ActionProvider.VisibilityListener " +
234                    "when one is already set. Are you reusing this " + getClass().getSimpleName() +
235                    " instance while it is still in use somewhere else?");
236        }
237        mVisibilityListener = listener;
238    }
239
240    /**
241     * @hide
242     */
243    public void reset() {
244        mVisibilityListener = null;
245        mSubUiVisibilityListener = null;
246    }
247
248    /**
249     * @hide Internal use only
250     */
251    public interface SubUiVisibilityListener {
252        public void onSubUiVisibilityChanged(boolean isVisible);
253    }
254
255    /**
256     * Listens to changes in visibility as reported by {@link ActionProvider#refreshVisibility()}.
257     *
258     * @see ActionProvider#overridesItemVisibility()
259     * @see ActionProvider#isVisible()
260     */
261    public interface VisibilityListener {
262        public void onActionProviderVisibilityChanged(boolean isVisible);
263    }
264}
265