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