package com.kickstarter.libs;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.support.v4.content.ContextCompat;
import android.util.Pair;
import com.kickstarter.R;
import com.kickstarter.libs.qualifiers.ApplicationContext;
import com.kickstarter.libs.rx.transformers.Transformers;
import com.kickstarter.libs.transformations.CircleTransformation;
import com.kickstarter.libs.transformations.CropSquareTransformation;
import com.kickstarter.libs.utils.ObjectUtils;
import com.kickstarter.models.Update;
import com.kickstarter.models.pushdata.Activity;
import com.kickstarter.models.pushdata.GCM;
import com.kickstarter.services.ApiClientType;
import com.kickstarter.services.apiresponses.PushNotificationEnvelope;
import com.kickstarter.ui.IntentKey;
import com.kickstarter.ui.activities.ProjectActivity;
import com.kickstarter.ui.activities.WebViewActivity;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.RequestCreator;
import java.io.IOException;
import rx.Observable;
import rx.schedulers.Schedulers;
import rx.subjects.PublishSubject;
import rx.subscriptions.CompositeSubscription;
import timber.log.Timber;
public final class PushNotifications {
private final @ApplicationContext Context context;
private final ApiClientType client;
private final DeviceRegistrarType deviceRegistrar;
private final PublishSubject<PushNotificationEnvelope> notifications = PublishSubject.create();
private final CompositeSubscription subscriptions = new CompositeSubscription();
public PushNotifications(final @ApplicationContext @NonNull Context context, final @NonNull ApiClientType client,
final @NonNull DeviceRegistrarType deviceRegistrar) {
this.context = context;
this.client = client;
this.deviceRegistrar = deviceRegistrar;
}
public void initialize() {
subscriptions.add(notifications
.onBackpressureBuffer()
.filter(PushNotificationEnvelope::isFriendFollow)
.observeOn(Schedulers.newThread())
.subscribe(this::displayNotificationFromFriendFollowActivity));
subscriptions.add(notifications
.onBackpressureBuffer()
.filter(PushNotificationEnvelope::isProjectActivity)
.observeOn(Schedulers.newThread())
.subscribe(this::displayNotificationFromProjectActivity));
subscriptions.add(notifications
.onBackpressureBuffer()
.filter(PushNotificationEnvelope::isProjectReminder)
.observeOn(Schedulers.newThread())
.subscribe(this::displayNotificationFromProjectReminder));
subscriptions.add(notifications
.onBackpressureBuffer()
.filter(PushNotificationEnvelope::isProjectUpdateActivity)
.flatMap(this::fetchUpdateWithEnvelope)
.filter(ObjectUtils::isNotNull)
.observeOn(Schedulers.newThread())
.subscribe(envelopeAndUpdate -> displayNotificationFromUpdateActivity(envelopeAndUpdate.first, envelopeAndUpdate.second)));
deviceRegistrar.registerDevice();
}
public void add(final @NonNull PushNotificationEnvelope envelope) {
notifications.onNext(envelope);
}
private void displayNotificationFromFriendFollowActivity(final @NonNull PushNotificationEnvelope envelope) {
final GCM gcm = envelope.gcm();
final Activity activity = envelope.activity();
if (activity == null) {
return;
}
final Notification notification = notificationBuilder(gcm.title(), gcm.alert())
.setLargeIcon(fetchBitmap(activity.userPhoto(), true))
.build();
notificationManager().notify(envelope.signature(), notification);
}
private void displayNotificationFromProjectActivity(final @NonNull PushNotificationEnvelope envelope) {
final GCM gcm = envelope.gcm();
final Activity activity = envelope.activity();
if (activity == null) {
return;
}
final Long projectId = activity.projectId();
if (projectId == null) {
return;
}
final String projectPhoto = activity.projectPhoto();
final String projectParam = ObjectUtils.toString(projectId);
NotificationCompat.Builder notificationBuilder = notificationBuilder(gcm.title(), gcm.alert())
.setContentIntent(projectContentIntent(envelope, projectParam));
if (projectPhoto != null) {
notificationBuilder = notificationBuilder.setLargeIcon(fetchBitmap(projectPhoto, false));
}
final Notification notification = notificationBuilder.build();
notificationManager().notify(envelope.signature(), notification);
}
private void displayNotificationFromProjectReminder(final @NonNull PushNotificationEnvelope envelope) {
final GCM gcm = envelope.gcm();
final PushNotificationEnvelope.Project project = envelope.project();
if (project == null) {
return;
}
final Notification notification = notificationBuilder(gcm.title(), gcm.alert())
.setContentIntent(projectContentIntent(envelope, ObjectUtils.toString(project.id())))
.setLargeIcon(fetchBitmap(project.photo(), false))
.build();
notificationManager().notify(envelope.signature(), notification);
}
private void displayNotificationFromUpdateActivity(final @NonNull PushNotificationEnvelope envelope, final @NonNull Update update) {
final GCM gcm = envelope.gcm();
final Activity activity = envelope.activity();
if (activity == null) {
return;
}
final Long updateId = activity.updateId();
if (updateId == null) {
return;
}
final Long projectId = activity.projectId();
if (projectId == null) {
return;
}
final String projectParam = ObjectUtils.toString(projectId);
final Notification notification = notificationBuilder(gcm.title(), gcm.alert())
.setContentIntent(projectUpdateContentIntent(envelope, update, projectParam))
.setLargeIcon(fetchBitmap(activity.projectPhoto(), false))
.build();
notificationManager().notify(envelope.signature(), notification);
}
private @NonNull NotificationCompat.Builder notificationBuilder(final @NonNull String title, final @NonNull String text) {
return new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_kickstarter_k)
.setColor(ContextCompat.getColor(context, R.color.green))
.setContentText(text)
.setContentTitle(title)
.setStyle(new NotificationCompat.BigTextStyle().bigText(text))
.setAutoCancel(true);
}
private @NonNull PendingIntent projectContentIntent(final @NonNull PushNotificationEnvelope envelope, final @NonNull String projectParam) {
final Intent projectIntent = new Intent(context, ProjectActivity.class)
.putExtra(IntentKey.PROJECT_PARAM, projectParam)
.putExtra(IntentKey.PUSH_NOTIFICATION_ENVELOPE, envelope);
final TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(projectIntent);
return taskStackBuilder.getPendingIntent(envelope.signature(), PendingIntent.FLAG_UPDATE_CURRENT);
}
private @NonNull PendingIntent projectUpdateContentIntent(final @NonNull PushNotificationEnvelope envelope, final @NonNull Update update, final @NonNull String projectParam) {
final Intent projectIntent = new Intent(context, ProjectActivity.class)
.putExtra(IntentKey.PROJECT_PARAM, projectParam);
final Intent updateIntent = new Intent(context, WebViewActivity.class)
.putExtra(IntentKey.URL, update.urls().web().update())
.putExtra(IntentKey.PUSH_NOTIFICATION_ENVELOPE, envelope);
final TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(projectIntent)
.addNextIntent(updateIntent);
return taskStackBuilder.getPendingIntent(envelope.signature(), PendingIntent.FLAG_UPDATE_CURRENT);
}
private @Nullable Bitmap fetchBitmap(final @Nullable String url, final boolean transformIntoCircle) {
if (url == null) {
return null;
}
try {
RequestCreator requestCreator = Picasso.with(context).load(url).transform(new CropSquareTransformation());
if (transformIntoCircle) {
requestCreator = requestCreator.transform(new CircleTransformation());
}
return requestCreator.get();
} catch (IOException e) {
Timber.e("Failed to load large icon: %s", e);
return null;
}
}
private @NonNull NotificationManager notificationManager() {
return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
private @Nullable Observable<Pair<PushNotificationEnvelope, Update>> fetchUpdateWithEnvelope(final @NonNull PushNotificationEnvelope envelope) {
final Activity activity = envelope.activity();
if (activity == null) {
return null;
}
final Long updateId = activity.updateId();
if (updateId == null) {
return null;
}
final Long projectId = activity.projectId();
if (projectId == null) {
return null;
}
final String projectParam = ObjectUtils.toString(projectId);
final String updateParam = ObjectUtils.toString(updateId);
final Observable<Update> update = client.fetchUpdate(projectParam, updateParam)
.compose(Transformers.neverError());
return Observable.just(envelope)
.compose(Transformers.combineLatestPair(update));
}
}