How to develop Slices apps

How to develop Slices apps

Transcript

  1. Slices 氏 wasabeef Shibuya.aab #25

  2. About me Daichi Furiya Google Developer Expert CyberAgent, Inc. @wasabeef_jp

    wasabeef
  3. Jetpack

  4. Slices

  5. Slices Google検索結果の中に自分のアプリ のUI、アクションなど表示すること ができる アプリ名やアクションーワードなど eg. [maps], [lyft], [airbnb] eg.

    [get a ride], [trending videos]
  6. Next future of Slices Search Push Long Press Home Screen

  7. Slices patterns

  8. Slices   Slices will start appearing soon for users, but you

    can start building today.
  9. Slice Viewer 4.4 or later For Debug https://g.co/Slices $ adb

    install -r -t slice-viewer.apk
  10. <provider /> in AndroidManifest.xml SliceProvider ListBuilder extends TemplateSliceBuilder SliceAction SliceView

    (SliceManager, SliceLiveData) Points
  11. SliceManager https://blog.novoda.com/android-p-slices-missing-documentation-part-2/ Host Provider

  12. <provider /> in AndroidManifest.xml SliceProvider ListBuilder extends TemplateSliceBuilder SliceAction SliceView

    (SliceManager, SliceLiveData) Points
  13. <provider /> in AndroidManifest.xml SliceProvider ListBuilder extends TemplateSliceBuilder SliceAction SliceView

    (SliceManager, SliceLiveData) Points
  14. <provider /> in AndroidManifest.xml SliceProvider ListBuilder extends TemplateSliceBuilder SliceAction SliceView

    (SliceManager, SliceLiveData) Points
  15. <provider /> in AndroidManifest.xml SliceProvider ListBuilder extends TemplateSliceBuilder SliceAction SliceView

    (SliceManager, SliceLiveData) Points
  16. <provider /> in AndroidManifest.xml SliceProvider ListBuilder extends TemplateSliceBuilder SliceAction SliceView

    (SliceManager, SliceLiveData) Points
  17. Testing & Permission $ adb shell am start -a android.intent.action.VIEW

    \ -d slice-<your slice URI>
  18. Gradle buildscript { repositories { google() } dependencies { classpath

    'com.android.tools.build:gradle:3.2.0-alpha15' } } // ... dependencies { implementation 'androidx.slice:slice-core:1.0.0-alpha1' implementation ‘androidx.slice:slice-builders:1.0.0-alpha1' // ssIf you wanna use SliceView implementation 'androidx.slice:slice-view:1.0.0-alpha1' }
  19. AndroidManifest.xml <application ... <provider android:name=".MySliceProvider" android:authorities="jp.wasabeef.slices" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW"

    /> <category android:name="android.app.slice.category.SLICE" /> </intent-filter> </provider> <activity android:name=".MainActivity"> ... </activity> </application>
  20. AndroidManifest.xml <application ... <provider android:name=".MySliceProvider" android:authorities="jp.wasabeef.slices" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW"

    /> <category android:name="android.app.slice.category.SLICE" /> </intent-filter> </provider> <activity android:name=".MainActivity"> ... </activity> </application>
  21. AndroidManifest.xml <application ... <provider android:name=".MySliceProvider" android:authorities="jp.wasabeef.slices" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW"

    /> <category android:name="android.app.slice.category.SLICE" /> </intent-filter> </provider> <activity android:name=".MainActivity"> ... </activity> </application>
  22. AndroidManifest.xml <application ... <provider android:name=".MySliceProvider" android:authorities="jp.wasabeef.slices" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW"

    /> <category android:name="android.app.slice.category.SLICE" /> </intent-filter> </provider> <activity android:name=".MainActivity"> ... </activity> </application>
  23. SliceProvider class MySliceProvider : SliceProvider() { override fun onCreateSliceProvider(): Boolean

    = true override fun onBindSlice(sliceUri: Uri): Slice? { return when (sliceUri.path) { "/ping" -> { ListBuilder(context, sliceUri, ListBuilder.INFINITY) .addRow { it.setTitle("pong") } .build() } else -> null } } }
  24. SliceProvider class MySliceProvider : SliceProvider() { override fun onCreateSliceProvider(): Boolean

    = true override fun onBindSlice(sliceUri: Uri): Slice? { return when (sliceUri.path) { "/ping" -> { ListBuilder(context, sliceUri, ListBuilder.INFINITY) .addRow { it.setTitle("pong") } .build() } else -> null } } }
  25. SliceProvider class MySliceProvider : SliceProvider() { override fun onCreateSliceProvider(): Boolean

    = true override fun onBindSlice(sliceUri: Uri): Slice? { return when (sliceUri.path) { "/ping" -> { ListBuilder(context, sliceUri, ListBuilder.INFINITY) .addRow { it.setTitle("pong") } .build() } else -> null } } }
  26. SliceProvider class MySliceProvider : SliceProvider() { override fun onCreateSliceProvider(): Boolean

    = true override fun onBindSlice(sliceUri: Uri): Slice? { return when (sliceUri.path) { "/ping" -> { /** content://jp.wasabeef.slices/ping **/ } "/open" -> { /** content://jp.wasabeef.slices/open **/ } "/map" -> { /** content://jp.wasabeef.slices/map **/ } "/dog" -> { /** content://jp.wasabeef.slices/dog **/ } "/cat" -> { /** content://jp.wasabeef.slices/cat **/ } else -> null } } }
  27. SliceProvider class MySliceProvider : SliceProvider() { override fun onCreateSliceProvider(): Boolean

    = true override fun onBindSlice(sliceUri: Uri): Slice? { return when (sliceUri.path) { "/ping" -> { ListBuilder(context, sliceUri, ListBuilder.INFINITY) .addRow { it.setTitle("pong") } .build() } else -> null } } }
  28. SliceProvider class MySliceProvider : SliceProvider() { override fun onCreateSliceProvider(): Boolean

    = true override fun onBindSlice(sliceUri: Uri): Slice? { return when (sliceUri.path) { "/ping" -> { ListBuilder(context, sliceUri, ListBuilder.INFINITY) .addRow { it.setTitle("pong") } .build() } else -> null } } }
  29. SliceProvider class MySliceProvider : SliceProvider() { override fun onCreateSliceProvider(): Boolean

    = true override fun onBindSlice(sliceUri: Uri): Slice? { return when (sliceUri.path) { "/ping" -> { ListBuilder(context, sliceUri, ListBuilder.INFINITY) .addRow { it.setTitle("pong") } .build() } else -> null } } }
  30. SliceProvider

  31. Basic Sample OK $ adb shell am start -a android.intent.action.VIEW

    \ -d slice-content://jp.wasabeef.slices/ping NG $ adb shell am start -a android.intent.action.VIEW \ -d slice-content://jp.wasabeef.slices/hoge
  32. SliceAction override fun onBindSlice(sliceUri: Uri): Slice? { return when (sliceUri.path)

    { "/open" -> createOpenActivity(sliceUri) else -> null } } private fun createOpenActivity(sliceUri: Uri): Slice { return ListBuilder(context, sliceUri, ListBuilder.INFINITY) .addRow { it.apply { setTitle("Perform action in app") setPrimaryAction(createActivityAction()) } }.build() } private fun createActivityAction(): SliceAction { val intent = Intent(context, MainActivity::class.java) return SliceAction(PendingIntent.getActivity(context, 0, intent, 0), IconCompat.createWithResource(context, R.mipmap.ic_launcher), "Open MainActivity.") }
  33. SliceAction override fun onBindSlice(sliceUri: Uri): Slice? { return when (sliceUri.path)

    { "/open" -> createOpenActivity(sliceUri) else -> null } } private fun createOpenActivity(sliceUri: Uri): Slice { return ListBuilder(context, sliceUri, ListBuilder.INFINITY) .addRow { it.apply { setTitle("Perform action in app") setPrimaryAction(createActivityAction()) } }.build() } private fun createActivityAction(): SliceAction { val intent = Intent(context, MainActivity::class.java) return SliceAction(PendingIntent.getActivity(context, 0, intent, 0), IconCompat.createWithResource(context, R.mipmap.ic_launcher), "Open MainActivity.") }
  34. SliceAction package androidx.slice.core; ... /** * Interface for a slice

    action, supports tappable icons, custom toggle icons, * and default toggles. */ public interface SliceAction { @Nullable SliceAction setContentDescription(@NonNull CharSequence description); SliceAction setChecked(boolean isChecked); SliceAction setPriority(@IntRange(from = 0) int priority); @Nullable PendingIntent getAction(); @Nullable IconCompat getIcon(); @Nullable CharSequence getTitle(); @Nullable CharSequence getContentDescription(); int getPriority(); boolean isToggle(); boolean isChecked(); @SliceHints.ImageMode int getImageMode(); boolean isDefaultToggle(); }
  35. SliceAction /** * Construct a SliceAction representing a tappable icon.

    * * @param action the pending intent to invoke for this action. * @param actionIcon the icon to display for this action. * @param actionTitle the title for this action, also used for content description * if one hasn't * been set via {@link #setContentDescription(CharSequence)}. */ public SliceAction(@NonNull PendingIntent action, @NonNull IconCompat actionIcon, @NonNull CharSequence actionTitle) { this(action, actionIcon, ICON_IMAGE, actionTitle); }
  36. Primary Action Sample http://www.meg-snow.com/products/ $ adb shell am start -a

    android.intent.action.VIEW \ -d slice-content://jp.wasabeef.slices/open
  37. GridRow class MySliceProvider : SliceProvider() { override fun onCreateSliceProvider(): Boolean

    = true override fun onBindSlice(sliceUri: Uri): Slice? { return when (sliceUri.path) { "/ping" -> { ListBuilder(context, sliceUri, INFINITY) .addRow { it.setTitle("pong") } .build() } else -> null } } } http://www.meg-snow.com/products/
  38. GridRow class MySliceProvider : SliceProvider() { override fun onCreateSliceProvider(): Boolean

    = true override fun onBindSlice(sliceUri: Uri): Slice? { return when (sliceUri.path) { "/ping" -> { ListBuilder(context, sliceUri, INFINITY) .addRow { it.setTitle("pong") } .build() } else -> null } } }
  39. GridRow return ListBuilder(context, sliceUri, INFINITY) .addGridRow { it.apply { addCell

    { // title, image, intent …etc } addCell { // title, image, intent …etc } addCell { // title, image, intent …etc } } }.build() http://www.meg-snow.com/products/
  40. Slices patterns

  41. Types of Row Row GridRow RangeRow InputRangeRow

  42. SliceView http://www.meg-snow.com/products/ http://www.meg-snow.com/products/ val sliceUri = Uri.parse("content://jp.wasabeef.slices/open") // Sync val

    manager = SliceManager.getInstance(context!!) sliceView.slice = manager.bindSlice(sliceUri) // Async and LiveData SliceLiveData.fromUri(context, sliceUri) .observe(this, Observer({ sliceResult -> sliceView.slice = sliceResult }))
  43. SliceManager https://blog.novoda.com/android-p-slices-missing-documentation-part-2/ Host Provider

  44. How to update val sliceUri = Uri.parse("content://jp.wasabeef.slices/open") context.contentResolver.notifyChange(sliceUri, null)

  45. Conclusion

  46. Thank you. twitter.com/wasabeef_jp wasabeef.jp github.com/wasabeef