Slide 1

Slide 1 text

Widget։ൃ࠶๚ Re-Introduction to Widget Development DroidKaigi2018, Room 2 ʵ 2018/02/08 14:00-14:30

Slide 2

Slide 2 text

Sample App https://github.com/ymnder/WidgetSample 2

Slide 3

Slide 3 text

Today's menu 1. About Widget 2. Widget Design 3. Let's make Widget 4. New API 3

Slide 4

Slide 4 text

About Widget

Slide 5

Slide 5 text

What Widget • Widget͸ϛχΞϓϦͰ͋Δ • ϝΠϯͷΞϓϦʹόϯυϧͯ͠ఏڙ͞ΕΔ • ΞϓϦͷίΞػೳΛΞϓϦͷ֎Ͱ࢖͑Δ • ΞϓϦͷػೳ΍ίϯςϯπʹϗʔϜը໘͔Β৮ΕΔ • ϗʔϜը໘ͰίϯςϯπΛ໨ʹ͢Δػձ͕૿͑Δ • ΞϓϦΛ։͔ͳͯ͘΋ΞΫηεͰ͖Δʂʂ 5

Slide 6

Slide 6 text

Why Widget now? • ϝΠϯΞϓϦͷ֎Ͱίϯςϯπʹ৮Εͯ΋Β͑Δ • ։ൃʹ؆୯ʹऔΓֻ͔ΕΔ • AndroidO͔Β৽͍͠API͕௥Ճ͞Εͨ 6

Slide 7

Slide 7 text

What is New Api • Widget Dialog • WidgetͷΠϯετʔϧϑϩʔ͕վળ͞Εͨ 7

Slide 8

Slide 8 text

Widget Picker • StoreΠϯετʔϧޙʹࣗ෼Ͱ௥Ճ͢Δඞཁ͕͋Δ • ϗʔϜը໘Λϩϯάλοϓ͢Δ • ϥϯνϟʔʹΑͬͯ௥Ճํ๏͕ҟͳΔ • ଞͷΞϓϦͱಉ͡Ϧετʹஔ͔Εͯ୳͠ʹ͍͘ 8

Slide 9

Slide 9 text

Widget Picker 9

Slide 10

Slide 10 text

Widget Dialog • ΞϓϦ಺͔Β௚઀WidgetΛ͸Γ͚ͭΔ͜ͱ͕Ͱ͖Δ • ௥Ճϑϩʔ͕ܶతʹ෼͔Γ΍͘͢ͳͬͨʂʂ ref: https://developers-jp.googleblog.com/2017/08/whats-new-for-shortcuts-and-widgets-in.html 10

Slide 11

Slide 11 text

Widget Dialog 11

Slide 12

Slide 12 text

Widget Design

Slide 13

Slide 13 text

ͲΜͳWidget͕͋Δ͔ ͍ΖΜͳWidget 13

Slide 14

Slide 14 text

Widget Category ᶃ Information widgets ᶄ Collection widgets ᶅ Control widgets ᶆ Hybrid widgets ΨΠυϥΠϯɿhttps://material.io/guidelines/components/widgets.html#widgets-types-of-widgets چΨΠυϥΠϯɿhttps://developer.android.com/design/patterns/widgets.html 14

Slide 15

Slide 15 text

Widget Category ᶃ Information widgets ᶄ Collection widgets ᶅ Control widgets ᶆ Hybrid widgets ΨΠυϥΠϯɿhttps://material.io/guidelines/components/widgets.html#widgets-types-of-widgets چΨΠυϥΠϯɿhttps://developer.android.com/design/patterns/widgets.html 15

Slide 16

Slide 16 text

Information widgets • ࣌ܭɾఱؾͳͲγϯϓϧͳ৘ใΛग़͢ • λοϓͨ͠Βৄࡉͳ৘ใΛΞϓϦͰදࣔ͢Δ 16

Slide 17

Slide 17 text

Widget Category ᶃ Information widgets ᶄ Collection widgets ᶅ Control widgets ᶆ Hybrid widgets ΨΠυϥΠϯɿhttps://material.io/guidelines/components/widgets.html#widgets-types-of-widgets چΨΠυϥΠϯɿhttps://developer.android.com/design/patterns/widgets.html 17

Slide 18

Slide 18 text

Collection widgets • هࣄͳͲͷಉ͡λΠϓͷෳ਺ͷΞΠςϜΛ·ͱΊΔ • ίϨΫγϣϯΛҰཡ͢Δ • λοϓͯ͠ৄࡉΛ֬ೝ͢Δ 18

Slide 19

Slide 19 text

Widget Category ᶃ Information widgets ᶄ Collection widgets ᶅ Control widgets ᶆ Hybrid widgets ΨΠυϥΠϯɿhttps://material.io/guidelines/components/widgets.html#widgets-types-of-widgets چΨΠυϥΠϯɿhttps://developer.android.com/design/patterns/widgets.html 19

Slide 20

Slide 20 text

Control widgets • ΞϓϦΛίϯτϩʔϧ͢ΔͨΊͷΞΠςϜΛදࣔ͢Δ • ࠶ੜϓϨʔϠʔͷϘλϯ΍ϥϯνϟʔ 20

Slide 21

Slide 21 text

Widget Category ᶃ Information widgets ᶄ Collection widgets ᶅ Control widgets ᶆ Hybrid widgets ΨΠυϥΠϯɿhttps://material.io/guidelines/components/widgets.html#widgets-types-of-widgets چΨΠυϥΠϯɿhttps://developer.android.com/design/patterns/widgets.html 21

Slide 22

Slide 22 text

Hybrid widgets • Ҏ্ͷཁૉΛ૊Έ߹Θͤͨ΋ͷ • ྫɿԻָϓϨʔϠʔͷϥϯνϟʔˍδϟέοτදࣔ 22

Slide 23

Slide 23 text

Widget Design: News Widget Case

Slide 24

Slide 24 text

