In-app notifications #969

Merged
akinwale merged 13 commits from in-app-notifications into master 2020-08-18 15:19:35 +02:00
13 changed files with 171 additions and 37 deletions
Showing only changes of commit 7f9ab48855 - Show all commits

View file

@ -36,6 +36,7 @@ public class LbrynetMessagingService extends FirebaseMessagingService {
private static final String TAG = "LbrynetMessagingService";
private static final String NOTIFICATION_CHANNEL_ID = "io.lbry.browser.LBRY_ENGAGEMENT_CHANNEL";
private static final String TYPE_COMMENT = "comment";
private static final String TYPE_SUBSCRIPTION = "subscription";
private static final String TYPE_REWARD = "reward";
private static final String TYPE_INTERESTS = "interests";
@ -169,6 +170,9 @@ public class LbrynetMessagingService extends FirebaseMessagingService {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
List<String> enabledTypes = new ArrayList<String>();
if (sp.getBoolean(MainActivity.PREFERENCE_KEY_NOTIFICATION_COMMENTS, true)) {
enabledTypes.add(TYPE_COMMENT);
}
if (sp.getBoolean(MainActivity.PREFERENCE_KEY_NOTIFICATION_SUBSCRIPTIONS, true)) {
enabledTypes.add(TYPE_SUBSCRIPTION);
}

View file

@ -286,6 +286,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
public static final String PREFERENCE_KEY_DARK_MODE = "io.lbry.browser.preference.userinterface.DarkMode";
public static final String PREFERENCE_KEY_SHOW_MATURE_CONTENT = "io.lbry.browser.preference.userinterface.ShowMatureContent";
public static final String PREFERENCE_KEY_SHOW_URL_SUGGESTIONS = "io.lbry.browser.preference.userinterface.UrlSuggestions";
public static final String PREFERENCE_KEY_NOTIFICATION_COMMENTS = "io.lbry.browser.preference.notifications.Comments";
public static final String PREFERENCE_KEY_NOTIFICATION_SUBSCRIPTIONS = "io.lbry.browser.preference.notifications.Subscriptions";
public static final String PREFERENCE_KEY_NOTIFICATION_REWARDS = "io.lbry.browser.preference.notifications.Rewards";
public static final String PREFERENCE_KEY_NOTIFICATION_CONTENT_INTERESTS = "io.lbry.browser.preference.notifications.ContentInterests";
@ -571,7 +572,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
public boolean isBackgroundPlaybackEnabled() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
return sp.getBoolean(PREFERENCE_KEY_BACKGROUND_PLAYBACK, false);
return sp.getBoolean(PREFERENCE_KEY_BACKGROUND_PLAYBACK, true);
}
public boolean initialSubscriptionMergeDone() {

View file

@ -193,6 +193,10 @@ public class ClaimListAdapter extends RecyclerView.Adapter<ClaimListAdapter.View
protected TextView fileSizeView;
protected ProgressBar downloadProgressView;
protected TextView deviceView;
protected View loadingImagePlaceholder;
protected View loadingTextPlaceholder1;
protected View loadingTextPlaceholder2;
public ViewHolder(View v) {
super(v);
feeContainer = v.findViewById(R.id.claim_fee_container);
@ -212,6 +216,10 @@ public class ClaimListAdapter extends RecyclerView.Adapter<ClaimListAdapter.View
fileSizeView = v.findViewById(R.id.claim_file_size);
downloadProgressView = v.findViewById(R.id.claim_download_progress);
deviceView = v.findViewById(R.id.claim_view_device);
loadingImagePlaceholder = v.findViewById(R.id.claim_thumbnail_placeholder);
loadingTextPlaceholder1 = v.findViewById(R.id.claim_text_loading_placeholder_1);
loadingTextPlaceholder2 = v.findViewById(R.id.claim_text_loading_placeholder_2);
}
}
@ -394,7 +402,7 @@ public class ClaimListAdapter extends RecyclerView.Adapter<ClaimListAdapter.View
});
vh.publishTimeView.setVisibility(!isPending ? View.VISIBLE : View.GONE);
vh.pendingTextView.setVisibility(isPending ? View.VISIBLE : View.GONE);
vh.pendingTextView.setVisibility(isPending && !item.isLoadingPlaceholder() ? View.VISIBLE : View.GONE);
vh.repostInfoView.setVisibility(isRepost && type != VIEW_TYPE_FEATURED ? View.VISIBLE : View.GONE);
vh.repostChannelView.setText(isRepost ? original.getSigningChannel().getName() : null);
vh.repostChannelView.setOnClickListener(new View.OnClickListener() {
@ -417,6 +425,13 @@ public class ClaimListAdapter extends RecyclerView.Adapter<ClaimListAdapter.View
vh.noThumbnailView.setVisibility(Helper.isNullOrEmpty(thumbnailUrl) ? View.VISIBLE : View.GONE);
Helper.setIconViewBackgroundColor(vh.noThumbnailView, bgColor, false, context);
Helper.setViewVisibility(vh.loadingImagePlaceholder, item.isLoadingPlaceholder() ? View.VISIBLE : View.GONE);
Helper.setViewVisibility(vh.loadingTextPlaceholder1, item.isLoadingPlaceholder() ? View.VISIBLE : View.GONE);
Helper.setViewVisibility(vh.loadingTextPlaceholder2, item.isLoadingPlaceholder() ? View.VISIBLE : View.GONE);
Helper.setViewVisibility(vh.titleView, !item.isLoadingPlaceholder() ? View.VISIBLE : View.GONE);
Helper.setViewVisibility(vh.publisherView, !item.isLoadingPlaceholder() ? View.VISIBLE : View.GONE);
Helper.setViewVisibility(vh.publishTimeView, !item.isLoadingPlaceholder() ? View.VISIBLE : View.GONE);
if (type == VIEW_TYPE_FEATURED && item.isUnresolved()) {
vh.durationView.setVisibility(View.GONE);
vh.titleView.setText("Nothing here. Publish something!");

View file

@ -1,12 +1,14 @@
package io.lbry.browser.adapter;
import android.content.Context;
import android.graphics.Color;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
@ -14,12 +16,16 @@ import java.util.List;
import io.lbry.browser.R;
import io.lbry.browser.model.lbryinc.LbryNotification;
import io.lbry.browser.ui.controls.SolidIconView;
import io.lbry.browser.utils.Helper;
import lombok.Getter;
import lombok.Setter;
public class NotificationListAdapter extends RecyclerView.Adapter<NotificationListAdapter.ViewHolder> {
private static final String RULE_CREATOR_SUBSCRIBER = "creator_subscriber";
private static final String RULE_COMMENT = "comment";
private Context context;
private List<LbryNotification> items;
@Setter
@ -37,11 +43,13 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi
protected TextView titleView;
protected TextView bodyView;
protected TextView timeView;
protected SolidIconView iconView;
public ViewHolder(View v) {
super(v);
titleView = v.findViewById(R.id.notification_title);
bodyView = v.findViewById(R.id.notification_body);
timeView = v.findViewById(R.id.notification_time);
iconView = v.findViewById(R.id.notification_icon);
}
}
@ -77,6 +85,27 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi
return new NotificationListAdapter.ViewHolder(v);
}
private int getStringIdForRule(String rule) {
if (RULE_CREATOR_SUBSCRIBER.equalsIgnoreCase(rule)) {
return R.string.fa_heart;
}
if (RULE_COMMENT.equalsIgnoreCase(rule)) {
return R.string.fa_comment_alt;
}
return R.string.fa_asterisk;
}
private int getColorForRule(String rule) {
if (RULE_CREATOR_SUBSCRIBER.equalsIgnoreCase(rule)) {
return Color.RED;
}
if (RULE_COMMENT.equalsIgnoreCase(rule)) {
return ContextCompat.getColor(context, R.color.nextLbryGreen);
}
return ContextCompat.getColor(context, R.color.lbryGreen);
}
@Override
public void onBindViewHolder(NotificationListAdapter.ViewHolder vh, int position) {
LbryNotification notification = items.get(position);
@ -87,6 +116,10 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi
notification.getTimestamp().getTime(),
System.currentTimeMillis(), 0, DateUtils.FORMAT_ABBREV_RELATIVE));
vh.iconView.setText(getStringIdForRule(notification.getRule()));
vh.iconView.setTextColor(getColorForRule(notification.getRule()));
vh.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {

View file

@ -21,7 +21,7 @@ import io.lbry.browser.utils.Helper;
import io.lbry.browser.utils.LbryUri;
public class DatabaseHelper extends SQLiteOpenHelper {
public static final int DATABASE_VERSION = 4;
public static final int DATABASE_VERSION = 5;
public static final String DATABASE_NAME = "LbryApp.db";
private static DatabaseHelper instance;
@ -55,7 +55,9 @@ public class DatabaseHelper extends SQLiteOpenHelper {
", description TEXT" +
", thumbnail_url TEXT" +
", target_url TEXT" +
", rule TEXT" +
", is_read INTEGER DEFAULT 0 NOT NULL" +
", is_seen INTEGER DEFAULT 0 NOT NULL " +
", timestamp TEXT NOT NULL)",
};
private static final String[] SQL_CREATE_INDEXES = {
@ -87,7 +89,11 @@ public class DatabaseHelper extends SQLiteOpenHelper {
private static final String[] SQL_V3_V4_UPGRADE = {
"ALTER TABLE notifications ADD COLUMN remote_id INTEGER",
"CREATE UNIQUE INDEX idx_notification_remote_id ON notifications (remote_id)",
"CREATE UNIQUE INDEX idx_notification_remote_id ON notifications (remote_id)"
};
private static final String[] SQL_V4_V5_UPGRADE = {
"ALTER TABLE notifications ADD COLUMN rule TEXT",
"ALTER TABLE notifications ADD COLUMN is_seen TEXT"
};
private static final String SQL_INSERT_SUBSCRIPTION = "REPLACE INTO subscriptions (channel_name, url) VALUES (?, ?)";
@ -100,8 +106,8 @@ public class DatabaseHelper extends SQLiteOpenHelper {
private static final String SQL_CLEAR_URL_HISTORY_BEFORE_TIME = "DELETE FROM url_history WHERE timestamp < ?";
private static final String SQL_GET_RECENT_URL_HISTORY = "SELECT value, url, type FROM url_history ORDER BY timestamp DESC LIMIT 10";
private static final String SQL_INSERT_NOTIFICATION = "REPLACE INTO notifications (remote_id, title, description, target_url, is_read, timestamp) VALUES (?, ?, ?, ?, ?, ?)";
private static final String SQL_GET_NOTIFICATIONS = "SELECT id, title, description, target_url, timestamp FROM notifications ORDER BY timestamp DESC LIMIT 500";
private static final String SQL_INSERT_NOTIFICATION = "REPLACE INTO notifications (remote_id, title, description, rule, target_url, is_read, is_seen, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
private static final String SQL_GET_NOTIFICATIONS = "SELECT id, title, description, rule, target_url, is_read, is_seen, timestamp FROM notifications ORDER BY timestamp DESC LIMIT 500";
private static final String SQL_GET_UNREAD_NOTIFICATIONS_COUNT = "SELECT COUNT(id) FROM notifications WHERE is_read <> 1";
private static final String SQL_MARK_NOTIFICATIONS_READ = "UPDATE notifications SET is_read = 1 WHERE is_read = 0";
@ -153,6 +159,11 @@ public class DatabaseHelper extends SQLiteOpenHelper {
db.execSQL(sql);
}
}
if (oldVersion < 5) {
for (String sql : SQL_V4_V5_UPGRADE) {
db.execSQL(sql);
}
}
}
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
@ -298,8 +309,10 @@ public class DatabaseHelper extends SQLiteOpenHelper {
notification.getRemoteId(),
notification.getTitle(),
notification.getDescription(),
notification.getRule(),
notification.getTargetUrl(),
notification.isRead() ? 1 : 0,
notification.isSeen() ? 1 : 0,
new SimpleDateFormat(Helper.ISO_DATE_FORMAT_PATTERN).format(notification.getTimestamp() != null ? notification.getTimestamp() : new Date())
});
}
@ -310,12 +323,16 @@ public class DatabaseHelper extends SQLiteOpenHelper {
cursor = db.rawQuery(SQL_GET_NOTIFICATIONS, null);
while (cursor.moveToNext()) {
LbryNotification notification = new LbryNotification();
notification.setId(cursor.getLong(0));
notification.setTitle(cursor.getString(1));
notification.setDescription(cursor.getString(2));
notification.setTargetUrl(cursor.getString(3));
int columnIndex = 0;
notification.setId(cursor.getLong(columnIndex++));
notification.setTitle(cursor.getString(columnIndex++));
notification.setDescription(cursor.getString(columnIndex++));
notification.setRule(cursor.getString(columnIndex++));
notification.setTargetUrl(cursor.getString(columnIndex++));
notification.setRead(cursor.getInt(columnIndex++) == 1);
notification.setSeen(cursor.getInt(columnIndex++) == 1);
try {
notification.setTimestamp(new SimpleDateFormat(Helper.ISO_DATE_FORMAT_PATTERN).parse(cursor.getString(4)));
notification.setTimestamp(new SimpleDateFormat(Helper.ISO_DATE_FORMAT_PATTERN).parse(cursor.getString(columnIndex++)));
} catch (ParseException ex) {
// invalid timestamp (which shouldn't happen). Skip this item
continue;

View file

@ -54,6 +54,7 @@ public class Claim {
@EqualsAndHashCode.Include
private boolean placeholder;
private boolean placeholderAnonymous;
private boolean loadingPlaceholder;
private boolean featured;
private boolean unresolved; // used for featured
private String address;

View file

@ -10,8 +10,10 @@ public class LbryNotification {
private long remoteId;
private String title;
private String description;
private String rule;
private String thumbnailUrl;
private String targetUrl;
private boolean read;
private boolean seen;
private Date timestamp;
}

View file

@ -70,8 +70,10 @@ public class NotificationListTask extends AsyncTask<Void, Void, List<LbryNotific
}
}
notification.setRule(Helper.getJSONString("notification_rule", null, item));
notification.setRemoteId(Helper.getJSONLong("id", 0, item));
notification.setRead(Helper.getJSONBoolean("is_read", false, item));
notification.setSeen(Helper.getJSONBoolean("is_seen", false, item));
try {
SimpleDateFormat dateFormat = new SimpleDateFormat(Helper.ISO_DATE_FORMAT_JSON, Locale.US);

View file

@ -2086,10 +2086,21 @@ public class FileViewFragment extends BaseFragment implements
// reset the list view
View root = getView();
if (claim != null && root != null) {
Context context = getContext();
List<Claim> loadingPlaceholders = new ArrayList<>();
for (int i = 0; i < 15; i++) {
Claim placeholder = new Claim();
placeholder.setLoadingPlaceholder(true);
loadingPlaceholders.add(placeholder);
}
relatedContentAdapter = new ClaimListAdapter(loadingPlaceholders, context);
RecyclerView relatedContentList = root.findViewById(R.id.file_view_related_content_list);
relatedContentList.setAdapter(relatedContentAdapter);
String title = claim.getTitle();
String claimId = claim.getClaimId();
ProgressBar relatedLoading = root.findViewById(R.id.file_view_related_content_progress);
Context context = getContext();
boolean canShowMatureContent = false;
if (context != null) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
@ -2109,10 +2120,14 @@ public class FileViewFragment extends BaseFragment implements
Context ctx = getContext();
if (ctx != null) {
relatedContentAdapter = new ClaimListAdapter(filteredClaims, ctx);
relatedContentAdapter.setItems(filteredClaims);
relatedContentAdapter.setListener(new ClaimListAdapter.ClaimListItemListener() {
@Override
public void onClaimClicked(Claim claim) {
if (claim.isLoadingPlaceholder()) {
return;
}
if (context instanceof MainActivity) {
MainActivity activity = (MainActivity) context;
if (claim.getName().startsWith("@")) {
@ -2154,7 +2169,7 @@ public class FileViewFragment extends BaseFragment implements
View root = getView();
ProgressBar commentsLoading = root.findViewById(R.id.file_view_comments_progress);
if (claim != null && root != null) {
CommentListTask task = new CommentListTask(1, 100, claim.getClaimId(), commentsLoading, new CommentListHandler() {
CommentListTask task = new CommentListTask(1, 200, claim.getClaimId(), commentsLoading, new CommentListHandler() {
@Override
public void onSuccess(List<Comment> comments, boolean hasReachedEnd) {
Context ctx = getContext();
@ -2224,7 +2239,7 @@ public class FileViewFragment extends BaseFragment implements
long st = System.currentTimeMillis();;
List<String> urlsToResolve = new ArrayList<>(commentListAdapter.getClaimUrlsToResolve());
if (urlsToResolve.size() > 0) {
ResolveTask task = new ResolveTask(urlsToResolve, Lbry.SDK_CONNECTION_STRING, null, new ClaimListResultHandler() {
ResolveTask task = new ResolveTask(urlsToResolve, Lbry.LBRY_TV_CONNECTION_STRING, null, new ClaimListResultHandler() {
@Override
public void onSuccess(List<Claim> claims) {
if (commentListAdapter != null) {

View file

@ -9,30 +9,42 @@
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<TextView
android:id="@+id/notification_title"
android:layout_width="wrap_content"
android:orientation="horizontal">
<io.lbry.browser.ui.controls.SolidIconView
android:id="@+id/notification_icon"
android:layout_marginLeft="16dp"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center_vertical"
android:textSize="24dp" />
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:fontFamily="@font/inter"
android:textStyle="bold"
android:textSize="13sp" />
<TextView
android:id="@+id/notification_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:fontFamily="@font/inter"
android:textSize="14sp" />
<TextView
android:id="@+id/notification_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/inter"
android:textSize="11sp" />
android:padding="16dp">
<TextView
android:id="@+id/notification_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:fontFamily="@font/inter"
android:textStyle="bold"
android:textSize="13sp" />
<TextView
android:id="@+id/notification_body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:fontFamily="@font/inter"
android:textSize="14sp" />
<TextView
android:id="@+id/notification_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/inter"
android:textSize="11sp" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>

View file

@ -138,6 +138,13 @@
android:src="@drawable/ic_check"
android:tint="@color/nextLbryGreen" />
</RelativeLayout>
<LinearLayout
android:id="@+id/claim_thumbnail_placeholder"
android:background="@color/lighterGrey"
android:orientation="vertical"
android:layout_width="160dp"
android:layout_height="90dp"
android:visibility="gone" />
</RelativeLayout>
<LinearLayout
@ -146,6 +153,22 @@
android:orientation="vertical"
android:layout_marginLeft="16dp"
android:layout_toRightOf="@id/claim_media_container">
<LinearLayout
android:id="@+id/claim_text_loading_placeholder_1"
android:background="@color/lighterGrey"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="16dp"
android:visibility="gone" />
<LinearLayout
android:id="@+id/claim_text_loading_placeholder_2"
android:background="@color/lighterGrey"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="16dp"
android:layout_marginTop="8dp"
android:visibility="gone" />
<TextView
android:id="@+id/claim_vanity_url"
android:layout_width="wrap_content"

View file

@ -629,4 +629,7 @@
<string name="fa_mobile_alt" translatable="false">&#xf3cd;</string>
<string name="fa_repost" translatable="false">&#xf079;</string>
<string name="fa_folder_open" translatable="false">&#xf07c;</string>
<string name="fa_asterisk" translatable="false">&#xf069;</string>
<string name="fa_comment_alt" translatable="false">&#xf27a;</string>
</resources>

View file

@ -7,6 +7,7 @@
app:iconSpaceReserved="false">
<SwitchPreferenceCompat
app:key="io.lbry.browser.preference.userinterface.BackgroundPlayback"
app:defaultValue="true"
app:title="@string/enable_background_playback"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
@ -27,6 +28,11 @@
<PreferenceCategory
android:title="@string/notifications"
app:iconSpaceReserved="false">
<SwitchPreferenceCompat
app:key="io.lbry.browser.preference.notifications.Comments"
app:title="@string/comments"
app:defaultValue="true"
app:iconSpaceReserved="false" />
<SwitchPreferenceCompat
app:key="io.lbry.browser.preference.notifications.Subscriptions"
app:title="@string/subscriptions"