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