Layout ᶃ Ұߦܕ • ҰߦͰχϡʔεΛίϯύΫτʹදࣔ ᶄ Ϧετܕ • هࣄΛϦετͰදࣔ͢Δ(GridΑΓList͕ଟ͍) ᶅ ΢Υʔϧϖʔύʔܕ • ڧௐ͍ͨࣸ͠ਅΛนࢴͷΑ͏ʹදࣔ͢Δ 24

Slide 25

Slide 25 text

Layout ᶃ Ұߦܕ • ҰߦͰχϡʔεΛίϯύΫτʹදࣔ ᶄ Ϧετܕ • هࣄΛϦετͰදࣔ͢Δ(GridΑΓList͕ଟ͍) ᶅ ΢Υʔϧϖʔύʔܕ • ڧௐ͍ͨࣸ͠ਅΛนࢴͷΑ͏ʹදࣔ͢Δ 25

Slide 26

Slide 26 text

Layout: Ұߦܕ 26

Slide 27

Slide 27 text

Layout ᶃ Ұߦܕ • ҰߦͰχϡʔεΛίϯύΫτʹදࣔ ᶄ Ϧετܕ • هࣄΛϦετͰදࣔ͢Δ(GridΑΓList͕ଟ͍) ᶅ ΢Υʔϧϖʔύʔܕ • ڧௐ͍ͨࣸ͠ਅΛนࢴͷΑ͏ʹදࣔ͢Δ 27

Slide 28

Slide 28 text

Layout: Ϧετܕ 28

Slide 29

Slide 29 text

Layout ᶃ Ұߦܕ • ҰߦͰχϡʔεΛίϯύΫτʹදࣔ ᶄ Ϧετܕ • هࣄΛϦετͰදࣔ͢Δ(GridΑΓList͕ଟ͍) ᶅ ΢Υʔϧϖʔύʔܕ • ڧௐ͍ͨࣸ͠ਅΛนࢴͷΑ͏ʹදࣔ͢Δ 29

Slide 30

Slide 30 text

Layout: ΢Υʔϧϖʔύʔܕ 30

Slide 31

Slide 31 text

Configuration ઃఆՄೳͳ߲໨͸ҎԼͷΑ͏ͳ΋ͷ͕ఏڙ͞Ε͍ͯΔ • ৭ɿന஍, ࠇ஍… • ߋ৽ස౓ɿ30min, 1h, more… • χϡʔεͷ಺༰ɿtop, ranking… 31

Slide 32

Slide 32 text

NewsܥWidgetͷදࣔཁૉͷಛ௃ ᶃ هࣄຊମ ᶄ اۀϩΰ ᶅ ߋ৽Ϙλϯ ᶆ ઃఆϘλϯ ᶇ ࠷ऴߋ৽೔࣌ 32

Slide 33

Slide 33 text

Let's make Widget

Slide 34

Slide 34 text

Widget Classes • AppWidgetService • WidgetIdͷൃߦ΍ϥϯνϟʔ΁ͷϦΫΤετΛߦ͏ • AppWidgetHost • WidgetͱϗʔϜը໘ͷ஥ཱͪΛߦ͏ • AppWidgetHostView • WidgetΛදࣔ͢ΔͨΊͷදࣔྖҬΛఏڙ͢Δ • AppWidgetManager • Widgetͷঢ়ଶΛߋ৽ͨ͠Γɺ৘ใΛऔಘ͢Δ؅ཧΫϥε • AppWidgetProvider • Widgetͷ֤छͷΠϕϯτΛϋϯυϦϯά͢Δ • AppWidgetProviderInfo • WidgetͷϝλσʔλΛѻ͏ 34

Slide 35

Slide 35 text

Widget Classes • AppWidgetService • WidgetIdͷൃߦ΍ϥϯνϟʔ΁ͷϦΫΤετΛߦ͏ • AppWidgetHost • WidgetͱϗʔϜը໘ͷ஥ཱͪΛߦ͏ • AppWidgetHostView • WidgetΛදࣔ͢ΔͨΊͷදࣔྖҬΛఏڙ͢Δ • AppWidgetManager • Widgetͷঢ়ଶΛߋ৽ͨ͠Γɺ৘ใΛऔಘ͢Δ؅ཧΫϥε • AppWidgetProvider • Widgetͷ֤छͷΠϕϯτΛϋϯυϦϯά͢Δ • AppWidgetProviderInfo • WidgetͷϝλσʔλΛѻ͏ 35 ←࣮૷Ͱ࢖͏

Slide 36

Slide 36 text

Widget Classes: Simplify • AppWidgetManager • WidgetͷViewͷߋ৽Λߦ͏ • AppWidgetProvider • Widgetͷߋ৽Λϋϯυϧ͢Δ • AppWidgetProviderInfo • Widgetͷجຊ৘ใΛxmlʹهड़͢Δ • xml/layout • WidgetͷϨΠΞ΢τɺ௨ৗͷϨΠΞ΢τͱ֓Ͷಉ͡ 36

Slide 37

Slide 37 text

How to make it: Simplify ᶃ manifestʹreceiverΛ௥Ճ ᶄ AppWidgetProviderInfoͰWidget৘ใΛઃఆ ᶅ AppWidgetProviderͰRemoteViewΛੜ੒ˍUpdate 37

Slide 38

Slide 38 text

Help me AndroidStudio!! • AndroidStudioͷFile>New͔Β࡞੒ը໘Λग़ͤΔʂʂ • [W]idget͔ͩΒϦετͷҰ൪Լʹ͋Δ 38

Slide 39

Slide 39 text

Help me AndroidStudio!! 39

Slide 40

Slide 40 text

Help me AndroidStudio!! 40

Slide 41

Slide 41 text

Help me AndroidStudio!! 41

Slide 42

Slide 42 text

Let’s make Widget Layout

Slide 43

Slide 43 text

