Slide 1

Slide 1 text

Slices 氏 wasabeef Shibuya.aab #25

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

Jetpack

Slide 4

Slide 4 text

Slices

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

Next future of Slices Search Push Long Press Home Screen

Slide 7

Slide 7 text

Slices patterns

Slide 8

Slide 8 text

Slices   Slices will start appearing soon for users, but you can start building today.

Slide 9

Slide 9 text

Slice Viewer 4.4 or later For Debug https://g.co/Slices $ adb install -r -t slice-viewer.apk

Slide 10

Slide 10 text

in AndroidManifest.xml SliceProvider ListBuilder extends TemplateSliceBuilder SliceAction SliceView (SliceManager, SliceLiveData) Points

Slide 11

Slide 11 text

SliceManager https://blog.novoda.com/android-p-slices-missing-documentation-part-2/ Host Provider

Slide 12

Slide 12 text

in AndroidManifest.xml SliceProvider ListBuilder extends TemplateSliceBuilder SliceAction SliceView (SliceManager, SliceLiveData) Points

Slide 13

Slide 13 text

in AndroidManifest.xml SliceProvider ListBuilder extends TemplateSliceBuilder SliceAction SliceView (SliceManager, SliceLiveData) Points

Slide 14

Slide 14 text

in AndroidManifest.xml SliceProvider ListBuilder extends TemplateSliceBuilder SliceAction SliceView (SliceManager, SliceLiveData) Points

Slide 15

Slide 15 text

in AndroidManifest.xml SliceProvider ListBuilder extends TemplateSliceBuilder SliceAction SliceView (SliceManager, SliceLiveData) Points

Slide 16

Slide 16 text

in AndroidManifest.xml SliceProvider ListBuilder extends TemplateSliceBuilder SliceAction SliceView (SliceManager, SliceLiveData) Points

Slide 17

Slide 17 text

Testing & Permission $ adb shell am start -a android.intent.action.VIEW \ -d slice-

Slide 18

Slide 18 text

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' }

Slide 19

Slide 19 text

AndroidManifest.xml ...

Slide 20

Slide 20 text

AndroidManifest.xml ...

Slide 21

Slide 21 text

AndroidManifest.xml ...

Slide 22

Slide 22 text

AndroidManifest.xml ...

Slide 23

Slide 23 text

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 } } }

Slide 24

Slide 24 text

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 } } }

Slide 25

Slide 25 text

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 } } }

Slide 26

Slide 26 text

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 } } }

Slide 27

Slide 27 text

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 } } }

Slide 28

Slide 28 text

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 } } }

Slide 29

Slide 29 text

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 } } }

Slide 30

Slide 30 text

SliceProvider

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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.") }

Slide 33

Slide 33 text

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.") }

Slide 34

Slide 34 text

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(); }

Slide 35

Slide 35 text

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); }

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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/

Slide 38

Slide 38 text

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 } } }

Slide 39

Slide 39 text

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/

Slide 40

Slide 40 text

Slices patterns

Slide 41

Slide 41 text

Types of Row Row GridRow RangeRow InputRangeRow

Slide 42

Slide 42 text

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 }))

Slide 43

Slide 43 text

SliceManager https://blog.novoda.com/android-p-slices-missing-documentation-part-2/ Host Provider

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

Conclusion

Slide 46

Slide 46 text

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