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