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