Slide 1

Slide 1 text

Build apps for Cars Keishin Yokomaku (@KeithYokoma) / Giftmall, inc. DroidKaigi.collect { #5@Nagoya }

Slide 2

Slide 2 text

Build apps for Cars About me ▸ Keishin Yokomaku - @KeithYokoma ▸ GitHub / Twitter / Stack Over fl ow ▸ Giftmall, Inc. / Android App Engineer DroidKaigi.collect { #5@Nagoya }

Slide 3

Slide 3 text

Build apps for Cars DroidKaigi.collect { #5@Nagoya }

Slide 4

Slide 4 text

https://developer.android.com/training/cars DroidKaigi.collect { #5@Nagoya }

Slide 5

Slide 5 text

https://developer.android.com/training/cars DroidKaigi.collect { #5@Nagoya }

Slide 6

Slide 6 text

Build apps for Cars Android Auto / Automotive OS ▸ Android Auto ▸ ंࡌγεςϜ (Head Unit) ʹઐ༻ͷ UI Λදࣔ͢ΔϞόΠϧ୺຤ͷ࢓૊Έ ▸ ंࡌγεςϜͱϞόΠϧ୺຤͕ Android Auto ʹରԠ͍ͯ͠Δඞཁ͕͋Δ ▸ Android Automotive OS ▸ Android ϕʔεͷΠϯϑΥςΠϯϝϯτγεςϜ༻ͷ OS ▸ ं͕ Android Automotive OS Λ౥ࡌ͍ͯ͠Δඞཁ͕͋Δ DroidKaigi.collect { #5@Nagoya }

Slide 7

Slide 7 text

▸ ϞόΠϧ୺຤ʹ౥ࡌ͞Ε͍ͯΔ Android Auto ͕த৺ (Host) ▸ Android Auto ͕ରԠͨ͠ΞϓϦͱ΍ΓऔΓ (IPC) ͢Δ ▸ Android Auto ͕ंࡌγεςϜͷ UI Λ੍ޚ Build apps for Cars Android Auto DroidKaigi.collect { #5@Nagoya } Icons: https://icons8.com/icon/1oqfhfFvXU8c/starbucks

Slide 8

Slide 8 text

Build apps for Cars Android Automotive OS DroidKaigi.collect { #5@Nagoya } ▸ ंʹ౥ࡌ͞Ε͍ͯΔ Android Automotive OS ͕த৺ (Host) ▸ Android Automotive OS ͸ Android ͷѥछ ▸ Google PlayStore ͔ΒΞϓϦͷΠϯετʔϧ͕Մೳ Icons: https://icons8.com/icon/1oqfhfFvXU8c/starbucks

Slide 9

Slide 9 text

Build apps for Cars Features DroidKaigi.collect { #5@Nagoya } ▸ Turn by turn φϏήʔγϣϯ / POI ▸ ࠶ੜίϯτϩʔϧ ▸ ϝοηʔδϯά

Slide 10

Slide 10 text

Build apps for Cars Features DroidKaigi.collect { #5@Nagoya } ▸ Turn by turn φϏήʔγϣϯ ▸ ໨త஍ͷݕࡧͱԻ੠Ҋ಺ ▸ ަ௨ঢ়گͷදࣔ ▸ Point of Interest (POI) ▸ ߦ͖ઌΛφϏήʔγϣϯΞϓϦʹఏڙ͢Δ Icons: https://icons8.com/icon/1oqfhfFvXU8c/starbucks

Slide 11

Slide 11 text

Build apps for Cars Features DroidKaigi.collect { #5@Nagoya } ▸ ࠶ੜίϯτϩʔϧ ▸ Ի੠ɾಈըϝσΟΞͷ࠶ੜ ▸ ϓϨΠϦετͷมߋ

Slide 12

Slide 12 text

Build apps for Cars Features DroidKaigi.collect { #5@Nagoya } ▸ ϝοηʔδϯά ▸ ৽ணϝοηʔδͷԻ੠ಡΈ্͛ ▸ Ի੠ೖྗʹΑΔฦ৴

Slide 13

Slide 13 text

Build apps for Cars AndroidX Libraries DroidKaigi.collect { #5@Nagoya } ▸ φϏήʔγϣϯ / POI ▸ Car App Library ▸ ࠶ੜίϯτϩʔϧ ▸ AndroidX Media3 ▸ ϝοηʔδϯά ▸ AnroidX Core

Slide 14

Slide 14 text

Build apps for Cars Media Playback with AndroidX Media3 DroidKaigi.collect { #5@Nagoya } ▸ Android Auto / Automotive OS ରԠͰ࢖͏ओཁͳΫϥε ▸ MediaBrowserServiceCompat ▸ MediaSessionCompat ▸ MediaControllerCompat

Slide 15

Slide 15 text

Build apps for Cars Media Playback with AndroidX Media3 DroidKaigi.collect { #5@Nagoya } ▸ MediaBrowserServiceCompat ▸ ΞϓϦ಺ʹ͋Δ࠶ੜՄೳͳϝσΟΞίϯςϯπͷ৘ใΛ 
 Android Auto / Automotive OS ʹఏڙ͢Δ

Slide 16

Slide 16 text

Build apps for Cars Media Playback with AndroidX Media3 DroidKaigi.collect { #5@Nagoya } ▸ MediaBrowserServiceCompat ▸ Android Auto / Automotive OS ͕ࣗ෼ͷΞϓϦͷ 
 MediaBrowserService ʹόΠϯυ͠ʹ͘Δ Icons: https://icons8.com/icon/0dttLEPe98bJ/ fi at-500 onGetRoot ίϯςϯπ֊૚࠷্Ґͷ৘ใ onLoadChildren ࢠ֊૚ͷίϯςϯπ৘ใ MediaBrowserService MediaBrowser

Slide 17

Slide 17 text

Build apps for Cars Media Playback with AndroidX Media3 DroidKaigi.collect { #5@Nagoya } ▸ MediaSessionCompmat ▸ ϝσΟΞίϯςϯπͷ࠶ੜঢ়گΛఏڙ͢Δ ▸ MediaBrowserService ͕ಈ͖࢝ΊͨΒ͸͡ΊʹॳظԽ͢Δ Icons: https://icons8.com/icon/0dttLEPe98bJ/ fi at-500 ίϯςϯπ֊૚࠷্Ґͷ৘ใ MediaBrowserService MediaBrowser onCreate: MediaSession ͷॳظԽ bindService connected onGetRoot

Slide 18

Slide 18 text

Build apps for Cars Media Playback with AndroidX Media3 DroidKaigi.collect { #5@Nagoya } ▸ MediaControllerCompat ▸ ϝσΟΞίϯςϯπͷ࠶ੜঢ়گΛૢ࡞͢Δ ▸ Android Auto / Automotive OS ͷ UI ૢ࡞͕ى఺ Icons: https://icons8.com/icon/0dttLEPe98bJ/ fi at-500 ίϯςϯπ֊૚࠷্Ґͷ৘ใ MediaBrowserService MediaBrowser onCreate: MediaSession ͷॳظԽ bindService connected onGetRoot MediaController ͷॳظԽ MediaController.play ࠶ੜΛ։࢝ ࠶ੜΛ։࢝

Slide 19

Slide 19 text

Build apps for Cars Media Playback with AndroidX Media3 DroidKaigi.collect { #5@Nagoya } ▸ Caveats ▸ MediaBrowser ͸୭Ͱ΋࣮૷Ͱ͖Δ ▸ MediaBrowserService ͸ bind ͖ͯͨ͠ΞϓϦͷݕূΛ͢΂͖ Icons: https://icons8.com/icon/EQ0QKBwiUSDM/pennywise ۭͷϦετΛฦ͢ MediaBrowserService MediaBrowser onGetRoot ύοέʔδΛݕূ: ෆڐՄ

Slide 20

Slide 20 text

Build apps for Cars Messaging with AndroidX Core DroidKaigi.collect { #5@Nagoya } ▸ Android Auto / Automotive OS ͕ϝοηʔδΛಡΈ্͛Δ࢓૊Έ Noti fi cationManager Noti fi cationListenerService app Icons: https://icons8.com/icon/0dttLEPe98bJ/ fi at-500 / https://icons8.com/icon/wT8BPIITf9UK/android-phone notify(): Noti fi cation Λදࣔ ৽͍͠ Noti fi cation Λ௨஌ Noti fi cation ΛಡΈऔΔ ϝοηʔδΛಡΈ্͛Δ طಡʹ͢Δ ฦ৴͢Δ Ի੠ೖྗΛड͚෇͚Δ

Slide 21

Slide 21 text

Build apps for Cars Messaging with AndroidX Core DroidKaigi.collect { #5@Nagoya } ▸ ࣍ʹ্͛ΔཁٻΛຬͨ͢௨஌ΛදࣔՄೳ ▸ Noti fi cation.MessagingStyle Λ࢖͍ͬͯΔ ▸ CATEGORY_MESSAGE ΧςΰϦʹଐ͢Δ ▸ ະಡϝοηʔδͷΈΛอ࣋͢Δ ▸ طಡʹ͢ΔΞΫγϣϯϘλϯ͕͋Δ ▸ SEMANTIC_ACTION_MARK_AS_READ ▸ ΞΫγϣϯϘλϯͷΠϕϯτॲཧʹ௥Ճͷ UI ͕ଘࡏ͠ͳ͍

Slide 22

Slide 22 text

Build apps for Cars Messaging with AndroidX Core DroidKaigi.collect { #5@Nagoya } ▸ ฦ৴ػೳΛ࢖͏৔߹͸௥ՃͰ࣍ͷཁٻ΋ຬͨ͢ ▸ ฦ৴ઐ༻ͷΞΫγϣϯϘλϯΛઃఆ͢Δ ▸ SEMANTIC_ACTION_REPLY ▸ ΞΫγϣϯϘλϯͷΠϕϯτॲཧʹ௥Ճͷ UI ͕ଘࡏ͠ͳ͍ ▸ RemoteInput ΦϒδΣΫτΛ͍࣋ͬͯΔ

Slide 23

Slide 23 text

Build apps for Cars Messaging with AndroidX Core DroidKaigi.collect { #5@Nagoya } val sender = Person.Builder() .setName("John Doe") .build() val style = NotificationCompat.MessagingStyle(sender) .addMessage("hello!", System.currentTimeMillis(), sender) val markAsReadIntent = PendingIntent.getBroadcast(/* */) val markAsReadAction = NotificationCompat.Action.Builder(R.drawable.ic_mark_as_read, "Mark as read", markAsReadIntent) .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ) .build() val replyPendingIntent = PendingIntent.getBroadcast(/* */) val replyInput = RemoteInput.Builder("input") .build() val replyAction = NotificationCompat.Action.Builder(R.drawable.ic_reply, "Reply", replyPendingIntent) .addRemoteInput(replyInput) .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY) .build() val notification = NotificationCompat.Builder(context, "notification_channel") .setContentTitle("title") .setContentText("message") .setCategory(NotificationCompat.CATEGORY_MESSAGE) .addAction(markAsReadAction) .addAction(replyAction) .setStyle(style) .build()

Slide 24

Slide 24 text

Build apps for Cars Messaging with AndroidX Core DroidKaigi.collect { #5@Nagoya } val sender = Person.Builder() .setName("John Doe") .build() val style = NotificationCompat.MessagingStyle(sender) .addMessage("hello!", System.currentTimeMillis(), sender) val markAsReadIntent = PendingIntent.getBroadcast(/* */) val markAsReadAction = NotificationCompat.Action.Builder(R.drawable.ic_mark_as_read, "Mark as read", markAsReadIntent) .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ) .build() val replyPendingIntent = PendingIntent.getBroadcast(/* */) val replyInput = RemoteInput.Builder("input") .build() val replyAction = NotificationCompat.Action.Builder(R.drawable.ic_reply, "Reply", replyPendingIntent) .addRemoteInput(replyInput) .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY) .build() val notification = NotificationCompat.Builder(context, "notification_channel") .setContentTitle("title") .setContentText("message") .setCategory(NotificationCompat.CATEGORY_MESSAGE) .addAction(markAsReadAction) .addAction(replyAction) .setStyle(style) .build()

Slide 25

Slide 25 text

Build apps for Cars Messaging with AndroidX Core DroidKaigi.collect { #5@Nagoya } val sender = Person.Builder() .setName("John Doe") .build() val style = NotificationCompat.MessagingStyle(sender) .addMessage("hello!", System.currentTimeMillis(), sender) val markAsReadIntent = PendingIntent.getBroadcast(/* */) val markAsReadAction = NotificationCompat.Action.Builder(R.drawable.ic_mark_as_read, "Mark as read", markAsReadIntent) .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ) .build() val replyPendingIntent = PendingIntent.getBroadcast(/* */) val replyInput = RemoteInput.Builder("input") .build() val replyAction = NotificationCompat.Action.Builder(R.drawable.ic_reply, "Reply", replyPendingIntent) .addRemoteInput(replyInput) .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY) .build() val notification = NotificationCompat.Builder(context, "notification_channel") .setContentTitle("title") .setContentText("message") .setCategory(NotificationCompat.CATEGORY_MESSAGE) .addAction(markAsReadAction) .addAction(replyAction) .setStyle(style) .build()

Slide 26

Slide 26 text

Build apps for Cars Messaging with AndroidX Core DroidKaigi.collect { #5@Nagoya } val sender = Person.Builder() .setName("John Doe") .build() val style = NotificationCompat.MessagingStyle(sender) .addMessage("hello!", System.currentTimeMillis(), sender) val markAsReadIntent = PendingIntent.getBroadcast(/* */) val markAsReadAction = NotificationCompat.Action.Builder(R.drawable.ic_mark_as_read, "Mark as read", markAsReadIntent) .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ) .build() val replyPendingIntent = PendingIntent.getBroadcast(/* */) val replyInput = RemoteInput.Builder("input") .build() val replyAction = NotificationCompat.Action.Builder(R.drawable.ic_reply, "Reply", replyPendingIntent) .addRemoteInput(replyInput) .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY) .build() val notification = NotificationCompat.Builder(context, "notification_channel") .setContentTitle("title") .setContentText("message") .setCategory(NotificationCompat.CATEGORY_MESSAGE) .addAction(markAsReadAction) .addAction(replyAction) .setStyle(style) .build()

Slide 27

Slide 27 text

Build apps for Cars Messaging with AndroidX Core DroidKaigi.collect { #5@Nagoya } ▸ CarExtender ▸ Android Auto / Automotive OS ޲͚ʹ௨஌Λ֦ு͢ΔͨΊͷศརΫϥε ▸ ௨஌ͷࡉ͔͍ઃఆΛ೚͍ͤͨͱ͖͸ CarExtender Λ࢖͏

Slide 28

Slide 28 text

Build apps for Cars Messaging with AndroidX Core DroidKaigi.collect { #5@Nagoya } ▸ Caveats ▸ Noti fi cationListenerService ͸୭Ͱ΋࣮૷Մೳ ▸ MediaBrowserService ͷΑ͏ʹύοέʔδͷݕূ͸ෆՄ ▸ ௨ৗͷ Permission ͱ͸ผͷೝՄͷ࢓૊Έ͕༻ҙ͞Ε͍ͯΔ ▸ ref: https://www.jssec.org/dl/android_securecoding.pdf ▸ ௨஌ʹ͓͚ΔϓϥΠόγʔ؅ཧͷϦϑΝϨϯε

Slide 29

Slide 29 text

Build apps for Cars ৄղ Android Auto DroidKaigi.collect { #5@Nagoya } ▸ https://www.youtube.com/watch?v=0ffNY26f-q4 ▸ DroidKaigi 2018 ▸ ࠶ੜίϯτϩʔϧ΍ϝοηʔδϯάʹ͍ͭͯղઆ

Slide 30

Slide 30 text

Build apps for Cars Car App Library DroidKaigi.collect { #5@Nagoya } ▸ Android Auto / Automotive OS ޲͚ͷػೳ։ൃϥΠϒϥϦ ▸ ݱࡏ͸φϏήʔγϣϯͱ POI ޲͚ͷػೳΛ࣮૷͢ΔͨΊͷ API Λఆٛ ▸ UI Λߏங͢ΔͨΊͷπʔϧΩοτ΋ఏڙ ▸ ςϯϓϨʔτϕʔεͷ UI ▸ YouTube: https://www.youtube.com/watch?v=watUEk6_i-4 ▸ Codelab: https://developer.android.com/codelabs/car-app-library- fundamentals

Slide 31

Slide 31 text

Build apps for Cars Car App Library DroidKaigi.collect { #5@Nagoya } ▸ Car App Library ͰఏڙՄೳͳػೳ ▸ φϏήʔγϣϯ ▸ POI ▸ IOT (experimental)

Slide 32

Slide 32 text

Build apps for Cars Car App Library DroidKaigi.collect { #5@Nagoya } ▸ Android Auto / Automotive OS ޲͚ΞϓϦͷ API Ϩϕϧ ▸ Android OS ͷόʔδϣϯͱ͸ผͷ API Ϩϕϧఆ͕ٛଘࡏ͢Δ ▸ API ϨϕϧʹΑͬͯ Car App Library Ͱఏڙ͞ΕΔػೳʹ͕ࠩ͋ΔͨΊ

Slide 33

Slide 33 text

Build apps for Cars Car App Library DroidKaigi.collect { #5@Nagoya }

Slide 34

Slide 34 text

Build apps for Cars Car App Library DroidKaigi.collect { #5@Nagoya } ▸ ΞϓϦ͕࣋ͭ CarAppService Λܦ༝ͯ͠ Android Auto / Automotive OS ͱ௨৴ ▸ CarAppService Ͱ Session Λੜ੒͠ɺSession ʹඥͮ͘ Screen Λ࡞Δ Android Auto / Automotive OS CarAppService bindService Session ύοέʔδΛݕূ Session ͔Βը໘Λߏங

Slide 35

Slide 35 text

Build apps for Cars Car App Library DroidKaigi.collect { #5@Nagoya } ▸ ंʹ͸ෳ਺ͷσΟεϓϨΠΛ౥ࡌՄೳ ▸ σΟεϓϨΠ͝ͱݸผʹ CarAppService ʹ bind ͠ʹ͘Δ Android Auto / Automotive OS 
 Dispay 1 CarAppService bindService Android Auto / Automotive OS 
 Dispay 2 bindService

Slide 36

Slide 36 text

Build apps for Cars Car App Library DroidKaigi.collect { #5@Nagoya } ▸ ंʹ͸ෳ਺ͷσΟεϓϨΠΛ౥ࡌՄೳ ▸ σΟεϓϨΠ͝ͱݸผʹ Session Λͭ͘Δ Android Auto / Automotive OS 
 Dispay 1 CarAppService Session Android Auto / Automotive OS 
 Dispay 2 Session

Slide 37

Slide 37 text

Build apps for Cars CarAppService DroidKaigi.collect { #5@Nagoya } class MyCarAppService : CarAppService() { override fun createHostValidator(): HostValidator { return HostValidator.ALLOW_ALL_HOSTS_VALIDATOR } override fun onCreateSession(): Session { return MyCarAppSession() } }

Slide 38

Slide 38 text

Build apps for Cars Session Implementation DroidKaigi.collect { #5@Nagoya } class MyCarAppSession : Session() { override fun onCreateScreen(intent: Intent): Screen { return MyCarAppScreen(carCotnext) } }

Slide 39

Slide 39 text

Build apps for Cars Screen implementation DroidKaigi.collect { #5@Nagoya } class MyCarAppScreen( carContext: CarContext ) : Screen(carContext) { override fun onGetTemplate(): Template { val row = Row.Builder() .setTitle("Hello, world!") .build() val pane = Pane.Builder() .addRow(row) .build() return PaneTemplate.Builder(pane) .setHeaderAction(Action.APP_ICON) .build() } }

Slide 40

Slide 40 text

Build apps for Cars Car App Library DroidKaigi.collect { #5@Nagoya } ▸ ༷ʑͳςϯϓϨʔτ͕ϥΠϒϥϦ಺ʹ͋Δ ▸ Google Maps Λදࣔ͢ΔςϯϓϨʔτ ▸ ೝূը໘Λදࣔ͢ΔςϯϓϨʔτ ▸ etc…

Slide 41

Slide 41 text

Build apps for Cars Car App Library DroidKaigi.collect { #5@Nagoya } ▸ Automotive OS ޲͚ʹ͸ઐ༻ͷରԠ͕ඞཁ ▸ app Ϟδϡʔϧͱ͸ผʹ automotive ༻ͷϞδϡʔϧ͕ඞཁ ▸ app ϞδϡʔϧΛ run ͢Δͱී௨ͷΞϓϦ͕ͦͷ·· deploy ͞Εͯ͠·͏ ▸ automotive ༻ͷϞδϡʔϧͰઐ༻ͷ apk Λు͖ग़͢ ▸ uses-feature Ͱ Automotive OS ޲͚ͷ΋ͷΛ required ʹ͢Δ ▸ automotive ༻ͷ Activity Λ࢖͏

Slide 42

Slide 42 text

Build apps for Cars Car App Library DroidKaigi.collect { #5@Nagoya }

Slide 43

Slide 43 text

Build apps for Cars Emulators: Android Auto ▸ Android Auto Desktop Head Unit (DHU) Emulator ▸ Android Virtual Device (AVD) Ͱ͸ͳ͍͜ͱʹؾΛ͚ͭΔ ▸ ࣍ͷίϚϯυͰ DHU Λىಈ͢Δ ▸ PC ʹ USB Ͱܨ͍ͩ୺຤Λ࢖͏৔߹ DroidKaigi.collect { #5@Nagoya } $SDK_LOCATION/extras/google/auto/desktop-head-unit $SDK_LOCATION/extras/google/auto/desktop-head-unit -u

Slide 44

Slide 44 text

Build apps for Cars Emulators: Android Automotive OS ▸ ઐ༻ AVD ͕͋Δ ▸ Play Store ෇ଐͷΤϛϡϨʔλΛ࢖͏ DroidKaigi.collect { #5@Nagoya }

Slide 45

Slide 45 text

Build apps for Cars Emulators: Android Automotive OS ▸ Google Assistant / Google Maps ͳͲϓϦΠϯετʔϧࡁΈ ▸ ηϯαʔ৘ใ (ं଎΍ΪΞ౳) ΋ΤϛϡϨʔγϣϯͰ͖Δ ▸ ηϯαʔ৘ใΛ·ͱΊͯ import ͯ͠ϦϓϨʔ΋Մ ▸ λονૢ࡞ / ෺ཧϘλϯૢ࡞྆ํରԠ DroidKaigi.collect { #5@Nagoya }

Slide 46

Slide 46 text

Build apps for Cars Emulators: Android Automotive OS ▸ Caveats ▸ AndroidStudio ͷ Tool Window ಺Ͱ͸ಈ͔ͳ͍ ▸ ىಈ͸Ͱ͖Δ͕ද͕ࣔͰ͖ͳ͍… ▸ ඞͣ Standalone Ͱಈ͔͢͜ͱ DroidKaigi.collect { #5@Nagoya }

Slide 47

Slide 47 text

Build apps for Cars Emulators: Android Automotive OS ▸ ࣗಈंϝʔΧʔ͕࡞͍ͬͯΔ system image ΋͋Δ ▸ GM ▸ Honda ▸ Polestar ▸ Volvo DroidKaigi.collect { #5@Nagoya }

Slide 48

Slide 48 text

Build apps for Cars Emulators: Android Automotive OS ▸ Caveats ▸ x86 system images ͔͠ͳ͍৔߹ Apple Silicon Ͱಈ͔ͳ͍͜ͱ͕͋Δ DroidKaigi.collect { #5@Nagoya }

Slide 49

Slide 49 text

Build apps for Cars Wrap up DroidKaigi.collect { #5@Nagoya } ▸ Android Auto / Automotive OS ʹରԠ͢Δ࢓૊Έ ▸ AndroidX Media3 ▸ AndroidX Core ▸ Car App Library ▸ ಉ͡ϥΠϒϥϦͰ Android Auto / Automotive OS ͲͪΒʹ΋ରԠՄೳ ▸ Ұ෦ Automotive OS ઐ༻ͷରԠ͕͋Δ

Slide 50

Slide 50 text

Build apps for Cars Keishin Yokomaku (@KeithYokoma) / Giftmall, inc. DroidKaigi.collect { #5@Nagoya }