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

BBM Call Out 101

KMKLabs
December 12, 2018

BBM Call Out 101

Pengenalan fitur callOut di BBM.

KMKLabs

December 12, 2018
Tweet

More Decks by KMKLabs

Other Decks in Programming

Transcript

  1. We Have BBM Call Already Why We Need Call Out

    ? https://giphy.com/gifs/reaction-a5viI92PAF89q
  2. Why Call Out is Made • Needs to contact family/relatives

    in areas with internet connectivity issue • Phone call is still main way of people to communicate • Demand for competitive rate of calls
  3. What is BBM Call Out • A service to provide

    BBM account a virtual phone number (VN) • A virtual phone number can be used for voice and SMS, both in and out • Business Cooperation with Smartfren as Telco Provider
  4. Requirement To Get VN • Accept ToC • KTP &

    KK - Indonesian Resident • VN ready to use • Voice/SMS In is free of charge • Voice/SMS Out is chargeable (DANA)
  5. VN can Expire • VN will expire if not used

    to make chargeable call within 90 days • To prevent expiry, make outgoing call or receive incoming call from non Smartfren number • Once expired, we can not get same VN if re-register
  6. Tech? Challenge • Platform Team: • Leverage MediaCallManager • Instead

    of user uri, use phone number as ID • Backend & Platform Team • Working with 3rd party require effective communication and patience
  7. Tech? Challenge • Android Team: • Smartfren require user to

    give exact location when user access Call Out • Team does not want to reinvent the wheels • Leverage BBMLocationManager
  8. Tech? Challenge • Instead of using BBMLocationManager, we decide to

    use
 Android’s Fused Location Provider API 
 (part of Google Play Services) • Simple API and battery-efficient • Manages underlying location technologies 
 (GPS and Wi-Fi) • Best accuracy possible with no additional power consumption • Provide Last Known Location and Query Location Updates
  9. FusedLocationProviderClient <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> // Play Location

    Services implementation "com.google.android.gms:play-services-location:16.0.0" implementation "com.android.support:support-v4:$support_version" app/build.gradle app/../AndroidManifest.xml
  10. FusedLocationProviderClient <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> // Play Location

    Services implementation "com.google.android.gms:play-services-location:16.0.0" implementation "com.android.support:support-v4:$support_version" app/build.gradle app/../AndroidManifest.xml
  11. Implementation typealias LocationData = Pair<Double, Double> interface FusedLocationWrapper { fun

    lastKnownLocation(): Single<LocationData> fun locationUpdates(): Observable<LocationData> }
  12. Last (Known) Location class FusedLocationWrapperImpl( private val client: FusedLocationProviderClient )

    : FusedLocationWrapper { @SuppressLint("MissingPermission") @MainThread override fun lastKnownLocation(): Single<LocationData> = Single.create<LocationData> { emitter -> client.lastLocation .addOnSuccessListener { location -> location?.let { emitter.onSuccess(it.latitude to it.longitude) } } .addOnFailureListener { emitter.onError(it) } }.subscribeOn(AndroidSchedulers.mainThread())
  13. Last (Known) Location • Last Location usually equivalent with user

    current location • Last Location may return null value when • Location (GPS) is turned off 
 (will clear last location cache if already retrieved) • Device never record location 
 (new device or factory resetted) • Google Play Services restarted on device
  14. Location Updates class FusedLocationWrapperImpl( private val client: FusedLocationProviderClient ) :

    FusedLocationWrapper { . . . override fun lastKnownLocation(): Single<LocationData> = . . . @SuppressLint("MissingPermission") @MainThread override fun locationUpdates(): Observable<LocationData> = Observable.create<LocationData> { emitter -> val request = LocationRequest().apply { interval = 1_000 fastestInterval = 500 priority = LocationRequest.PRIORITY_HIGH_ACCURACY } val callback = object : LocationCallback() { override fun onLocationResult(location: LocationResult?) { location?.lastLocation?.let { emitter.onNext(it.latitude to it.longitude) } } } client.requestLocationUpdates(request, callback, Looper.myLooper()) emitter.setCancellable { client.removeLocationUpdates(callback) } }.subscribeOn(AndroidSchedulers.mainThread())
  15. Location Updates class FusedLocationWrapperImpl( private val client: FusedLocationProviderClient ) :

    FusedLocationWrapper { . . . override fun lastKnownLocation(): Single<LocationData> = . . . @SuppressLint("MissingPermission") @MainThread override fun locationUpdates(): Observable<LocationData> = Observable.create<LocationData> { emitter -> val request = LocationRequest().apply { interval = 1_000 fastestInterval = 500 priority = LocationRequest.PRIORITY_HIGH_ACCURACY } val callback = object : LocationCallback() { override fun onLocationResult(location: LocationResult?) { location?.lastLocation?.let { emitter.onNext(it.latitude to it.longitude) } } } client.requestLocationUpdates(request, callback, Looper.myLooper()) emitter.setCancellable { client.removeLocationUpdates(callback) } }.subscribeOn(AndroidSchedulers.mainThread())
  16. Location Request Priority • PRIORITY_BALANCED_POWER_ACCURACY • City block accuracy of

    approx 100m, • Coarse & consume less power • Likely use Wi-Fi and cell tower positioning
  17. Location Updates class FusedLocationWrapperImpl( private val client: FusedLocationProviderClient ) :

    FusedLocationWrapper { . . . override fun lastKnownLocation(): Single<LocationData> = . . . @SuppressLint("MissingPermission") @MainThread override fun locationUpdates(): Observable<LocationData> = Observable.create<LocationData> { emitter -> val request = LocationRequest().apply { interval = 1_000 fastestInterval = 500 priority = LocationRequest.PRIORITY_HIGH_ACCURACY } val callback = object : LocationCallback() { override fun onLocationResult(location: LocationResult?) { location?.lastLocation?.let { emitter.onNext(it.latitude to it.longitude) } } } client.requestLocationUpdates(request, callback, Looper.myLooper()) emitter.setCancellable { client.removeLocationUpdates(callback) } }.subscribeOn(AndroidSchedulers.mainThread())
  18. Location Updates class FusedLocationWrapperImpl( private val client: FusedLocationProviderClient ) :

    FusedLocationWrapper { . . . override fun lastKnownLocation(): Single<LocationData> = . . . @SuppressLint("MissingPermission") @MainThread override fun locationUpdates(): Observable<LocationData> = Observable.create<LocationData> { emitter -> val request = LocationRequest().apply { interval = 1_000 fastestInterval = 500 priority = LocationRequest.PRIORITY_HIGH_ACCURACY } val callback = object : LocationCallback() { override fun onLocationResult(location: LocationResult?) { location?.lastLocation?.let { emitter.onNext(it.latitude to it.longitude) } } } client.requestLocationUpdates(request, callback, Looper.myLooper()) emitter.setCancellable { client.removeLocationUpdates(callback) } }.subscribeOn(AndroidSchedulers.mainThread())
  19. Location Updates class FusedLocationWrapperImpl( private val client: FusedLocationProviderClient ) :

    FusedLocationWrapper { . . . override fun lastKnownLocation(): Single<LocationData> = . . . @SuppressLint("MissingPermission") @MainThread override fun locationUpdates(): Observable<LocationData> = Observable.create<LocationData> { emitter -> val request = LocationRequest().apply { interval = 1_000 fastestInterval = 500 priority = LocationRequest.PRIORITY_HIGH_ACCURACY } val callback = object : LocationCallback() { override fun onLocationResult(location: LocationResult?) { location?.lastLocation?.let { emitter.onNext(it.latitude to it.longitude) } } } client.requestLocationUpdates(request, callback, Looper.myLooper()) emitter.setCancellable { client.removeLocationUpdates(callback) } }.subscribeOn(AndroidSchedulers.mainThread())
  20. Location Updates class FusedLocationWrapperImpl( private val client: FusedLocationProviderClient ) :

    FusedLocationWrapper { . . . override fun lastKnownLocation(): Single<LocationData> = . . . @SuppressLint("MissingPermission") @MainThread override fun locationUpdates(): Observable<LocationData> = Observable.create<LocationData> { emitter -> val request = LocationRequest().apply { interval = 1_000 fastestInterval = 500 priority = LocationRequest.PRIORITY_HIGH_ACCURACY } val callback = object : LocationCallback() { override fun onLocationResult(location: LocationResult?) { location?.lastLocation?.let { emitter.onNext(it.latitude to it.longitude) } } } client.requestLocationUpdates(request, callback, Looper.myLooper()) emitter.setCancellable { client.removeLocationUpdates(callback) } }.subscribeOn(AndroidSchedulers.mainThread())
  21. Location Updates class FusedLocationWrapperImpl( private val client: FusedLocationProviderClient ) :

    FusedLocationWrapper { . . . override fun lastKnownLocation(): Single<LocationData> = . . . @SuppressLint("MissingPermission") @MainThread override fun locationUpdates(): Observable<LocationData> = Observable.create<LocationData> { emitter -> val request = LocationRequest().apply { interval = 1_000 fastestInterval = 500 priority = LocationRequest.PRIORITY_HIGH_ACCURACY } val callback = object : LocationCallback() { override fun onLocationResult(location: LocationResult?) { location?.lastLocation?.let { emitter.onNext(it.latitude to it.longitude) } } } client.requestLocationUpdates(request, callback, Looper.myLooper()) emitter.setCancellable { client.removeLocationUpdates(callback) } }.subscribeOn(AndroidSchedulers.mainThread())
  22. How to use interface LocationGateway { fun currentLocation(): Single<LocationData> }

    class LocationGatewayImpl( private val fusedLocation: FusedLocationWrapper ) : LocationGateway { override fun currentLocation(): Single<LocationData> = Single.merge( fusedLocation.lastKnownLocation(), fusedLocation.locationUpdates().firstOrError() ).firstOrError() }
  23. How to use interface LocationGateway { fun currentLocation(): Single<LocationData> }

    class LocationGatewayImpl( private val fusedLocation: FusedLocationWrapper ) : LocationGateway { override fun currentLocation(): Single<LocationData> = Single.merge( fusedLocation.lastKnownLocation(), fusedLocation.locationUpdates().firstOrError() ).firstOrError() }