WidgetͷView • جຊతʹ௨ৗͷΞϓϦͱಉ༷ʹ૊Ή͜ͱ͕Ͱ͖·͢ • WidgetͰ͸ɺ • @RemoteView͕෇͍ͨView͕࢖͑Δ • @RemotableViewMethod͕෇͍ͨϝιου͕࢖͑Δ 43

Slide 44

Slide 44 text

View Overview 44 Home Launcher

Slide 45

Slide 45 text

View Overview 45 Home Launcher

Slide 46

Slide 46 text

View: Layout • FrameLayout • LinearLayout • RelativeLayout • GridLayout ※ConstraintLayout͸࢖͑ͳ͍ʂʂ 46

Slide 47

Slide 47 text

View: جຊతͳ΋ͷ • Button • ImageButton • ImageView • ProgressBar • TextView • ViewFlipper 47

Slide 48

Slide 48 text

View: λΠϚʔܥ • Chronometer -> λΠϚʔΛͭ͘Δ • TextClock -> σδλϧ࣌ܭΛදࣔͤ͞Δ • AnalogClock -> deprecated 48

Slide 49

Slide 49 text

View: Collection • ListView • GridView • StackView • AdapterViewFlipper 49

Slide 50

Slide 50 text

View Overview 50 Home Launcher

Slide 51

Slide 51 text

RemoteViews • RemoteViews • ϏϡʔώΤϥϧΩʔΛ࣋ͭ • Widgetͷදࣔͷཁ 51

Slide 52

Slide 52 text

View Overview 52 Home Launcher

Slide 53

Slide 53 text

RemoteViewsFactory • RemoteViewsFactory • ListViewͷItemͳͲΛ࡞੒͢Δ͜ͱ͕Ͱ͖Δ • AdapterͷΑ͏ͳଘࡏ 53

Slide 54

Slide 54 text

RemoteViewsFactory: Lifecycle 54 sourceɿhttps://developer.android.com/guide/topics/appwidgets/index.html

Slide 55

Slide 55 text

RemoteViewsFactory: Lifecycle 55

Slide 56

Slide 56 text

Let’s make Widget Provider

Slide 57

Slide 57 text

AppWidgetProvider • BroadcastReceiverΛwrapͨ͠ศརͳΫϥε • onReceivedͰɺΠϕϯτΛϋϯυϦϯά • Widgetͷ࡞੒ʗߋ৽ʗ࡟আͳͲ • onReceivedΛ΋ͱʹࣗྗͰ࣮૷΋Մೳ 57

Slide 58

Slide 58 text

onRecieve 58 public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) { this.onUpdate(…); } else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) { this.onDeleted(…); } else if (AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED.equals(action)) { this.onAppWidgetOptionsChanged(…); } else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) { this.onEnabled(…); } else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) { this.onDisabled(…); } else if (AppWidgetManager.ACTION_APPWIDGET_RESTORED.equals(action)) { this.onRestored(…); this.onUpdate(…); } }

Slide 59

Slide 59 text

AppWidgetProviderͷϥΠϑαΠΫϧ • onEnabled • onUpdated • onDeleted • onDisabled • onAppWidgetOptionsChanged • onRestored 59

Slide 60

Slide 60 text

AppWidgetProviderͷϥΠϑαΠΫϧ 60

Slide 61

Slide 61 text

onUpdated͸͍ͭݺ͹ΕΔ͔ • onEnabledͷ௚ޙ • onRestoredͷ௚ޙ • AppWidgetManager.ACTION_APPWIDGET_UPDATE 61

Slide 62

Slide 62 text

When call ACTION_APPWIDGET_UPDATE • ΢ΟδΣοτઃஔ࣌ • updatePeriodMillisͷ͕࣌ؒདྷͨͱ͖ • systemͷىಈ࣌ 62

Slide 63

Slide 63 text

updatePeriodMills • WidgetͷΞοϓσʔτִؒɺProviderInfoͰࢦఆ͢Δ • ࠷௿ߋ৽ִؒ͸30minͰ͋Δ • 0ҎԼΛࢦఆ͢Δͱݺ͹Εͳ͘ͳΔ • 30minະຬͷ৔߹͸ɺ30minʹ͞ΕΔ • ߋ৽ִؒΛ୹͍ͨ͘͠৔߹͸AlarmManagerͳͲΛ࢖͏ • updatePeriodMills͸0Λࢦఆ͓ͯ͘͜͠ͱ 63

Slide 64

Slide 64 text

AppWidgetProviderͷϥΠϑαΠΫϧ • onEnabled • onUpdated • onDeleted • onDisabled • onAppWidgetOptionsChanged • onRestored 64

Slide 65

Slide 65 text

onAppWidgetOptionsChanged • ϦαΠζ͞Εͨͱ͖ʹݺ͹ΕΔ • added in API level 16 • ը໘αΠζΛdpͰऔಘͰ͖Δ • খ͘͞ͳͬͨͱ͖ʹཁૉΛඇදࣔͨ͠ΓͰ͖Δ •OPTION_MIN_WIDTHɿݱࡏͷwidthͷԼݶ •OPTION_MAX_WIDTHɿݱࡏͷwidthͷ্ݶ •OPTION_MIN_HEIGHTɿݱࡏͷheightͷԼݶ •OPTION_MAX_HEIGHTɿݱࡏͷheightͷ্ݶ 65

Slide 66

Slide 66 text

onRestored • ௚ޙʹonUpdateΛݺͼग़͢ • added in API level 21 • backup͔Βprovider͕ϦετΞ͞Εͨͱ͖ʹݺ͹ΕΔ • widgetʹඥ෇͍ͨσʔλΛอ͍࣋ͨ͠৔߹ʹ࢖͏ • ݹ͍id͔Β৽͍͠id΁ͷҾ͖ܧ͗Λߦ͏ • widgetIdͱ͸ʁʔʼAppendix΁ʂ 66

Slide 67

Slide 67 text

Sample Code

Slide 68

Slide 68 text

