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

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

Realm

March 01, 2017
Tweet

More Decks by Realm

Other Decks in Technology

Transcript

  1. 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); ... }
  2. 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); }
  3. ⋅ Authentication & account management ⋅ Supports: ⋅ Email &

    password ⋅ Google, Facebook, Twitter, and GitHub sign-in ⋅ Existing auth systems ⋅ Out-of-the box UI
  4. 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 { ... }
  5. 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); } }
  6. 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(); } } }
  7. 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(); }
  8. LoginActivity.java @Override public void onStart() { super.onStart(); auth.addAuthStateListener(authListener); } @Override

    public void onStop() { super.onStop(); if (authListener != null) { auth.removeAuthStateListener(authListener); } }
  9. 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()); } } }); }
  10. 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" } }
  11. public class Taco { private String name; private String description;

    private boolean isFavorite; /* getters & setters */ } Taco.java
  12. 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; } ... }
  13. 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); }
  14. 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); }
  15. 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); }
  16. 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)); } }
  17. ⋅ Easy file storage ⋅ Handles poor connectivity ⋅ Backed

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

    audio downloaded from your app ⋅ Read-write blobs of data ⋅ User-generated content ⋅ App-generated content
  19. 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); }
  20. 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) { ... } }) });
  21. • Modify without deploying • Easily tune & experiment •

    Customize for Audiences ⋅ text ⋅ text ⋅ text ⋅ Server-side variables ⋅ Modify without deploying ⋅ Integrated with Analytics
  22. 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); ... } } });
  23. ⋅ No Cost cross-platform messaging solution ⋅ Notifications to drive

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

    Messaging ⋅ Audience targeting ⋅ Conversion funnel insights
  25. 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>
  26. MessagingServiceHandler.java public class MessagingServiceHandler extends FirebaseMessagingService { @Override public void

    onMessageReceived(RemoteMessage remoteMessage) { super.onMessageReceived(remoteMessage); if (remoteMessage.getNotification() != null) { sendNotification(remoteMessage.getNotification().getBody()); } } }
  27. 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()); }
  28. ⋅ Designed for apps ⋅ Event and user centric ⋅

    Connects across Firebase ⋅ Free & unlimited
  29. 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);
  30. 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);
  31. 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); }
  32. ⋅ Test on the most popular devices before you ship

    ⋅ Reports & screenshots ⋅ Robo & custom tests
  33. 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); }