Home | History | Annotate | Download | only in menu
      1 /*
      2  * Copyright (C) 2015 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.support.car.app.menu;
     18 
     19 import android.graphics.Bitmap;
     20 import android.graphics.drawable.Drawable;
     21 import android.os.Bundle;
     22 import android.support.car.app.menu.compat.CarMenuConstantsComapt.MenuItemConstants;
     23 import android.util.DisplayMetrics;
     24 import android.widget.RemoteViews;
     25 import java.util.ArrayList;
     26 import java.util.List;
     27 
     28 /**
     29  * CarMenu is used to pass back the menu items of a sublevel to the CarMenu subscriber.
     30  * Use the {@link Builder} to populate the contents of the sublevel.
     31  * @hide
     32  */
     33 public class CarMenu {
     34     private boolean mDetachCalled;
     35     private boolean mSendResultCalled;
     36     private final DisplayMetrics mMetrics;
     37 
     38     public CarMenu(DisplayMetrics metrics) {
     39         mMetrics = metrics;
     40     }
     41 
     42     /**
     43      * Send the result back to the caller.
     44      */
     45     public void sendResult(List<Item> results) {
     46         if (mSendResultCalled) {
     47             throw new IllegalStateException("sendResult() called twice.");
     48         }
     49         mSendResultCalled = true;
     50 
     51         List<Bundle> resultBundle = new ArrayList<>();
     52         for (Item item : results) {
     53             ItemImpl impl = (ItemImpl) item;
     54             if (impl.mIcon != null) {
     55                 impl.mBundle.putParcelable(MenuItemConstants.KEY_LEFTICON, snapshot(impl.mIcon));
     56             }
     57             if (impl.mRightIcon != null) {
     58                 impl.mBundle.putParcelable(
     59                         MenuItemConstants.KEY_RIGHTICON, snapshot(impl.mRightIcon));
     60             }
     61             resultBundle.add(impl.mBundle);
     62         }
     63         onResultReady(resultBundle);
     64     }
     65 
     66     private Bitmap snapshot(Drawable drawable) {
     67         return Utils.snapshot(mMetrics, drawable);
     68     }
     69 
     70     /**
     71      * Detach this message from the current thread and allow the {@link #sendResult}
     72      * call to happen later. This stops blocking the current thread.
     73      */
     74     public void detach() {
     75         if (mDetachCalled) {
     76             throw new IllegalStateException("detach() called when detach() had already"
     77                     + " been called.");
     78         }
     79         if (mSendResultCalled) {
     80             throw new IllegalStateException("detach() called when sendResult() had already"
     81                     + " been called");
     82         }
     83         mDetachCalled = true;
     84     }
     85 
     86     /**
     87      * Returns whether results were actually sent.
     88      *
     89      * @return {@code true} if {@link #sendResult(java.util.List)} or {@link #detach()} has been called.
     90      * @hide
     91      */
     92     public boolean isDone() {
     93         return mDetachCalled || mSendResultCalled;
     94     }
     95 
     96     /**
     97      * Called when the result is sent, after assertions about not being called twice
     98      * have happened.
     99      * @hide
    100      */
    101     protected void onResultReady(List<Bundle> result) {
    102     }
    103 
    104     /**
    105      * An individual item in a menu.
    106      */
    107     public interface Item {
    108         /**
    109          * Gets the id of the menu item.
    110          *
    111          * @return The id of the menu item.
    112          */
    113         String getId();
    114 
    115         /**
    116          * Gets the title of the menu item.
    117          *
    118          * @return The title of the menu item. {@code null} if there is no title.
    119          */
    120         String getTitle();
    121 
    122         /**
    123          * Gets the text of the menu item.
    124          *
    125          * @return The text of the menu item. {@code null} if there is no text.
    126          */
    127         String getText();
    128 
    129         /**
    130          * Gets the integer constant for the widget.
    131          *
    132          * @return Either {@link MenuItemConstants.WidgetTypes#WIDGET_CHECKBOX} or -1,
    133          *         if no widget was set.
    134          */
    135         int getWidget();
    136 
    137         /**
    138          * Gets the widget state. The return value is only valid if a widget was set.
    139          *
    140          * @return {@code true} if the widget is enabled, {@code false} if the widget is disabled.
    141          */
    142         boolean getWidgetState();
    143 
    144         /**
    145          * Gets the flags set for this menu item.
    146          *
    147          * @return The flags set.
    148          */
    149         int getFlags();
    150     }
    151 
    152     /** @hide */
    153     static class ItemImpl implements Item {
    154         final Bundle mBundle;
    155         final Drawable mIcon;
    156         final Drawable mRightIcon;
    157 
    158         ItemImpl(Bundle bundle, Drawable icon, Drawable rightIcon) {
    159             mBundle = bundle;
    160             mIcon = icon;
    161             mRightIcon = rightIcon;
    162         }
    163 
    164         @Override
    165         public String getId() {
    166             return mBundle.getString(MenuItemConstants.KEY_ID);
    167         }
    168 
    169         @Override
    170         public String getTitle() {
    171             return mBundle.getString(MenuItemConstants.KEY_TITLE);
    172         }
    173 
    174         @Override
    175         public String getText() {
    176             return mBundle.getString(MenuItemConstants.KEY_TEXT);
    177         }
    178 
    179         @Override
    180         public int getWidget() {
    181             return mBundle.getInt(MenuItemConstants.KEY_WIDGET, -1);
    182         }
    183 
    184         @Override
    185         public boolean getWidgetState() {
    186             return mBundle.getBoolean(MenuItemConstants.KEY_WIDGET_STATE);
    187         }
    188 
    189         @Override
    190         public int getFlags() {
    191             return mBundle.getInt(MenuItemConstants.KEY_FLAGS);
    192         }
    193     }
    194 
    195     /**
    196      * Builder to build an {@link Item}. Calls to the builder can be chained.
    197      */
    198     public static class Builder {
    199         private final Bundle mBundle = new Bundle();
    200         // Drawable icons that will later be turned into Bitmaps and inserted into the Bundle
    201         private Drawable mIcon;
    202         private Drawable mRightIcon;
    203 
    204         /**
    205          * Construct a Builder with a specific id.
    206          *
    207          * @param id Unique id used to identify this menu item. If it is browsable, then it
    208          * will also be used to fetch this item's submenu.
    209          */
    210         public Builder(String id) {
    211             if (id == null) {
    212                 throw new IllegalStateException("Cannot pass a null id to the Builder.");
    213             }
    214             mBundle.putString(MenuItemConstants.KEY_ID, id);
    215         }
    216 
    217         /**
    218          * Sets the title.
    219          *
    220          * @param title Title to set
    221          * @return This to chain calls
    222          */
    223         public Builder setTitle(String title) {
    224             mBundle.putString(MenuItemConstants.KEY_TITLE, title);
    225             return this;
    226         }
    227 
    228         /**
    229          * Sets the body text.
    230          *
    231          * @param text Text to set
    232          * @return This {@link Builder} to chain calls
    233          */
    234         public Builder setText(String text) {
    235             mBundle.putString(MenuItemConstants.KEY_TEXT, text);
    236             return this;
    237         }
    238 
    239         /**
    240          * Sets the icon.
    241          *
    242          * @param bitmap Icon to set
    243          * @return This {@link Builder} to chain calls
    244          */
    245         public Builder setIcon(Bitmap bitmap) {
    246             mBundle.putParcelable(MenuItemConstants.KEY_LEFTICON, bitmap);
    247             return this;
    248         }
    249 
    250         /**
    251          * Sets the icon.
    252          *
    253          * A snapshot of the {@link android.graphics.drawable.Drawable} is captured at the time the {@link Item} obtained
    254          * from this Builder is passed to {@link #sendResult(java.util.List)}. Any changes that are
    255          * made to the Drawable after that point will not affect what is displayed by the menu.
    256          *
    257          * @param drawable Icon to set
    258          * @return This {@link Builder} to chain calls
    259          */
    260         public Builder setIconFromSnapshot(Drawable drawable) {
    261             mIcon = drawable;
    262             return this;
    263         }
    264 
    265         /**
    266          * Sets the right icon.
    267          *
    268          * @param bitmap Icon to set
    269          * @return This {@link Builder} to chain calls
    270          */
    271         public Builder setRightIcon(Bitmap bitmap) {
    272             mBundle.putParcelable(MenuItemConstants.KEY_RIGHTICON, bitmap);
    273             return this;
    274         }
    275 
    276         /**
    277          * Sets the right icon.
    278          *
    279          * A snapshot of the {@link android.graphics.drawable.Drawable} is captured at the time the
    280          * {@link Item} obtained
    281          * from this Builder is passed to {@link #sendResult(java.util.List)}. Any changes that are
    282          * made to the Drawable after that point will not affect what is displayed by the menu.
    283          *
    284          * @param drawable Icon to set
    285          * @return This {@link Builder} to chain calls
    286          */
    287         public Builder setRightIconFromSnapshot(Drawable drawable) {
    288             mRightIcon = drawable;
    289             return this;
    290         }
    291 
    292         /**
    293          * The widget to set.
    294          * It can be anyone of the following: button, checkbox, toggle
    295          *
    296          * @param widget
    297          * @return This {@link Builder} to chain calls
    298          */
    299         public Builder setWidget(int widget) {
    300             mBundle.putInt(MenuItemConstants.KEY_WIDGET, widget);
    301             return this;
    302         }
    303 
    304         /**
    305          * If a widget is set, the state the widget is in.
    306          * This is only applicable if the widget is "checkbox" or "toggle"
    307          *
    308          * @param on If true, a checkbox is checked and a toggle is set to "on".
    309          *           If false, a checkbox will be unchecked and a toggle will be set to "off".
    310          * @return This {@link Builder} to chain calls
    311          */
    312         public Builder setWidgetState(boolean on) {
    313             mBundle.putBoolean(MenuItemConstants.KEY_WIDGET_STATE, on);
    314             return this;
    315         }
    316 
    317         /**
    318          * Indicates that this is an empty placeholder menu item.
    319          * Only title and icon will be available in this situation.
    320          *
    321          * @param isEmptyPlaceHolder If true, this CarMenu will be a placeholder item for no data
    322          *                           in menu list.
    323          * @return This {@link Builder} to chain calls
    324          */
    325         public Builder setIsEmptyPlaceHolder(boolean isEmptyPlaceHolder) {
    326             mBundle.putBoolean(MenuItemConstants.KEY_EMPTY_PLACEHOLDER, isEmptyPlaceHolder);
    327             return this;
    328         }
    329 
    330         /**
    331          * If the widget is {@link MenuItemConstants#WIDGET_TEXT_VIEW}, then this will allow setting
    332          * the right text.
    333          *
    334          * @param text The text to set
    335          * @return this {@link Builder} to chain calls
    336          */
    337         public Builder setRightText(String text) {
    338             mBundle.putString(MenuItemConstants.KEY_RIGHTTEXT, text);
    339             return this;
    340         }
    341 
    342         public Builder setRemoteViews(RemoteViews views) {
    343             mBundle.putParcelable(MenuItemConstants.KEY_REMOTEVIEWS, views);
    344             return this;
    345         }
    346 
    347         /**
    348          * Sets additional flags for this item.
    349          * {@link MenuItemConstants#FLAG_BROWSABLE} is the only one that can be currently set
    350          *
    351          * @param flags flags to set
    352          * @return This {@link Builder} to chain calls
    353          */
    354         public Builder setFlags(@MenuItemConstants.MenuItemFlags int flags) {
    355             mBundle.putInt(MenuItemConstants.KEY_FLAGS, flags);
    356             return this;
    357         }
    358 
    359         /**
    360          * Add this item to the list of items to be sent when {@link CarMenu#sendResult(java.util.List)}
    361          * is called
    362          */
    363         public Item build() {
    364             return new ItemImpl(mBundle, mIcon, mRightIcon);
    365         }
    366     }
    367 }
    368