࣮૷ 68 class SampleAppWidgetProvider : AppWidgetProvider() { override fun onUpdate(…) { for (appWidgetId in appWidgetIds) { val intent = Intent(context, MainActivity::class.java) val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0) val views = RemoteViews(context.packageName, R.layout.widget_container) views.setTextViewText(R.id.textWidget, "Hello:)") views.setOnClickPendingIntent(R.id.button, pendingIntent) appWidgetManager.updateAppWidget(appWidgetId, views) } } }

Slide 69

Slide 69 text

ղઆ 69 //RemoteViewsʹ࢖༻͍ͨ͠ϨΠΞ΢τΛ౉͢ val views = RemoteViews(context.packageName, R.layout.widget_container) //TextΛಈతʹॻ͖׵͑Δ views.setTextViewText(R.id.textWidget, "Hello:)") //Click࣌ͷಈ࡞Ληοτ͍ͨ͠ͱ͖ views.setOnClickPendingIntent(R.id.button, pendingIntent)

Slide 70

Slide 70 text

ղઆ 70 views.setTextViewText(R.id.textWidget, "Hello:)") //setTextViewText͸͜ͷϝιουΛwrapͨ͠΋ͷ setCharSequence(viewId, "setText", text) //͜ͷΑ͏ʹ͢Ε͹RemoteViewsʹੜ͑ͯͳ͍ϝιου΋ݺ΂Δ views.setInt(viewId, "setBackgroundColor", color)

Slide 71

Slide 71 text

