Merge branch '794-global-notifications' of https://github.com/cvuorinen/habitica-android into cvuorinen-794-global-notifications

# Conflicts:
#	Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt
#	Habitica/src/main/java/com/habitrpg/android/habitica/modules/ApiModule.java
#	Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt
This commit is contained in:
Phillip Thelen 2019-06-03 12:36:33 +02:00
commit 73114f14f1
60 changed files with 2529 additions and 1668 deletions

View file

@ -64,6 +64,13 @@
android:pathPattern="/settings/.*"/>
</intent-filter>
</activity>
<activity
android:name=".ui.activities.NotificationsActivity"
android:parentActivityName=".ui.activities.MainActivity"
android:label="@string/notifications"
android:screenOrientation="portrait"
tools:ignore="UnusedAttribute">
</activity>
<activity
android:name=".ui.activities.FixCharacterValuesActivity"
android:parentActivityName=".ui.activities.PrefsActivity"

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 960 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View file

@ -4,10 +4,5 @@
<corners
android:radius="10dip"/>
<solid
android:color="@color/red_50" />
<padding
android:left="5dip"
android:right="5dip"
android:top="5dip"
android:bottom="5dip" />
android:color="@color/brand_400" />
</shape>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:radius="100dip"/>
<solid
android:color="@color/gray_600" />
<padding
android:left="6dip"
android:right="6dip" />
</shape>

View file

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.activities.NotificationsActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:theme="@style/Toolbar"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/ThemeOverlay.AppCompat.Dark" />
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top|center"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/notifications_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/notification_items"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="?android:listDivider"
android:orientation="vertical"
android:showDividers="middle" />
</androidx.core.widget.NestedScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</FrameLayout>
</LinearLayout>

View file

@ -12,7 +12,7 @@
android:layout_width="match_parent"
android:layout_height="64dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingRight="1dp"
android:layout_marginTop="16dp"
android:gravity="center_vertical"
android:orientation="horizontal">
@ -58,10 +58,42 @@
</LinearLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/messagesButtonWrapper"
android:layout_width="45dp"
android:id="@+id/notificationsButtonWrapper"
android:layout_width="33dp"
android:layout_height="match_parent"
android:layout_marginLeft="16dp">
<ImageView
android:id="@+id/notificationsButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/transparent"
android:src="@drawable/menu_notifications"
android:layout_centerVertical="true"
android:clickable="false" />
<TextView
android:id="@+id/notificationsBadge"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_alignLeft="@id/notificationsButton"
android:layout_alignTop="@id/notificationsButton"
android:layout_marginLeft="13dp"
android:layout_marginTop="-13dp"
android:background="@drawable/badge_circle"
android:gravity="center"
android:minWidth="20dp"
android:paddingTop="0dp"
android:textColor="#FFF"
android:textSize="12sp"
android:visibility="gone"
tools:text="1"
tools:visibility="visible" />
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/messagesButtonWrapper"
android:layout_width="33dp"
android:layout_height="match_parent"
android:layout_marginLeft="8dp">
<ImageView
android:id="@+id/messagesButton"
android:layout_width="wrap_content"
@ -70,21 +102,24 @@
android:src="@drawable/menu_messages"
android:layout_centerVertical="true"
android:clickable="false" />
<TextView
android:id="@+id/messagesBadge"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:paddingTop="0dp"
android:layout_alignTop="@id/messagesButton"
android:layout_alignLeft="@id/messagesButton"
tools:text="1"
android:layout_alignTop="@id/messagesButton"
android:layout_marginLeft="13dp"
android:layout_marginTop="-12dp"
android:background="@drawable/badge_circle"
android:gravity="center"
android:minWidth="20dp"
android:paddingTop="0dp"
android:textColor="#FFF"
android:textSize="12sp"
android:background="@drawable/badge_circle"
android:layout_marginTop="-12dp"
android:layout_marginLeft="13dp"
android:visibility="gone"
tools:visibility="visible"/>
tools:text="1"
tools:visibility="visible" />
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/settingsButtonWrapper"
@ -98,21 +133,25 @@
android:background="@color/transparent"
android:src="@drawable/menu_settings"
android:layout_centerVertical="true"/>
<TextView
android:id="@+id/settingsBadge"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:paddingTop="0dp"
android:layout_alignTop="@id/settingsButton"
android:layout_alignLeft="@id/settingsButton"
tools:text="1"
android:layout_alignTop="@id/settingsButton"
android:layout_centerVertical="true"
android:layout_marginLeft="13dp"
android:layout_marginTop="-12dp"
android:background="@drawable/badge_circle"
android:gravity="center"
android:minWidth="20dp"
android:paddingTop="0dp"
android:textColor="#FFF"
android:textSize="12sp"
android:background="@drawable/badge_circle"
android:layout_marginTop="-12dp"
android:layout_marginLeft="13dp"
android:visibility="gone"
tools:visibility="visible"/>
tools:text="1"
tools:visibility="visible" />
</RelativeLayout>
</LinearLayout>

View file

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="@dimen/spacing_large">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:paddingLeft="@dimen/spacing_medium"
android:paddingTop="44dp"
android:paddingRight="@dimen/spacing_medium">
<ImageView
android:id="@+id/noNotifications"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/transparent"
android:clickable="false"
android:gravity="center"
android:src="@drawable/no_notifications" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="40dp"
android:paddingTop="26dp"
android:paddingRight="40dp">
<TextView
style="@style/SectionTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/no_notifications_title" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:lineSpacingExtra="5dp"
android:paddingTop="16dp"
android:text="@string/no_notifications_text" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/notification_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:orientation="horizontal"
android:paddingLeft="20dp"
android:paddingTop="10dp"
android:paddingRight="20dp"
android:paddingBottom="10dp">
<ImageView
android:id="@+id/notification_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="20dp"
android:background="@color/transparent"
android:visibility="gone" />
<TextView
android:id="@+id/message_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp"
android:layout_weight="1"
android:lineSpacingExtra="5dp"
android:text="Message" />
<ImageView
android:id="@+id/dismiss_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:background="@color/transparent"
android:clickable="true"
android:focusable="true"
android:padding="10dp"
android:src="@drawable/notification_close" />
</LinearLayout>

