Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Adrián Catalan: Droids on fire: Firebase-ing your app for fun & profit

1fa9cb8c7997c8c4d3d251fb5e41f749?s=47 Realm
March 01, 2017

Adrián Catalan: Droids on fire: Firebase-ing your app for fun & profit

Speaker: Adrián has been involved in software industry for 10+ years, working both in web and mobile apps. GDG Guatemala, GuatemalaJS and Nodebots former co-organizer. Currently he leads the Innovation Lab at Galileo University and is a Google Developer Expert(GDE) for Android, IoT and Firebase.

Abstract: Firebase, is an all-in-one swiss army toolset for mobile developers. It handles infrastructure, speeds up the developing proccess and allow us to connect with the users and their behaviors. But be on the lookout, it's no silver bullet and having such powerful hammer in hand, might turn everything to start looking like a nail. Come and learn about leveraging on Firebase to save time developing, detect bugs and crashes, gain insight and engage with users. Using a demo app, let's discuss on features, mix-and-match them for our benefit and explore opportunities for implementation to build great apps.

Twitter Link: https://twitter.com/ykro

1fa9cb8c7997c8c4d3d251fb5e41f749?s=128

Realm

March 01, 2017
Tweet

Transcript

  1. DROIDS ON FIRE Adrián Catalán @ykro

  2. https://goo.gl/ iGe2vk

  3. None
  4. None
  5. None
  6. None
  7. None
  8. Developer experience matters

  9. Cross-platform

  10. Integrated

  11. Getting Started with Firebase

  12. None
  13. None
  14. None
  15. None
  16. None
  17. None
  18. None
  19. None
  20. None
  21. http://github.com /ykro/WikiTaco

  22. App.java @Override public void onCreate() { super.onCreate(); firebaseAuth = FirebaseAuth.getInstance();

    FirebaseDatabase db = FirebaseDatabase.getInstance(); FirebaseStorage storage = FirebaseStorage.getInstance(); firebaseAnalytics = FirebaseAnalytics.getInstance(this); //db & storage references firebaseDatabseReference = db.getReference(); firebaseStorageReference = storage.getReference(); //notifications FirebaseMessaging.getInstance().subscribeToTopic(NEW_TACOS_TOPIC); ... }
  23. App.java @Override public void onCreate() { ... //remote config firebaseRemoteConfig

    = FirebaseRemoteConfig.getInstance(); //enable developer mode for frequent refreshes FirebaseRemoteConfigSettings configSettings = new FirebaseRemoteConfigSettings.Builder() .setDeveloperModeEnabled(BuildConfig.DEBUG) .build(); firebaseRemoteConfig.setConfigSettings(configSettings); firebaseRemoteConfig.setDefaults(R.xml.remote_config_defaults); }
  24. STEP 1 LET’S START WITH SOME AUTH

  25. ⋅ Authentication & account management ⋅ Supports: ⋅ Email &

    password ⋅ Google, Facebook, Twitter, and GitHub sign-in ⋅ Existing auth systems ⋅ Out-of-the box UI
  26. None
  27. None
  28. None
  29. None
  30. ENTER FIREBASE UI

  31. None
  32. LoginActivity.java private FirebaseAuth auth; private void doLogin() { FirebaseUser currentUser

    = auth.getCurrentUser(); if (currentUser != null) { Intent i = new Intent(this, MainActivity.class); i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(i); } else { ... }
  33. LoginActivity.java private void doLogin() { ... if (currentUser != null)

    { ... } else { startActivityForResult( AuthUI.getInstance() .createSignInIntentBuilder() .setProviders(Arrays.asList( new AuthUI.IdpConfig.Builder(AuthUI.EMAIL_PROVIDER).build(), new AuthUI.IdpConfig.Builder(AuthUI.GOOGLE_PROVIDER).build(), new AuthUI.IdpConfig.Builder(AuthUI.FACEBOOK_PROVIDER).build(), new AuthUI.IdpConfig.Builder(AuthUI.TWITTER_PROVIDER).build())) .setIsSmartLockEnabled(false) .build(), RC_SIGN_IN); } }
  34. LoginActivity.java @Override public void onActivityResult(int requestCode, int resultCode, Intent data)

    { super.onActivityResult(requestCode, resultCode, data); if (requestCode == RC_SIGN_IN) { if (resultCode == ResultCodes.OK) { doLogin(); } } }
  35. WITHOUT FIREBASE UI

  36. None
  37. LoginActivity.java @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); auth

    = ((Aplicacion)getApplicationContext()).getAuth(); authListener = new FirebaseAuth.AuthStateListener() { @Override public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) { hideProgress(); doLogin(); } }; ... doLogin(); }
  38. LoginActivity.java @Override public void onStart() { super.onStart(); auth.addAuthStateListener(authListener); } @Override

    public void onStop() { super.onStop(); if (authListener != null) { auth.removeAuthStateListener(authListener); } }
  39. LoginActivity.java public void signin(View v){ String email = inputEmail.getText().toString(); String

    password = inputPassword.getText().toString(); showProgress(); auth.signInWithEmailAndPassword(email, password) .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { if (!task.isSuccessful()) { hideProgress(); showSnackbar(task.getException().getLocalizedMessage()); } } }); }
  40. STEP 2 TACOSDB.SAVE( )

  41. ⋅ Cloud-hosted NoSQL database ⋅ Synchronization & conflict resolution ⋅

    Access directly from your app
  42. None
  43. None
  44. None
  45. https://gist.github.com/ykro/ca8010e657256a150800 // Root // https://android-to-do-list.firebaseio.com/ { // https://android-to-do-list.firebaseio.com/message "message" :

    "Hello, World!", // https://android-to-do-list.firebaseio.com/words "words" : { // https://android-to-do-list.firebaseio.com/words/-KKGDzmrcC8o5EunCptc "-KKGDzmrcC8o5EunCptc" : "test!", // https://android-to-do-list.firebaseio.com/words/-KKGEIIqvk-R0yitCXIc "-KKGEIIqvk-R0yitCXIc" : "another item" } }
  46. None
  47. None
  48. public class Taco { private String name; private String description;

    private boolean isFavorite; /* getters & setters */ } Taco.java
  49. TacoRecyclerAdapter.java public class TacoRecyclerAdapter extends FirebaseRecyclerAdapter<Taco,TacoRecyclerAdapter.TacoViewHolder> { private Context context;

    private final static int layoutId = R.layout.item_taco; public TacoRecyclerAdapter(Context context, DatabaseReference databaseReference) { super(Taco.class, layoutId, TacoViewHolder.class, databaseReference); this.context = context; } ... }
  50. TacoRecyclerAdapter.java @Override protected void populateViewHolder(TacoViewHolder viewHolder, Taco model, int position)

    { viewHolder.tvTacoName.setText(model.getName()); ... }
  51. WITHOUT FIREBASE UI

  52. TacoRecyclerAdapter.java private ArrayList<String> keys; private ArrayList<DataSnapshot> items; protected DatabaseReference databaseReference;

    public TacoRecyclerAdapter(Context context, DatabaseReference ref) { ... ref.addChildEventListener(this); } public void destroy() { booksReference.removeEventListener(this); }
  53. TacoRecyclerAdapter.java private ArrayList<String> keys; private ArrayList<DataSnapshot> items; protected DatabaseReference databaseReference;

    public TacoRecyclerAdapter(Context context, DatabaseReference ref) { ... ref.addChildEventListener(this); } public void destroy() { booksReference.removeEventListener(this); }
  54. TacoRecyclerAdapter.java private ArrayList<String> keys; private ArrayList<DataSnapshot> items; protected DatabaseReference databaseReference;

    public TacoRecyclerAdapter(Context context, DatabaseReference ref) { ... ref.addChildEventListener(this); } public void destroy() { booksReference.removeEventListener(this); }
  55. TacoRecyclerAdapter.java @Override public void onChildAdded(DataSnapshot dataSnapshot, String s) { items.add(dataSnapshot);

    keys.add(dataSnapshot.getKey()); notifyItemInserted(getItemCount()-1); }
  56. TacoRecyclerAdapter.java @Override public void onChildChanged(DataSnapshot dataSnapshot, String s) { String

    key = dataSnapshot.getKey(); if (keys.contains(key)) { int index = keys.indexOf(key); items.set(index, dataSnapshot); notifyItemChanged(index, dataSnapshot.getValue(Taco.class)); } }
  57. https://console.firebase.google.com/project/android-to-do-list/database/rules https://gist.github.com/ykro/63197874df603a13089e8651ba79a039

  58. STEP 3 TACOS AND NO PICTURES, HUH?

  59. ⋅ Easy file storage ⋅ Handles poor connectivity ⋅ Backed

    by & accessible from Google Cloud Storage
  60. ⋅ Static, "read-only" data ⋅ Website assets ⋅ Images /

    audio downloaded from your app ⋅ Read-write blobs of data ⋅ User-generated content ⋅ App-generated content
  61. None
  62. TacoRecyclerAdapter.java @Override protected void populateViewHolder(TacoViewHolder viewHolder, Taco model, int position)

    { ... StorageReference storageReference = ((App)context.getApplicationContext()) .getTacoStorageReference( getRef(position) .getKey()); Glide.with(context) .using(new FirebaseImageLoader()) .load(storageReference) .centerCrop() .crossFade() .into(viewHolder.ivTacoImg); }
  63. WITHOUT FIREBASE UI

  64. TacoRecyclerAdapter.java storageRef.child(downloadPath).getStream( new StreamDownloadTask.StreamProcessor() { @Override public void doInBackground(StreamDownloadTask.TaskSnapshot taskSnapshot,

    InputStream inputStream) throws IOException { ... } }) .addOnSuccessListener(new OnSuccessListener<StreamDownloadTask.TaskSnapshot>() { @Override public void onSuccess(StreamDownloadTask.TaskSnapshot taskSnapshot) { ... } }) });
  65. STEP 4 MOAR INGREDIENTS & NO DEPLOY

  66. • Modify without deploying • Easily tune & experiment •

    Customize for Audiences ⋅ text ⋅ text ⋅ text ⋅ Server-side variables ⋅ Modify without deploying ⋅ Integrated with Analytics
  67. None
  68. None
  69. None
  70. TacoRecyclerAdapter.java firebaseRemoteConfig.fetch(cacheExpiration) .addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void>

    task) { if (task.isSuccessful()) { firebaseRemoteConfig.activateFetched(); favoritesEnabled = firebaseRemoteConfig.getBoolean("favorites_enabled"); String tacoListExperiment = firebaseRemoteConfig .getString("taco_list_columns_experiment"); firebaseAnalytics.setUserProperty("taco_list_columns",tacoListExperiment); ... } } });
  71. STEP 5 SPREAD THE WORD

  72. ⋅ No Cost cross-platform messaging solution ⋅ Notifications to drive

    user interaction ⋅ Versatile Messaging Targeting
  73. ⋅ Simple UI, with no coding ⋅ Built on Cloud

    Messaging ⋅ Audience targeting ⋅ Conversion funnel insights
  74. None
  75. None
  76. None
  77. AndroidManifest.xml <application ... > <service android:name=".notificationservice.MessagingServiceHandler"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT"/> </intent-filter>

    </service> <meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/ic_mail_white_24dp" /> <meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/colorAccent" /> </application>
  78. MessagingServiceHandler.java public class MessagingServiceHandler extends FirebaseMessagingService { @Override public void

    onMessageReceived(RemoteMessage remoteMessage) { super.onMessageReceived(remoteMessage); if (remoteMessage.getNotification() != null) { sendNotification(remoteMessage.getNotification().getBody()); } } }
  79. MessagingServiceHandler.java private void sendNotification(String messageBody) { Intent intent = new

    Intent(this, TacosListActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 , intent, PendingIntent.FLAG_ONE_SHOT); Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this) .setContentTitle("Wiki Taco") .setContentText(messageBody) .setAutoCancel(true) .setSound(defaultSoundUri) .setContentIntent(pendingIntent); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify(0, notificationBuilder.build()); }
  80. STEP 6 SLICE, ANALYZE & IMPROVE

  81. ⋅ Designed for apps ⋅ Event and user centric ⋅

    Connects across Firebase ⋅ Free & unlimited
  82. None
  83. None
  84. None
  85. None
  86. None
  87. None
  88. None
  89. App.java String tacoListColumnsExperiment = firebaseRemoteConfig .getString("taco_list_columns_experiment"); int tacoListColumns = 1;

    if (tacoListColumnsExperiment.equals(TACO_LIST_EXPERIMENT_VARIANT_A)) { tacoListColumns = 2; } else if (tacoListColumnsExperiment.equals(TACO_LIST_EXPERIMENT_VARIANT_B)) { tacoListColumns = 3; } gridLayoutManager.setSpanCount(tacoListColumns); //analytics binding for a/b test firebaseAnalytics.setUserProperty("taco_list_columns",tacoListColumnsExperiment);
  90. App.java String tacoListColumnsExperiment = firebaseRemoteConfig .getString("taco_list_columns_experiment"); int tacoListColumns = 1;

    if (tacoListColumnsExperiment.equals(TACO_LIST_EXPERIMENT_VARIANT_A)) { tacoListColumns = 2; } else if (tacoListColumnsExperiment.equals(TACO_LIST_EXPERIMENT_VARIANT_B)) { tacoListColumns = 3; } gridLayoutManager.setSpanCount(tacoListColumns); //analytics binding for a/b test firebaseAnalytics.setUserProperty("taco_list_columns",tacoListColumnsExperiment);
  91. TacoDetailActivity.java @Override protected void onCreate(Bundle savedInstanceState) { ... Bundle bundle

    = new Bundle(); bundle.putString(FirebaseAnalytics.Param.ITEM_ID, id); bundle.putString(FirebaseAnalytics.Param.ITEM_NAME, name); app.logEvent(FirebaseAnalytics.Event.VIEW_ITEM, bundle); }
  92. STEP 7 TIME TO TASTE TEST IT

  93. ⋅ Test on the most popular devices before you ship

    ⋅ Reports & screenshots ⋅ Robo & custom tests
  94. None
  95. None
  96. None
  97. None
  98. None
  99. STEP 8 TOO SPICY?

  100. ⋅ See crashes & impact ⋅ Version & OS drill-down

    ⋅ Integrated with Analytics
  101. None
  102. None
  103. TacoDetailActivity.java @Override protected void onCreate(Bundle savedInstanceState) { String description =

    intent.getStringExtra(TACO_DESCRIPTION_KEY); if (description == null) { description = ""; FirebaseCrash.log("No description available for " + id); } TextView txtDescription = (TextView) findViewById(R.id.txtDescription); txtDescription.setText(description); }
  104. None
  105. None
  106. http://github.com /ykro/WikiTaco

  107. https://goo.gl/ iGe2vk

  108. None
  109. DROIDS ON FIRE Adrián Catalán @ykro