No Play Service? No Worries!

No Play Service? No Worries!

Lots of our applications depends on the technical bricks provided by the Play Services such as Google Maps, the push notifications by Firebase or even the Play Store for deploying our apps all around the world.

What if you are building apps for a device that does not implement the Play Services. With this talk you will have a glimpse of the tools you can use to build your Android applications without the Play Services such as OpenStreetMap, OpenPush or alternative ways to deploy an APK.

56047a7b11797f42c2e7030d771fe803?s=128

Julien Salvi

November 21, 2020
Tweet

Transcript

  1. No Play Services? No Worries! Julien Salvi Senior Android Engineer,

    Aircall devfest Egypt 2020
  2. Facing the No Play Services

  3. How you can end up developing apps without the Play

    Services? • Supporting a larger set of devices • Build apps for devices that don’t have the Play Services (Huawei, Hardware for B2B...) • Company’s choice • Trying something new, open-source… Let’s see how we can deal with it
  4. Meet Happy! Happy is a… happy dev! • Using good

    practices • Building apps with Kotlin • Testing his apps • Doing crazy animations! Hi I’m Happy!
  5. Happy has built a new app! • Using Google Map

    SDK for displaying routes • Going for Firebase messaging to send nice notifications • CI/CD for deploying on the Play Store • So far so good! Let’s do this!
  6. After a while, app got a lot of crashes! Hum…

    Something’s wrong! GooglePlayServicesRepairableException GooglePlayServicesNotAvailableExceptio n
  7. OMG! Happy learns that the app has been deployed on

    a lot of devices that don’t have the Play Services installed Sometimes because the app is distributed outside the Play Store or for B2B devices without the Play Services
  8. Woop woop! But no worries Happy! Let see what tools

    and libraries we can use for developing applications without using the Play Services
  9. Play Services alternative tools

  10. Let see nice alternative libraries to...

  11. Is there always an alternative? Short answer… no :(

  12. Firebase libraries • Firestore, • Storage • Crashlytics • InAppMessaging

    Now lots of Firebase libraries works without the Play Services • ML libs • Ads • Push messaging (FCM) • Database • Config • Authentication ✅ ⛔
  13. Push notifications

  14. Push Notifications • FCM is tied up to the Play

    Services for receiving messages • There are many alternatives for push messages • Let see how Pushy, OpenPush or RabbitMQ can a good replacement tool!
  15. Push Notifications • Reliable push service • Cross-platform solution •

    Free plan at first, the premium • Similar implementation to FCM • Based on MQTT protocol
  16. MQTT MQTT Broker publish subscribe Client publish subscribe publish

  17. override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (getToken() != null)

    { viewModel.registerPushy() } else { Pushy.listen(context) } }
  18. scope.launch(Dispatchers.IO) { val token = Pushy.register(context) // Save in the

    shared preferences for example saveToken() // send token to your backend sendToken() }
  19. class PushReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent:

    Intent) { val msg = intent.getStringExtra("message") // Do amazing stuff here! (Notification, routing...) } }
  20. OpenPush • Open-source and free alternative to FCM • Decentralized

    and self-hosted • No developer key required • A lot of code on server and client side • More complex than FCM or Pushy
  21. OpenPush

  22. OpenPush OpenPush - A Free, Decentralized Push Messaging Framework for

    Android by Marcus Hoffmann https://f-droid.org/en/2020/02/03/openpush-talk.html
  23. RabbitMQ for Android • Open-source message broker • Based on

    AMQP protocol but now support STOMP or MQTT • Receive AND send data • Might be a good solution if your app is always on like a kiosk app
  24. RabbitMQ Consumer/ Subscriber RabbitMQ Server publish publish subscribe subscribe Queue

    Queue Consumer/ Subscriber
  25. // publishing messages private val queue = LinkedBlockingDeque>String>() fun publishMessage(message:

    String) { try { queue.putLast(message) } catch (e: InterruptedException) { e.printStackTrace() } }
  26. // create and setup a connection val factory = ConnectionFactory()

    factory.setUsername(config.getUsername()) factory.setPassword(config.getPassword()) factory.setVirtualHost(config.getVhost()) factory.setHost(config.getHost()) factory.setPort(config.getPort()) factory.setAutomaticRecoveryEnabled(true)
  27. // Create publish channel val connection = factory.newConnection() val channel

    = connection.createChannel() channel.confirmSelect() // Publish messages added to the queue transactionsQueue.add(Message(comment)) while (!transactionsQueue.isEmpty()) { val message = transactionsQueue.get() val exchange = "amqp.direct.app.messages" val routingKey = "comment_v1" channel.basicPublish(exchange, routingKey, null, message.getBytes()) channel.waitForConfirms() }
  28. // Create subscriber/consumer val connection = factory.newConnection() val channel =

    connection.createChannel() val q = channel.queueDeclare() channel.queueBind(q.getQueue(), "amqp.direct.app.messages", "chat") val consumer = QueueingConsumer(channel) channel.basicConsume(q.getQueue(), true, consumer) while (true) { val delivery = consumer.nextDelivery() val message = new String(delivery.getBody()) // Handle the data received }
  29. Maps

  30. Google Maps replacement... ...may be Google Maps itself! • Starting

    with v3.0.0, Maps SDK is now a standalone library • This SDK is still in BETA • Might not work in countries where Google services are blocked
  31. OpenStreetMap SDK • Free, open-source and collaborative map project •

    Many features: routes, geocoding, markers but not as rich as Google Maps • Used by Microsoft, Uber, FB… • Huge dependency in your app
  32. <org.osmdroid.views.MapView android:id="@+id/map" android:layout_width="match_parent" android:layout_height="match_parent" /> binding.map.setTileSource(TileSourceFactory.MAPNIK) binding.map.zoomController.setVisibility(Visibility. SHOW_AND_FADEOUT) binding.map.setMultiTouchControls(true) //

    Enable pinch-to-zoom and set center to our city binding.map.controller.apply { setZoom(10.0) setCenter(GeoPoint(-26.2041028, 28.0473051)) }
  33. HERE map SDK • Map SDK backed by Nokia •

    Many features: vector maps, navigation, 3D buildings • API very similar to Google Maps with good documentation • Huge dependency in your app
  34. <com.here.sdk.mapview.MapView android:id="@+id/map" android:layout_width="match_parent" android:layout_height="match_parent"/> binding.map.setOnReadyListener { binding.map.setCenter(GeoCoordinate(-26.2041028, 28.0473051, 0.0), Map.Animation.NONE)

    binding.map.setZoomLevel(8); val mapImage = MapImageFactory.fromResource(resources, R.drawable.pin) val anchor2D = new Anchor2D(0.5f, 1) val mapMarker = new MapMarker(geoCoordinates, mapImage, anchor2D) binding.map.getMapScene().addMapMarker(mapMarker) }
  35. App distribution

  36. F-Droid or APK Mirror • F-Droid for distributing your free

    and open-source apps to the market • Community maintained since 2010 • APK Mirror for common apps, very similar to the Play Store without all pain points
  37. Mobile Device Management • Build your own solution or use

    existing (AppCenter, TinyMDM…) • Mostly for enterprise apps or B2B • Let see how we can be build our own MDM!
  38. When choosing a store or a MDM? Your app target

    mass maker, or is open-source and you want something similar to the Play Store go for… You target enterprise apps or you want to have the control of the device fleet go for…
  39. MDM Conception SDK Web Service Yes! Have a new version?

    Download and install the new version Database Storage
  40. <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <application> <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.myapp.provider"

    android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/iostore_provider_path" /> </provider> <receiver android:name=".InstallReceiver" /> </application>
  41. val packageInstaller = context.packageManager.packageInstaller val apkUri = uriFromFile(context, file) context.contentResolver.openInputStream(apkUri)?.use

    { src -> val length = DocumentFile.fromSingleUri(context, apkUri)?.length() ?: -1 val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL) val sessionId = packageInstaller.createSession(params) val session = packageInstaller.openSession(sessionId) session.openWrite("app", 0, length).use { dest -> src.copyTo(dest) session.fsync(dest) } val intent = Intent(context, InstallReceiver::class.java) val pendingIntent = PendingIntent.getBroadcast(context, INSTALL_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT) session.commit(pendingIntent.intentSender) session.close() }
  42. val packageInstaller = context.packageManager.packageInstaller val apkUri = uriFromFile(context, file) context.contentResolver.openInputStream(apkUri)?.use

    { src -> val length = DocumentFile.fromSingleUri(context, apkUri)?.length() ?: -1 val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL) val sessionId = packageInstaller.createSession(params) val session = packageInstaller.openSession(sessionId) session.openWrite("app", 0, length).use { dest -> src.copyTo(dest) session.fsync(dest) } val intent = Intent(context, InstallReceiver::class.java) val pendingIntent = PendingIntent.getBroadcast(context, INSTALL_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT) session.commit(pendingIntent.intentSender) session.close() }
  43. val packageInstaller = context.packageManager.packageInstaller val apkUri = uriFromFile(context, file) context.contentResolver.openInputStream(apkUri)?.use

    { src -> val length = DocumentFile.fromSingleUri(context, apkUri)?.length() ?: -1 val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL) val sessionId = packageInstaller.createSession(params) val session = packageInstaller.openSession(sessionId) session.openWrite("app", 0, length).use { dest -> src.copyTo(dest) session.fsync(dest) } val intent = Intent(context, InstallReceiver::class.java) val pendingIntent = PendingIntent.getBroadcast(context, INSTALL_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT) session.commit(pendingIntent.intentSender) session.close() }
  44. WebView

  45. GeckoView • Developed by Firefox • Displaying web content in

    your app or build your own browser • Standalone library, huge dependency! • Standards compliant, excellent support for modern web
  46. val session = GeckoSession().apply { progressDelegate = progressDelegate() settings.useTrackingProtection =

    true } geckoRuntime = GeckoRuntime.create(requireContext()) session.open(geckoRuntime) binding.geckoView.setSession(session) session.loadUri("https://devfestegypt2020.sessionize.com")
  47. Code sample in action!

  48. Sample application • Let’s build our first app without the

    Play Services • Showcasing OpenStreetMap and GeckoView • Spotting the similarities between Google Map and OSM
  49. LIVE DEMO!

  50. Thanks ! Have fun with Android! Any questions? @JulienSalvi