SampleSliceProvider.java revision ba4198f4bc37284c5a6e8ce993f3652d3e3c88e6
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 android.app.PendingIntent; 22import android.content.ContentResolver; 23import android.content.Context; 24import android.content.Intent; 25import android.graphics.drawable.Icon; 26import android.net.Uri; 27import android.net.wifi.WifiManager; 28import android.os.Handler; 29import android.provider.Settings; 30import android.support.annotation.NonNull; 31import android.text.SpannableString; 32import android.text.format.DateUtils; 33import android.text.style.ForegroundColorSpan; 34 35import androidx.app.slice.Slice; 36import androidx.app.slice.SliceProvider; 37import androidx.app.slice.builders.GridBuilder; 38import androidx.app.slice.builders.ListBuilder; 39import androidx.app.slice.builders.MessagingSliceBuilder; 40 41/** 42 * Examples of using slice template builders. 43 */ 44public class SampleSliceProvider extends SliceProvider { 45 46 public static final String ACTION_WIFI_CHANGED = 47 "com.example.androidx.slice.action.WIFI_CHANGED"; 48 public static final String ACTION_TOAST = 49 "com.example.androidx.slice.action.TOAST"; 50 public static final String EXTRA_TOAST_MESSAGE = "com.example.androidx.extra.TOAST_MESSAGE"; 51 52 public static final int LOADING_DELAY_MS = 4000; 53 54 public static final String[] URI_PATHS = {"message", "wifi", "note", "ride", "toggle", 55 "toggle2", "contact", "gallery", "weather", "reservation", "loadlist", "loadlist2", 56 "loadgrid", "loadgrid2"}; 57 58 /** 59 * @return Uri with the provided path 60 */ 61 public static Uri getUri(String path, Context context) { 62 return new Uri.Builder() 63 .scheme(ContentResolver.SCHEME_CONTENT) 64 .authority(context.getPackageName()) 65 .appendPath(path) 66 .build(); 67 } 68 69 @Override 70 public boolean onCreateSliceProvider() { 71 return true; 72 } 73 74 @NonNull 75 @Override 76 public Uri onMapIntentToUri(Intent intent) { 77 return getUri("wifi", getContext()); 78 } 79 80 @Override 81 public Slice onBindSlice(Uri sliceUri) { 82 String path = sliceUri.getPath(); 83 switch (path) { 84 case "/message": 85 return createMessagingSlice(sliceUri); 86 case "/wifi": 87 return createWifiSlice(sliceUri); 88 case "/note": 89 return createNoteSlice(sliceUri); 90 case "/ride": 91 return createRideSlice(sliceUri); 92 case "/toggle": 93 return createCustomToggleSlice(sliceUri); 94 case "/toggle2": 95 return createTwoCustomToggleSlices(sliceUri); 96 case "/contact": 97 return createContact(sliceUri); 98 case "/gallery": 99 return createGallery(sliceUri); 100 case "/weather": 101 return createWeather(sliceUri); 102 case "/reservation": 103 return createReservationSlice(sliceUri); 104 case "/loadlist": 105 return createLoadingSlice(sliceUri, false /* loadAll */, true /* isList */); 106 case "/loadlist2": 107 return createLoadingSlice(sliceUri, true /* loadAll */, true /* isList */); 108 case "/loadgrid": 109 return createLoadingSlice(sliceUri, false /* loadAll */, false /* isList */); 110 case "/loadgrid2": 111 return createLoadingSlice(sliceUri, true /* loadAll */, false /* isList */); 112 } 113 throw new IllegalArgumentException("Unknown uri " + sliceUri); 114 } 115 116 private Slice createWeather(Uri sliceUri) { 117 return new GridBuilder(getContext(), sliceUri) 118 .addCell(cb -> cb 119 .addLargeImage(Icon.createWithResource(getContext(), R.drawable.weather_1)) 120 .addText("MON") 121 .addTitleText("69\u00B0")) 122 .addCell(cb -> cb 123 .addLargeImage(Icon.createWithResource(getContext(), R.drawable.weather_2)) 124 .addText("TUE") 125 .addTitleText("71\u00B0")) 126 .addCell(cb -> cb 127 .addLargeImage(Icon.createWithResource(getContext(), R.drawable.weather_3)) 128 .addText("WED") 129 .addTitleText("76\u00B0")) 130 .addCell(cb -> cb 131 .addLargeImage(Icon.createWithResource(getContext(), R.drawable.weather_4)) 132 .addText("THU") 133 .addTitleText("72\u00B0")) 134 .addCell(cb -> cb 135 .addLargeImage(Icon.createWithResource(getContext(), R.drawable.weather_1)) 136 .addText("FRI") 137 .addTitleText("68\u00B0")) 138 .build(); 139 } 140 141 private Slice createGallery(Uri sliceUri) { 142 return new ListBuilder(getContext(), sliceUri) 143 .setColor(0xff4285F4) 144 .addRow(b -> b 145 .setTitle("Family trip to Hawaii") 146 .setSubtitle("Sep 30, 2017 - Oct 2, 2017") 147 .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_cast), 148 getBroadcastIntent(ACTION_TOAST, "cast photo album")) 149 .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_share), 150 getBroadcastIntent(ACTION_TOAST, "share photo album"))) 151 .addGrid(b -> b 152 .addCell(cb -> cb 153 .addLargeImage(Icon.createWithResource(getContext(), R.drawable.slices_1))) 154 .addCell(cb -> cb 155 .addLargeImage(Icon.createWithResource(getContext(), R.drawable.slices_2))) 156 .addCell(cb -> cb 157 .addLargeImage(Icon.createWithResource(getContext(), R.drawable.slices_3))) 158 .addCell(cb -> cb 159 .addLargeImage(Icon.createWithResource(getContext(), R.drawable.slices_4)))) 160 .build(); 161 } 162 163 private Slice createContact(Uri sliceUri) { 164 return new ListBuilder(getContext(), sliceUri) 165 .setColor(0xff3949ab) 166 .addRow(b -> b 167 .setTitle("Mady Pitza") 168 .setSubtitle("Frequently contacted contact") 169 .addEndItem(Icon.createWithResource(getContext(), R.drawable.mady))) 170 .addGrid(b -> b 171 .addCell(cb -> cb 172 .addImage(Icon.createWithResource(getContext(), R.drawable.ic_call)) 173 .addText("Call") 174 .setContentIntent(getBroadcastIntent(ACTION_TOAST, "call"))) 175 .addCell(cb -> cb 176 .addImage(Icon.createWithResource(getContext(), R.drawable.ic_text)) 177 .addText("Text") 178 .setContentIntent(getBroadcastIntent(ACTION_TOAST, "text"))) 179 .addCell(cb ->cb 180 .addImage(Icon.createWithResource(getContext(), R.drawable.ic_video)) 181 .setContentIntent(getBroadcastIntent(ACTION_TOAST, "video")) 182 .addText("Video")) 183 .addCell(cb -> cb 184 .addImage(Icon.createWithResource(getContext(), R.drawable.ic_email)) 185 .addText("Email") 186 .setContentIntent(getBroadcastIntent(ACTION_TOAST, "email")))) 187 .build(); 188 } 189 190 private Slice createMessagingSlice(Uri sliceUri) { 191 // TODO: Remote input. 192 return new MessagingSliceBuilder(getContext(), sliceUri) 193 .add(b -> b 194 .addText("yo home \uD83C\uDF55, I emailed you the info") 195 .addTimestamp(System.currentTimeMillis() - 20 * DateUtils.MINUTE_IN_MILLIS) 196 .addSource(Icon.createWithResource(getContext(), R.drawable.mady))) 197 .add(b -> b 198 .addText("just bought my tickets") 199 .addTimestamp(System.currentTimeMillis() - 10 * DateUtils.MINUTE_IN_MILLIS)) 200 .add(b -> b 201 .addText("yay! can't wait for getContext() weekend!\n" 202 + "\uD83D\uDE00") 203 .addTimestamp(System.currentTimeMillis() - 5 * DateUtils.MINUTE_IN_MILLIS) 204 .addSource(Icon.createWithResource(getContext(), R.drawable.mady))) 205 .build(); 206 207 } 208 209 private Slice createNoteSlice(Uri sliceUri) { 210 // TODO: Remote input. 211 return new ListBuilder(getContext(), sliceUri) 212 .setColor(0xfff4b400) 213 .addRow(b -> b 214 .setTitle("Create new note") 215 .setSubtitle("with this note taking app") 216 .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_create), 217 getBroadcastIntent(ACTION_TOAST, "create note")) 218 .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_voice), 219 getBroadcastIntent(ACTION_TOAST, "voice note")) 220 .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_camera), 221 getIntent("android.media.action.IMAGE_CAPTURE"))) 222 .build(); 223 } 224 225 private Slice createReservationSlice(Uri sliceUri) { 226 return new ListBuilder(getContext(), sliceUri) 227 .setColor(0xffFF5252) 228 .addRow(b -> b 229 .setTitle("Upcoming trip to Seattle") 230 .setSubtitle("Feb 1 - 19 | 2 guests") 231 .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_location), 232 getBroadcastIntent(ACTION_TOAST, "show reservation location on map")) 233 .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_text), 234 getBroadcastIntent(ACTION_TOAST, "contact host"))) 235 .addGrid(b -> b 236 .addCell(cb -> cb 237 .addLargeImage( 238 Icon.createWithResource(getContext(), R.drawable.reservation)))) 239 .addGrid(b -> b 240 .addCell(cb -> cb 241 .addTitleText("Check In") 242 .addText("12:00 PM, Feb 1")) 243 .addCell(cb -> cb 244 .addTitleText("Check Out") 245 .addText("11:00 AM, Feb 19"))) 246 .build(); 247 } 248 249 private Slice createRideSlice(Uri sliceUri) { 250 final ForegroundColorSpan colorSpan = new ForegroundColorSpan(0xff0F9D58); 251 SpannableString headerSubtitle = new SpannableString("Ride in 4 min"); 252 headerSubtitle.setSpan(colorSpan, 8, headerSubtitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE); 253 SpannableString homeSubtitle = new SpannableString("12 miles | 12 min | $9.00"); 254 homeSubtitle.setSpan(colorSpan, 20, homeSubtitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE); 255 SpannableString workSubtitle = new SpannableString("44 miles | 1 hour 45 min | $31.41"); 256 workSubtitle.setSpan(colorSpan, 27, workSubtitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE); 257 258 return new ListBuilder(getContext(), sliceUri) 259 .setColor(0xff0F9D58) 260 .addRow(b -> b 261 .setTitle("Work") 262 .setSubtitle(workSubtitle) 263 .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_work), 264 getBroadcastIntent(ACTION_TOAST, "work"))) 265 .addRow(b -> b 266 .setTitle("Home") 267 .setSubtitle("2 hours 33 min via 101") 268 .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_home), 269 getBroadcastIntent(ACTION_TOAST, "home"))) 270 .build(); 271 } 272 273 private Slice createCustomToggleSlice(Uri sliceUri) { 274 return new ListBuilder(getContext(), sliceUri) 275 .setColor(0xffff4081) 276 .addRow(b -> b 277 .setTitle("Custom toggle") 278 .setSubtitle("It can support two states") 279 .addToggle(getBroadcastIntent(ACTION_TOAST, "star toggled"), 280 true /* isChecked */, 281 Icon.createWithResource(getContext(), R.drawable.toggle_star))) 282 .build(); 283 } 284 285 private Slice createTwoCustomToggleSlices(Uri sliceUri) { 286 return new ListBuilder(getContext(), sliceUri) 287 .setColor(0xffff4081) 288 .addRow(b -> b 289 .setTitle("2 toggles") 290 .setSubtitle("each supports two states") 291 .addToggle(getBroadcastIntent(ACTION_TOAST, "first star toggled"), 292 true /* isChecked */, 293 Icon.createWithResource(getContext(), R.drawable.toggle_star)) 294 .addToggle(getBroadcastIntent(ACTION_TOAST, "second star toggled"), 295 false /* isChecked */, 296 Icon.createWithResource(getContext(), R.drawable.toggle_star))) 297 .build(); 298 } 299 300 private Slice createWifiSlice(Uri sliceUri) { 301 // Get wifi state 302 WifiManager wifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE); 303 int wifiState = wifiManager.getWifiState(); 304 boolean wifiEnabled = false; 305 String state; 306 switch (wifiState) { 307 case WifiManager.WIFI_STATE_DISABLED: 308 case WifiManager.WIFI_STATE_DISABLING: 309 state = "disconnected"; 310 break; 311 case WifiManager.WIFI_STATE_ENABLED: 312 case WifiManager.WIFI_STATE_ENABLING: 313 state = wifiManager.getConnectionInfo().getSSID(); 314 wifiEnabled = true; 315 break; 316 case WifiManager.WIFI_STATE_UNKNOWN: 317 default: 318 state = ""; // just don't show anything? 319 break; 320 } 321 boolean finalWifiEnabled = wifiEnabled; 322 return new ListBuilder(getContext(), sliceUri) 323 .setColor(0xff4285f4) 324 .addRow(b -> b 325 .setTitle("Wi-fi") 326 .setTitleItem(Icon.createWithResource(getContext(), R.drawable.ic_wifi)) 327 .setSubtitle(state) 328 .addToggle(getBroadcastIntent(ACTION_WIFI_CHANGED, null), finalWifiEnabled) 329 .setContentIntent(getIntent(Settings.ACTION_WIFI_SETTINGS))) 330 .build(); 331 } 332 333 private Handler mHandler = new Handler(); 334 private Runnable mLoader; 335 private boolean mLoaded = false; 336 337 private Slice createLoadingSlice(Uri sliceUri, boolean loadAll, boolean isList) { 338 if (!mLoaded || mLoader != null) { 339 // Need to load content or we're still loading so just return partial 340 if (!mLoaded) { 341 mLoader = () -> { 342 // Note that we've loaded things 343 mLoader = null; 344 mLoaded = true; 345 // Notify to update the slice 346 getContext().getContentResolver().notifyChange(sliceUri, null); 347 }; 348 mHandler.postDelayed(mLoader, LOADING_DELAY_MS); 349 } 350 if (loadAll) { 351 return isList 352 ? new ListBuilder(getContext(), sliceUri).build() 353 : new GridBuilder(getContext(), sliceUri).build(); 354 } 355 return createPartialSlice(sliceUri, true, isList); 356 } else { 357 mLoaded = false; 358 return createPartialSlice(sliceUri, false, isList); 359 } 360 } 361 362 private Slice createPartialSlice(Uri sliceUri, boolean isPartial, boolean isList) { 363 Icon icon = Icon.createWithResource(getContext(), R.drawable.ic_star_on); 364 PendingIntent intent = getBroadcastIntent(ACTION_TOAST, "star tapped"); 365 PendingIntent intent2 = getBroadcastIntent(ACTION_TOAST, "toggle tapped"); 366 if (isPartial) { 367 if (isList) { 368 return new ListBuilder(getContext(), sliceUri) 369 .addRow(b -> createRow(b, "Slice that has content to load", 370 "Temporary subtitle", icon, intent, true)) 371 .addRow(b -> createRow(b, null, null, null, intent, true)) 372 .addRow(b -> b.setTitle("My title").addToggle(intent2, false, true)) 373 .build(); 374 } else { 375 return new GridBuilder(getContext(), sliceUri) 376 .addCell(b -> createCell(b, null, null, null, true)) 377 .addCell(b -> createCell(b, "Two stars", null, icon, true)) 378 .addCell(b -> createCell(b, null, null, null, true)) 379 .build(); 380 } 381 } else { 382 if (isList) { 383 return new ListBuilder(getContext(), sliceUri) 384 .addRow(b -> createRow(b, "Slice that has content to load", 385 "Subtitle loaded", icon, intent, false)) 386 .addRow(b -> createRow(b, "Loaded row", "Loaded subtitle", 387 icon, intent, false)) 388 .addRow(b -> b.setTitle("My title").addToggle(intent2, false)) 389 .build(); 390 } else { 391 return new GridBuilder(getContext(), sliceUri) 392 .addCell(b -> createCell(b, "One star", "meh", icon, false)) 393 .addCell(b -> createCell(b, "Two stars", "good", icon, false)) 394 .addCell(b -> createCell(b, "Three stars", "best", icon, false)) 395 .build(); 396 } 397 } 398 } 399 400 private ListBuilder.RowBuilder createRow(ListBuilder.RowBuilder rb, String title, 401 String subtitle, Icon icon, PendingIntent content, boolean isLoading) { 402 return rb.setTitle(title, isLoading) 403 .setSubtitle(subtitle, isLoading) 404 .addEndItem(icon, isLoading) 405 .setContentIntent(content); 406 } 407 408 private GridBuilder.CellBuilder createCell(GridBuilder.CellBuilder cb, String text1, 409 String text2, Icon icon, boolean isLoading) { 410 return cb.addText(text1, isLoading).addText(text2, isLoading).addImage(icon, isLoading); 411 } 412 413 private PendingIntent getIntent(String action) { 414 Intent intent = new Intent(action); 415 PendingIntent pi = PendingIntent.getActivity(getContext(), 0, intent, 0); 416 return pi; 417 } 418 419 private PendingIntent getBroadcastIntent(String action, String message) { 420 Intent intent = new Intent(action); 421 intent.setClass(getContext(), SliceBroadcastReceiver.class); 422 // Ensure a new PendingIntent is created for each message. 423 int requestCode = 0; 424 if (message != null) { 425 intent.putExtra(EXTRA_TOAST_MESSAGE, message); 426 requestCode = message.hashCode(); 427 } 428 return PendingIntent.getBroadcast(getContext(), requestCode, intent, 429 PendingIntent.FLAG_UPDATE_CURRENT); 430 } 431} 432