diff --git a/.gitignore b/.gitignore index 6e6e04e44..80f87efef 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,4 @@ fabric.properties Habitica/res/values/secret_strings.xml habitica.properties habitica.resources +Habitica/google-services.json diff --git a/.travis.yml b/.travis.yml index 15b9c450c..52df7c594 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,5 +41,6 @@ android: script: - cp habitica.properties.travis habitica.properties - cp habitica.resources.example habitica.resources + - cp Habitica/google-services.json.example Habitica/google-services.json - ./gradlew assembleDebug - ./gradlew testDebugUnitTest --info diff --git a/Habitica/AndroidManifest.xml b/Habitica/AndroidManifest.xml index 1d5bea92c..acf754425 100644 --- a/Habitica/AndroidManifest.xml +++ b/Habitica/AndroidManifest.xml @@ -122,12 +122,35 @@ + + + + + + + + + + + + + + + + + + + + + Username E-mail https://habitica.com + + + ACCEPT_PARTY_INVITE + REJECT_PARTY_INVITE + ACCEPT_GUILD_INVITE + REJECT_GUILD_INVITE + ACCEPT_QUEST_INVITE + REJECT_QUEST_INVITE \ No newline at end of file diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml index 876d3f9da..0aa13106f 100644 --- a/Habitica/res/values/strings.xml +++ b/Habitica/res/values/strings.xml @@ -22,6 +22,17 @@ Activate Reminder Set Reminder Time + User Push Notifications + Push Notifications + You won a Challenge! + Received a Private Message + Gifted Gems + Gifted Subscription + Invited to Party + Invited to Guiild + Your Quest has Begun + Invited to Quest + Value New todo diff --git a/Habitica/res/xml/preferences_fragment.xml b/Habitica/res/xml/preferences_fragment.xml index e88c4312f..46d5b2e1b 100644 --- a/Habitica/res/xml/preferences_fragment.xml +++ b/Habitica/res/xml/preferences_fragment.xml @@ -75,4 +75,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/HabitDatabase.java b/Habitica/src/main/java/com/habitrpg/android/habitica/HabitDatabase.java index c0c6af461..f4d1cc3d4 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/HabitDatabase.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/HabitDatabase.java @@ -7,5 +7,5 @@ public class HabitDatabase { public static final String NAME = "Habitica"; - public static final int VERSION = 23; + public static final int VERSION = 24; } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java b/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java index 1f4a1450e..988db627a 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java @@ -1,8 +1,10 @@ package com.habitrpg.android.habitica.components; import com.habitrpg.android.habitica.HabiticaApplication; +import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManager; import com.habitrpg.android.habitica.modules.ApiModule; import com.habitrpg.android.habitica.modules.AppModule; +import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver; import com.habitrpg.android.habitica.ui.activities.AboutActivity; import com.habitrpg.android.habitica.ui.activities.ClassSelectionActivity; import com.habitrpg.android.habitica.ui.activities.GroupFormActivity; @@ -156,4 +158,8 @@ public interface AppComponent { void inject(ShopsFragment shopsFragment); void inject(ShopFragment shopFragment); + + void inject(PushNotificationManager pushNotificationManager); + + void inject(LocalNotificationActionReceiver localNotificationActionReceiver); } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/GuildInviteLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/GuildInviteLocalNotification.java new file mode 100644 index 000000000..a728ac0cd --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/GuildInviteLocalNotification.java @@ -0,0 +1,56 @@ +package com.habitrpg.android.habitica.helpers.notifications; + +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.NotificationCompat; + +import com.habitrpg.android.habitica.R; +import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver; + +import java.util.Map; + +/** + * Created by keithholliday on 7/1/16. + */ +public class GuildInviteLocalNotification extends HabiticaLocalNotification { + + @Override + public void notifyLocally(Context context, String title, String message) { + super.notifyLocally(context, title, message); + this.setNotificationActions(); + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.notify(10, notificationBuilder.build()); + } + + protected void setNotificationActions() { + Resources res = context.getResources(); + + Intent acceptInviteIntent = new Intent(context, LocalNotificationActionReceiver.class); + acceptInviteIntent.setAction(res.getString(R.string.accept_guild_invite)); + acceptInviteIntent.putExtra("groupID", this.data.get("groupID")); + PendingIntent pendingIntentAccept = PendingIntent.getBroadcast( + context, + 3000, + acceptInviteIntent, + PendingIntent.FLAG_UPDATE_CURRENT + ); + notificationBuilder.addAction(0, "Accept", pendingIntentAccept); + + Intent rejectInviteIntent = new Intent(context, LocalNotificationActionReceiver.class); + rejectInviteIntent.setAction(res.getString(R.string.reject_guild_invite)); + rejectInviteIntent.putExtra("groupID", this.data.get("groupID")); + PendingIntent pendingIntentReject = PendingIntent.getBroadcast( + context, + 2000, + rejectInviteIntent, + PendingIntent.FLAG_UPDATE_CURRENT + ); + notificationBuilder.addAction(0, "Reject", pendingIntentReject); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseInstanceIDService.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseInstanceIDService.java new file mode 100644 index 000000000..c9504930a --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseInstanceIDService.java @@ -0,0 +1,28 @@ +package com.habitrpg.android.habitica.helpers.notifications; + +import android.util.Log; + +import com.google.firebase.iid.FirebaseInstanceId; +import com.google.firebase.iid.FirebaseInstanceIdService; +import com.habitrpg.android.habitica.APIHelper; + +import java.util.HashMap; +import java.util.Map; + +import javax.inject.Inject; + +/** + * Created by keithholliday on 6/24/16. + */ +public class HabiticaFirebaseInstanceIDService extends FirebaseInstanceIdService { + + public PushNotificationManager pushNotificationManager; + + @Override + public void onTokenRefresh() { + pushNotificationManager = PushNotificationManager.getInstance(this); + String refreshedToken = FirebaseInstanceId.getInstance().getToken(); + pushNotificationManager.setRefreshedToken(refreshedToken); + } + +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.java new file mode 100644 index 000000000..a4c66251c --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.java @@ -0,0 +1,17 @@ +package com.habitrpg.android.habitica.helpers.notifications; + +import android.util.Log; + +import com.google.firebase.messaging.FirebaseMessagingService; +import com.google.firebase.messaging.RemoteMessage; + +/** + * Created by keithholliday on 6/24/16. + */ +public class HabiticaFirebaseMessagingService extends FirebaseMessagingService { + @Override + public void onMessageReceived(RemoteMessage remoteMessage) { + PushNotificationManager pushNotificationManager = PushNotificationManager.getInstance(this); + pushNotificationManager.displayNotification(remoteMessage); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaLocalNotification.java new file mode 100644 index 000000000..bad509da4 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaLocalNotification.java @@ -0,0 +1,47 @@ +package com.habitrpg.android.habitica.helpers.notifications; + +import android.content.Context; +import android.media.RingtoneManager; +import android.net.Uri; +import android.support.annotation.CallSuper; +import android.support.v4.app.NotificationCompat; + +import com.habitrpg.android.habitica.R; + +import java.util.Map; + +/** + * Created by keithholliday on 6/28/16. + */ +public abstract class HabiticaLocalNotification { + + protected Map data; + protected Context context; + protected String title; + protected String message; + + protected NotificationCompat.Builder notificationBuilder; + + @CallSuper + public void notifyLocally(Context context, String title, String message) { + this.context = context; + this.title = title; + this.message = message; + + Uri path = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); + + this.notificationBuilder = + new NotificationCompat.Builder(context) + .setSmallIcon(R.drawable.ic_gryphon) + .setContentTitle(title) + .setContentText(message) + .setAutoCancel(true) + .setSound(path); + } + + public void setExtras(Map data) { + this.data = data; + } + + protected abstract void setNotificationActions(); +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaLocalNotificationFactory.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaLocalNotificationFactory.java new file mode 100644 index 000000000..3abb8db9d --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaLocalNotificationFactory.java @@ -0,0 +1,35 @@ +package com.habitrpg.android.habitica.helpers.notifications; + +/** + * Created by keithholliday on 6/28/16. + */ +public class HabiticaLocalNotificationFactory { + + //use getShape method to get object of type shape + public HabiticaLocalNotification build(String notificationType){ + if(notificationType == null){ + return null; + } + + if (notificationType.equalsIgnoreCase(PushNotificationManager.PARTY_INVITE_PUSH_NOTIFICATION_KEY)) { + return new PartyInviteLocalNotification(); + } else if (notificationType.contains(PushNotificationManager.RECEIVED_PRIVATE_MESSAGE_PUSH_NOTIFICATION_KEY)) { + return new ReceivedPrivateMessageLocalNotification(); + } else if (notificationType.contains(PushNotificationManager.RECEIVED_GEMS_PUSH_NOTIFICATION_KEY)) { + return new ReceivedGemsGiftLocalNotification(); + } else if (notificationType.contains(PushNotificationManager.RECEIVED_SUBSCRIPTION_GIFT_PUSH_NOTIFICATION_KEY)) { + return new ReceivedSubscriptionGiftLocalNotification(); + } else if (notificationType.contains(PushNotificationManager.GUILD_INVITE_PUSH_NOTIFICATION_KEY)) { + return new GuildInviteLocalNotification(); + } else if (notificationType.contains(PushNotificationManager.QUEST_INVITE_PUSH_NOTIFICATION_KEY)) { + return new QuestInviteLocalNotification(); + } else if (notificationType.contains(PushNotificationManager.QUEST_BEGUN_PUSH_NOTIFICATION_KEY)) { + return new QuestBegunLocalNotification(); + } else if (notificationType.contains(PushNotificationManager.WON_CHALLENGE_PUSH_NOTIFICATION_KEY)) { + return new WonChallengeLocalNotification(); + } + + return null; + } + +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/PartyInviteLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/PartyInviteLocalNotification.java new file mode 100644 index 000000000..3b1e2d5e3 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/PartyInviteLocalNotification.java @@ -0,0 +1,56 @@ +package com.habitrpg.android.habitica.helpers.notifications; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.media.RingtoneManager; +import android.net.Uri; + +import android.support.v4.app.NotificationCompat; +import android.util.Log; + +import com.habitrpg.android.habitica.R; +import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver; +import com.habitrpg.android.habitica.ui.activities.MainActivity; + +import java.util.Map; + +/** + * Created by keithholliday on 6/28/16. + */ +public class PartyInviteLocalNotification extends HabiticaLocalNotification { + + public void notifyLocally(Context context, String title, String message) { + super.notifyLocally(context, title, message); + this.setNotificationActions(); + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.notify(10, notificationBuilder.build()); + } + + protected void setNotificationActions() { + Resources res = context.getResources(); + + Intent acceptInviteIntent = new Intent(context, LocalNotificationActionReceiver.class); + acceptInviteIntent.setAction(res.getString(R.string.accept_party_invite)); + PendingIntent pendingIntentAccept = PendingIntent.getBroadcast( + context, + 3000, + acceptInviteIntent, + PendingIntent.FLAG_UPDATE_CURRENT + ); + notificationBuilder.addAction(0, "Accept", pendingIntentAccept); + + Intent rejectInviteIntent = new Intent(context, LocalNotificationActionReceiver.class); + rejectInviteIntent.setAction(res.getString(R.string.reject_party_invite)); + PendingIntent pendingIntentReject = PendingIntent.getBroadcast( + context, + 2000, + rejectInviteIntent, + PendingIntent.FLAG_UPDATE_CURRENT + ); + notificationBuilder.addAction(0, "Reject", pendingIntentReject); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/PushNotificationManager.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/PushNotificationManager.java new file mode 100644 index 000000000..8b0ec84a0 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/PushNotificationManager.java @@ -0,0 +1,159 @@ +package com.habitrpg.android.habitica.helpers.notifications; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.util.Log; + +import com.google.firebase.iid.FirebaseInstanceId; +import com.google.firebase.messaging.RemoteMessage; +import com.habitrpg.android.habitica.APIHelper; +import com.habitrpg.android.habitica.HabiticaApplication; +import com.habitrpg.android.habitica.callbacks.HabitRPGUserCallback; +import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser; +import com.magicmicky.habitrpgwrapper.lib.models.PushDevice; + +import java.util.HashMap; +import java.util.Map; + +import javax.inject.Inject; + +/** + * Created by keithholliday on 6/27/16. + */ +public class PushNotificationManager { + + private static PushNotificationManager instance = null; + public static String DEVICE_TOKEN_PREFERENCE_KEY = "device-token-preference"; + + public static String PARTY_INVITE_PUSH_NOTIFICATION_KEY = "invitedParty"; + public static String RECEIVED_PRIVATE_MESSAGE_PUSH_NOTIFICATION_KEY = "newPM"; + public static String RECEIVED_GEMS_PUSH_NOTIFICATION_KEY = "giftedGems"; + public static String RECEIVED_SUBSCRIPTION_GIFT_PUSH_NOTIFICATION_KEY = "giftedSubscription"; + public static String GUILD_INVITE_PUSH_NOTIFICATION_KEY = "invitedGuild"; + public static String QUEST_INVITE_PUSH_NOTIFICATION_KEY = "questInvitation"; + public static String QUEST_BEGUN_PUSH_NOTIFICATION_KEY = "questStarted"; + public static String WON_CHALLENGE_PUSH_NOTIFICATION_KEY = "wonChallenge"; + + + @Inject + public APIHelper apiHelper; + + private String refreshedToken; + private SharedPreferences sharedPreferences; + private Context context; + private HabitRPGUser user; + + protected PushNotificationManager(Context context) { + HabiticaApplication.getInstance(context).getComponent().inject(this); + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + } + + public void setUser(HabitRPGUser user) { + this.user = user; + } + + public static PushNotificationManager getInstance(Context context) { + if(instance == null) { + instance = new PushNotificationManager(context); + } + + instance.refreshedToken = instance.sharedPreferences.getString(DEVICE_TOKEN_PREFERENCE_KEY, ""); + instance.context = context; + + return instance; + } + + public void setRefreshedToken (String refreshedToken) { + if (this.refreshedToken == null) { + return; + } + + this.refreshedToken = refreshedToken; + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString(DEVICE_TOKEN_PREFERENCE_KEY, refreshedToken); + editor.commit(); + } + + //@TODO: Use preferences + public void addPushDeviceUsingStoredToken () { + if (this.refreshedToken == null || this.refreshedToken.isEmpty()) { + this.refreshedToken = FirebaseInstanceId.getInstance().getToken(); + } + + if (this.refreshedToken == null || this.refreshedToken.isEmpty()) { + return; + } + + if (this.user == null || this.userHasPushDevice()) { + return; + } + + if (!this.userIsSubscribedToNotifications()) { + return; + } + + Map pushDeviceData = new HashMap(); + pushDeviceData.put("regId", this.refreshedToken); + pushDeviceData.put("type", "android"); + apiHelper.apiService.addPushDevice(pushDeviceData) + .compose(apiHelper.configureApiCallObserver()) + .subscribe(aVoid -> {}, throwable -> {}); + } + + public void removePushDeviceUsingStoredToken () { + apiHelper.apiService.deletePushDevice(this.refreshedToken) + .compose(apiHelper.configureApiCallObserver()) + .subscribe(aVoid -> {}, throwable -> {}); + } + + private Boolean userHasPushDevice() { + for(PushDevice pushDevice : this.user.getPushDevices()) { + if(pushDevice.getRegId().equals(this.refreshedToken)) { + return true; + } + } + return false; + } + + public void displayNotification (RemoteMessage remoteMessage) { + String remoteMessageIdentifier = remoteMessage.getData().get("identifier"); + + HabiticaLocalNotificationFactory notificationFactory = new HabiticaLocalNotificationFactory(); + HabiticaLocalNotification notification = notificationFactory.build(remoteMessageIdentifier); + if (userIsSubscribedToNotificationType(remoteMessageIdentifier) && notification != null) { + notification.setExtras(remoteMessage.getData()); + notification.notifyLocally(this.context, remoteMessage.getData().get("title"), remoteMessage.getData().get("body")); + } + } + + private boolean userIsSubscribedToNotifications() { + return sharedPreferences.getBoolean("pushNotifications", true); + } + + private boolean userIsSubscribedToNotificationType(String type) { + String key = ""; + + //@TODO: If user has push turned off to send + + if (type.equals(PARTY_INVITE_PUSH_NOTIFICATION_KEY)) { + key = "preference_push_invited_to_party"; + } else if (type.contains(RECEIVED_PRIVATE_MESSAGE_PUSH_NOTIFICATION_KEY)) { + key = "preference_push_received_a_private_message"; + } else if (type.contains(RECEIVED_GEMS_PUSH_NOTIFICATION_KEY)) { + key = "preference_push_gifted_gems"; + } else if (type.contains(RECEIVED_SUBSCRIPTION_GIFT_PUSH_NOTIFICATION_KEY)) { + key = "preference_push_gifted_subscription"; + } else if (type.contains(GUILD_INVITE_PUSH_NOTIFICATION_KEY)) { + key = "preference_push_invited_to_guild"; + } else if (type.contains(QUEST_INVITE_PUSH_NOTIFICATION_KEY)) { + key = "preference_push_invited_to_quest"; + } else if (type.contains(QUEST_BEGUN_PUSH_NOTIFICATION_KEY)) { + key = "preference_push_your_quest_has_begun"; + } else if (type.contains(WON_CHALLENGE_PUSH_NOTIFICATION_KEY)) { + key = "preference_push_you_won_challenge"; + } + + return sharedPreferences.getBoolean(key, true); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/QuestBegunLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/QuestBegunLocalNotification.java new file mode 100644 index 000000000..f49d32ec5 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/QuestBegunLocalNotification.java @@ -0,0 +1,38 @@ +package com.habitrpg.android.habitica.helpers.notifications; + +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.media.RingtoneManager; +import android.net.Uri; +import android.support.v4.app.NotificationCompat; + +import com.habitrpg.android.habitica.R; +import com.habitrpg.android.habitica.ui.activities.MainActivity; + +import java.util.Map; + +/** + * Created by keithholliday on 7/1/16. + */ +public class QuestBegunLocalNotification extends HabiticaLocalNotification { + @Override + public void notifyLocally(Context context, String title, String message) { + super.notifyLocally(context, title, message); + this.setNotificationActions(); + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.notify(10000, notificationBuilder.build()); + } + + protected void setNotificationActions() { + Intent intent = new Intent(context, MainActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity( + context, + 3000, + intent, + PendingIntent.FLAG_UPDATE_CURRENT + ); + notificationBuilder.setContentIntent(pendingIntent); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/QuestInviteLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/QuestInviteLocalNotification.java new file mode 100644 index 000000000..87ddbc61e --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/QuestInviteLocalNotification.java @@ -0,0 +1,52 @@ +package com.habitrpg.android.habitica.helpers.notifications; + +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.media.RingtoneManager; +import android.net.Uri; +import android.support.v4.app.NotificationCompat; + +import com.habitrpg.android.habitica.R; +import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver; + +import java.util.Map; + +/** + * Created by keithholliday on 7/1/16. + */ +public class QuestInviteLocalNotification extends HabiticaLocalNotification { + @Override + public void notifyLocally(Context context, String title, String message) { + super.notifyLocally(context, title, message); + this.setNotificationActions(); + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.notify(10000, notificationBuilder.build()); + } + + protected void setNotificationActions() { + Resources res = context.getResources(); + + Intent acceptInviteIntent = new Intent(context, LocalNotificationActionReceiver.class); + acceptInviteIntent.setAction(res.getString(R.string.accept_quest_invite)); + PendingIntent pendingIntentAccept = PendingIntent.getBroadcast( + context, + 3000, + acceptInviteIntent, + PendingIntent.FLAG_UPDATE_CURRENT + ); + notificationBuilder.addAction(0, "Accept", pendingIntentAccept); + + Intent rejectInviteIntent = new Intent(context, LocalNotificationActionReceiver.class); + rejectInviteIntent.setAction(res.getString(R.string.reject_quest_invite)); + PendingIntent pendingIntentReject = PendingIntent.getBroadcast( + context, + 2000, + rejectInviteIntent, + PendingIntent.FLAG_UPDATE_CURRENT + ); + notificationBuilder.addAction(0, "Reject", pendingIntentReject); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedGemsGiftLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedGemsGiftLocalNotification.java new file mode 100644 index 000000000..7e4d394c4 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedGemsGiftLocalNotification.java @@ -0,0 +1,40 @@ +package com.habitrpg.android.habitica.helpers.notifications; + +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.media.RingtoneManager; +import android.net.Uri; +import android.support.v4.app.NotificationCompat; + +import com.habitrpg.android.habitica.R; +import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver; +import com.habitrpg.android.habitica.ui.activities.MainActivity; + +import java.util.Map; + +/** + * Created by keithholliday on 7/1/16. + */ +public class ReceivedGemsGiftLocalNotification extends HabiticaLocalNotification { + @Override + public void notifyLocally(Context context, String title, String message) { + super.notifyLocally(context, title, message); + this.setNotificationActions(); + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.notify(10, notificationBuilder.build()); + } + + protected void setNotificationActions() { + Intent intent = new Intent(context, MainActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity( + context, + 3000, + intent, + PendingIntent.FLAG_UPDATE_CURRENT + ); + notificationBuilder.setContentIntent(pendingIntent); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedPrivateMessageLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedPrivateMessageLocalNotification.java new file mode 100644 index 000000000..e36f9b605 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedPrivateMessageLocalNotification.java @@ -0,0 +1,46 @@ +package com.habitrpg.android.habitica.helpers.notifications; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.media.RingtoneManager; +import android.net.Uri; +import android.support.v4.app.NotificationCompat; +import android.support.v4.app.RemoteInput; +import android.util.Log; +import android.widget.RemoteViews; + +import com.habitrpg.android.habitica.R; +import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver; +import com.habitrpg.android.habitica.ui.activities.MainActivity; + +import java.util.Map; + +/** + * Created by keithholliday on 7/1/16. + */ +public class ReceivedPrivateMessageLocalNotification extends HabiticaLocalNotification { + + @Override + public void notifyLocally(Context context, String title, String message) { + super.notifyLocally(context, title, message); + this.setNotificationActions(); + NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE); + notificationManager.notify(10, notificationBuilder.build()); + } + + protected void setNotificationActions() { + Intent intent = new Intent(context, LocalNotificationActionReceiver.class); + PendingIntent pendingIntent = PendingIntent.getActivity( + context, + 3000, + intent, + PendingIntent.FLAG_UPDATE_CURRENT + ); + + notificationBuilder.setContentIntent(pendingIntent); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedSubscriptionGiftLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedSubscriptionGiftLocalNotification.java new file mode 100644 index 000000000..e2658ff6f --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/ReceivedSubscriptionGiftLocalNotification.java @@ -0,0 +1,38 @@ +package com.habitrpg.android.habitica.helpers.notifications; + +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.media.RingtoneManager; +import android.net.Uri; +import android.support.v4.app.NotificationCompat; + +import com.habitrpg.android.habitica.R; +import com.habitrpg.android.habitica.ui.activities.MainActivity; + +import java.util.Map; + +/** + * Created by keithholliday on 7/1/16. + */ +public class ReceivedSubscriptionGiftLocalNotification extends HabiticaLocalNotification { + @Override + public void notifyLocally(Context context, String title, String message) { + super.notifyLocally(context, title, message); + this.setNotificationActions(); + NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE); + notificationManager.notify(10, notificationBuilder.build()); + } + + protected void setNotificationActions() { + Intent intent = new Intent(context, MainActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity( + context, + 3000, + intent, + PendingIntent.FLAG_UPDATE_CURRENT + ); + notificationBuilder.setContentIntent(pendingIntent); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/WonChallengeLocalNotification.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/WonChallengeLocalNotification.java new file mode 100644 index 000000000..62e709169 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/WonChallengeLocalNotification.java @@ -0,0 +1,38 @@ +package com.habitrpg.android.habitica.helpers.notifications; + +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.media.RingtoneManager; +import android.net.Uri; +import android.support.v4.app.NotificationCompat; + +import com.habitrpg.android.habitica.R; +import com.habitrpg.android.habitica.ui.activities.MainActivity; + +import java.util.Map; + +/** + * Created by keithholliday on 7/2/16. + */ +public class WonChallengeLocalNotification extends HabiticaLocalNotification { + @Override + public void notifyLocally(Context context, String title, String message) { + super.notifyLocally(context, title, message); + this.setNotificationActions(); + NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE); + notificationManager.notify(10, notificationBuilder.build()); + } + + protected void setNotificationActions() { + Intent intent = new Intent(context, MainActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity( + context, + 3000, + intent, + PendingIntent.FLAG_UPDATE_CURRENT + ); + notificationBuilder.setContentIntent(pendingIntent); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/LocalNotificationActionReceiver.java b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/LocalNotificationActionReceiver.java new file mode 100644 index 000000000..506b41c67 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/LocalNotificationActionReceiver.java @@ -0,0 +1,97 @@ +package com.habitrpg.android.habitica.receivers; + +import android.app.NotificationManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.os.Bundle; +import android.util.Log; + +import com.habitrpg.android.habitica.APIHelper; +import com.habitrpg.android.habitica.HabiticaApplication; +import com.habitrpg.android.habitica.R; +import com.habitrpg.android.habitica.callbacks.HabitRPGUserCallback; +import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser; + +import javax.inject.Inject; + +/** + * Created by keithholliday on 6/30/16. + */ +public class LocalNotificationActionReceiver extends BroadcastReceiver implements HabitRPGUserCallback.OnUserReceived { + @Inject + public APIHelper apiHelper; + + private HabitRPGUser user; + private String action; + private Resources resources; + private Intent intent; + private Context context; + + @Override + public void onReceive(Context context, Intent intent) { + HabiticaApplication.getInstance(context).getComponent().inject(this); + this.resources = context.getResources(); + + this.action = intent.getAction(); + this.intent = intent; + this.context = context; + + this.apiHelper.apiService.getUser() + .compose(this.apiHelper.configureApiCallObserver()) + .subscribe(new HabitRPGUserCallback(this), throwable -> {}); + } + + @Override + public void onUserReceived(HabitRPGUser user) { + this.user = user; + this.handleLocalNotificationAction(action); + } + + private void handleLocalNotificationAction(String action) { + NotificationManager notificationManager = (NotificationManager) this.context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancelAll(); + + //@TODO: This is a good place for a factory and event emitter pattern + if (action.equals(this.resources.getString(R.string.accept_party_invite))) { + if (this.user.getInvitations().getParty() == null) return; + String partyId = this.user.getInvitations().getParty().getId(); + apiHelper.apiService.joinGroup(partyId) + .compose(apiHelper.configureApiCallObserver()) + .subscribe(aVoid -> {}, throwable -> {}); + } else if (action.equals(this.resources.getString(R.string.reject_party_invite))) { + if (this.user.getInvitations().getParty() == null) return; + String partyId = this.user.getInvitations().getParty().getId(); + apiHelper.apiService.rejectGroupInvite(partyId) + .compose(apiHelper.configureApiCallObserver()) + .subscribe(aVoid -> {}, throwable -> {}); + } else if (action.equals(this.resources.getString(R.string.accept_quest_invite))) { + if (this.user.getParty() == null) return; + String partyId = this.user.getParty().getId(); + apiHelper.apiService.acceptQuest(partyId) + .compose(apiHelper.configureApiCallObserver()) + .subscribe(aVoid -> {}, throwable -> {}); + } else if (action.equals(this.resources.getString(R.string.reject_quest_invite))) { + if (this.user.getParty() == null) return; + String partyId = this.user.getParty().getId(); + apiHelper.apiService.rejectQuest(partyId) + .compose(apiHelper.configureApiCallObserver()) + .subscribe(aVoid -> {}, throwable -> {}); + } else if (action.equals(this.resources.getString(R.string.accept_guild_invite))) { + Bundle extras = this.intent.getExtras(); + String guildId = extras.getString("groupID"); + if (guildId == null) return; + apiHelper.apiService.joinGroup(guildId) + .compose(apiHelper.configureApiCallObserver()) + .subscribe(aVoid -> {}, throwable -> {}); + } else if (action.equals(this.resources.getString(R.string.reject_guild_invite))) { + Bundle extras = this.intent.getExtras(); + String guildId = extras.getString("groupID"); + if (guildId == null) return; + apiHelper.apiService.rejectGroupInvite(guildId) + .compose(apiHelper.configureApiCallObserver()) + .subscribe(aVoid -> {}, throwable -> {}); + } + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.java index 364968838..a36181a04 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.java @@ -84,55 +84,6 @@ public class LoginActivity extends BaseActivity TextView mForgotPWTV; private Menu menu; private CallbackManager callbackManager; - private View.OnClickListener mLoginNormalClick = new View.OnClickListener() { - @Override - public void onClick(View v) { - mProgressBar.setVisibility(View.VISIBLE); - if (isRegistering) { - String username, email, password, cpassword; - username = String.valueOf(mUsernameET.getText()).trim(); - email = String.valueOf(mEmail.getText()).trim(); - password = String.valueOf(mPasswordET.getText()); - cpassword = String.valueOf(mConfirmPassword.getText()); - if (username.length() == 0 || password.length() == 0 || email.length() == 0 || cpassword.length() == 0) { - showValidationError(R.string.login_validation_error_fieldsmissing); - return; - } - apiHelper.registerUser(username, email, password, cpassword) - .compose(apiHelper.configureApiCallObserver()) - .subscribe(LoginActivity.this, throwable -> { - hideProgress(); - }); - } else { - String username, password; - username = String.valueOf(mUsernameET.getText()).trim(); - password = String.valueOf(mPasswordET.getText()); - if (username.length() == 0 || password.length() == 0) { - showValidationError(R.string.login_validation_error_fieldsmissing); - return; - } - apiHelper.connectUser(username, password) - .compose(apiHelper.configureApiCallObserver()) - .subscribe(LoginActivity.this, throwable -> { - hideProgress(); - }); - } - } - }; - private View.OnClickListener mForgotPWClick = v -> { - String url = BuildConfig.BASE_URL; - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse(url)); - startActivity(i); - }; - - public static void show(final View v) { - v.setVisibility(View.VISIBLE); - } - - public static void hide(final View v) { - v.setVisibility(View.GONE); - } @Override protected int getLayoutResId() { @@ -214,6 +165,54 @@ public class LoginActivity extends BaseActivity hide(this.mConfirmPasswordRow); } } + } + + private View.OnClickListener mLoginNormalClick = new View.OnClickListener() { + @Override + public void onClick(View v) { + mProgressBar.setVisibility(View.VISIBLE); + if (isRegistering) { + String username, email,password,cpassword; + username = String.valueOf(mUsernameET.getText()).trim(); + email = String.valueOf(mEmail.getText()).trim(); + password = String.valueOf(mPasswordET.getText()); + cpassword = String.valueOf(mConfirmPassword.getText()); + if (username.length() == 0 || password.length() == 0 || email.length() == 0 || cpassword.length() == 0) { + showValidationError(R.string.login_validation_error_fieldsmissing); + return; + } + apiHelper.registerUser(username,email,password, cpassword) + .compose(apiHelper.configureApiCallObserver()) + .subscribe(LoginActivity.this, throwable -> {hideProgress();}); + } else { + String username,password; + username = String.valueOf(mUsernameET.getText()).trim(); + password = String.valueOf(mPasswordET.getText()); + if (username.length() == 0 || password.length() == 0) { + showValidationError(R.string.login_validation_error_fieldsmissing); + return; + } + apiHelper.connectUser(username,password) + .compose(apiHelper.configureApiCallObserver()) + .subscribe(LoginActivity.this, throwable -> {hideProgress();}); + } + } + }; + + private View.OnClickListener mForgotPWClick = v -> { + String url = BuildConfig.BASE_URL; + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(url)); + startActivity(i); + }; + + + public static void show(final View v) { + v.setVisibility(View.VISIBLE); + } + + public static void hide(final View v) { + v.setVisibility(View.GONE); } private void startMainActivity() { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.java index a6dcd5465..bb81fb7a7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.java @@ -32,6 +32,7 @@ import com.habitrpg.android.habitica.events.commands.OpenMenuItemCommand; import com.habitrpg.android.habitica.events.commands.SellItemCommand; import com.habitrpg.android.habitica.events.commands.UnlockPathCommand; import com.habitrpg.android.habitica.events.commands.UpdateUserCommand; +import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManager; import com.habitrpg.android.habitica.ui.AvatarView; import com.habitrpg.android.habitica.ui.AvatarWithBarsViewModel; import com.habitrpg.android.habitica.ui.TutorialView; @@ -206,6 +207,8 @@ public class MainActivity extends BaseActivity implements Action1, Ha return (Math.round(value * Math.pow(10, n))) / (Math.pow(10, n)); } + PushNotificationManager pushNotificationManager; + @Override protected int getLayoutResId() { return R.layout.activity_main; @@ -222,6 +225,8 @@ public class MainActivity extends BaseActivity implements Action1, Ha //Check if reminder alarm is set scheduleReminder(this); + pushNotificationManager = PushNotificationManager.getInstance(this); + new Select().from(HabitRPGUser.class).where(Condition.column("id").eq(hostConfig.getUser())).async().querySingle(userTransactionListener); setupToolbar(toolbar); @@ -349,8 +354,9 @@ public class MainActivity extends BaseActivity implements Action1, Ha displayDeathDialogIfNeeded(); if (!fromLocalDb) { - displayNewInboxMessagesBadge(); + pushNotificationManager.setUser(user); + pushNotificationManager.addPushDeviceUsingStoredToken(); // Update the oldEntries new Thread(() -> { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PrefsActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PrefsActivity.java index cf74940b9..37e2f01d2 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PrefsActivity.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PrefsActivity.java @@ -6,6 +6,7 @@ import com.habitrpg.android.habitica.R; import com.habitrpg.android.habitica.components.AppComponent; import com.habitrpg.android.habitica.ui.fragments.preferences.AccountDetailsFragment; import com.habitrpg.android.habitica.ui.fragments.preferences.PreferencesFragment; +import com.habitrpg.android.habitica.ui.fragments.preferences.PushNotificationsPreferencesFragment; import android.content.Context; import android.content.SharedPreferences; @@ -88,6 +89,10 @@ public class PrefsActivity extends BaseActivity implements if (preferenceScreen.getKey().equals("accountDetails")) { fragment = new AccountDetailsFragment(); } + + if (preferenceScreen.getKey().equals("pushNotifications")) { + fragment = new PushNotificationsPreferencesFragment(); + } return fragment; } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PreferencesFragment.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PreferencesFragment.java index 1498261ce..72bfc2cf7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PreferencesFragment.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PreferencesFragment.java @@ -4,6 +4,7 @@ import com.habitrpg.android.habitica.APIHelper; import com.habitrpg.android.habitica.HabiticaApplication; import com.habitrpg.android.habitica.NotificationPublisher; import com.habitrpg.android.habitica.R; +import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManager; import com.habitrpg.android.habitica.prefs.TimePreference; import com.habitrpg.android.habitica.ui.activities.ClassSelectionActivity; import com.habitrpg.android.habitica.ui.activities.MainActivity; @@ -21,6 +22,8 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.support.v7.app.AlertDialog; import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; +import android.util.Log; import java.util.Calendar; @@ -33,8 +36,11 @@ public class PreferencesFragment extends BasePreferencesFragment implements public APIHelper apiHelper; private Context context; private TimePreference timePreference; + private PreferenceScreen pushNotificationsPreference; private Preference classSelectionPreference; private HabitRPGUser user; + private PushNotificationManager pushNotificationManager; + private TransactionListener userTransactionListener = new TransactionListener() { @Override public void onResultReceived(HabitRPGUser habitRPGUser) { @@ -64,6 +70,7 @@ public class PreferencesFragment extends BasePreferencesFragment implements new Select().from(HabitRPGUser.class).where(Condition.column("id").eq(userID)).async().querySingle(userTransactionListener); } + pushNotificationManager = PushNotificationManager.getInstance(this.getActivity()); } @Override @@ -72,6 +79,12 @@ public class PreferencesFragment extends BasePreferencesFragment implements boolean useReminder = getPreferenceManager().getSharedPreferences().getBoolean("use_reminder", false); timePreference.setEnabled(useReminder); + + pushNotificationsPreference = (PreferenceScreen) findPreference("pushNotifications"); + boolean userPushNotifications = getPreferenceManager().getSharedPreferences().getBoolean("usePushNotifications", true); + pushNotificationsPreference.setEnabled(userPushNotifications); + + classSelectionPreference = findPreference("choose_class"); classSelectionPreference.setVisible(false); } @@ -176,6 +189,14 @@ public class PreferencesFragment extends BasePreferencesFragment implements } else if (key.equals("reminder_time")) { removeNotifications(); scheduleNotifications(); + } else if (key.equals("usePushNotifications")) { + boolean userPushNotifications = sharedPreferences.getBoolean(key, false); + pushNotificationsPreference.setEnabled(userPushNotifications); + if (userPushNotifications) { + pushNotificationManager.addPushDeviceUsingStoredToken(); + } else { + pushNotificationManager.removePushDeviceUsingStoredToken(); + } } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PushNotificationsPreferencesFragment.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PushNotificationsPreferencesFragment.java new file mode 100644 index 000000000..92f1074e0 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PushNotificationsPreferencesFragment.java @@ -0,0 +1,31 @@ +package com.habitrpg.android.habitica.ui.fragments.preferences; + +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.SharedPreferences; +import android.support.v7.preference.Preference; +import android.widget.Toast; + +import com.habitrpg.android.habitica.R; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * Created by keithholliday on 6/27/16. + */ +public class PushNotificationsPreferencesFragment extends BasePreferencesFragment implements + SharedPreferences.OnSharedPreferenceChangeListener { + + @Override + protected void setupPreferences() { + + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + + } +} \ No newline at end of file diff --git a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/api/ApiService.java b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/api/ApiService.java index df990a3cf..af6d08371 100644 --- a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/api/ApiService.java +++ b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/api/ApiService.java @@ -208,6 +208,9 @@ public interface ApiService { @POST("groups/{gid}/invite") Observable inviteToGroup(@Path("gid") String groupId, @Body Map inviteData); + @POST("groups/{gid}/reject-invite") + Observable rejectGroupInvite(@Path("gid") String groupId); + @POST("groups/{gid}/quests/accept") Observable acceptQuest(@Path("gid") String groupId); @@ -235,10 +238,17 @@ public interface ApiService { //Members URL @POST("members/send-private-message") Observable postPrivateMessage(@Body HashMap messageDetails); - + @GET("shops/{identifier}") Observable fetchShopInventory(@Path("identifier") String identifier); + //Push notifications + @POST("user/push-devices") + Observable addPushDevice(@Body Map pushDeviceData); + + @DELETE("user/push-devices/{regId}") + Observable deletePushDevice(@Path("regId") String regId); + //DEBUG: These calls only work on a local development server @POST("debug/add-ten-gems") diff --git a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/HabitRPGUser.java b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/HabitRPGUser.java index 211d0ee4a..1092cfd45 100644 --- a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/HabitRPGUser.java +++ b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/HabitRPGUser.java @@ -4,6 +4,7 @@ import com.google.gson.annotations.SerializedName; import com.habitrpg.android.habitica.HabitDatabase; import com.habitrpg.android.habitica.ui.AvatarView; +import com.magicmicky.habitrpgwrapper.lib.models.invitations.Invitations; import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task; import com.magicmicky.habitrpgwrapper.lib.models.tasks.TasksOrder; import com.raizlabs.android.dbflow.annotation.Column; @@ -92,6 +93,14 @@ public class HabitRPGUser extends BaseModel { foreignColumnName = "user_id")}) private ContributorInfo contributor; + @Column + @ForeignKey(references = {@ForeignKeyReference(columnName = "invitations_id", + columnType = String.class, + foreignColumnName = "user_id")}) + private Invitations invitations; + + private List pushDevices = new ArrayList(); + private Purchases purchased; private TasksOrder tasksOrder; @@ -145,6 +154,13 @@ public class HabitRPGUser extends BaseModel { this.contributor = contributor; } + public Invitations getInvitations() { + return invitations; + } + + public void setInvitations(Invitations invitations) { + this.invitations = invitations; + } public UserParty getParty() { return party; @@ -282,6 +298,14 @@ public class HabitRPGUser extends BaseModel { this.tasksOrder = tasksOrder; } + public List getPushDevices() { + return this.pushDevices; + } + + public void setPushDevices(List pushDevices) { + this.pushDevices = pushDevices; + } + @Override public void save() { // We need to set the user_id to all other objects @@ -293,6 +317,9 @@ public class HabitRPGUser extends BaseModel { authentication.user_id = id; flags.user_id = id; if (contributor != null) { contributor.user_id = id; } + contributor.user_id = id; + invitations.user_id = id; + ArrayList allTasks = new ArrayList(); if (dailys != null) { diff --git a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/PushDevice.java b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/PushDevice.java new file mode 100644 index 000000000..c70ec2a06 --- /dev/null +++ b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/PushDevice.java @@ -0,0 +1,34 @@ +package com.magicmicky.habitrpgwrapper.lib.models; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * Created by keithholliday on 7/5/16. + */ +public class PushDevice { + + @SerializedName("regId") + @Expose + private String regId; + + @SerializedName("type") + @Expose + private String type; + + public String getRegId() { + return this.regId; + } + + public void setRegId(String regId) { + this.regId = regId; + } + + public String getType() { + return this.type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/GuildInvite.java b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/GuildInvite.java new file mode 100644 index 000000000..e3d1a7291 --- /dev/null +++ b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/GuildInvite.java @@ -0,0 +1,76 @@ +package com.magicmicky.habitrpgwrapper.lib.models.invitations; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * Created by keithholliday on 7/2/16. + */ +public class GuildInvite { + + @SerializedName("inviter") + @Expose + private String inviter; + + @SerializedName("name") + @Expose + private String name; + + @SerializedName("id") + @Expose + private String id; + + /** + * + * @return + * The inviter + */ + public String getInviter() { + return inviter; + } + + /** + * + * @param inviter + * The inviter + */ + public void setInviter(String inviter) { + this.inviter = inviter; + } + + /** + * + * @return + * The name + */ + public String getName() { + return name; + } + + /** + * + * @param name + * The name + */ + public void setName(String name) { + this.name = name; + } + + /** + * + * @return + * The id + */ + public String getId() { + return id; + } + + /** + * + * @param id + * The id + */ + public void setId(String id) { + this.id = id; + } +} diff --git a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/Invitations.java b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/Invitations.java new file mode 100644 index 000000000..d09fda0e8 --- /dev/null +++ b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/Invitations.java @@ -0,0 +1,71 @@ +package com.magicmicky.habitrpgwrapper.lib.models.invitations; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import com.habitrpg.android.habitica.HabitDatabase; +import com.magicmicky.habitrpgwrapper.lib.models.invitations.PartyInvite; +import com.raizlabs.android.dbflow.annotation.Column; +import com.raizlabs.android.dbflow.annotation.NotNull; +import com.raizlabs.android.dbflow.annotation.PrimaryKey; +import com.raizlabs.android.dbflow.annotation.Table; +import com.raizlabs.android.dbflow.structure.BaseModel; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Created by keithholliday on 7/2/16. + */ +@Table(databaseName = HabitDatabase.NAME) +public class Invitations extends BaseModel { + + @Column + @PrimaryKey + @NotNull + public String user_id; + + @SerializedName("party") + @Expose + private PartyInvite party; + + @SerializedName("guilds") + @Expose + private List guilds = new ArrayList(); + + /** + * + * @return + * The party invite + */ + public PartyInvite getParty() { + return party; + } + + /** + * + * @param party + * The party + */ + public void setParty(PartyInvite party) { + this.party = party; + } + + /** + * + * @return + * The guilds invite + */ + public List getGuilds() { + return guilds; + } + + /** + * + * @param guilds + * The guilds + */ + public void setGuilds(List guilds) { + this.guilds = guilds; + } +} diff --git a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/PartyInvite.java b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/PartyInvite.java new file mode 100644 index 000000000..05bb1e08c --- /dev/null +++ b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/invitations/PartyInvite.java @@ -0,0 +1,73 @@ +package com.magicmicky.habitrpgwrapper.lib.models.invitations; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** + * Created by keithholliday on 7/2/16. + */ +public class PartyInvite { + @SerializedName("id") + @Expose + private String id; + @SerializedName("name") + @Expose + private String name; + @SerializedName("inviter") + @Expose + private String inviter; + + /** + * + * @return + * The id + */ + public String getId() { + return id; + } + + /** + * + * @param id + * The id + */ + public void setId(String id) { + this.id = id; + } + + /** + * + * @return + * The name + */ + public String getName() { + return name; + } + + /** + * + * @param name + * The name + */ + public void setName(String name) { + this.name = name; + } + + /** + * + * @return + * The inviter + */ + public String getInviter() { + return inviter; + } + + /** + * + * @param inviter + * The inviter + */ + public void setInviter(String inviter) { + this.inviter = inviter; + } +} diff --git a/README.md b/README.md index e671d23ce..284422afb 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ It's also on Google Play: src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" /> -Having the application installed is a good way to be notified of new releases. However, clicking "Watch" on this +Having the application installed is a good way to be notified of new releases. However, clicking "Watch" on this repository will allow GitHub to email you whenever we publish a release. @@ -54,6 +54,7 @@ Setup Habitica build config files by simply copying the example habitica files. $ cp habitica.properties.example habitica.properties $ cp habitica.resources.example habitica.resources + $ cp Habitica/google-services.json.example Habitica/google-services.json (Get .json from Firebase Console) Note: this is the default production `habitica.properties` file for habitica.com. If you want to use a local habitica server, please modify the values in the properties file accordingly. diff --git a/build.gradle b/build.gradle index 2ca638a10..0e19da6e8 100644 --- a/build.gradle +++ b/build.gradle @@ -8,6 +8,7 @@ buildscript { classpath 'com.android.tools.build:gradle:2.1.2' classpath 'com.android.databinding:dataBinder:1.0-rc4' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' + classpath 'com.google.gms:google-services:3.0.0' } }