View file

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:orientation="horizontal"
android:paddingLeft="20dp"
android:paddingTop="10dp"
android:paddingRight="20dp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:orientation="horizontal"
android:paddingTop="@dimen/spacing_medium">
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/title"
style="@style/Body1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dip"
android:background="@color/transparent"
android:text="@string/notifications"
android:textAllCaps="true"
android:textColor="@color/gray_300"
android:textSize="12sp" />
<TextView
android:id="@+id/notifications_title_badge"
android:layout_width="wrap_content"
android:layout_height="24dp"
android:layout_marginLeft="10dp"
android:background="@drawable/badge_gray"
android:gravity="center"
android:minWidth="24dp"
android:textColor="@color/gray_300"
android:textSize="12sp"
tools:text="1" />
</LinearLayout>
<Button
android:id="@+id/dismiss_all_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/transparent"
android:text="@string/dismiss_all"
android:textColor="@color/brand_400" />
</LinearLayout>

View file

@ -210,6 +210,11 @@
app:argType="string"
app:nullable="true" />
</fragment>
<activity
android:id="@+id/notificationsActivity"
android:name="com.habitrpg.android.habitica.ui.activities.NotificationsActivity"
android:label="@string/notifications">
</activity>
<fragment
android:id="@+id/petDetailRecyclerFragment"
android:name="com.habitrpg.android.habitica.ui.fragments.inventory.stable.PetDetailRecyclerFragment"

View file

@ -534,6 +534,7 @@
<string name="open_settings">Open Settings</string>
<string name="dont_keep_activities_warning">It seems like you have the Developer option \“Don\'t keep Activities\” active. Currently this option causes issues with the habitica app, so we suggest disabling it.</string>
<string name="inbox">Messages</string>
<string name="notifications">Notifications</string>
<string name="FAQ">Frequently Asked Questions</string>
<string name="special">Special</string>
<string name="gem_for_gold_description">Because you subscribe to Habitica, you can purchase a number of Gems each month using Gold.</string>
@ -858,6 +859,14 @@
<string name="discover">Discover</string>
<string name="damage_paused">Damage paused</string>
<string name="preference_push_important_announcements">Important Announcements</string>
<string name="no_notifications_title">Youre all caught up!</string>
<string name="no_notifications_text">The notification fairies give you a raucous round of applause! Well done!</string>
<string name="dismiss_all">Dismiss All</string>
<string name="new_bailey_update">New Bailey Update!</string>
<string name="new_msg_guild"><![CDATA[<b>%1$s</b> has new posts]]></string>
<string name="new_msg_party"><![CDATA[Your Party, <b>%1$s</b>, has new posts]]></string>
<string name="unallocated_stats_points"><![CDATA[You have <b>%1$s unallocated Stat Points</b>]]></string>
<string name="new_subscriber_item"><![CDATA[You have new <b>Mystery Items</b>]]></string>
<string name="create">Create</string>
<string name="only_leader_create_challenge">Only leader can create Challenges</string>
<string name="create_party">Create Party</string>

View file

