Home | History | Annotate | Download | only in demos
      1 /*
      2  * Copyright 2017 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 com.example.androidx.slice.demos;
     18 
     19 import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
     20 
     21 import static androidx.slice.builders.ListBuilder.ICON_IMAGE;
     22 import static androidx.slice.builders.ListBuilder.INFINITY;
     23 import static androidx.slice.builders.ListBuilder.LARGE_IMAGE;
     24 import static androidx.slice.builders.ListBuilder.SMALL_IMAGE;
     25 
     26 import android.app.PendingIntent;
     27 import android.content.ContentResolver;
     28 import android.content.Context;
     29 import android.content.Intent;
     30 import android.net.Uri;
     31 import android.net.wifi.WifiManager;
     32 import android.os.Handler;
     33 import android.provider.Settings;
     34 import android.text.SpannableString;
     35 import android.text.TextUtils;
     36 import android.text.format.DateUtils;
     37 import android.text.style.ForegroundColorSpan;
     38 import android.util.SparseArray;
     39 
     40 import androidx.annotation.NonNull;
     41 import androidx.core.graphics.drawable.IconCompat;
     42 import androidx.slice.Slice;
     43 import androidx.slice.SliceProvider;
     44 import androidx.slice.builders.GridRowBuilder;
     45 import androidx.slice.builders.ListBuilder;
     46 import androidx.slice.builders.MessagingSliceBuilder;
     47 import androidx.slice.builders.SliceAction;
     48 
     49 import java.util.Arrays;
     50 import java.util.Calendar;
     51 import java.util.concurrent.TimeUnit;
     52 
     53 /**
     54  * Examples of using slice template builders.
     55  */
     56 public class SampleSliceProvider extends SliceProvider {
     57 
     58     private static final boolean TEST_CUSTOM_SEE_MORE = false;
     59 
     60     public static final String ACTION_WIFI_CHANGED =
     61             "com.example.androidx.slice.action.WIFI_CHANGED";
     62     public static final String ACTION_TOAST =
     63             "com.example.androidx.slice.action.TOAST";
     64     public static final String EXTRA_TOAST_MESSAGE = "com.example.androidx.extra.TOAST_MESSAGE";
     65     public static final String ACTION_TOAST_RANGE_VALUE =
     66             "com.example.androidx.slice.action.TOAST_RANGE_VALUE";
     67 
     68     public static final String[] URI_PATHS = {
     69             "message",
     70             "wifi",
     71             "note",
     72             "ride",
     73             "toggle",
     74             "toggle2",
     75             "toggletester",
     76             "contact",
     77             "contact2",
     78             "gallery",
     79             "gallery2",
     80             "weather",
     81             "reservation",
     82             "loadlist",
     83             "loadgrid",
     84             "inputrange",
     85             "range",
     86             "subscription",
     87             "singleitems",
     88     };
     89 
     90     /**
     91      * @return Uri with the provided path
     92      */
     93     public static Uri getUri(String path, Context context) {
     94         return new Uri.Builder()
     95                 .scheme(ContentResolver.SCHEME_CONTENT)
     96                 .authority(context.getPackageName())
     97                 .appendPath(path)
     98                 .build();
     99     }
    100 
    101     @Override
    102     public boolean onCreateSliceProvider() {
    103         return true;
    104     }
    105 
    106     @NonNull
    107     @Override
    108     public Uri onMapIntentToUri(Intent intent) {
    109         return getUri("wifi", getContext());
    110     }
    111 
    112     @Override
    113     public Slice onBindSlice(Uri sliceUri) {
    114         String path = sliceUri.getPath();
    115         if (!path.equals("/loadlist")) {
    116             mListSummaries.clear();
    117             mListLastUpdate = 0;
    118         }
    119         if (!path.equals("/loadgrid")) {
    120             mGridSummaries.clear();
    121             mGridLastUpdate = 0;
    122         }
    123         switch (path) {
    124             // TODO: add list / grid slices with 'see more' options
    125             case "/message":
    126                 return createMessagingSlice(sliceUri);
    127             case "/wifi":
    128                 return createWifiSlice(sliceUri);
    129             case "/note":
    130                 return createNoteSlice(sliceUri);
    131             case "/ride":
    132                 return createRideSlice(sliceUri);
    133             case "/toggle":
    134                 return createCustomToggleSlice(sliceUri);
    135             case "/toggle2":
    136                 return createTwoCustomToggleSlices(sliceUri);
    137             case "/toggletester":
    138                 return createdToggleTesterSlice(sliceUri);
    139             case "/contact":
    140                 return createContact(sliceUri);
    141             case "/contact2":
    142                 return createContact2(sliceUri);
    143             case "/gallery":
    144                 return createGallery(sliceUri, true /* showHeader */);
    145             case "/gallery2":
    146                 return createGallery(sliceUri, false /* showHeader */);
    147             case "/weather":
    148                 return createWeather(sliceUri);
    149             case "/reservation":
    150                 return createReservationSlice(sliceUri);
    151             case "/loadlist":
    152                 return createLoadingListSlice(sliceUri);
    153             case "/loadgrid":
    154                 return createLoadingGridSlice(sliceUri);
    155             case "/inputrange":
    156                 return createStarRatingInputRange(sliceUri);
    157             case "/range":
    158                 return createDownloadProgressRange(sliceUri);
    159             case "/subscription":
    160                 return createCatSlice(sliceUri, false /* customSeeMore */);
    161             case "/singleitems":
    162                 return createSingleSlice(sliceUri);
    163         }
    164         throw new IllegalArgumentException("Unknown uri " + sliceUri);
    165     }
    166 
    167     private Slice createWeather(Uri sliceUri) {
    168         SliceAction primaryAction = new SliceAction(getBroadcastIntent(ACTION_TOAST,
    169                 "open weather app"),
    170                 IconCompat.createWithResource(getContext(), R.drawable.weather_1), SMALL_IMAGE,
    171                 "Weather is happening!");
    172         return new ListBuilder(getContext(), sliceUri, INFINITY)
    173                 .addGridRow(gb -> gb
    174                         .setPrimaryAction(primaryAction)
    175                         .addCell(cb -> cb
    176                                 .addImage(IconCompat.createWithResource(getContext(),
    177                                         R.drawable.weather_1),
    178                                         SMALL_IMAGE)
    179                                 .addText("MON")
    180                                 .addTitleText("69\u00B0"))
    181                         .addCell(cb -> cb
    182                                 .addImage(IconCompat.createWithResource(getContext(),
    183                                         R.drawable.weather_2),
    184                                         SMALL_IMAGE)
    185                                 .addText("TUE")
    186                                 .addTitleText("71\u00B0"))
    187                         .addCell(cb -> cb
    188                                 .addImage(IconCompat.createWithResource(getContext(),
    189                                         R.drawable.weather_3),
    190                                         SMALL_IMAGE)
    191                                 .addText("WED")
    192                                 .addTitleText("76\u00B0"))
    193                         .addCell(cb -> cb
    194                                 .addImage(IconCompat.createWithResource(getContext(),
    195                                         R.drawable.weather_4),
    196                                         SMALL_IMAGE)
    197                                 .addText("THU")
    198                                 .addTitleText("72\u00B0"))
    199                         .addCell(cb -> cb
    200                                 .addImage(IconCompat.createWithResource(getContext(),
    201                                         R.drawable.weather_1),
    202                                         SMALL_IMAGE)
    203                                 .addText("FRI")
    204                                 .addTitleText("68\u00B0")))
    205                 .build();
    206     }
    207 
    208     private Slice createGallery(Uri sliceUri, boolean showHeader) {
    209         SliceAction primaryAction = new SliceAction(
    210                 getBroadcastIntent(ACTION_TOAST, "open photo album"),
    211                 IconCompat.createWithResource(getContext(), R.drawable.slices_1),
    212                 LARGE_IMAGE,
    213                 "Open photo album");
    214         ListBuilder lb = new ListBuilder(getContext(), sliceUri, INFINITY)
    215                 .setAccentColor(0xff4285F4);
    216         if (showHeader) {
    217             lb.addRow(b -> b
    218                     .setTitle("Family trip to Hawaii")
    219                     .setSubtitle("Sep 30, 2017 - Oct 2, 2017")
    220                     .setPrimaryAction(primaryAction))
    221                     .addAction(new SliceAction(
    222                             getBroadcastIntent(ACTION_TOAST, "cast photo album"),
    223                             IconCompat.createWithResource(getContext(), R.drawable.ic_cast),
    224                             "Cast photo album"))
    225                     .addAction(new SliceAction(
    226                             getBroadcastIntent(ACTION_TOAST, "share photo album"),
    227                             IconCompat.createWithResource(getContext(), R.drawable.ic_share),
    228                             "Share photo album"));
    229         }
    230         int[] galleryResId = new int[] {R.drawable.slices_1, R.drawable.slices_2,
    231                 R.drawable.slices_3, R.drawable.slices_4};
    232         int imageCount = 7;
    233         GridRowBuilder grb = new GridRowBuilder(lb);
    234         for (int i = 0; i < imageCount; i++) {
    235             IconCompat ic = IconCompat.createWithResource(getContext(),
    236                     galleryResId[i % galleryResId.length]);
    237             grb.addCell(cb -> cb.addImage(ic, LARGE_IMAGE));
    238         }
    239         grb.setPrimaryAction(primaryAction)
    240                 .setSeeMoreAction(getBroadcastIntent(ACTION_TOAST, "see your gallery"))
    241                 .setContentDescription("Images from your trip to Hawaii");
    242         return lb.addGridRow(grb).build();
    243     }
    244 
    245     private Slice createCatSlice(Uri sliceUri, boolean customSeeMore) {
    246         ListBuilder b = new ListBuilder(getContext(), sliceUri, INFINITY);
    247         GridRowBuilder gb = new GridRowBuilder(b);
    248         PendingIntent pi = getBroadcastIntent(ACTION_TOAST, "See cats you follow");
    249         if (customSeeMore) {
    250             GridRowBuilder.CellBuilder cb = new GridRowBuilder.CellBuilder(gb);
    251             cb.addImage(IconCompat.createWithResource(getContext(), R.drawable.ic_right_caret),
    252                     ICON_IMAGE);
    253             cb.setContentIntent(pi);
    254             cb.addTitleText("All cats");
    255             gb.setSeeMoreCell(cb);
    256         } else {
    257             gb.setSeeMoreAction(pi);
    258         }
    259         gb.addCell(new GridRowBuilder.CellBuilder(gb)
    260                 .addImage(IconCompat.createWithResource(getContext(), R.drawable.cat_1),
    261                         SMALL_IMAGE)
    262                 .addTitleText("Oreo"))
    263                 .addCell(new GridRowBuilder.CellBuilder(gb)
    264                         .addImage(IconCompat.createWithResource(getContext(), R.drawable.cat_2),
    265                                 SMALL_IMAGE)
    266                         .addTitleText("Silver"))
    267                 .addCell(new GridRowBuilder.CellBuilder(gb)
    268                         .addImage(IconCompat.createWithResource(getContext(), R.drawable.cat_3),
    269                                 SMALL_IMAGE)
    270                         .addTitleText("Drake"))
    271                 .addCell(new GridRowBuilder.CellBuilder(gb)
    272                         .addImage(IconCompat.createWithResource(getContext(), R.drawable.cat_5),
    273                                 SMALL_IMAGE)
    274                         .addTitleText("Olive"))
    275                 .addCell(new GridRowBuilder.CellBuilder(gb)
    276                         .addImage(IconCompat.createWithResource(getContext(), R.drawable.cat_4),
    277                                 SMALL_IMAGE)
    278                         .addTitleText("Lady Marmalade"))
    279                 .addCell(new GridRowBuilder.CellBuilder(gb)
    280                         .addImage(IconCompat.createWithResource(getContext(), R.drawable.cat_6),
    281                                 SMALL_IMAGE)
    282                         .addTitleText("Grapefruit"));
    283         return b.addGridRow(gb).build();
    284     }
    285 
    286     private Slice createContact2(Uri sliceUri) {
    287         ListBuilder b = new ListBuilder(getContext(), sliceUri, INFINITY);
    288         ListBuilder.RowBuilder rb = new ListBuilder.RowBuilder(b);
    289         GridRowBuilder gb = new GridRowBuilder(b);
    290         return b.setAccentColor(0xff3949ab)
    291                 .addRow(rb
    292                         .setTitle("Mady Pitza")
    293                         .setSubtitle("Frequently contacted contact")
    294                         .addEndItem(IconCompat.createWithResource(getContext(), R.drawable.mady),
    295                                 SMALL_IMAGE))
    296                 .addGridRow(gb
    297                         .addCell(new GridRowBuilder.CellBuilder(gb)
    298                                 .addImage(IconCompat.createWithResource(getContext(),
    299                                         R.drawable.ic_call),
    300                                         ICON_IMAGE)
    301                                 .addText("Call")
    302                                 .setContentIntent(getBroadcastIntent(ACTION_TOAST, "call")))
    303                         .addCell(new GridRowBuilder.CellBuilder(gb)
    304                                 .addImage(IconCompat.createWithResource(getContext(),
    305                                         R.drawable.ic_text),
    306                                         ICON_IMAGE)
    307                                 .addText("Text")
    308                                 .setContentIntent(getBroadcastIntent(ACTION_TOAST, "text")))
    309                         .addCell(new GridRowBuilder.CellBuilder(gb)
    310                                 .addImage(IconCompat.createWithResource(getContext(),
    311                                         R.drawable.ic_video), ICON_IMAGE)
    312                                 .setContentIntent(getBroadcastIntent(ACTION_TOAST, "video"))
    313                                 .addText("Video"))
    314                         .addCell(new GridRowBuilder.CellBuilder(gb)
    315                                 .addImage(IconCompat.createWithResource(getContext(),
    316                                         R.drawable.ic_email), ICON_IMAGE)
    317                                 .addText("Email")
    318                                 .setContentIntent(getBroadcastIntent(ACTION_TOAST, "email"))))
    319                 .build();
    320     }
    321 
    322     private Slice createContact(Uri sliceUri) {
    323         final long lastCalled = System.currentTimeMillis() - 20 * DateUtils.MINUTE_IN_MILLIS;
    324         CharSequence lastCalledString = DateUtils.getRelativeTimeSpanString(lastCalled,
    325                 Calendar.getInstance().getTimeInMillis(),
    326                 DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE);
    327         SliceAction primaryAction = new SliceAction(getBroadcastIntent(ACTION_TOAST,
    328                 "See contact info"), IconCompat.createWithResource(getContext(),
    329                 R.drawable.mady), SMALL_IMAGE, "Mady");
    330 
    331         return new ListBuilder(getContext(), sliceUri, INFINITY)
    332                 .setAccentColor(0xff3949ab)
    333                 .setHeader(b -> b
    334                         .setTitle("Mady Pitza")
    335                         .setSummary("Called " + lastCalledString)
    336                         .setPrimaryAction(primaryAction))
    337                 .addRow(b -> b
    338                         .setTitleItem(
    339                                 IconCompat.createWithResource(getContext(), R.drawable.ic_call),
    340                                 ICON_IMAGE)
    341                         .setTitle("314-259-2653")
    342                         .setSubtitle("Call lasted 1 hr 17 min")
    343                         .addEndItem(lastCalled))
    344                 .addRow(b -> b
    345                         .setTitleItem(
    346                                 IconCompat.createWithResource(getContext(), R.drawable.ic_text),
    347                                 ICON_IMAGE)
    348                         .setTitle("You: Coooooool see you then")
    349                         .addEndItem(System.currentTimeMillis() - 40 * DateUtils.MINUTE_IN_MILLIS))
    350                 .addAction(new SliceAction(getBroadcastIntent(ACTION_TOAST, "call"),
    351                         IconCompat.createWithResource(getContext(), R.drawable.ic_call),
    352                         "Call mady"))
    353                 .addAction(new SliceAction(getBroadcastIntent(ACTION_TOAST, "text"),
    354                         IconCompat.createWithResource(getContext(), R.drawable.ic_text),
    355                         "Text mady"))
    356                 .addAction(new SliceAction(getBroadcastIntent(ACTION_TOAST, "video"),
    357                         IconCompat.createWithResource(getContext(), R.drawable.ic_video),
    358                         "Video call mady"))
    359                 .addAction(new SliceAction(getBroadcastIntent(ACTION_TOAST, "email"),
    360                         IconCompat.createWithResource(getContext(), R.drawable.ic_email),
    361                         "Email mady"))
    362                 .build();
    363     }
    364 
    365     private Slice createMessagingSlice(Uri sliceUri) {
    366         // TODO: Remote input.
    367         return new MessagingSliceBuilder(getContext(), sliceUri)
    368                 .add(b -> b
    369                         .addText("yo home \uD83C\uDF55, I emailed you the info")
    370                         .addTimestamp(System.currentTimeMillis() - 20 * DateUtils.MINUTE_IN_MILLIS)
    371                         .addSource(IconCompat.createWithResource(getContext(), R.drawable.mady)))
    372                 .add(b -> b
    373                         .addText("just bought my tickets")
    374                         .addTimestamp(System.currentTimeMillis() - 10 * DateUtils.MINUTE_IN_MILLIS))
    375                 .add(b -> b
    376                         .addText("yay! can't wait for getContext() weekend!\n"
    377                                 + "\uD83D\uDE00")
    378                         .addTimestamp(System.currentTimeMillis() - 5 * DateUtils.MINUTE_IN_MILLIS)
    379                         .addSource(IconCompat.createWithResource(getContext(), R.drawable.mady)))
    380                 .build();
    381 
    382     }
    383 
    384     private Slice createNoteSlice(Uri sliceUri) {
    385         // TODO: Remote input.
    386         return new ListBuilder(getContext(), sliceUri, INFINITY)
    387                 .setAccentColor(0xfff4b400)
    388                 .addRow(b -> b.setTitle("Create new note"))
    389                 .addAction(new SliceAction(getBroadcastIntent(ACTION_TOAST, "create note"),
    390                         IconCompat.createWithResource(getContext(), R.drawable.ic_create),
    391                         "Create note"))
    392                 .addAction(new SliceAction(getBroadcastIntent(ACTION_TOAST, "voice note"),
    393                         IconCompat.createWithResource(getContext(), R.drawable.ic_voice),
    394                         "Voice note"))
    395                 .addAction(new SliceAction(getIntent("android.media.action.IMAGE_CAPTURE"),
    396                         IconCompat.createWithResource(getContext(), R.drawable.ic_camera),
    397                         "Photo note"))
    398                 .build();
    399     }
    400 
    401     private Slice createReservationSlice(Uri sliceUri) {
    402         return new ListBuilder(getContext(), sliceUri, INFINITY)
    403                 .setAccentColor(0xffFF5252)
    404                 .setHeader(b -> b
    405                         .setTitle("Upcoming trip to Seattle")
    406                         .setSubtitle("Feb 1 - 19 | 2 guests"))
    407                 .addAction(new SliceAction(
    408                         getBroadcastIntent(ACTION_TOAST, "show location on map"),
    409                         IconCompat.createWithResource(getContext(), R.drawable.ic_location),
    410                         "Show reservation location"))
    411                 .addAction(new SliceAction(getBroadcastIntent(ACTION_TOAST, "contact host"),
    412                         IconCompat.createWithResource(getContext(), R.drawable.ic_text),
    413                         "Contact host"))
    414                 .addGridRow(b -> b
    415                         .addCell(cb -> cb
    416                                 .addImage(IconCompat.createWithResource(getContext(),
    417                                         R.drawable.reservation),
    418                                         LARGE_IMAGE)
    419                                 .setContentDescription("Image of your reservation in Seattle")))
    420                 .addGridRow(b -> b
    421                         .addCell(cb -> cb
    422                                 .addTitleText("Check In")
    423                                 .addText("12:00 PM, Feb 1"))
    424                         .addCell(cb -> cb
    425                                 .addTitleText("Check Out")
    426                                 .addText("11:00 AM, Feb 19")))
    427                 .build();
    428     }
    429 
    430     private Slice createRideSlice(Uri sliceUri) {
    431         final ForegroundColorSpan colorSpan = new ForegroundColorSpan(0xff0F9D58);
    432         SpannableString headerSubtitle = new SpannableString("Ride in 4 min");
    433         headerSubtitle.setSpan(colorSpan, 8, headerSubtitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE);
    434         SpannableString homeSubtitle = new SpannableString("12 miles | 12 min | $9.00");
    435         homeSubtitle.setSpan(colorSpan, 20, homeSubtitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE);
    436         SpannableString workSubtitle = new SpannableString("44 miles | 1 hour 45 min | $31.41");
    437         workSubtitle.setSpan(colorSpan, 27, workSubtitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE);
    438 
    439         SliceAction primaryAction = new SliceAction(getBroadcastIntent(ACTION_TOAST, "get ride"),
    440                 IconCompat.createWithResource(getContext(), R.drawable.ic_car), "Get Ride");
    441         return new ListBuilder(getContext(), sliceUri, TimeUnit.SECONDS.toMillis(10))
    442                 .setAccentColor(0xff0F9D58)
    443                 .setHeader(b -> b
    444                         .setTitle("Get ride")
    445                         .setSubtitle(headerSubtitle)
    446                         .setSummary("Ride to work in 12 min | Ride home in 1 hour 45 min")
    447                         .setPrimaryAction(primaryAction))
    448                 .addRow(b -> b
    449                         .setTitle("Work")
    450                         .setSubtitle(workSubtitle)
    451                         .addEndItem(new SliceAction(getBroadcastIntent(ACTION_TOAST, "work"),
    452                                 IconCompat.createWithResource(getContext(), R.drawable.ic_work),
    453                                 "Get ride to work")))
    454                 .addRow(b -> b
    455                         .setTitle("Home")
    456                         .setSubtitle(homeSubtitle)
    457                         .addEndItem(new SliceAction(getBroadcastIntent(ACTION_TOAST, "home"),
    458                                 IconCompat.createWithResource(getContext(), R.drawable.ic_home),
    459                                 "Get ride home")))
    460                 .build();
    461     }
    462 
    463     private Slice createCustomToggleSlice(Uri sliceUri) {
    464         return new ListBuilder(getContext(), sliceUri, INFINITY)
    465                 .setAccentColor(0xffff4081)
    466                 .addRow(b -> b
    467                         .setTitle("Custom toggle")
    468                         .setSubtitle("It can support two states")
    469                         .setPrimaryAction(new SliceAction(getBroadcastIntent(ACTION_TOAST,
    470                                 "star toggled"),
    471                                 IconCompat.createWithResource(getContext(), R.drawable.toggle_star),
    472                                 "Toggle star", true /* isChecked */)))
    473                 .build();
    474     }
    475 
    476     private Slice createTwoCustomToggleSlices(Uri sliceUri) {
    477         return new ListBuilder(getContext(), sliceUri, INFINITY)
    478                 .setAccentColor(0xffff4081)
    479                 .addRow(b -> b
    480                         .setTitle("2 toggles")
    481                         .setSubtitle("each supports two states")
    482                         .addEndItem(new SliceAction(
    483                                 getBroadcastIntent(ACTION_TOAST, "first star toggled"),
    484                                 IconCompat.createWithResource(getContext(), R.drawable.toggle_star),
    485                                 "Toggle star", true /* isChecked */))
    486                         .addEndItem(new SliceAction(
    487                                 getBroadcastIntent(ACTION_TOAST, "second star toggled"),
    488                                 IconCompat.createWithResource(getContext(), R.drawable.toggle_star),
    489                                 "Toggle star", false /* isChecked */)))
    490                 .build();
    491     }
    492 
    493     private Slice createWifiSlice(Uri sliceUri) {
    494         // Get wifi state
    495         WifiManager wifiManager = (WifiManager) getContext()
    496                 .getApplicationContext().getSystemService(Context.WIFI_SERVICE);
    497         int wifiState = wifiManager.getWifiState();
    498         boolean wifiEnabled = false;
    499         String state;
    500         switch (wifiState) {
    501             case WifiManager.WIFI_STATE_DISABLED:
    502             case WifiManager.WIFI_STATE_DISABLING:
    503                 state = "disconnected";
    504                 break;
    505             case WifiManager.WIFI_STATE_ENABLED:
    506             case WifiManager.WIFI_STATE_ENABLING:
    507                 state = wifiManager.getConnectionInfo().getSSID();
    508                 wifiEnabled = true;
    509                 break;
    510             case WifiManager.WIFI_STATE_UNKNOWN:
    511             default:
    512                 state = ""; // just don't show anything?
    513                 break;
    514         }
    515 
    516         // Set the first row as a toggle
    517         boolean finalWifiEnabled = wifiEnabled;
    518         SliceAction primaryAction = new SliceAction(getIntent(Settings.ACTION_WIFI_SETTINGS),
    519                 IconCompat.createWithResource(getContext(), R.drawable.ic_wifi), "Wi-fi Settings");
    520         String toggleCDString = wifiEnabled ? "Turn wifi off" : "Turn wifi on";
    521         String sliceCDString = wifiEnabled ? "Wifi connected to " + state
    522                 : "Wifi disconnected, 10 networks available";
    523         ListBuilder lb = new ListBuilder(getContext(), sliceUri, INFINITY)
    524                 .setAccentColor(0xff4285f4)
    525                 .setHeader(b -> b
    526                         .setTitle("Wi-fi")
    527                         .setSubtitle(state)
    528                         .setContentDescription(sliceCDString)
    529                         .setPrimaryAction(primaryAction))
    530                 .addAction((new SliceAction(getBroadcastIntent(ACTION_WIFI_CHANGED, null),
    531                         toggleCDString, finalWifiEnabled)));
    532 
    533         // Add fake wifi networks
    534         int[] wifiIcons = new int[]{R.drawable.ic_wifi_full, R.drawable.ic_wifi_low,
    535                 R.drawable.ic_wifi_fair};
    536         for (int i = 0; i < 10; i++) {
    537             final int iconId = wifiIcons[i % wifiIcons.length];
    538             IconCompat icon = IconCompat.createWithResource(getContext(), iconId);
    539             final String networkName = "Network" + i;
    540             ListBuilder.RowBuilder rb = new ListBuilder.RowBuilder(lb);
    541             rb.setTitleItem(icon, ICON_IMAGE).setTitle(networkName);
    542             boolean locked = i % 3 == 0;
    543             if (locked) {
    544                 rb.addEndItem(IconCompat.createWithResource(getContext(), R.drawable.ic_lock),
    545                         ICON_IMAGE);
    546                 rb.setContentDescription("Connect to " + networkName + ", password needed");
    547             } else {
    548                 rb.setContentDescription("Connect to " + networkName);
    549             }
    550             String message = locked ? "Open wifi password dialog" : "Connect to " + networkName;
    551             rb.setPrimaryAction(new SliceAction(getBroadcastIntent(ACTION_TOAST, message), icon,
    552                     message));
    553             lb.addRow(rb);
    554         }
    555 
    556         // Add keywords
    557         String[] keywords = new String[]{"internet", "wifi", "data", "network"};
    558         lb.setKeywords(Arrays.asList(keywords));
    559 
    560         // Add see more intent
    561         if (TEST_CUSTOM_SEE_MORE) {
    562             lb.setSeeMoreRow(rb -> rb
    563                     .setTitle("See all available networks")
    564                     .addEndItem(
    565                             IconCompat.createWithResource(getContext(), R.drawable.ic_right_caret),
    566                             SMALL_IMAGE)
    567                     .setPrimaryAction(primaryAction));
    568         } else {
    569             lb.setSeeMoreAction(primaryAction.getAction());
    570         }
    571         return lb.build();
    572     }
    573 
    574     private Slice createStarRatingInputRange(Uri sliceUri) {
    575         IconCompat icon = IconCompat.createWithResource(getContext(), R.drawable.ic_star_on);
    576         SliceAction primaryAction =
    577                 new SliceAction(getBroadcastIntent(ACTION_TOAST, "open star rating"),
    578                         icon, "Rate");
    579         return new ListBuilder(getContext(), sliceUri, INFINITY)
    580                 .setAccentColor(0xffff4081)
    581                 .addInputRange(c -> c
    582                         .setTitle("Star rating")
    583                         .setSubtitle("Rate from 5 to 10 because it's weird")
    584                         .setMin(5)
    585                         .setThumb(icon)
    586                         .setInputAction(getBroadcastIntent(ACTION_TOAST_RANGE_VALUE, null))
    587                         .setMax(10)
    588                         .setValue(8)
    589                         .setPrimaryAction(primaryAction)
    590                         .setContentDescription("Slider for star ratings"))
    591                 .build();
    592     }
    593 
    594     private Slice createDownloadProgressRange(Uri sliceUri) {
    595         IconCompat icon = IconCompat.createWithResource(getContext(), R.drawable.ic_star_on);
    596         SliceAction primaryAction =
    597                 new SliceAction(
    598                         getBroadcastIntent(ACTION_TOAST, "open download"), icon, "Download");
    599         return new ListBuilder(getContext(), sliceUri, INFINITY)
    600                 .setAccentColor(0xffff4081)
    601                 .addRange(c -> c
    602                         .setTitle("Download progress")
    603                         .setSubtitle("Download is happening")
    604                         .setMax(100)
    605                         .setValue(75)
    606                         .setPrimaryAction(primaryAction))
    607                 .build();
    608     }
    609 
    610     private Slice createdToggleTesterSlice(Uri uri) {
    611         IconCompat star = IconCompat.createWithResource(getContext(), R.drawable.toggle_star);
    612         IconCompat icon = IconCompat.createWithResource(getContext(), R.drawable.ic_star_on);
    613 
    614         SliceAction primaryAction = new SliceAction(
    615                 getBroadcastIntent(ACTION_TOAST, "primary action"), icon, "Primary action");
    616         SliceAction toggleAction = new SliceAction(
    617                 getBroadcastIntent(ACTION_TOAST, "star note"), star, "Star note", false);
    618         SliceAction toggleAction2 = new SliceAction(
    619                 getBroadcastIntent(ACTION_TOAST, "star note 2"), star, "Star note 2", true);
    620         SliceAction toggleAction3 = new SliceAction(
    621                 getBroadcastIntent(ACTION_TOAST, "star note 3"), star, "Star note 3", false);
    622 
    623         ListBuilder lb = new ListBuilder(getContext(), uri, INFINITY);
    624 
    625         // Primary action toggle
    626         ListBuilder.RowBuilder primaryToggle = new ListBuilder.RowBuilder(lb);
    627         primaryToggle.setTitle("Primary action is a toggle")
    628                 .setPrimaryAction(toggleAction);
    629 
    630         // End toggle + normal primary action
    631         ListBuilder.RowBuilder endToggle = new ListBuilder.RowBuilder(lb);
    632         endToggle.setTitle("Only end toggles")
    633                 .setSubtitle("Normal primary action")
    634                 .setPrimaryAction(primaryAction)
    635                 .addEndItem(toggleAction)
    636                 .addEndItem(toggleAction2);
    637 
    638         // Start toggle + normal primary
    639         ListBuilder.RowBuilder startToggle = new ListBuilder.RowBuilder(lb);
    640         startToggle.setTitle("One start toggle")
    641                 .setTitleItem(toggleAction)
    642                 .setSubtitle("Normal primary action")
    643                 .setPrimaryAction(primaryAction);
    644 
    645         // Start + end toggles + normal primary action
    646         ListBuilder.RowBuilder someToggles = new ListBuilder.RowBuilder(lb);
    647         someToggles.setTitleItem(toggleAction)
    648                 .setPrimaryAction(primaryAction)
    649                 .setTitle("Start & end toggles")
    650                 .setSubtitle("Normal primary action")
    651                 .addEndItem(toggleAction2)
    652                 .addEndItem(toggleAction3);
    653 
    654         // Start toggle ONLY
    655         ListBuilder.RowBuilder startToggleOnly = new ListBuilder.RowBuilder(lb);
    656         startToggleOnly.setTitle("Start action is a toggle")
    657                 .setSubtitle("No other actions")
    658                 .setTitleItem(toggleAction);
    659 
    660         // End toggle ONLY
    661         ListBuilder.RowBuilder endToggleOnly = new ListBuilder.RowBuilder(lb);
    662         endToggleOnly.setTitle("End action is a toggle")
    663                 .setSubtitle("No other actions")
    664                 .addEndItem(toggleAction);
    665 
    666         // All toggles: end item should be ignored / replaced with primary action
    667         ListBuilder.RowBuilder muchToggles = new ListBuilder.RowBuilder(lb);
    668         muchToggles.setTitleItem(toggleAction)
    669                 .setTitle("All toggles")
    670                 .setSubtitle("Even the primary action")
    671                 .setPrimaryAction(toggleAction2)
    672                 .addEndItem(toggleAction3);
    673 
    674         lb.addRow(primaryToggle);
    675         lb.addRow(endToggleOnly);
    676         lb.addRow(endToggle);
    677         lb.addRow(startToggleOnly);
    678         lb.addRow(startToggle);
    679         lb.addRow(someToggles);
    680         lb.addRow(muchToggles);
    681         return lb.build();
    682     }
    683 
    684     private Slice createSingleSlice(Uri uri) {
    685         IconCompat ic2 = IconCompat.createWithResource(getContext(), R.drawable.ic_create);
    686         IconCompat image = IconCompat.createWithResource(getContext(), R.drawable.cat_3);
    687         IconCompat toggle = IconCompat.createWithResource(getContext(), R.drawable.toggle_star);
    688         SliceAction toggleAction = new SliceAction(
    689                 getBroadcastIntent(ACTION_TOAST, "toggle action"), toggle, "toggle", false);
    690         SliceAction simpleAction = new SliceAction(
    691                 getBroadcastIntent(ACTION_TOAST, "icon action"), ic2, "icon");
    692         ListBuilder lb = new ListBuilder(getContext(), uri, INFINITY);
    693         return lb.addRow(new ListBuilder.RowBuilder(lb)
    694                 .setTitle("Single title"))
    695                 .addRow(new ListBuilder.RowBuilder(lb)
    696                         .setSubtitle("Single subtitle"))
    697                  //Time stamps
    698                 .addRow(new ListBuilder.RowBuilder(lb)
    699                         .setTitleItem(System.currentTimeMillis()))
    700                 .addRow(new ListBuilder.RowBuilder(lb)
    701                         .addEndItem(System.currentTimeMillis()))
    702                 // Toggle actions
    703                 .addRow(new ListBuilder.RowBuilder(lb)
    704                         .setTitleItem(toggleAction))
    705                 .addRow(new ListBuilder.RowBuilder(lb)
    706                         .addEndItem(toggleAction))
    707                 // Icon actions
    708                 .addRow(new ListBuilder.RowBuilder(lb)
    709                         .setTitleItem(simpleAction))
    710                 .addRow(new ListBuilder.RowBuilder(lb)
    711                         .addEndItem(simpleAction))
    712                 // Images
    713                 .addRow(new ListBuilder.RowBuilder(lb)
    714                         .setTitleItem(image, SMALL_IMAGE))
    715                 .addRow(new ListBuilder.RowBuilder(lb)
    716                         .addEndItem(image, SMALL_IMAGE))
    717                 .build();
    718     }
    719 
    720     private Handler mHandler = new Handler();
    721     private SparseArray<String> mListSummaries = new SparseArray<>();
    722     private long mListLastUpdate;
    723     private SparseArray<String> mGridSummaries = new SparseArray<>();
    724     private long mGridLastUpdate;
    725 
    726     private void update(long delay, SparseArray<String> summaries, int id, String s, Uri uri,
    727             Runnable r) {
    728         mHandler.postDelayed(() -> {
    729             summaries.put(id, s);
    730             getContext().getContentResolver().notifyChange(uri, null);
    731             r.run();
    732         }, delay);
    733     }
    734 
    735     private Slice createLoadingListSlice(Uri sliceUri) {
    736         boolean updating = mListLastUpdate == 0
    737                 || mListLastUpdate < (System.currentTimeMillis() - 10 * System.currentTimeMillis());
    738         if (updating) {
    739             Runnable r = () -> mListLastUpdate = System.currentTimeMillis();
    740             update(1000, mListSummaries, 0, "44 miles | 1 hour 45 min | $31.41", sliceUri, r);
    741             update(1500, mListSummaries, 1, "12 miles | 12 min | $9.00", sliceUri, r);
    742             update(1700, mListSummaries, 2, "5 miles | 10 min | $8.00", sliceUri, r);
    743         }
    744         CharSequence work = mListSummaries.get(0, "");
    745         CharSequence home = mListSummaries.get(1, "");
    746         CharSequence school = mListSummaries.get(2, "");
    747         Slice s = new ListBuilder(getContext(), sliceUri, -TimeUnit.MINUTES.toMillis(50))
    748                 .addRow(b -> b
    749                         .setTitle("Work")
    750                         .setSubtitle(work,
    751                                 updating || TextUtils.isEmpty(work))
    752                         .addEndItem(IconCompat.createWithResource(getContext(), R.drawable.ic_work),
    753                                 ICON_IMAGE))
    754                 .addRow(b -> b
    755                         .setTitle("Home")
    756                         .setSubtitle(mListSummaries.get(1, ""),
    757                                 updating || TextUtils.isEmpty(home))
    758                         .addEndItem(
    759                                 IconCompat.createWithResource(getContext(), R.drawable.ic_home),
    760                                 ICON_IMAGE))
    761                 .addRow(b -> b
    762                         .setTitle("School")
    763                         .setSubtitle(mListSummaries.get(2, ""),
    764                                 updating || TextUtils.isEmpty(school))
    765                         .addEndItem(
    766                                 IconCompat.createWithResource(getContext(), R.drawable.ic_school),
    767                                 ICON_IMAGE))
    768                 .build();
    769         return s;
    770     }
    771 
    772     // TODO: Should test large image grids
    773     private Slice createLoadingGridSlice(Uri sliceUri) {
    774         boolean updating = mGridLastUpdate == 0
    775                 || mGridLastUpdate < (System.currentTimeMillis() - 10 * System.currentTimeMillis());
    776         if (updating) {
    777             Runnable r = () -> mGridLastUpdate = System.currentTimeMillis();
    778             update(2000, mGridSummaries, 0, "Heavy traffic in your area", sliceUri, r);
    779             update(3500, mGridSummaries, 1, "Typical conditions with delays up to 28 min",
    780                     sliceUri, r);
    781             update(3000, mGridSummaries, 2, "41 min", sliceUri, r);
    782             update(1500, mGridSummaries, 3, "33 min", sliceUri, r);
    783             update(1000, mGridSummaries, 4, "12 min", sliceUri, r);
    784         }
    785         CharSequence title = mGridSummaries.get(0, "");
    786         CharSequence subtitle = mGridSummaries.get(1, "");
    787         CharSequence home = mGridSummaries.get(2, "");
    788         CharSequence work = mGridSummaries.get(3, "");
    789         CharSequence school = mGridSummaries.get(4, "");
    790         Slice s = new ListBuilder(getContext(), sliceUri, INFINITY)
    791                 .setHeader(hb -> hb
    792                         .setTitle(title,
    793                                 updating || TextUtils.isEmpty(title))
    794                         .setSubtitle(subtitle,
    795                                 updating || TextUtils.isEmpty(subtitle)))
    796                 .addGridRow(gb -> gb
    797                         .addCell(cb -> cb
    798                                 .addImage(IconCompat.createWithResource(getContext(),
    799                                         R.drawable.ic_home),
    800                                         ICON_IMAGE)
    801                                 .addTitleText("Home")
    802                                 .addText(home,
    803                                         updating || TextUtils.isEmpty(home)))
    804                         .addCell(cb -> cb
    805                                 .addImage(IconCompat.createWithResource(getContext(),
    806                                         R.drawable.ic_work),
    807                                         ICON_IMAGE)
    808                                 .addTitleText("Work")
    809                                 .addText(work,
    810                                         updating || TextUtils.isEmpty(work)))
    811                         .addCell(cb -> cb
    812                                 .addImage(IconCompat.createWithResource(getContext(),
    813                                         R.drawable.ic_school),
    814                                         ICON_IMAGE)
    815                                 .addTitleText("School")
    816                                 .addText(school,
    817                                         updating || TextUtils.isEmpty(school))))
    818                 .build();
    819         return s;
    820     }
    821 
    822     private PendingIntent getIntent(String action) {
    823         Intent intent = new Intent(action);
    824         PendingIntent pi = PendingIntent.getActivity(getContext(), 0, intent, 0);
    825         return pi;
    826     }
    827 
    828     private PendingIntent getBroadcastIntent(String action, String message) {
    829         Intent intent = new Intent(action);
    830         intent.setClass(getContext(), SliceBroadcastReceiver.class);
    831         // Ensure a new PendingIntent is created for each message.
    832         int requestCode = 0;
    833         if (message != null) {
    834             intent.putExtra(EXTRA_TOAST_MESSAGE, message);
    835             requestCode = message.hashCode();
    836         }
    837         return PendingIntent.getBroadcast(getContext(), requestCode, intent,
    838                 PendingIntent.FLAG_UPDATE_CURRENT);
    839     }
    840 }
    841