package co.smartreceipts.android.push; import android.content.Context; import android.support.annotation.NonNull; import android.support.annotation.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.firebase.messaging.RemoteMessage; import java.util.Collections; import java.util.concurrent.CopyOnWriteArrayList; import javax.inject.Inject; import co.smartreceipts.android.analytics.Analytics; import co.smartreceipts.android.analytics.events.ErrorEvent; import co.smartreceipts.android.analytics.events.Event; import co.smartreceipts.android.analytics.events.Events; import co.smartreceipts.android.di.scopes.ApplicationScope; import co.smartreceipts.android.identity.IdentityManager; import co.smartreceipts.android.push.apis.me.UpdatePushTokensRequest; import co.smartreceipts.android.push.apis.me.UpdateUserPushTokens; import co.smartreceipts.android.push.internal.FcmTokenRetriever; import co.smartreceipts.android.push.store.PushDataStore; import co.smartreceipts.android.utils.log.Logger; import io.reactivex.Scheduler; import io.reactivex.schedulers.Schedulers; @ApplicationScope public class PushManager { private final IdentityManager identityManager; private final Analytics analytics; private final FcmTokenRetriever fcmTokenRetriever; private final PushDataStore pushDataStore; private final Scheduler subscribeOnScheduler; private final CopyOnWriteArrayList<PushMessageReceiver> pushMessageReceivers = new CopyOnWriteArrayList<>(); @Inject public PushManager(@NonNull Context context, @NonNull IdentityManager identityManager, @NonNull Analytics analytics) { this(identityManager, analytics, new FcmTokenRetriever(), new PushDataStore(context), Schedulers.io()); } @VisibleForTesting public PushManager(@NonNull IdentityManager identityManager, @NonNull Analytics analytics, @NonNull FcmTokenRetriever fcmTokenRetriever, @NonNull PushDataStore pushDataStore, @NonNull Scheduler subscribeOnScheduler) { this.identityManager = Preconditions.checkNotNull(identityManager); this.analytics = Preconditions.checkNotNull(analytics); this.fcmTokenRetriever = Preconditions.checkNotNull(fcmTokenRetriever); this.pushDataStore = Preconditions.checkNotNull(pushDataStore); this.subscribeOnScheduler = Preconditions.checkNotNull(subscribeOnScheduler); } public void initialize() { identityManager.isLoggedInStream() .subscribeOn(subscribeOnScheduler) .flatMapSingle(isLoggedIn -> pushDataStore.isRemoteRefreshRequiredSingle() .map(isRefreshRequired -> isRefreshRequired && isLoggedIn)) .filter(shouldPushTokenBeUploaded -> { Logger.debug(PushManager.this, "Is a push token update required? {}.", shouldPushTokenBeUploaded); return shouldPushTokenBeUploaded; }) .doOnNext(ignore -> analytics.record(Events.Identity.PushTokenUploadRequired)) .flatMap(ignore -> fcmTokenRetriever.getFcmTokenObservable()) .flatMap(token -> { final UpdatePushTokensRequest request = new UpdatePushTokensRequest(new UpdateUserPushTokens(Collections.singletonList(Preconditions.checkNotNull(token)))); return identityManager.updateMe(request); }) .subscribe(meResponse -> { Logger.info(PushManager.this, "Successfully uploaded our push notification token"); pushDataStore.setRemoteRefreshRequired(false); analytics.record(Events.Identity.PushTokenSucceeded); }, throwable -> { analytics.record(Events.Identity.PushTokenFailed); analytics.record(new ErrorEvent(PushManager.this, throwable)); Logger.error(PushManager.this, "Failed to upload our push notification token", throwable); }); } public void registerReceiver(@NonNull PushMessageReceiver receiver) { pushMessageReceivers.add(Preconditions.checkNotNull(receiver)); } public void unregisterReceiver(@NonNull PushMessageReceiver receiver) { pushMessageReceivers.remove(Preconditions.checkNotNull(receiver)); } public void onTokenRefresh() { pushDataStore.setRemoteRefreshRequired(true); initialize(); } public void onMessageReceived(@NonNull RemoteMessage remoteMessage) { for (final PushMessageReceiver pushMessageReceiver : pushMessageReceivers) { pushMessageReceiver.onMessageReceived(remoteMessage); } } }