@ -348,6 +348,12 @@ public interface ApiService {
@POST("notifications/{notificationId}/read")
Flowable<HabitResponse<List>> readNotification(@Path("notificationId") String notificationId);
@POST("notifications/read")
Flowable<HabitResponse<List>> readNotifications(@Body Map<String, List<String>> notificationIds);
@POST("notifications/see")
Flowable<HabitResponse<List>> seeNotifications(@Body Map<String, List<String>> notificationIds);
@POST("user/open-mystery-item")
Flowable<HabitResponse<Equipment>> openMysteryItem();

View file

@ -6,6 +6,7 @@ import com.google.gson.reflect.TypeToken;
import com.habitrpg.android.habitica.models.Achievement;
import com.habitrpg.android.habitica.models.ContentResult;
import com.habitrpg.android.habitica.models.FAQArticle;
import com.habitrpg.android.habitica.models.Notification;
import com.habitrpg.android.habitica.models.Skill;
import com.habitrpg.android.habitica.models.Tag;
import com.habitrpg.android.habitica.models.TutorialStep;
@ -45,6 +46,7 @@ import com.habitrpg.android.habitica.utils.GroupSerialization;
import com.habitrpg.android.habitica.utils.MemberSerialization;
import com.habitrpg.android.habitica.utils.OwnedItemListDeserializer;
import com.habitrpg.android.habitica.utils.OwnedMountListDeserializer;
import com.habitrpg.android.habitica.utils.NotificationDeserializer;
import com.habitrpg.android.habitica.utils.OwnedPetListDeserializer;
import com.habitrpg.android.habitica.utils.PurchasedDeserializer;
import com.habitrpg.android.habitica.utils.QuestCollectDeserializer;
@ -118,6 +120,7 @@ public class GSonFactoryCreator {
.registerTypeAdapter(Member.class, new MemberSerialization())
.registerTypeAdapter(WorldState.class, new WorldStateSerialization())
.registerTypeAdapter(FindUsernameResult.class, new FindUsernameResultDeserializer())
.registerTypeAdapter(Notification.class, new NotificationDeserializer())
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.create();
return GsonConverterFactory.create(gson);

View file

@ -27,6 +27,7 @@ import com.habitrpg.android.habitica.ui.activities.IntroActivity;
import com.habitrpg.android.habitica.ui.activities.LoginActivity;
import com.habitrpg.android.habitica.ui.activities.MainActivity;
import com.habitrpg.android.habitica.ui.activities.MaintenanceActivity;
import com.habitrpg.android.habitica.ui.activities.NotificationsActivity;
import com.habitrpg.android.habitica.ui.activities.GroupInviteActivity;
import com.habitrpg.android.habitica.ui.activities.PrefsActivity;
import com.habitrpg.android.habitica.ui.activities.ReportMessageActivity;
@ -94,6 +95,7 @@ import com.habitrpg.android.habitica.ui.fragments.social.party.PartyMemberListFr
import com.habitrpg.android.habitica.ui.fragments.tasks.TaskRecyclerViewFragment;
import com.habitrpg.android.habitica.ui.fragments.tasks.TasksFragment;
import com.habitrpg.android.habitica.ui.viewmodels.GroupViewModel;
import com.habitrpg.android.habitica.ui.viewmodels.NotificationsViewModel;
import com.habitrpg.android.habitica.ui.views.social.ChatBarView;
import com.habitrpg.android.habitica.ui.views.stats.BulkAllocateStatsDialog;
import com.habitrpg.android.habitica.ui.views.shops.PurchaseDialog;
@ -134,6 +136,8 @@ public interface AppComponent {
void inject(PrefsActivity prefsActivity);
void inject(NotificationsActivity notificationsActivity);
void inject(SetupActivity setupActivity);
void inject(SkillTasksActivity skillTasksActivity);
@ -302,6 +306,8 @@ public interface AppComponent {
void inject(@NotNull GroupViewModel viewModel);
void inject(@NotNull NotificationsViewModel viewModel);
void inject(@NotNull ChatFragment chatFragment);
void inject(@NotNull GiftIAPActivity giftIAPActivity);

View file

@ -212,6 +212,8 @@ interface ApiClient {
// Notifications
fun readNotification(notificationId: String): Flowable<List<*>>
fun readNotifications(notificationIds: Map<String, List<String>>): Flowable<List<*>>
fun seeNotifications(notificationIds: Map<String, List<String>>): Flowable<List<*>>
fun getErrorResponse(throwable: HttpException): ErrorResponse

View file

@ -52,6 +52,8 @@ interface UserRepository : BaseRepository {
fun runCron()
fun readNotification(id: String): Flowable<List<*>>
fun readNotifications(notificationIds: Map<String, List<String>>): Flowable<List<*>>
fun seeNotifications(notificationIds: Map<String, List<String>>): Flowable<List<*>>
fun changeCustomDayStart(dayStartTime: Int): Flowable<User>

View file

@ -172,6 +172,12 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli
override fun readNotification(id: String): Flowable<List<*>> = apiClient.readNotification(id)
override fun readNotifications(notificationIds: Map<String, List<String>>): Flowable<List<*>> =
apiClient.readNotifications(notificationIds)
override fun seeNotifications(notificationIds: Map<String, List<String>>): Flowable<List<*>> =
apiClient.seeNotifications(notificationIds)
override fun changeCustomDayStart(dayStartTime: Int): Flowable<User> {
val updateObject = HashMap<String, Any>()
updateObject["dayStart"] = dayStartTime

View file

@ -0,0 +1,95 @@
package com.habitrpg.android.habitica.helpers
import android.content.Context
import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable
import io.reactivex.subjects.BehaviorSubject
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.ApiClient
import com.habitrpg.android.habitica.events.ShowCheckinDialog
import com.habitrpg.android.habitica.events.ShowSnackbarEvent
import com.habitrpg.android.habitica.models.Notification
import com.habitrpg.android.habitica.models.notifications.LoginIncentiveData
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
import io.reactivex.functions.Consumer
import org.greenrobot.eventbus.EventBus
import java.util.HashMap
/**
* Created by krh12 on 12/9/2016.
*/
class NotificationsManager (private val context: Context) {
// @TODO: A queue for displaying alert dialogues
private val seenNotifications: MutableMap<String, Boolean>
private var apiClient: ApiClient? = null
private val notifications: BehaviorSubject<List<Notification>>
init {
this.seenNotifications = HashMap()
this.notifications = BehaviorSubject.create()
}
fun setNotifications(current: List<Notification>) {
this.notifications.onNext(current)
this.handlePopupNotifications(current)
}
fun getNotifications(): Flowable<List<Notification>> {
return this.notifications.toFlowable(BackpressureStrategy.LATEST)
}
fun getNotification(id: String): Notification? {
return this.notifications.value?.find { it.id == id }
}
fun setApiClient(apiClient: ApiClient?) {
this.apiClient = apiClient
}
fun handlePopupNotifications(notifications: List<Notification>): Boolean? {
notifications
.filter { !this.seenNotifications.containsKey(it.id) }
.map {
val notificationDisplayed = when (it.type) {
Notification.Type.LOGIN_INCENTIVE.type -> displayLoginIncentiveNotification(it)
else -> false
}
if (notificationDisplayed == true) {
this.seenNotifications[it.id] = true
}
}
return true
}
fun displayLoginIncentiveNotification(notification: Notification): Boolean? {
val notificationData = notification.data as LoginIncentiveData?
val nextUnlockText = context.getString(R.string.nextPrizeUnlocks, notificationData!!.nextRewardAt)
if (notificationData.rewardKey != null) {
val event = ShowCheckinDialog()
event.notification = notification
event.nextUnlockText = nextUnlockText
EventBus.getDefault().post(event)
} else {
val event = ShowSnackbarEvent()
event.title = notificationData.message
event.text = nextUnlockText
event.type = HabiticaSnackbar.SnackbarDisplayType.BLUE
EventBus.getDefault().post(event)
if (apiClient != null) {
// @TODO: This should be handled somewhere else? MAybe we notifiy via event
apiClient!!.readNotification(notification.id)
.subscribe(Consumer {}, RxErrorHandler.handleEmptyError())
}
}
return true
}
}

View file

@ -1,88 +0,0 @@
package com.habitrpg.android.habitica.models;
/**
* Created by krh12 on 11/28/2016.
*/
import com.habitrpg.android.habitica.models.notifications.NotificationData;
import java.util.HashMap;
import java.util.Map;
public class Notification {
public NotificationData data;
private String type;
private String createdAt;
private String id;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
/**
* @return The type
*/
public String getType() {
return type;
}
/**
* @param type The type
*/
public void setType(String type) {
this.type = type;
}
/**
* @return The createdAt
*/
public String getCreatedAt() {
return createdAt;
}
/**
* @param createdAt The createdAt
*/
public void setCreatedAt(String createdAt) {
this.createdAt = createdAt;
}
/**
*
* @return
* The data
*/
// public T getData() {
// return data;
// }
/**
*
* @param data
* The data
*/
// public void setData(T data) {
// this.data = data;
// }
/**
* @return The id
*/
public String getId() {
return id;
}
/**
* @param id The id
*/
public void setId(String id) {
this.id = id;
}
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}

View file

@ -0,0 +1,34 @@
package com.habitrpg.android.habitica.models
import com.habitrpg.android.habitica.models.notifications.*
class Notification {
enum class Type(val type: String) {
LOGIN_INCENTIVE("LOGIN_INCENTIVE"),
NEW_STUFF("NEW_STUFF"),
NEW_CHAT_MESSAGE("NEW_CHAT_MESSAGE"),
NEW_MYSTERY_ITEMS("NEW_MYSTERY_ITEMS"),
GROUP_TASK_NEEDS_WORK("GROUP_TASK_NEEDS_WORK"),
GROUP_TASK_APPROVED("GROUP_TASK_APPROVED"),
UNALLOCATED_STATS_POINTS("UNALLOCATED_STATS_POINTS");
}
var id: String = ""
var type: String? = null
var seen: Boolean? = null
var data: NotificationData? = null
fun getDataType(): java.lang.reflect.Type? {
return when (type) {
Type.LOGIN_INCENTIVE.type -> LoginIncentiveData::class.java
Type.NEW_STUFF.type -> NewStuffData::class.java
Type.NEW_CHAT_MESSAGE.type -> NewChatMessageData::class.java
Type.GROUP_TASK_NEEDS_WORK.type -> GroupTaskNeedsWorkData::class.java
Type.GROUP_TASK_APPROVED.type -> GroupTaskApprovedData::class.java
Type.UNALLOCATED_STATS_POINTS.type -> UnallocatedPointsData::class.java
else -> null
}
}
}

View file

@ -0,0 +1,7 @@
package com.habitrpg.android.habitica.models.notifications
open class GroupTaskApprovedData : NotificationData {
var groupId: String = ""
var message: String? = null
}

View file

@ -0,0 +1,7 @@
package com.habitrpg.android.habitica.models.notifications
open class GroupTaskNeedsWorkData : NotificationData {
var group: NotificationGroup? = null
var message: String? = null
}

View file

@ -0,0 +1,11 @@
package com.habitrpg.android.habitica.models.notifications
open class LoginIncentiveData : NotificationData {
var message: String? = null
var nextRewardAt: Int? = null
var rewardText: String? = null
var rewardKey: List<String>? = null
var reward: List<Reward>? = null
}

View file

@ -0,0 +1,5 @@
package com.habitrpg.android.habitica.models.notifications
open class NewChatMessageData : NotificationData {
var group: NotificationGroup? = null
}

View file

@ -0,0 +1,5 @@
package com.habitrpg.android.habitica.models.notifications
open class NewStuffData : NotificationData {
var title: String? = null
}

View file

@ -1,18 +0,0 @@
package com.habitrpg.android.habitica.models.notifications;
import java.util.List;
/**
* Created by krh12 on 11/30/2016.
*/
public class NotificationData {
public String groupId;
public String message;
public Integer nextRewardAt;
public String rewardText;
public List<String> rewardKey;
public List<Reward> reward;
}

View file

@ -0,0 +1,3 @@
package com.habitrpg.android.habitica.models.notifications
interface NotificationData

View file

@ -0,0 +1,7 @@
package com.habitrpg.android.habitica.models.notifications
open class NotificationGroup {
var id: String = ""
var name: String? = null
}

View file

@ -0,0 +1,5 @@
package com.habitrpg.android.habitica.models.notifications
open class UnallocatedPointsData : NotificationData {
var points: Int? = null
}

View file

@ -7,8 +7,8 @@ import com.habitrpg.android.habitica.api.HostConfig;
import com.habitrpg.android.habitica.api.MaintenanceApiService;
import com.habitrpg.android.habitica.data.ApiClient;
import com.habitrpg.android.habitica.data.implementation.ApiClientImpl;
import com.habitrpg.android.habitica.helpers.NotificationsManager;
import com.habitrpg.android.habitica.helpers.KeyHelper;
import com.habitrpg.android.habitica.helpers.PopupNotificationsManager;
import com.habitrpg.android.habitica.proxy.CrashlyticsProxy;
import javax.annotation.Nullable;
@ -36,14 +36,14 @@ public class ApiModule {
@Provides
@Singleton
public PopupNotificationsManager providesPopupNotificationsManager(Context context) {
return new PopupNotificationsManager(context);
public NotificationsManager providesPopupNotificationsManager(Context context) {
return new NotificationsManager(context);
}
@Provides
@Singleton
public ApiClient providesApiHelper(GsonConverterFactory gsonConverter, HostConfig hostConfig, CrashlyticsProxy crashlyticsProxy, PopupNotificationsManager popupNotificationsManager, Context context) {
return new ApiClientImpl(gsonConverter, hostConfig, crashlyticsProxy, popupNotificationsManager, context);
public ApiClient providesApiHelper(GsonConverterFactory gsonConverter, HostConfig hostConfig, CrashlyticsProxy crashlyticsProxy, NotificationsManager notificationsManager, Context context) {
return new ApiClientImpl(gsonConverter, hostConfig, crashlyticsProxy, notificationsManager, context);
}
@Provides

View file

@ -0,0 +1,250 @@
package com.habitrpg.android.habitica.ui.activities
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.Html
import android.view.LayoutInflater
import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProviders
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.Notification
import com.habitrpg.android.habitica.models.notifications.*
import com.habitrpg.android.habitica.ui.activities.MainActivity.Companion.NOTIFICATION_CLICK
import com.habitrpg.android.habitica.ui.viewmodels.NotificationsViewModel
import io.reactivex.functions.Consumer
import kotlinx.android.synthetic.main.activity_notifications.*
class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener {
lateinit var viewModel: NotificationsViewModel
lateinit var inflater: LayoutInflater
override fun getLayoutResId(): Int = R.layout.activity_notifications
private var notifications: List<Notification> = emptyList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupToolbar(toolbar)
inflater = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
viewModel = ViewModelProviders.of(this)
.get(NotificationsViewModel::class.java)
compositeSubscription.add(viewModel.getNotifications().subscribe(Consumer {
this.setNotifications(it)
viewModel.markNotificationsAsSeen(it)
}, RxErrorHandler.handleEmptyError()))
notifications_refresh_layout?.setOnRefreshListener(this)
}
override fun injectActivity(component: AppComponent?) {
component?.inject(this)
}
override fun onSupportNavigateUp(): Boolean {
if (supportFragmentManager.backStackEntryCount > 0) {
onBackPressed()
return true
}
return super.onSupportNavigateUp()
}
override fun onRefresh() {
notifications_refresh_layout.isRefreshing = true
compositeSubscription.add(viewModel.refreshNotifications().subscribe(Consumer {
notifications_refresh_layout.isRefreshing = false
}, RxErrorHandler.handleEmptyError()))
}
private fun setNotifications(notifications: List<Notification>) {
this.notifications = notifications
if (notification_items == null) {
return
}
notification_items.removeAllViewsInLayout()
when {
notifications.isEmpty() -> displayNoNotificationsView()
else -> displayNotificationsListView(notifications)
}
}
private fun displayNoNotificationsView() {
notification_items.showDividers = LinearLayout.SHOW_DIVIDER_NONE
notification_items.addView(
inflater.inflate(R.layout.no_notifications, notification_items, false)
)
}
private fun displayNotificationsListView(notifications: List<Notification>) {
notification_items.showDividers = LinearLayout.SHOW_DIVIDER_MIDDLE or LinearLayout.SHOW_DIVIDER_END
notification_items.addView(
createNotificationsHeaderView(notifications.count())
)
notifications.map {
val item: View? = when (it.type) {
Notification.Type.NEW_CHAT_MESSAGE.type -> createNewChatMessageNotification(it)
Notification.Type.NEW_STUFF.type -> createNewStuffNotification(it)
Notification.Type.UNALLOCATED_STATS_POINTS.type -> createUnallocatedStatsNotification(it)
Notification.Type.NEW_MYSTERY_ITEMS.type -> createMysteryItemsNotification(it)
Notification.Type.GROUP_TASK_NEEDS_WORK.type -> createGroupTaskNeedsWorkNotification(it)
Notification.Type.GROUP_TASK_APPROVED.type -> createGroupTaskApprovedNotification(it)
//TODO rest of the notification types
else -> null
}
notification_items.addView(item)
}
}
private fun createNotificationsHeaderView(notificationCount: Int): View? {
val header = inflater.inflate(R.layout.notifications_header, notification_items, false)
val badge = header?.findViewById(R.id.notifications_title_badge) as? TextView
badge?.text = notificationCount.toString()
val dismissAllButton = header?.findViewById(R.id.dismiss_all_button) as? Button
dismissAllButton?.setOnClickListener { viewModel.dismissAllNotifications(notifications) }
return header
}
private fun createNewChatMessageNotification(notification: Notification): View? {
val data = notification.data as? NewChatMessageData
val stringId = if (viewModel.isPartyMessage(data)) R.string.new_msg_party else R.string.new_msg_guild
return createNotificationItem(
notification,
fromHtml(getString(stringId, data?.group?.name))
)
}
private fun createNewStuffNotification(notification: Notification): View? {
val data = notification.data as? NewStuffData
val text = fromHtml("<b>" + getString(R.string.new_bailey_update) + "</b><br>" + data?.title)
return createNotificationItem(
notification,
text,
R.drawable.notifications_bailey
)
}
private fun createUnallocatedStatsNotification(notification: Notification): View? {
val data = notification.data as? UnallocatedPointsData
return createNotificationItem(
notification,
fromHtml(getString(R.string.unallocated_stats_points, data?.points.toString())),
R.drawable.notification_stat_sparkles
)
}
private fun createMysteryItemsNotification(notification: Notification): View? {
return createNotificationItem(
notification,
fromHtml(getString(R.string.new_subscriber_item)),
R.drawable.notification_mystery_item
)
}
private fun createGroupTaskNeedsWorkNotification(notification: Notification): View? {
val data = notification.data as? GroupTaskNeedsWorkData
val message = convertGroupMessageHtml(data?.message ?: "")
return createNotificationItem(
notification,
fromHtml(message),
null,
R.color.yellow_5
)
}
private fun createGroupTaskApprovedNotification(notification: Notification): View? {
val data = notification.data as? GroupTaskApprovedData
val message = convertGroupMessageHtml(data?.message ?: "")
return createNotificationItem(
notification,
fromHtml(message),
null,
R.color.green_10
)
}
/**
* Group task notifications have the message text in the notification data as HTML
* with <span class="notification-bold"> tags around emphasized words. So we just
* convert the span-tags to strong-tags to display correct parts as bold, since
* Html.fromHtml does not support CSS.
*/
private fun convertGroupMessageHtml(message: String): String {
// Using positive lookbehind to make sure "span" is preceded by "<" or "</"
val pattern = "(?<=</?)span".toRegex()
return message.replace(pattern, "strong")
}
private fun createNotificationItem(
notification: Notification,
messageText: CharSequence,
imageResourceId: Int? = null,
textColor: Int? = null
): View? {
val item = inflater.inflate(R.layout.notification_item, notification_items, false)
val container = item?.findViewById(R.id.notification_item) as? View
container?.setOnClickListener {
val resultIntent = Intent()
resultIntent.putExtra("notificationId", notification.id)
setResult(NOTIFICATION_CLICK, resultIntent)
finish()
}
val dismissButton = item?.findViewById(R.id.dismiss_button) as? ImageView
dismissButton?.setOnClickListener { viewModel.dismissNotification(notification) }
val messageTextView = item?.findViewById(R.id.message_text) as? TextView
messageTextView?.text = messageText
if (imageResourceId != null) {
val notificationImage = item?.findViewById(R.id.notification_image) as? ImageView
notificationImage?.setImageResource(imageResourceId)
notificationImage?.visibility = View.VISIBLE
}
if (textColor != null) {
messageTextView?.setTextColor(ContextCompat.getColor(this, textColor))
}
return item
}
private fun fromHtml(text: String): CharSequence {
return if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY)
} else {
@Suppress("DEPRECATION")
Html.fromHtml(text)
}
}
}

View file

@ -2,11 +2,15 @@ package com.habitrpg.android.habitica.ui.fragments
import android.app.ActionBar
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.view.GravityCompat
import androidx.fragment.app.DialogFragment
import com.habitrpg.android.habitica.HabiticaBaseApplication
@ -21,6 +25,8 @@ import com.habitrpg.android.habitica.models.inventory.Quest
import com.habitrpg.android.habitica.models.inventory.QuestContent
import com.habitrpg.android.habitica.models.social.Group
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.activities.MainActivity.Companion.NOTIFICATION_CLICK
import com.habitrpg.android.habitica.ui.activities.NotificationsActivity
import com.habitrpg.android.habitica.ui.adapter.NavigationDrawerAdapter
import com.habitrpg.android.habitica.ui.fragments.social.TavernDetailFragment
import com.habitrpg.android.habitica.ui.helpers.NavbarUtils
@ -97,6 +103,7 @@ class NavigationDrawerFragment : DialogFragment() {
messagesBadge.visibility = View.GONE
settingsBadge.visibility = View.GONE
notificationsBadge.visibility = View.GONE
/* Reenable this once the boss art can be displayed correctly.
@ -192,6 +199,7 @@ class NavigationDrawerFragment : DialogFragment() {
messagesButtonWrapper.setOnClickListener { setSelection(R.id.inboxFragment) }
settingsButtonWrapper.setOnClickListener { setSelection(R.id.prefsActivity) }
notificationsButtonWrapper.setOnClickListener { startNotificationsActivity() }
}
override fun onDestroy() {
@ -245,6 +253,18 @@ class NavigationDrawerFragment : DialogFragment() {
}
}
fun startNotificationsActivity() {
closeDrawer()
val activity = activity as? MainActivity
if (activity != null) {
// NotificationsActivity will return a result intent with a notificationId if a
// notification item was clicked
val intent = Intent(activity, NotificationsActivity::class.java)
activity.startActivityForResult(intent, NOTIFICATION_CLICK)
}
}
/**
* Users of this fragment must call this method to set UP the navigation drawer interactions.
*
@ -305,6 +325,24 @@ class NavigationDrawerFragment : DialogFragment() {
outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition)
}
fun setNotificationsCount(unreadNotifications: Int) {
if (unreadNotifications == 0) {
notificationsBadge.visibility = View.GONE
} else {
notificationsBadge.visibility = View.VISIBLE
notificationsBadge.text = unreadNotifications.toString()
}
}
fun setNotificationsSeen(allSeen: Boolean) {
context.notNull {
val colorId = if (allSeen) R.color.gray_200 else R.color.brand_400
val bg = notificationsBadge.background as GradientDrawable
bg.color = ColorStateList.valueOf(ContextCompat.getColor(it, colorId))
}
}
fun setMessagesCount(unreadMessages: Int) {
if (unreadMessages == 0) {
messagesBadge.visibility = View.GONE

View file

@ -0,0 +1,147 @@
package com.habitrpg.android.habitica.ui.viewmodels
import android.os.Bundle
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.helpers.NotificationsManager
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.Notification
import com.habitrpg.android.habitica.models.notifications.NewChatMessageData
import com.habitrpg.android.habitica.models.social.UserParty
import io.reactivex.Flowable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.Consumer
import java.util.HashMap
import javax.inject.Inject
open class NotificationsViewModel : BaseViewModel() {
@Inject
lateinit var notificationsManager: NotificationsManager
/**
* A list of notification types handled by this component.
* NOTE: Those not listed here won't be shown in the notification panel
*/
private val supportedNotificationTypes = listOf(
Notification.Type.NEW_STUFF.type,
Notification.Type.NEW_CHAT_MESSAGE.type,
Notification.Type.NEW_MYSTERY_ITEMS.type,
Notification.Type.GROUP_TASK_NEEDS_WORK.type,
Notification.Type.GROUP_TASK_APPROVED.type,
Notification.Type.UNALLOCATED_STATS_POINTS.type
)
/**
* Keep track of users party so we can determine which chat notifications are party chat
* instead of guild chat notifications.
*/
private var party: UserParty? = null
override fun inject(component: AppComponent) {
component.inject(this)
}
init {
disposable.add(userRepository.getUser()
.subscribe(Consumer {
party = it.party
}, RxErrorHandler.handleEmptyError()))
}
fun getNotifications(): Flowable<List<Notification>> {
return notificationsManager.getNotifications()
.map { filterSupportedTypes(it) }
.observeOn(AndroidSchedulers.mainThread())
}
fun getNotificationCount(): Flowable<Int> {
return getNotifications()
.map { it.count() }
.distinctUntilChanged()
}
fun allNotificationsSeen(): Flowable<Boolean> {
return getNotifications()
.map { it.all { notification -> notification.seen == true } }
.distinctUntilChanged()
}
fun refreshNotifications(): Flowable<*> {
return userRepository.retrieveUser(withTasks = false, forced = true)
}
private fun filterSupportedTypes(notifications: List<Notification>): List<Notification> {
return notifications.filter { supportedNotificationTypes.contains(it.type) }
}
fun isPartyMessage(data: NewChatMessageData?): Boolean {
if (party == null || data?.group?.id == null) {
return false
}
return party?.id == data.group?.id
}
fun dismissNotification(notification: Notification) {
disposable.add(userRepository.readNotification(notification.id)
.subscribe(Consumer {}, RxErrorHandler.handleEmptyError()))
}
fun dismissAllNotifications(notifications: List<Notification>) {
if (notifications.isEmpty()) {
return
}
val notificationIds = HashMap<String, List<String>>()
notificationIds["notificationIds"] = notifications.map { notification -> notification.id }
disposable.add(userRepository.readNotifications(notificationIds)
.subscribe(Consumer {}, RxErrorHandler.handleEmptyError()))
}
fun markNotificationsAsSeen(notifications: List<Notification>) {
val unseenIds = notifications.filter { notification -> notification.seen != true }
.map { notification -> notification.id }
if (unseenIds.isEmpty()) {
return
}
val notificationIds = HashMap<String, List<String>>()
notificationIds["notificationIds"] = unseenIds
disposable.add(userRepository.seeNotifications(notificationIds)
.subscribe(Consumer {}, RxErrorHandler.handleEmptyError()))
}
fun click(notificationId: String, navController: MainNavigationController) {
val notification = notificationsManager.getNotification(notificationId) ?: return
dismissNotification(notification)
when (notification.type) {
Notification.Type.NEW_STUFF.type -> navController.navigate(R.id.newsFragment)
Notification.Type.NEW_CHAT_MESSAGE.type -> clickNewChatMessage(notification, navController)
Notification.Type.NEW_MYSTERY_ITEMS.type -> navController.navigate(R.id.itemsFragment)
Notification.Type.UNALLOCATED_STATS_POINTS.type -> navController.navigate(R.id.statsFragment)
// Group tasks should go to Group tasks view if that is added to this app at some point
Notification.Type.GROUP_TASK_APPROVED.type -> navController.navigate(R.id.tasksFragment)
Notification.Type.GROUP_TASK_NEEDS_WORK.type -> navController.navigate(R.id.tasksFragment)
}
}
private fun clickNewChatMessage(notification: Notification, navController: MainNavigationController) {
val data = notification.data as? NewChatMessageData
if (isPartyMessage(data)) {
navController.navigate(R.id.partyFragment)
} else {
val bundle = Bundle()
bundle.putString("groupID", data?.group?.id)
bundle.putBoolean("isMember", true) // safe to assume user is member since they got the notification
navController.navigate(R.id.guildFragment, bundle)
}
}
}

View file

@ -0,0 +1,35 @@
package com.habitrpg.android.habitica.utils
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonParseException
import com.habitrpg.android.habitica.models.Notification
import java.lang.reflect.Type
class NotificationDeserializer : JsonDeserializer<Notification> {
@Throws(JsonParseException::class)
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Notification {
val notification = Notification()
val obj = json.asJsonObject
if (obj.has("id")) {
notification.id = obj.get("id").asString
}
if (obj.has("type")) {
notification.type = obj.get("type").asString
}
if (obj.has("seen")) {
notification.seen = obj.get("seen").asBoolean
}
val dataType = notification.getDataType()
if (obj.has("data") && dataType != null) {
notification.data = context.deserialize(obj.getAsJsonObject("data"), dataType)
}
return notification
}
}

View file

@ -2,7 +2,7 @@
//
//
//import com.habitrpg.android.habitica.data.implementation.ApiClientImpl;
//import com.habitrpg.android.habitica.helpers.PopupNotificationsManager;
//import com.habitrpg.android.habitica.helpers.NotificationsManager;
//import com.habitrpg.android.habitica.proxy.implementation.EmptyCrashlyticsProxy;
//import com.habitrpg.android.habitica.data.ApiClient;
//import com.habitrpg.android.habitica.BuildConfig;
@ -62,7 +62,7 @@
// BuildConfig.PORT,
// "",
// "");
// //apiClient = new ApiClientImpl(ApiClientImpl.createGsonFactory(), hostConfig, new EmptyCrashlyticsProxy(), new PopupNotificationsManager(context), context);
// //apiClient = new ApiClientImpl(ApiClientImpl.createGsonFactory(), hostConfig, new EmptyCrashlyticsProxy(), new NotificationsManager(context), context);
// //generateUser();
// }
//

View file

@ -1,26 +1,19 @@
package com.habitrpg.android.habitica.helpers;
import com.habitrpg.android.habitica.data.implementation.ApiClientImpl;
import com.habitrpg.android.habitica.BuildConfig;
import com.habitrpg.android.habitica.HabiticaApplication;
import com.habitrpg.android.habitica.api.HostConfig;
import com.habitrpg.android.habitica.proxy.implementation.EmptyCrashlyticsProxy;
import com.habitrpg.android.habitica.data.ApiClient;
import com.habitrpg.android.habitica.models.Notification;
import com.habitrpg.android.habitica.models.notifications.NotificationData;
import com.habitrpg.android.habitica.models.notifications.LoginIncentiveData;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowAlertDialog;
import org.robolectric.shadows.ShadowApplication;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Build;
@ -42,7 +35,7 @@ public class PopupNotificationsManagerTest {
public HostConfig hostConfig;
private Context context;
private PopupNotificationsManager popupNotificationsManager;
private NotificationsManager notificationsManager;
@Before
public void setUp() {
@ -51,13 +44,13 @@ public class PopupNotificationsManagerTest {
BuildConfig.PORT,
"",
"");
popupNotificationsManager =new PopupNotificationsManager(context);
notificationsManager =new NotificationsManager(context);
}
@Test
public void itDoesNothingWhenNotificationsListIsEmpty() {
List<Notification> notifications = new ArrayList<>();
popupNotificationsManager.showNotificationDialog(notifications);
notificationsManager.handlePopupNotifications(notifications);
AlertDialog alert =
ShadowAlertDialog.getLatestAlertDialog();
@ -74,13 +67,13 @@ public class PopupNotificationsManagerTest {
notifications.add(notification);
final PopupNotificationsManager testClass = Mockito.mock(PopupNotificationsManager.class);
Mockito.when(testClass.displayNotification(notification)).thenReturn(true);
Mockito.when(testClass.showNotificationDialog(notifications)).thenCallRealMethod();
final NotificationsManager testClass = Mockito.mock(NotificationsManager.class);
Mockito.when(testClass.displayLoginIncentiveNotification(notification)).thenReturn(true);
Mockito.when(testClass.handlePopupNotifications(notifications)).thenCallRealMethod();
testClass.showNotificationDialog(notifications);
testClass.handlePopupNotifications(notifications);
verify(testClass, times(0)).displayNotification(notification);
verify(testClass, times(0)).displayLoginIncentiveNotification(notification);
}
@Test
@ -89,22 +82,22 @@ public class PopupNotificationsManagerTest {
List<Notification> notifications = new ArrayList<>();
NotificationData notificationData = new NotificationData();
notificationData.message = testTitle;
LoginIncentiveData notificationData = new LoginIncentiveData();
notificationData.setMessage(testTitle);
Notification notification = new Notification();
notification.setType("LOGIN_INCENTIVE");
notification.data = notificationData;
notification.setData(notificationData);
notifications.add(notification);
final PopupNotificationsManager testClass = Mockito.mock(PopupNotificationsManager.class);
Mockito.when(testClass.displayNotification(notification)).thenReturn(true);
Mockito.when(testClass.showNotificationDialog(notifications)).thenCallRealMethod();
final NotificationsManager testClass = Mockito.mock(NotificationsManager.class);
Mockito.when(testClass.displayLoginIncentiveNotification(notification)).thenReturn(true);
Mockito.when(testClass.handlePopupNotifications(notifications)).thenCallRealMethod();
testClass.showNotificationDialog(notifications);
testClass.handlePopupNotifications(notifications);
verify(testClass, times(1)).displayNotification(notification);
verify(testClass, times(1)).displayLoginIncentiveNotification(notification);
}
@Test
@ -113,22 +106,22 @@ public class PopupNotificationsManagerTest {
List<Notification> notifications = new ArrayList<>();
NotificationData notificationData = new NotificationData();
notificationData.message = testTitle;
LoginIncentiveData notificationData = new LoginIncentiveData();
notificationData.setMessage(testTitle);
Notification notification = new Notification();
notification.setType("LOGIN_INCENTIVE");
notification.data = notificationData;
notification.setData(notificationData);
notifications.add(notification);
notifications.add(notification);
final PopupNotificationsManager testClass = Mockito.mock(PopupNotificationsManager.class);
Mockito.when(testClass.displayNotification(notification)).thenReturn(true);
Mockito.when(testClass.showNotificationDialog(notifications)).thenCallRealMethod();
final NotificationsManager testClass = Mockito.mock(NotificationsManager.class);
Mockito.when(testClass.displayLoginIncentiveNotification(notification)).thenReturn(true);
Mockito.when(testClass.handlePopupNotifications(notifications)).thenCallRealMethod();
testClass.showNotificationDialog(notifications);
testClass.handlePopupNotifications(notifications);
verify(testClass, times(1)).displayNotification(notification);
verify(testClass, times(1)).displayLoginIncentiveNotification(notification);
}
}