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