࣮૷ɿListViewͷέʔε 71 fun updateWidget(context: Context, appWidgetId: Int) { val manager = AppWidgetManager.getInstance(context) val remoteViews = RemoteViews(context.getPackageName(), R.layout.widget_container) // ListItemʹߋ৽௨஌Λߦ͏ͨΊͷintentΛ࡞੒ val intent = Intent(context, SampleWidgetService::class.java) .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) remoteViews.setRemoteAdapter(R.id.widgetListView, intent) // ListItem͕ۭͩͬͨ৔߹ͷEmptyදࣔ remoteViews.setEmptyView(R.id.widgetListView, android.R.id.empty) // ListItemΛλοϓͨ͠ͱ͖ͷintentΛηοτ // ListItemͷଆͰηοτ͢ΔͷͰͳ͘ɺ͜͜Ͱηοτ͢Δ val onClickIntent = Intent(context, DetailActivity::class.java) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) val onClickPendingIntent = PendingIntent.getActivity(context, 0, onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT) remoteViews.setPendingIntentTemplate(R.id.widgetListView, onClickPendingIntent) manager.updateAppWidget(appWidgetId, remoteViews) }

Slide 72

Slide 72 text

࣮૷ɿListViewͷέʔε 72 fun updateWidget(context: Context, appWidgetId: Int) { val manager = AppWidgetManager.getInstance(context) val remoteViews = RemoteViews(context.getPackageName(), R.layout.widget_container) // ListItemʹߋ৽௨஌Λߦ͏ͨΊͷintentΛ࡞੒ val intent = Intent(context, SampleWidgetService::class.java) .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) remoteViews.setRemoteAdapter(R.id.widgetListView, intent) // ListItem͕ۭͩͬͨ৔߹ͷEmptyදࣔ remoteViews.setEmptyView(R.id.widgetListView, android.R.id.empty) // ListItemΛλοϓͨ͠ͱ͖ͷintentΛηοτ // ListItemͷଆͰηοτ͢ΔͷͰͳ͘ɺ͜͜Ͱηοτ͢Δ val onClickIntent = Intent(context, DetailActivity::class.java) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) val onClickPendingIntent = PendingIntent.getActivity(context, 0, onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT) remoteViews.setPendingIntentTemplate(R.id.widgetListView, onClickPendingIntent) manager.updateAppWidget(appWidgetId, remoteViews) }

Slide 73

Slide 73 text

࣮૷ɿListViewͷέʔε 73 fun updateWidget(context: Context, appWidgetId: Int) { val manager = AppWidgetManager.getInstance(context) val remoteViews = RemoteViews(context.getPackageName(), R.layout.widget_container) // ListItemʹߋ৽௨஌Λߦ͏ͨΊͷintentΛ࡞੒ val intent = Intent(context, SampleWidgetService::class.java) .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) remoteViews.setRemoteAdapter(R.id.widgetListView, intent) // ListItem͕ۭͩͬͨ৔߹ͷEmptyදࣔ remoteViews.setEmptyView(R.id.widgetListView, android.R.id.empty) // ListItemΛλοϓͨ͠ͱ͖ͷintentΛηοτ // ListItemͷଆͰηοτ͢ΔͷͰͳ͘ɺ͜͜Ͱηοτ͢Δ val onClickIntent = Intent(context, DetailActivity::class.java) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) val onClickPendingIntent = PendingIntent.getActivity(context, 0, onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT) remoteViews.setPendingIntentTemplate(R.id.widgetListView, onClickPendingIntent) manager.updateAppWidget(appWidgetId, remoteViews) }

Slide 74

Slide 74 text

࣮૷ɿListView(Item)ͷέʔε 74 override fun getViewAt(position: Int): RemoteViews { val rv = RemoteViews(context.packageName, R.layout.list_item) rv.setTextViewText(R.id.list_text, items[position]) //͜͜ͰIntentΛ٧ΊΔɻ͜ͷintent͸͜ͷΫϥεͷݺͼग़͠ݩ΁౉͞ΕΔ //ListItemͷ࣋ͭσʔλ͕ཉ͍͠৔߹͸͜ͷintentʹ٧ΊΔ rv.setOnClickFillInIntent(R.id.list_container, createIntent(position)) return rv }

Slide 75

Slide 75 text

Let’s make Widget ProviderInfo

Slide 76

Slide 76 text

AppWidgetProviderInfo Widgetͦͷ΋ͷͷઃఆΛxmlͰهड़͢Δ 76

Slide 77

Slide 77 text

ը໘ͷେ͖͞Λࢦఆ͢Δ΋ͷ • int minWidth / int minHeight • σϑΥϧτͷ෯ͱߴ͞ΛdpͰࢦఆ͢Δ • int minResizeWidth / int minResizeHeight • ϦαΠζͷԼݶΛdpͰࢦఆ͢Δ • minResizeWidth <= minWidth • minResizeHeight <= minHeight • int resizeMode(horizontal|vertical or none) • Ͳͷํ޲ͷϦαΠζΛڐՄ͢Δ͔ 77

Slide 78

Slide 78 text

ϓϨϏϡʔ • int previewImage • Widget PickerʗDialogͷϓϨϏϡʔʹ༻͍ΒΕΔ 78

Slide 79

Slide 79 text

ϨΠΞ΢τ • int initialKeyguardLayout • KeyGuardʹදࣔͨ͠ͱ͖ͷϨΠΞ΢τΛࢦఆ͢Δ • int initialLayout • ΢ΟδΣοτͷϨΠΞ΢τΛࢦఆ͢Δ • int widgetCategory(home_screen|keyguard) • ΢ΟδΣοτΛͲ͜ʹஔ͚Δ͔ࢦఆ͢Δ • keyguard͸Android 5.0Ҏ্Ͱ͸࢖༻Ͱ͖ͳ͍ 79

Slide 80

Slide 80 text

ը໘ߋ৽ܥ • int updatePeriodMillis • ϛϦඵͰWidgetͷߋ৽ִؒΛࢦఆ͢Δ(30minҎ্) • ComponentName configure • WidgetΛ௥Ճͨ͠৔߹ʹࢦఆͨ͠ActivityΛݺͼग़ͯ͘͠ΕΔ • onUpdate͸ݺ͹Εͳ͍ͷͰผ్ݺͿඞཁ͕͋Δ • Widget Dialog͔Βઃஔͨ͠ͱ͖ʹ͸ݺ͹Εͳ͍ • int autoAdvanceViewId • StackViewͳͲࢠཁૉΛ࣋ͭϨΠΞ΢τͷidΛࢦఆ͢Δ • Ϣʔβʔͷૢ࡞͕ͳͯ͘΋ࣗಈͰViewΛਐΊΔ 80

Slide 81

Slide 81 text

configureิ଍ • ίϯϑΟάը໘ʹ࢖༻͢ΔActivity͸manifestʹintent- filterΛηοτ͢Δ 81

Slide 82

Slide 82 text

ProviderͱProviderInfoͷิ଍ • ProviderͱProviderInfo͸manifestͰҎԼͷΑ͏ʹࢦఆ ͢Δ 82

Slide 83

Slide 83 text

New API

Slide 84

Slide 84 text

requestPinAppWidget & isRequestPinAppWidgetSupported

Slide 85

Slide 85 text

what is requestPinAppWidget? • AndroidO͔Β௥Ճ͞ΕͨAPI • ΞϓϦͷத͔ΒWidgetͷ௥ՃΛߦ͑ΔΑ͏ʹʂʂ 85

Slide 86

Slide 86 text

API's • requestPinAppWidget • ΞϓϦ಺ʹWidgetΛ௥Ճ͢Δ • isRequestPinAppWidgetSupported • ϗʔϜը໘ʹ௥ՃͰ͖Δ͔νΣοΫ͢Δ 86

Slide 87

Slide 87 text

࣮૷ 87 val widgetManager = AppWidgetManager.getInstance(this) val provider = ComponentName(this, SampleAppWidgetProvider::class.java) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return if (widgetManager.isRequestPinAppWidgetSupported) { val callbackIntent = Intent(this, SubActivity::class.java) val successCallback = PendingIntent.getActivity(this, 0, callbackIntent, 0) val extras = Bundle() val remoteViews = RemoteViews(this.packageName, R.layout.widget_container) remoteViews.setTextViewText(R.id.button, "ͯ͢ͱ") extras.putParcelable(EXTRA_APPWIDGET_PREVIEW, remoteViews) widgetManager.requestPinAppWidget(provider, extras, null) }

Slide 88

Slide 88 text

࣮૷ 88 val widgetManager = AppWidgetManager.getInstance(this) val provider = ComponentName(this, SampleAppWidgetProvider::class.java) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return if (widgetManager.isRequestPinAppWidgetSupported) { val callbackIntent = Intent(this, SubActivity::class.java) val successCallback = PendingIntent.getActivity(this, 0, callbackIntent, 0) val extras = Bundle() val remoteViews = RemoteViews(this.packageName, R.layout.widget_container) remoteViews.setTextViewText(R.id.button, "ͯ͢ͱ") extras.putParcelable(EXTRA_APPWIDGET_PREVIEW, remoteViews) widgetManager.requestPinAppWidget(provider, extras, null) }

Slide 89

Slide 89 text

ղઆ: isRequestPinAppWidgetSupported • request api͕࢖༻Ͱ͖Δ͔࣮ߦલʹ֬ೝ͢Δ • requestPinAppWidgetͷํͰ΋booleanΛฦ͢ • →AppendixʹԿΛ֬ೝ͍ͯ͠Δ͔·ͱΊͨ 89

Slide 90

Slide 90 text

࣮૷ 90 val widgetManager = AppWidgetManager.getInstance(this) val provider = ComponentName(this, SampleAppWidgetProvider::class.java) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return if (widgetManager.isRequestPinAppWidgetSupported) { val callbackIntent = Intent(this, SubActivity::class.java) val successCallback = PendingIntent.getActivity(this, 0, callbackIntent, 0) val extras = Bundle() val remoteViews = RemoteViews(this.packageName, R.layout.widget_container) remoteViews.setTextViewText(R.id.button, "ͯ͢ͱ") extras.putParcelable(EXTRA_APPWIDGET_PREVIEW, remoteViews) widgetManager.requestPinAppWidget(provider, extras, null) }

Slide 91

Slide 91 text

ղઆ: callbackIntent • ੒ޭ࣌ͷcallbackͰݺͼ͍ͨintentΛ͜͜Ͱ༻ҙ͢Δ • bundleΛηοτ͢Δ͜ͱ΋Ͱ͖Δ • EXTRA_APPWIDGET_ID͕intentʹηοτ͞ΕΔ • ग़͠෼͚Δཁૉ͕͋Ε͹widgetIdΛอଘ͢Δ͜ͱ • config͸request੒ޭ࣌ʹݺ͹Εͳ͍ͷͰ஫ҙ 91

Slide 92

Slide 92 text

࣮૷ 92 val widgetManager = AppWidgetManager.getInstance(this) val provider = ComponentName(this, SampleAppWidgetProvider::class.java) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return if (widgetManager.isRequestPinAppWidgetSupported) { val callbackIntent = Intent(this, SubActivity::class.java) val successCallback = PendingIntent.getActivity(this, 0, callbackIntent, 0) val extras = Bundle() val remoteViews = RemoteViews(this.packageName, R.layout.widget_container) remoteViews.setTextViewText(R.id.button, "ͯ͢ͱ") extras.putParcelable(EXTRA_APPWIDGET_PREVIEW, remoteViews) widgetManager.requestPinAppWidget(provider, extras, null) }

Slide 93

Slide 93 text

ղઆ: successCallback • ઃஔޙʹݺͿcallbackΛPendingIntentͱͯ͠ηοτ • callback͸Widgetͷ௥ՃμΠΞϩάͰඞཁͱͳΔ • ࣗಈͰ௥ՃorखಈͰ௥Ճͷ̎௨Γ͋Δ • ࣗಈͰ௥Ճ • ΞϓϦͷը໘͔ΒભҠ͠ͳ͍ • खಈͰ௥Ճ • ϗʔϜϥϯνϟʔʹભҠ • ࣗ෼ͷΞϓϦʹ໭ͬͯ͜ͳ͍ • BroadcastͳͲΛ࢖༻ͯ͠෮ؼͯ͠΋Β͏ 93

Slide 94

Slide 94 text

ղઆ: successCallback • ઃஔޙʹݺͿcallbackΛPendingIntentͱͯ͠ηοτ • callback͸Widgetͷ௥ՃμΠΞϩάͰඞཁͱͳΔ • ࣗಈͰ௥ՃorखಈͰ௥Ճͷ̎௨Γ͋Δ • ࣗಈͰ௥Ճ • ΞϓϦͷը໘͔ΒભҠ͠ͳ͍ • खಈͰ௥Ճ • ϗʔϜϥϯνϟʔʹભҠ • ࣗ෼ͷΞϓϦʹ໭ͬͯ͜ͳ͍ • BroadcastͳͲΛ࢖༻ͯ͠෮ؼͯ͠΋Β͏ 94

Slide 95

Slide 95 text

ղઆ: successCallback • ઃஔޙʹݺͿcallbackΛPendingIntentͱͯ͠ηοτ • callback͸Widgetͷ௥ՃμΠΞϩάͰඞཁͱͳΔ • ࣗಈͰ௥ՃorखಈͰ௥Ճͷ̎௨Γ͋Δ • ࣗಈͰ௥Ճ • ΞϓϦͷը໘͔ΒભҠ͠ͳ͍ • खಈͰ௥Ճ • ϗʔϜϥϯνϟʔʹભҠ • ࣗ෼ͷΞϓϦʹ໭ͬͯ͜ͳ͍ • BroadcastͳͲΛ࢖༻ͯ͠෮ؼͯ͠΋Β͏ 95

Slide 96

Slide 96 text

࣮૷ 96 val widgetManager = AppWidgetManager.getInstance(this) val provider = ComponentName(this, SampleAppWidgetProvider::class.java) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return if (widgetManager.isRequestPinAppWidgetSupported) { val callbackIntent = Intent(this, SubActivity::class.java) val successCallback = PendingIntent.getActivity(this, 0, callbackIntent, 0) val extras = Bundle() val remoteViews = RemoteViews(this.packageName, R.layout.widget_container) remoteViews.setTextViewText(R.id.button, "ͯ͢ͱ") extras.putParcelable(EXTRA_APPWIDGET_PREVIEW, remoteViews) widgetManager.requestPinAppWidget(provider, extras, null) }

Slide 97

Slide 97 text

ղઆ: extras • custom previewΛηοτͰ͖Δ • previewImageͱҟͳΔ • RemoteViewsΛಈతʹΧελϜͯ͠ηοτՄೳ • preview༻Ͱ͋ΓɺonUpdate͞ΕΔࡍʹߋ৽͞ΕΔ 97

Slide 98

Slide 98 text

࣮૷ 98 val widgetManager = AppWidgetManager.getInstance(this) val provider = ComponentName(this, SampleAppWidgetProvider::class.java) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return if (widgetManager.isRequestPinAppWidgetSupported) { val callbackIntent = Intent(this, SubActivity::class.java) val successCallback = PendingIntent.getActivity(this, 0, callbackIntent, 0) val extras = Bundle() val remoteViews = RemoteViews(this.packageName, R.layout.widget_container) remoteViews.setTextViewText(R.id.button, "ͯ͢ͱ") extras.putParcelable(EXTRA_APPWIDGET_PREVIEW, remoteViews) widgetManager.requestPinAppWidget(provider, extras, null) }

Slide 99

Slide 99 text

ղઆ: requestPinAppWidget • WidgetDialogΛදࣔ͢Δϝιου • ฦΓ஋͸booleanͰ͋Δ • true͸ɺrequestPinAppWidgetʹରԠ͍ͯ͠Δ • Widget͕௥ՃͰ͖ͨ͜ͱΛҙຯ͠ͳ͍ʂʂ 99

Slide 100

Slide 100 text

requestPinAppWidgetͰWidgetͷ഑ஔʹࣦഊͨ͠ΒͲ͏ͳΔ͔ • Widgetͷ࡞੒੒ޭ͔࣌͠successCallbackΛݺ͹Εͳ͍ • fallback͕ͳ͍ • खಈ഑ஔͰࣦഊͨ͠৔߹ΞϓϦΛ࠶౓։͍ͯ΋Β͏ඞ ཁ͕͋Δ 100

Slide 101

Slide 101 text

requestPinAppWidgetͷิ଍ • ௥ՃμΠΞϩά͸ࣗ෼ͰΧελϜ͢Δ͜ͱ͸Ͱ͖ͳ͍ 101

Slide 102

Slide 102 text

Conclusion • WidgetDialogͰ௥Ճํ๏͕ܶతʹ෼͔Γ΍͘͢ͳͬͨ • ৽نAPIΛؚΊ࣮૷͕γϯϓϧͳͷͰ࡞Γ΍͍͢ • ΞϓϦΛΑΓࣗ෼ͷελΠϧʹ߹Θͤͯ࢖ͬͯ΋Β͑Δ 102

Slide 103

Slide 103 text

Conclusion • WidgetDialogͰ௥Ճํ๏͕ܶతʹ෼͔Γ΍͘͢ͳͬͨ • ৽نAPIΛؚΊ࣮૷͕γϯϓϧͳͷͰ࡞Γ΍͍͢ • ΞϓϦΛΑΓࣗ෼ͷελΠϧʹ߹Θͤͯ࢖ͬͯ΋Β͑Δ 103

Slide 104

Slide 104 text

whoami • twitter:@ymnd, github:@ymnder • Application Engineer • Android ೔ܦిࢠ൛ΞϓϦ • Android ࢴ໘ϏϡʔΞʔΞϓϦ • ٕज़ॻయ̐Ͱ೔ܦిࢠ൛ͷ஌ݟΛ·ͱΊͨຊΛग़͠·͢ 104

Slide 105

Slide 105 text

Widget΍͍͖ͬͯ

Slide 106

Slide 106 text

Appendix

Slide 107

Slide 107 text

How to Calculate minWidth and minHeight WidgetͷminWidth/minHeightͷࢉग़ࣜ 70 × cell − 30 = size ྫɿ1ߦ2ྻͷwidgetΛ࡞੒͢Δ৔߹͸ minHeight =70 x 1 - 30 = 40 minWidth = 70 x 2 - 30 = 110 107

Slide 108

Slide 108 text

How to Calculate minWidth and minHeight • API14ҎલͰܭࢉํ๏͕Ξοϓσʔτ͞Εͨʂ • targetSDK͕API14Ҏ্ͷ৔߹ɺϗʔϜը໘ͷ΢ΟδΣ οτͷ֤୺ʹϚʔδϯ͕ࣗಈతʹ௥Ճ͞ΕΔ • ͦͷͨΊɺAndroid4.0Ҏ্Ͱ͸widgetʹ༧Ί༨നΛ௥ Ճ͢Δඞཁ͸ͳ͍ 108

Slide 109

Slide 109 text

Config Best Practice(from Material Design Guideline) • config͸؆ܿʹ͠ɺ̎ʙ̏Ҏ্ͷઃఆ߲໨Λઃ͚ͳ͍ • ϑϧεΫϦʔϯΑΓμΠΞϩάͰબ୒ͯ͠΋Β͏ • WidgetͷઃఆͰ͋Δ͜ͱ͕෼͔Γ΍͍͢ • setupޙ͸Widgetʹ͸ઃఆ༻ͷϘλϯΛදࣔ͠ͳ͍ 109

Slide 110

Slide 110 text

how is appWidgetId created? • appWidgetId͸WidgetΛߋ৽͢Δͱ͖ʹ༻͍ΔID • ͜ͷID͕ͲͷΑ͏ʹৼΒΕ͍ͯΔ͔͕ؾʹͳΔ • ຊ౰ʹϢχʔΫͳͷʁ • AppWidgetHost.java#allocateAppWidgetId 110 //Get a appWidgetId for a host in the calling process. //@return a appWidgetId public int allocateAppWidgetId() {…}

Slide 111

Slide 111 text

AppWidgetServiceImpl.java 111 public int allocateAppWidgetId(String callingPackage, int hostId) { ... if (mNextAppWidgetIds.indexOfKey(userId) < 0) { // ॳظԽॲཧ // AppWidgetManager.INVALID_APPWIDGET_ID͸0 // Widget͸1͔Β࠾൪͞Ε͍ͯ͘ɻ mNextAppWidgetIds.put(userId, AppWidgetManager.INVALID_APPWIDGET_ID + 1); } final int appWidgetId = incrementAndGetAppWidgetIdLocked(userId); ... }

Slide 112

Slide 112 text

ͲͷΑ͏ʹ൪߸͕ৼΒΕΔ͔ʁ • mNextAppWidgetIds • {userId, appWidgetId}ͷϖΞͰid͕อଘ͞ΕΔ • ಡΈग़ͨ͠ޙɺ+1ͯ͠࠶౓อଘ͍ͯ͠Δ 112 private final SparseIntArray mNextAppWidgetIds = new SparseIntArray(); final int appWidgetId = peekNextAppWidgetIdLocked(userId) + 1; return mNextAppWidgetIds.get(userId);

Slide 113

Slide 113 text

ͲͷΑ͏ʹ൪߸͕ৼΒΕΔ͔ʁ • ݁࿦ɿಛผͳ஋Λੜ੒͍ͯ͠ΔΘ͚Ͱ͸ͳ͍ɻ • AppWidgetServiceImpl͸ServiceΫϥε • ଞͷΞϓϦʹ΋ׂΓ౰ͯΒΕΔ͕ϢχʔΫʹͳΔ • औಘ͍ͨ͠৔߹͸͜ΕΛ࢖͑͹औಘͰ͖Δ 113

Slide 114

Slide 114 text

requestPinAppWidget & isRequestPinAppWidgetSupportedͷฦΓ஋ • isRequestPinAppWidgetSupported • Return {@code TRUE} if the default launcher supports • updateAppWidget • {@code TRUE} if the launcher supports this feature. Note the API will return without waiting for the user to respond, so getting {@code TRUE} from this API does not mean the shortcut is pinned. {@code FALSE} if the launcher doesn't support this feature. • ͜Εʹ͸ͲΜͳҧ͍͕͋Δͷ͔ 114

Slide 115

Slide 115 text

isRequestPinAppWidgetSupportedͷฦΓ஋ AppWidgetManager → AppWidgetServiceImpl.java#isRequestPinAppWidgetSupported → ShortcutService.java#isRequestPinItemSupported → ShortcutRequestPinProcessor.java#isRequestPinItemSupported → ShortcutRequestPinProcessor.java#getRequestPinConfirmationActivity ShortcutService.java#getRequestPinConfirmationActivity 115

Slide 116

Slide 116 text

ShortcutService.java#getRequestPinConfirmationActivity • default launcher͔Βػೳ͕ϋϯυϧ͞Ε͍ͯΔActivity Λ୳͍ͯ͠Δ • औಘͰ͖ͳ͔ͬͨ৔߹͸nullΛฦ͢ • nullͷ৔߹falseͰɺ͜Ε͕ඇରԠͱ͍͏൑ఆͱͳΔ 116

Slide 117

Slide 117 text

requestPinAppWidgetͷฦΓ஋ AppWidgetManager → AppWidgetServiceImpl → ShortcutService#requestPinAppWidget → ShortcutService.java#requestPinItem → ShortcutRequestPinProcessor.java#requestPinItemLocked → ShortcutService.java#getRequestPinConfirmationActivity 117

Slide 118

Slide 118 text

requestPinAppWidgetͷฦΓ஋ • default launcher͔Βػೳ͕ϋϯυϧ͞Ε͍ͯΔActivity Λ୳͍ͯ͠Δ • isRequestPinAppWidgetSupportedͱಉ͡൑ఆΛߦ͏ • ΋͠trueͩͬͨ৔߹͸requestॲཧ͕ਐΜͰ͍͘ 118

Slide 119

Slide 119 text

ฦΓ஋ͷࠩҟ • ݁࿦ɿಉ͡΋ͷͰ͋Δ • ϦΫΤετલʹ൑ఆΛ͢΂͖ͳͷͰɺ isRequestPinAppWidgetSupportedΛ࢖͏ • docͷ௨ΓɺrequestPinAppWidget͸ɺWidgetΛஔ͚ ͔ͨͲ͏͔Λฦ͞ͳ͍ 119

Slide 120

Slide 120 text

requestPinAppWidgetͷcallback͸Ͳ͜Ͱݺ͹ΕΔ͔ʁ • ԿΛ΋ͬͯsuccessͱ൑ఆ͍ͯ͠ΔͷͩΖ͏͔ • ·ͨfallbackͱͯ͠Կ͔ݺ΂ͨΓ͸͠ͳ͍ͩΖ͏͔ 120

Slide 121

Slide 121 text

callback͸Ͳ͜Ͱݺ͹ΕΔ͔ʁ AppWidgetManager → AppWidgetServiceImpl → ShortcutService#requestPinAppWidget → ShortcutService.java#requestPinItem 121

Slide 122

Slide 122 text

callback͸ͲͷλΠϛϯάͰݺ͹ΕΔ͔ʁ → LauncherApps.java#PinItemRequest ੒ޭ࣌ʹacceptΛݺͿ → ShortcutRequestPinProcessor.java#PinAppWidgetRequestInner → ShortcutRequestPinProcessor.java#PinItemRequestInner ͜͜Ͱacceptͷத਎͕࣮૷͞Ε͍ͯΔ → ShortcutRequestPinProcessor.java#directPinShortcut ྫ֎ΛΩϟον͍ͯͯ͠ɺ࡞੒Ͱ͖ͳ͔ͬͨ৔߹͸falseΛฦ͢ tryAccept͕falseʹͳΓɺίʔϧόοΫ͕࣮ߦ͞Εͳ͍ → ShortcutService.java#fixUpIncomingShortcutInfo 122

Slide 123

Slide 123 text

callback͸ͲͷλΠϛϯάͰݺ͹ΕΔ͔ʁʢऴ఺ʣ • tryAccept͕trueͷͱ͖sendResultIntent͕࣮ߦ͞ΕΔ • ͢ͳΘͪ͜͜Ͱcallback͕࣮ߦ͞ΕΔ • ࣦഊ࣌͸Կ΋͞Εͳ͍Ͱfalse͕ฦΔ 123 // PinItemRequestInner#accept // Pin it and send the result intent. if (tryAccept()) { mProcessor.sendResultIntent(mResultIntent, extras); return true; } else { return false; }

Slide 124

Slide 124 text

WidgetͷCustomView • @RemoteViewΛ͚ͭΕ͹ಠࣗͷViewΛఆٛͰ͖Δʁ • Ͱ͖ͳ͍Α͏Ͱ͢ • http://d.hatena.ne.jp/westzaki/20120303/1330776734 124

Slide 125

Slide 125 text

References [ΨΠυϥΠϯ] https://developer.android.com/guide/topics/appwidgets/index.html [RemoteViews] https://qiita.com/KeithYokoma/items/9e021e2f26a9a784287d [Widgetͷͭ͘Γ͔ͨ] http://y-anz-m.blogspot.jp/2011/06/androidappwidget.html [Widget Dialogʹ͍ͭͯ] https://medium.com/wearebase/android-oreo-widget-pinning-in- kotlin-398d529eab28 ੋඇ͝ҰಡΛ :) 125

Slide 126

Slide 126 text

Widget΍͍͖ͬͯ