Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

ϝϧΧϦ Χ΢ϧͷ
 Debug Menu

Slide 8

Slide 8 text

ϝϧΧϦ Χ΢ϧͷDebug Menu

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

ϝϧΧϦ Χ΢ϧͷDebug Menu

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

Debug MenuΛ࡞Δίπ • ݟͨ໨͸ؾʹͤͣͱʹ͔͘ػೳ໘Λॏࢹͯ͠࡞Δ • ͍͍ػೳΛࢥ͍͍ͭͨΒͱʹ͔࣮͘૷ͯ͠ΈΔ • γϯϓϧʹ࣮૷͢Δ • ແବͳ΋ͷ͸࡞Βͳ͍ • ͲΜͳػೳ͕͋Δͱศར͔νʔϜϝϯόʔʹώΞϦϯάͯ͠ΈΔ

Slide 13

Slide 13 text

Debug Menu࣮ફೖ໳

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

ϦϦʔε൛ΞϓϦʹDebug MenuΛؚΊͳ͍

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

ৗઃ௨஌͔ΒDebug Menuͷը໘Λ։͘ • ϝϧΧϦ Χ΢ϧͰ͸௨஌ͷදࣔɾΫϦΞͷ੍ޚʹ Application.ActivityLifecycleCallbacksΛ࢖ͬͯΔ • ΞϓϦ͕ىಈͨ࣌͠ʹ௨஌Λදࣔ͢Δ • Activityͷback stack্Ͱ࠷ޙͷActivity͕Destroy͢Δ࣌ʹ ௨஌ΛΫϦΞ͢Δ • ࣮૷ͨ͠Application.ActivityLifecycleCallbacks͸ ApplicationͷonCreateͱ͔Ͱొ࿥͢Δ

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

Demo

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

Demo

Slide 31

Slide 31 text

͗͢ΐ͍

Slide 32

Slide 32 text

APIͷURLΛมߋ͢Δ

Slide 33

Slide 33 text

APIͷURLΛมߋ͢Δ • ϝϧΧϦ Χ΢ϧͷ։ൃͰ͸αʔόʔαΠυͰ৽͍͠ػೳΛ
 ௥Ճ͢Δ৔߹ɺػೳ͝ͱʹAPIͷURL͕ҟͳΔ • ΞϓϦ͔Βࢀর͢ΔAPIͷURLΛมߋ͍ͨ͠৔߹ʹ
 URLΛมߋͯ͠ΞϓϦΛϏϧυ͠௚ͯ͠Δͱ͕͔͔࣌ؒΔ • ։ൃ൛ͷΞϓϦ͸ΤϯδχΞ͚ͩͰͳ͘
 σβΠφʔ΍QAͳͲ΋৮Δ • ୭Ͱ΋؆୯ʹAPIͷURLΛมߋͰ͖Δඞཁ͕͋Δ

Slide 34

Slide 34 text

APIͷURLΛมߋ͢Δ • Debug Menu͔ΒAPIͷURLΛมߋͰ͖ΔΑ͏ʹͯ͠Δ • SharedPreferencesΛ࢖͑͹؆୯ʹ࣮૷Ͱ͖Δ

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

APIͷURLΛมߋ͢Δ • Debug MenuʹAPIͷURLΛมߋ͢Δ
 ػೳΛ௥Ճ͢Δ • EditTextͷaddTextChangedListenerͰ
 ؆୯ʹ࣮૷Ͱ͖Δ

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

Demo

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

Picassoͷσόοάϩάදࣔ΍Πϯδ έʔλදࣔͷ༗ޮɾແޮΛ੾Γସ͑Δ • ϝϧΧϦ Χ΢ϧͰ͸ը૾μ΢ϯϩʔυʹPicassoΛ࢖ͬͯΔ • Picassoʹ͸ɺ։ൃ࣌ʹ໾ཱͭػೳ͕͍͔ͭ͋͘Δ • σόοάϩάͷදࣔ • ಡΈࠐ·Εͨը૾͕ωοτϫʔΫɺσΟεΫɺϝϞϦ
 Ͳ͔͜Βདྷͨͷ͔൑ผͰ͖ΔΠϯδέʔλΛը૾্ʹදࣔ͢Δ

Slide 43

Slide 43 text

Picassoͷσόοάϩάදࣔ΍Πϯδ έʔλදࣔͷ༗ޮɾແޮΛ੾Γସ͑Δ • ศར͚ͩͲৗʹग़ͯ͠Δͱ͏͍ͬͨ͟ • ඞཁͳ࣌ʹDebug Menu͔Β༗ޮɾແޮΛ
 ੾Γସ͑Δ͍ͧʂ • SharedPreferencesΛ࢖͑͹؆୯ʹ࣮૷Ͱ͖Δ

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

Picassoͷσόοάϩάදࣔ΍Πϯδ έʔλදࣔͷ༗ޮɾແޮΛ੾Γସ͑Δ • ApplicationͷonCreateͱ͔Ͱ༗ޮɾແޮͷ
 ੾Γସ͑Λอ࣋ͯ͠ΔSharedPreferences
 ͔Β஋ΛऔΓग़ͯ͠Picassoʹઃఆ͢Δ • ࠶౓ΞϓϦΛ։͍ͨࡍʹ΋ઃఆ஋Λ൓өͰ͖Δ

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

Demo

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

͜͜·Ͱ͸ຊʹॻ͍ͨ͜ͱͰ͢ʂ

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

Slide 55

Slide 55 text

Demo

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

Demo

Slide 61

Slide 61 text

͗͢ΐ͍

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

·ͱΊ • Debug Menu͸γϯϓϧͳ࣮૷ͰศརͳػೳΛఏڙͰ͖Δ • ڵຯΛ࣋ͬͨํ͸ɺڞʹ։ൃ͢ΔਓͨͪʹͲΜͳ
 Debug Menu͕͋Ε͹ศར͔ώΞϦϯάͯ͠
 ࣮૷ͯ͠ΈΔͱΑ͛͞ • ࢲୡͷΞϓϦͷDebug Menuʹ͸͜Μͳػೳ͕͋Δͥʂʯ
 తͳͷฉ͖͍ͨͷͰΈΜͳڞ༗͠Α͏ͥʂʂ

Slide 64

Slide 64 text

Debug Menu
 ΍͍͖ͬͯ·͠ΐʂ

Slide 65

Slide 65 text

Thanksʂʂ