(続) Debug Menuはじめました。

E77b6a5f919f7366d94f21eee9a014f5?s=47 operandoOS
October 27, 2017

(続) Debug Menuはじめました。

(続) Debug Menuはじめました。

shibuya.apk #19
https://shibuya-apk.connpass.com/event/68094/

Sample Project
https://github.com/operando/android-debug-menu-sample

E77b6a5f919f7366d94f21eee9a014f5?s=128

operandoOS

October 27, 2017
Tweet

Transcript

  1. (ଓ) Debug Menu
 ͸͡Ί·ͨ͠ɻ shibuya.apk #19

  2. About Me Shinobu Okano @operandoOS Mercari, Inc. Souzoh, Inc.

  3. ٕज़ॻయͰຊॻ͖·ͨ͠ʂ

  4. ୈ4ষ Debug Menu͸͡Ί·ͨ͠ɻ

  5. Debug Menu?? • ։ൃ࣌ͷ࡞ۀͳͲΛศརʹ͢Δ΍ʔͭʔ • AndroidͳΒ։ൃऀ޲͚Φϓγϣϯ͕͋ΔΑͶ

  6. Debug MenuΛ࡞Δ͜ͱͰ
 ಘΒΕΔϝϦοτ • ΞϓϦ։ൃ࣌ʹߦ͏࡞ۀͷख͕ؒল͚Δ • ઃఆ߲໨͕มߋͰ͖ɺΞϓϦΛϏϧυ͠௚͢
 ख͕ؒল͚Δ • ΤϯδχΞҎ֎΋ଟ༷ͳ؀ڥ΍ঢ়ଶ


    ઃఆͰΞϓϦͷಈ࡞Λ֬ೝͰ͖Δ
  7. ϝϧΧϦ Χ΢ϧͷ
 Debug Menu

  8. ϝϧΧϦ Χ΢ϧͷDebug Menu

  9. ϝϧΧϦ Χ΢ϧͷDebug Menu • Debug Menuͷը໘͸ৗઃ௨஌͔Β؆୯ʹ
 ։͚ΔΑ͏ʹͳͬͯΔ

  10. ϝϧΧϦ Χ΢ϧͷDebug Menu

  11. ϝϧΧϦ Χ΢ϧͷDebug Menu • ೔ʑࢥ͍͖ͭͰͲΜͲΜͱػೳΛ௥Ճ͓ͯ͠Γ
 ݱࡏ10ݸҎ্ͷػೳ͕͋Δ • ௥Ճ͢Δػೳ͕Ϋʔϧʢศརʣ͔Ͳ͏͔͕ܾΊख • iOS൛ͷ։ൃΞϓϦʹ΋ࣅͨΑ͏ͳDebug

    Menu Λ౥ࡌ͍ͯ͠·͢ɻ
  12. Debug MenuΛ࡞Δίπ • ݟͨ໨͸ؾʹͤͣͱʹ͔͘ػೳ໘Λॏࢹͯ͠࡞Δ • ͍͍ػೳΛࢥ͍͍ͭͨΒͱʹ͔࣮͘૷ͯ͠ΈΔ • γϯϓϧʹ࣮૷͢Δ • ແବͳ΋ͷ͸࡞Βͳ͍

    • ͲΜͳػೳ͕͋Δͱศར͔νʔϜϝϯόʔʹώΞϦϯάͯ͠ΈΔ
  13. Debug Menu࣮ફೖ໳

  14. DemoΞϓϦ͸Githubʹ͋Γ·͢ʂʂ https://github.com/operando/ android-debug-menu-sample Android debug menu sample

  15. ϦϦʔε൛ΞϓϦʹDebug MenuΛؚΊͳ͍ • Build TypeΛ׆༻͢Δ • ྫ͑͹ɺdebug build typeʹDebug Menuͷ

    ίʔυΛ࣮૷͢Δ
  16. ϦϦʔε൛ΞϓϦʹDebug MenuΛؚΊͳ͍

  17. ৗઃ௨஌͔ΒDebug Menuͷը໘Λ։͘ • NotificationManager࢖ͬͯʹ௨஌Λઃఆ͢Δ • NotificationCompat.BuilderͷsetOngoingϝιου ʹtrueΛࢦఆ͢Δ͜ͱͰɺΫϦΞͰ͖ͳ͍
 ৗઃ௨஌ͱͯ͠දࣔͰ͖Δ • ௨஌Λؒҧͬͯফ͞ͳ͍ͨΊͷରࡦ

  18. private static final String NOTIFICATION_TAG = "DebugMenuNotificationManager"; private static final

    int NOTIFICATION_ID = Integer.MAX_VALUE; private static final String CHANNEL_ID = "debug"; public static void showDebugMenuNotification(Context c) { Intent i = new Intent(c, DebugMenuActivity.class); PendingIntent pi = PendingIntent.getActivity(c, 0, i, PendingIntent.FLAG_CANCEL_CURRENT); Notification n = buildNotification(c, pi); NotificationManager nm = getNotificationManager(c); nm.notify(NOTIFICATION_TAG, NOTIFICATION_ID, n); } private static Notification buildNotification(Context c, PendingIntent pi) { PackageManager pm = c.getPackageManager(); String applicationName = c.getApplicationInfo().loadLabel(pm).toString(); NotificationCompat.Builder builder = new NotificationCompat.Builder(c, CHANNEL_ID); builder.setTicker("debug menu"); builder.setOngoing(true); builder.setContentTitle(applicationName); builder.setContentText("λοϓ͢ΔͱDebug Menu͕։͖·͢"); builder.setSmallIcon(R.drawable.ic_notification_small); builder.setContentIntent(pi); builder.setAutoCancel(false); return builder.build(); } private static NotificationManager getNotificationManager(Context c) { return (NotificationManager) c.getSystemService(Context.NOTIFICATION_SERVICE); }
  19. private static final String CHANNEL_ID = "debug"; private static Notification

    buildNotification(Context c, PendingIntent pi) { PackageManager pm = c.getPackageManager(); String applicationName = c.getApplicationInfo().loadLabel(pm).toString(); NotificationCompat.Builder builder = new NotificationCompat.Builder(c, CHANNEL_ID); builder.setTicker("debug menu"); builder.setOngoing(true); builder.setContentTitle(applicationName); builder.setContentText("λοϓ͢ΔͱDebug Menu͕։͖·͢"); builder.setSmallIcon(R.drawable.ic_notification_small); builder.setContentIntent(pi); builder.setAutoCancel(false); return builder.build(); }
  20. private static final String NOTIFICATION_TAG = "DebugMenuNotificationManager"; private static final

    int NOTIFICATION_ID = Integer.MAX_VALUE; public static void showDebugMenuNotification(Context c) { Intent i = new Intent(c, DebugMenuActivity.class); PendingIntent pi = PendingIntent.getActivity(c, 0, i, PendingIntent.FLAG_CANCEL_CURRENT); Notification n = buildNotification(c, pi); NotificationManager nm = getNotificationManager(c); nm.notify(NOTIFICATION_TAG, NOTIFICATION_ID, n); } private static NotificationManager getNotificationManager(Context c) { return (NotificationManager) c.getSystemService(Context.NOTIFICATION_SERVICE); }
  21. ৗઃ௨஌͔ΒDebug Menuͷը໘Λ։͘ • ϝϧΧϦ Χ΢ϧͰ͸௨஌ͷදࣔɾΫϦΞͷ੍ޚʹ Application.ActivityLifecycleCallbacksΛ࢖ͬͯΔ • ΞϓϦ͕ىಈͨ࣌͠ʹ௨஌Λදࣔ͢Δ • Activityͷback

    stack্Ͱ࠷ޙͷActivity͕Destroy͢Δ࣌ʹ ௨஌ΛΫϦΞ͢Δ • ࣮૷ͨ͠Application.ActivityLifecycleCallbacks͸ ApplicationͷonCreateͱ͔Ͱొ࿥͢Δ
  22. public class DebugMenuActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks { private boolean isShowDebugMenuNotification; @Override

    public void onActivityCreated(Activity activity, Bundle bundle) { if (!isShowDebugMenuNotification) { DebugMenuNotificationManager.showDebugMenuNotification(activity); isShowDebugMenuNotification = true; } } @Override public void onActivityDestroyed(Activity activity) { if (activity instanceof MainActivity) { DebugMenuNotificationManager.cancelDebugMenuNotification(activity); isShowDebugMenuNotification = false; } } }
  23. Demo

  24. γεςϜͷઃఆΞϓϦͰ
 ΞϓϦৄࡉը໘Λ։͘

  25. γεςϜͷઃఆΞϓϦͰ
 ΞϓϦৄࡉը໘Λ։͘ • ϓϦΠϯετʔϧ͞Ε͍ͯΔγεςϜͷ
 ઃఆΞϓϦ͸୺຤ϝʔΧʔʹΑͬͯ
 ݟͨ໨͕શવҧ͏ʂ • ݕূػछ͕ଟ͍ͱγεςϜͷઃఆΞϓϦ͔Β ΞϓϦৄࡉը໘Λ։͘͜ͱ΋Ұۤ࿑

  26. ͜Μͳ͜ͱͰϘΫ͸
 ೰Έͨ͘ͳ͍ʂʂ

  27. ͦΜͳ೰ΈΛղܾ͢Δ
 ૉఢͳػೳ͕؆୯ʹ࡞Ε·͢ʂ

  28. γεςϜͷઃఆΞϓϦͰ
 ΞϓϦৄࡉը໘Λ։͘ • γεςϜͷઃఆΞϓϦͰࣗ਎ͷΞϓϦৄࡉ
 ը໘Λ։͘IntentΛ࡞Δͷ͸؆୯ • ޙ͸ద౰ʹViewΛλοϓͨ͠ΒɺͦͷIntentΛ start͢Ε͹Α͍ • ͪΐͬͱ࣮ͨ͠૷ͰϝʔΧʔ͝ͱͷγεςϜͷઃఆ

    ΞϓϦʹ೰·͞ΕΔ͜ͱ͕ͳ͘ͳΓ·͢ʂ
  29. public class DebugMenuActivity extends AppCompatActivity { @Override protected void onCreate(Bundle

    savedInstanceState) { super.onCreate(savedInstanceState); ActivityDebugMenuBinding binding = DataBindingUtil.setContentView(this, R.layout…); binding.openApplicationSettings.setOnClickListener(v -> { Intent i = new Intent(); i.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); i.setData(Uri.fromParts("package", getPackageName(), null)); startActivity(i); }); } }
  30. Demo

  31. ͗͢ΐ͍

  32. APIͷURLΛมߋ͢Δ

  33. APIͷURLΛมߋ͢Δ • ϝϧΧϦ Χ΢ϧͷ։ൃͰ͸αʔόʔαΠυͰ৽͍͠ػೳΛ
 ௥Ճ͢Δ৔߹ɺػೳ͝ͱʹAPIͷURL͕ҟͳΔ • ΞϓϦ͔Βࢀর͢ΔAPIͷURLΛมߋ͍ͨ͠৔߹ʹ
 URLΛมߋͯ͠ΞϓϦΛϏϧυ͠௚ͯ͠Δͱ͕͔͔࣌ؒΔ • ։ൃ൛ͷΞϓϦ͸ΤϯδχΞ͚ͩͰͳ͘


    σβΠφʔ΍QAͳͲ΋৮Δ • ୭Ͱ΋؆୯ʹAPIͷURLΛมߋͰ͖Δඞཁ͕͋Δ
  34. APIͷURLΛมߋ͢Δ • Debug Menu͔ΒAPIͷURLΛมߋͰ͖ΔΑ͏ʹͯ͠Δ • SharedPreferencesΛ࢖͑͹؆୯ʹ࣮૷Ͱ͖Δ

  35. APIͷURLΛมߋ͢Δ • APIͷURL͸ΫϥεΛհͯ͠औಘ͢ΔΑ͏ʹ͢Δ • SharedPreferencesʹมߋޙͷAPIͷURL͕
 ͋Ε͹มߋޙͷAPIͷURLΛฦ͢ • ͦ͏Ͱͳ͍৔߹͸ΞϓϦ಺Ͱݩʑఆٛͯ͋͠Δ APIͷURLΛฦ͢

  36. public class UrlManager { public static final String API_URL =

    "https://tech-book-fest-debug.com"; private static DebugInformationPrefs debugInformationPrefs; public static void setDebugInformationPrefs(DebugInformationPrefs prefs) { UrlManager.debugInformationPrefs = prefs; } public static String getApiUrl() { // APIͷURLʹมߋ͕ͳ͍৔߹ɺఆٛࡁΈͷAPIͷURLΛฦ͢ if (debugInformationPrefs == null || !debugInformationPrefs.hasApiUrl()) { return API_URL; } return debugInformationPrefs.getApiUrl(); } }
  37. APIͷURLΛมߋ͢Δ • Debug MenuʹAPIͷURLΛมߋ͢Δ
 ػೳΛ௥Ճ͢Δ • EditTextͷaddTextChangedListenerͰ
 ؆୯ʹ࣮૷Ͱ͖Δ

  38. public class DebugMenuActivity extends AppCompatActivity { @Override protected void onCreate(Bundle

    savedInstanceState) { super.onCreate(savedInstanceState); ActivityDebugMenuBinding binding = DataBindingUtil.setContentView(this, R.layout…); binding.apiUrl.setText(UrlManager.getApiUrl()); binding.apiUrl.setSelection(binding.apiUrl.getText().toString().length()); binding.apiUrl.addTextChangedListener(new TextWatcher() { @Override public void afterTextChanged(Editable s) { DebugInformationPrefs.get(DebugMenuActivity.this) .setApiUrl(s.toString()); } }); } }
  39. Demo

  40. APIͷURLΛมߋ͢Δ ؾ͔͍ͮ • ΞϓϦ಺ʹݩʑఆ͍ٛͯ͠ΔAPIͷURLʹ
 ໭͢ૢ࡞΋සൟʹߦ͏ͨΊɺΫϦΞϘλϯ
 ΋༻ҙ͓ͯ͘͠ͱศརʂ

  41. Picassoͷσόοάϩάදࣔ΍ Πϯδέʔλදࣔͷ
 ༗ޮɾແޮΛ੾Γସ͑Δ

  42. Picassoͷσόοάϩάදࣔ΍Πϯδ έʔλදࣔͷ༗ޮɾແޮΛ੾Γସ͑Δ • ϝϧΧϦ Χ΢ϧͰ͸ը૾μ΢ϯϩʔυʹPicassoΛ࢖ͬͯΔ • Picassoʹ͸ɺ։ൃ࣌ʹ໾ཱͭػೳ͕͍͔ͭ͋͘Δ • σόοάϩάͷදࣔ •

    ಡΈࠐ·Εͨը૾͕ωοτϫʔΫɺσΟεΫɺϝϞϦ
 Ͳ͔͜Βདྷͨͷ͔൑ผͰ͖ΔΠϯδέʔλΛը૾্ʹදࣔ͢Δ
  43. Picassoͷσόοάϩάදࣔ΍Πϯδ έʔλදࣔͷ༗ޮɾແޮΛ੾Γସ͑Δ • ศར͚ͩͲৗʹग़ͯ͠Δͱ͏͍ͬͨ͟ • ඞཁͳ࣌ʹDebug Menu͔Β༗ޮɾແޮΛ
 ੾Γସ͑Δ͍ͧʂ • SharedPreferencesΛ࢖͑͹؆୯ʹ࣮૷Ͱ͖Δ

  44. Picassoͷσόοάϩάදࣔ΍Πϯδ έʔλදࣔͷ༗ޮɾແޮΛ੾Γସ͑Δ • SwitchΛ࢖ͬͯ༗ޮɾແޮͷ੾Γସ͑Λ
 ϋϯυϦϯάͯ͠ɺSharedPreferencesʹอଘ͢Δ • ༗ޮɾແޮͷ੾Γସ͑ΛPicassoʹઃఆ͢Δ

  45. public class DebugMenuActivity extends AppCompatActivity { @Override protected void onCreate(Bundle

    savedInstanceState) { super.onCreate(savedInstanceState); ActivityDebugMenuBinding binding = DataBindingUtil.setContentView(this, R.layout…); binding.picassoEnableDebugLog.setChecked( Picasso.with(this).isLoggingEnabled()); binding.picassoEnableDebugLog.setOnCheckedChangeListener((__, ic) -> { DebugInformationPrefs.get(this).setPicassoLoggingEnabled(ic); Picasso.with(this).setLoggingEnabled(ic); }); binding.picassoEnabledIndicators.setChecked( Picasso.with(this).areIndicatorsEnabled()); binding.picassoEnabledIndicators.setOnCheckedChangeListener((__, ic) -> { DebugInformationPrefs.get(this).setPicassoAreIndicatorsEnabled(ic); Picasso.with(this).setIndicatorsEnabled(ic); }); } }
  46. Picassoͷσόοάϩάදࣔ΍Πϯδ έʔλදࣔͷ༗ޮɾແޮΛ੾Γସ͑Δ • ApplicationͷonCreateͱ͔Ͱ༗ޮɾແޮͷ
 ੾Γସ͑Λอ࣋ͯ͠ΔSharedPreferences
 ͔Β஋ΛऔΓग़ͯ͠Picassoʹઃఆ͢Δ • ࠶౓ΞϓϦΛ։͍ͨࡍʹ΋ઃఆ஋Λ൓өͰ͖Δ

  47. public class DebugApplication extends Application { @Override public void onCreate()

    { super.onCreate(); DebugInformationPrefs prefs = DebugInformationPrefs.get(this); Picasso picasso = Picasso.with(this); picasso.setLoggingEnabled(prefs.getPicassoLoggingEnabled()); picasso.setIndicatorsEnabled(prefs.getPicassoAreIndicatorsEnabled()); } }
  48. Demo

  49. Picassoͷσόοάϩάදࣔ΍Πϯδ έʔλදࣔͷ༗ޮɾແޮΛ੾Γସ͑Δ • ͜ͷON/OFF੾Γସ͑ͷΞϓϩʔν͸
 ଞͷ੾Γସ͑Ͱ΋༗ޮ • Ұ౓࡞Ε͹ಉ͡Α͏ͳ࣮૷ͰON/OFF੾Γସ͑ܥ ͷDebug Menu͕௥ՃͰ͖Δ •

    ϝϧΧϦ Χ΢ϧͰ͸LeakCanaryͷON/OFFʹ
 ಉ͡Α͏ͳ࣮૷Λ࢖ͬͯΔ
  50. ͜͜·Ͱ͸ຊʹॻ͍ͨ͜ͱͰ͢ʂ

  51. σβΠϯνΣοΫΛ
 ͬ͘͞ͱ΍Δ

  52. σβΠϯνΣοΫΛͬ͘͞ͱ΍Δ • ΞϓϦͰڞ௨Ͱ࢖ͬͯΔStyleΛద༻ͨ͠View Λͬ͘͞ͱνΣοΫͰ͖Ε͹ָͦ͏ʂ • ಛʹΞϓϦΛ࡞Γ࢝Ίͨࠒ͸σβΠφʔͱͷ ΍ΓऔΓͰ࢖ͬͯͨ • มߋ͢Δ࣌΋ؔ࿈ͯ͠Δ΋ͷ͕·ͱΊͯ
 มΘΔ͔Ͳ͏͔΋Θ͔Γ΍͘͢ͳΔ

  53. σβΠϯνΣοΫΛͬ͘͞ͱ΍Δ • ࡞Δͷ͸ΊͬͪΌ؆୯ • StyleΛద༻ͨ͠ViewΛฒ΂ͨΓ
 ී௨ʹViewฒ΂ͯϦ͢Ε͹͍͍͚ͩʂ

  54. <?xml version="1.0" encoding="utf-8"?> <LinearLayout ...> <Button style="@style/Button.A" android:text="Button A" />

    <Button style="@style/Button.B" android:text="Button B" /> <Button style="@style/Button.C" android:text="Button C" /> </LinearLayout>
  55. Demo

  56. APIͷURLΛ΋ͬͱ؆୯ʹ
 มߋ͢Δ

  57. APIͷURLΛ΋ͬͱ؆୯ʹมߋ͢Δ • มߋ͢ΔͷʹҰʑURLଧͬͯΒΕΔ͔ʂʂ • ͦΕҎ֎ͷؾ͕༙͍࣋ͪͯ͜ͳ͍...

  58. APIͷURLΛ΋ͬͱ؆୯ʹมߋ͢Δ • APIͷURL͕ҰཡʹͳͬͯͨΒ੾Γସָ͑͡ΌͶʁ • Ϧετબ୒ͨ͠ΒURLมߋͨ͠Βָ͡ΌͶʁ

  59. APIͷURLΛ΋ͬͱ؆୯ʹมߋ͢Δ • RecyclerViewͱ͔ListView࢖͑͹؆୯ʹͰ͖Δʂ • બ୒͞Εͨ಺༰ΛIntentʹ٧Ίͯ
 setResult͢Ε͹؆୯ʹͰ͖Δʂ

  60. Demo

  61. ͗͢ΐ͍

  62. APIͷURLΛ΋ͬͱ؆୯ʹมߋ͢Δ • ࠷ߴʹͳͬͨʂ • QRΛಡΈࠐΜͰURLΛม͑Δͬͯ
 ࣮૷ͯ͠Δͷ΋ݟͨ͜ͱ͋Δͧʂ

  63. ·ͱΊ • Debug Menu͸γϯϓϧͳ࣮૷ͰศརͳػೳΛఏڙͰ͖Δ • ڵຯΛ࣋ͬͨํ͸ɺڞʹ։ൃ͢ΔਓͨͪʹͲΜͳ
 Debug Menu͕͋Ε͹ศར͔ώΞϦϯάͯ͠
 ࣮૷ͯ͠ΈΔͱΑ͛͞ •

    ࢲୡͷΞϓϦͷDebug Menuʹ͸͜Μͳػೳ͕͋Δͥʂʯ
 తͳͷฉ͖͍ͨͷͰΈΜͳڞ༗͠Α͏ͥʂʂ
  64. Debug Menu
 ΍͍͖ͬͯ·͠ΐʂ

  65. Thanksʂʂ