In-app notifications (#969)
* display notification bell icon beside URL bar * notification model * persist received notifications * Refresh notification list. Add is_read flag and display unread count. * Hide notifications when opening the navigatinon drawer. Open selected notification item before hiding the list. * update local store with remote notifications * use card view for notification items * make notification cards clickable * handle comment hash * fix remote notifications not loading. fix comment scroll. * Improve in-app comment notification linking. Add icons.
This commit is contained in:
parent
ddcb190457
commit
4d024c06cc
26 changed files with 892 additions and 57 deletions
|
@ -101,8 +101,8 @@ dependencies {
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||||
|
|
||||||
__32bitImplementation 'io.lbry:lbrysdk32:0.79.1'
|
__32bitImplementation 'io.lbry:lbrysdk32:0.80.0'
|
||||||
__64bitImplementation 'io.lbry:lbrysdk64:0.79.1'
|
__64bitImplementation 'io.lbry:lbrysdk64:0.80.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'com.google.gms.google-services'
|
apply plugin: 'com.google.gms.google-services'
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.media.RingtoneManager;
|
import android.media.RingtoneManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
@ -20,19 +21,22 @@ import com.google.firebase.analytics.FirebaseAnalytics;
|
||||||
import com.google.firebase.messaging.FirebaseMessagingService;
|
import com.google.firebase.messaging.FirebaseMessagingService;
|
||||||
import com.google.firebase.messaging.RemoteMessage;
|
import com.google.firebase.messaging.RemoteMessage;
|
||||||
|
|
||||||
|
import io.lbry.browser.data.DatabaseHelper;
|
||||||
|
import io.lbry.browser.model.lbryinc.LbryNotification;
|
||||||
|
import io.lbry.browser.utils.Helper;
|
||||||
import io.lbry.browser.utils.LbryAnalytics;
|
import io.lbry.browser.utils.LbryAnalytics;
|
||||||
import io.lbry.lbrysdk.LbrynetService;
|
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class LbrynetMessagingService extends FirebaseMessagingService {
|
public class LbrynetMessagingService extends FirebaseMessagingService {
|
||||||
|
public static final String ACTION_NOTIFICATION_RECEIVED = "io.lbry.browser.Broadcast.NotificationReceived";
|
||||||
|
|
||||||
private static final String TAG = "LbrynetMessagingService";
|
private static final String TAG = "LbrynetMessagingService";
|
||||||
private static final String NOTIFICATION_CHANNEL_ID = "io.lbry.browser.LBRY_ENGAGEMENT_CHANNEL";
|
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_SUBSCRIPTION = "subscription";
|
||||||
private static final String TYPE_REWARD = "reward";
|
private static final String TYPE_REWARD = "reward";
|
||||||
private static final String TYPE_INTERESTS = "interests";
|
private static final String TYPE_INTERESTS = "interests";
|
||||||
|
@ -52,10 +56,7 @@ public class LbrynetMessagingService extends FirebaseMessagingService {
|
||||||
String title = payload.get("title");
|
String title = payload.get("title");
|
||||||
String body = payload.get("body");
|
String body = payload.get("body");
|
||||||
String name = payload.get("name"); // notification name
|
String name = payload.get("name"); // notification name
|
||||||
String contentTitle = payload.get("content_title");
|
String hash = payload.get("hash"); // comment hash
|
||||||
String channelUrl = payload.get("channel_url");
|
|
||||||
//String publishTime = payload.get("publish_time");
|
|
||||||
String publishTime = null;
|
|
||||||
|
|
||||||
if (type != null && getEnabledTypes().indexOf(type) > -1 && body != null && body.trim().length() > 0) {
|
if (type != null && getEnabledTypes().indexOf(type) > -1 && body != null && body.trim().length() > 0) {
|
||||||
// only log the receive event for valid notifications received
|
// only log the receive event for valid notifications received
|
||||||
|
@ -65,7 +66,34 @@ public class LbrynetMessagingService extends FirebaseMessagingService {
|
||||||
firebaseAnalytics.logEvent(LbryAnalytics.EVENT_LBRY_NOTIFICATION_RECEIVE, bundle);
|
firebaseAnalytics.logEvent(LbryAnalytics.EVENT_LBRY_NOTIFICATION_RECEIVE, bundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendNotification(title, body, type, url, name, contentTitle, channelUrl, publishTime);
|
if (!Helper.isNullOrEmpty(hash)) {
|
||||||
|
url = String.format("%s?comment_hash=%s", url, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendNotification(title, body, type, url, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// persist the notification data
|
||||||
|
try {
|
||||||
|
DatabaseHelper helper = DatabaseHelper.getInstance();
|
||||||
|
SQLiteDatabase db = helper.getWritableDatabase();
|
||||||
|
LbryNotification lnotification = new LbryNotification();
|
||||||
|
lnotification.setTitle(title);
|
||||||
|
lnotification.setDescription(body);
|
||||||
|
lnotification.setTargetUrl(url);
|
||||||
|
lnotification.setTimestamp(new Date());
|
||||||
|
DatabaseHelper.createOrUpdateNotification(lnotification, db);
|
||||||
|
|
||||||
|
// send a broadcast
|
||||||
|
Intent intent = new Intent(ACTION_NOTIFICATION_RECEIVED);
|
||||||
|
intent.putExtra("title", title);
|
||||||
|
intent.putExtra("body", body);
|
||||||
|
intent.putExtra("url", url);
|
||||||
|
intent.putExtra("timestamp", lnotification.getTimestamp().getTime());
|
||||||
|
sendBroadcast(intent);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// don't fail if any error occurs while saving a notification
|
||||||
|
Log.e(TAG, "could not save notification", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,8 +125,7 @@ public class LbrynetMessagingService extends FirebaseMessagingService {
|
||||||
*
|
*
|
||||||
* @param messageBody FCM message body received.
|
* @param messageBody FCM message body received.
|
||||||
*/
|
*/
|
||||||
private void sendNotification(String title, String messageBody, String type, String url, String name,
|
private void sendNotification(String title, String messageBody, String type, String url, String name) {
|
||||||
String contentTitle, String channelUrl, String publishTime) {
|
|
||||||
//Intent intent = new Intent(this, MainActivity.class);
|
//Intent intent = new Intent(this, MainActivity.class);
|
||||||
//intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
//intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
|
@ -143,6 +170,9 @@ public class LbrynetMessagingService extends FirebaseMessagingService {
|
||||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
|
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
List<String> enabledTypes = new ArrayList<String>();
|
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)) {
|
if (sp.getBoolean(MainActivity.PREFERENCE_KEY_NOTIFICATION_SUBSCRIPTIONS, true)) {
|
||||||
enabledTypes.add(TYPE_SUBSCRIPTION);
|
enabledTypes.add(TYPE_SUBSCRIPTION);
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,7 @@ import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import io.lbry.browser.adapter.NavigationMenuAdapter;
|
import io.lbry.browser.adapter.NavigationMenuAdapter;
|
||||||
|
import io.lbry.browser.adapter.NotificationListAdapter;
|
||||||
import io.lbry.browser.adapter.UrlSuggestionListAdapter;
|
import io.lbry.browser.adapter.UrlSuggestionListAdapter;
|
||||||
import io.lbry.browser.data.DatabaseHelper;
|
import io.lbry.browser.data.DatabaseHelper;
|
||||||
import io.lbry.browser.dialog.ContentScopeDialogFragment;
|
import io.lbry.browser.dialog.ContentScopeDialogFragment;
|
||||||
|
@ -137,6 +138,7 @@ import io.lbry.browser.model.Tag;
|
||||||
import io.lbry.browser.model.UrlSuggestion;
|
import io.lbry.browser.model.UrlSuggestion;
|
||||||
import io.lbry.browser.model.WalletBalance;
|
import io.lbry.browser.model.WalletBalance;
|
||||||
import io.lbry.browser.model.WalletSync;
|
import io.lbry.browser.model.WalletSync;
|
||||||
|
import io.lbry.browser.model.lbryinc.LbryNotification;
|
||||||
import io.lbry.browser.model.lbryinc.Reward;
|
import io.lbry.browser.model.lbryinc.Reward;
|
||||||
import io.lbry.browser.model.lbryinc.Subscription;
|
import io.lbry.browser.model.lbryinc.Subscription;
|
||||||
import io.lbry.browser.tasks.GenericTaskHandler;
|
import io.lbry.browser.tasks.GenericTaskHandler;
|
||||||
|
@ -147,6 +149,7 @@ import io.lbry.browser.tasks.lbryinc.FetchRewardsTask;
|
||||||
import io.lbry.browser.tasks.LighthouseAutoCompleteTask;
|
import io.lbry.browser.tasks.LighthouseAutoCompleteTask;
|
||||||
import io.lbry.browser.tasks.MergeSubscriptionsTask;
|
import io.lbry.browser.tasks.MergeSubscriptionsTask;
|
||||||
import io.lbry.browser.tasks.claim.ResolveTask;
|
import io.lbry.browser.tasks.claim.ResolveTask;
|
||||||
|
import io.lbry.browser.tasks.lbryinc.NotificationListTask;
|
||||||
import io.lbry.browser.tasks.localdata.FetchRecentUrlHistoryTask;
|
import io.lbry.browser.tasks.localdata.FetchRecentUrlHistoryTask;
|
||||||
import io.lbry.browser.tasks.wallet.DefaultSyncTaskHandler;
|
import io.lbry.browser.tasks.wallet.DefaultSyncTaskHandler;
|
||||||
import io.lbry.browser.tasks.wallet.LoadSharedUserStateTask;
|
import io.lbry.browser.tasks.wallet.LoadSharedUserStateTask;
|
||||||
|
@ -224,6 +227,8 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
@Getter
|
@Getter
|
||||||
private String firebaseMessagingToken;
|
private String firebaseMessagingToken;
|
||||||
|
|
||||||
|
private NotificationListAdapter notificationListAdapter;
|
||||||
|
|
||||||
private Map<String, Fragment> openNavFragments;
|
private Map<String, Fragment> openNavFragments;
|
||||||
private static final Map<Class, Integer> fragmentClassNavIdMap = new HashMap<>();
|
private static final Map<Class, Integer> fragmentClassNavIdMap = new HashMap<>();
|
||||||
static {
|
static {
|
||||||
|
@ -282,6 +287,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_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_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_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_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_REWARDS = "io.lbry.browser.preference.notifications.Rewards";
|
||||||
public static final String PREFERENCE_KEY_NOTIFICATION_CONTENT_INTERESTS = "io.lbry.browser.preference.notifications.ContentInterests";
|
public static final String PREFERENCE_KEY_NOTIFICATION_CONTENT_INTERESTS = "io.lbry.browser.preference.notifications.ContentInterests";
|
||||||
|
@ -439,6 +445,8 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
|
|
||||||
// setup uri bar
|
// setup uri bar
|
||||||
setupUriBar();
|
setupUriBar();
|
||||||
|
initNotificationsPage();
|
||||||
|
loadUnreadNotificationsCount();
|
||||||
|
|
||||||
// other
|
// other
|
||||||
pendingSyncSetQueue = new ArrayList<>();
|
pendingSyncSetQueue = new ArrayList<>();
|
||||||
|
@ -475,6 +483,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
public void onDrawerSlide(View drawerView, float slideOffset) {
|
public void onDrawerSlide(View drawerView, float slideOffset) {
|
||||||
if (slideOffset != 0) {
|
if (slideOffset != 0) {
|
||||||
clearWunderbarFocus(findViewById(R.id.wunderbar));
|
clearWunderbarFocus(findViewById(R.id.wunderbar));
|
||||||
|
hideNotifications();
|
||||||
}
|
}
|
||||||
super.onDrawerSlide(drawerView, slideOffset);
|
super.onDrawerSlide(drawerView, slideOffset);
|
||||||
}
|
}
|
||||||
|
@ -503,10 +512,23 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
findViewById(R.id.wunderbar_notifications).setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
View container = findViewById(R.id.notifications_container);
|
||||||
|
if (container.getVisibility() != View.VISIBLE) {
|
||||||
|
showNotifications();
|
||||||
|
} else {
|
||||||
|
hideNotifications();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
findViewById(R.id.global_now_playing_card).setOnClickListener(new View.OnClickListener() {
|
findViewById(R.id.global_now_playing_card).setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
if (nowPlayingClaim != null && !Helper.isNullOrEmpty(nowPlayingClaimUrl)) {
|
if (nowPlayingClaim != null && !Helper.isNullOrEmpty(nowPlayingClaimUrl)) {
|
||||||
|
hideNotifications();
|
||||||
openFileUrl(nowPlayingClaimUrl);
|
openFileUrl(nowPlayingClaimUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -551,7 +573,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
|
|
||||||
public boolean isBackgroundPlaybackEnabled() {
|
public boolean isBackgroundPlaybackEnabled() {
|
||||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
|
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
return sp.getBoolean(PREFERENCE_KEY_BACKGROUND_PLAYBACK, false);
|
return sp.getBoolean(PREFERENCE_KEY_BACKGROUND_PLAYBACK, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean initialSubscriptionMergeDone() {
|
public boolean initialSubscriptionMergeDone() {
|
||||||
|
@ -717,8 +739,9 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openSelectedMenuItem() {
|
private void openSelectedMenuItem() {
|
||||||
|
hideNotifications();
|
||||||
|
|
||||||
switch (selectedMenuItemId) {
|
switch (selectedMenuItemId) {
|
||||||
// TODO: reverse map lookup for class?
|
|
||||||
case NavMenuItem.ID_ITEM_FOLLOWING:
|
case NavMenuItem.ID_ITEM_FOLLOWING:
|
||||||
openFragment(FollowingFragment.class, true, NavMenuItem.ID_ITEM_FOLLOWING);
|
openFragment(FollowingFragment.class, true, NavMenuItem.ID_ITEM_FOLLOWING);
|
||||||
break;
|
break;
|
||||||
|
@ -1067,7 +1090,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
View container = findViewById(R.id.url_suggestions_container);
|
View container = findViewById(R.id.url_suggestions_container);
|
||||||
View closeIcon = findViewById(R.id.wunderbar_close);
|
View closeIcon = findViewById(R.id.wunderbar_close);
|
||||||
EditText wunderbar = findViewById(R.id.wunderbar);
|
EditText wunderbar = findViewById(R.id.wunderbar);
|
||||||
wunderbar.setPadding(0, 0, visible ? getScaledValue(36) : 0, 0);
|
//wunderbar.setPadding(0, 0, visible ? getScaledValue(36) : 0, 0);
|
||||||
|
|
||||||
container.setVisibility(visible ? View.VISIBLE : View.GONE);
|
container.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||||
closeIcon.setVisibility(visible ? View.VISIBLE : View.GONE);
|
closeIcon.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||||
|
@ -1102,8 +1125,13 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
@Override
|
@Override
|
||||||
public void onFocusChange(View view, boolean hasFocus) {
|
public void onFocusChange(View view, boolean hasFocus) {
|
||||||
if (hasFocus) {
|
if (hasFocus) {
|
||||||
|
hideNotifications();
|
||||||
|
findViewById(R.id.wunderbar_notifications).setVisibility(View.INVISIBLE);
|
||||||
|
|
||||||
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
imm.showSoftInput(view, 0);
|
imm.showSoftInput(view, 0);
|
||||||
|
} else {
|
||||||
|
findViewById(R.id.wunderbar_notifications).setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canShowUrlSuggestions()) {
|
if (canShowUrlSuggestions()) {
|
||||||
|
@ -1636,6 +1664,14 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
private static final String CHANNEL_ID_PLAYBACK = "io.lbry.browser.LBRY_PLAYBACK_CHANNEL";
|
private static final String CHANNEL_ID_PLAYBACK = "io.lbry.browser.LBRY_PLAYBACK_CHANNEL";
|
||||||
private static final int PLAYBACK_NOTIFICATION_ID = 3;
|
private static final int PLAYBACK_NOTIFICATION_ID = 3;
|
||||||
|
|
||||||
|
public void initNotificationsPage() {
|
||||||
|
findViewById(R.id.notification_list_empty_container).setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
RecyclerView notificationsList = findViewById(R.id.notifications_list);
|
||||||
|
LinearLayoutManager llm = new LinearLayoutManager(this);
|
||||||
|
notificationsList.setLayoutManager(llm);
|
||||||
|
}
|
||||||
|
|
||||||
public void initPlaybackNotification() {
|
public void initPlaybackNotification() {
|
||||||
if (isBackgroundPlaybackEnabled()) {
|
if (isBackgroundPlaybackEnabled()) {
|
||||||
playerNotificationManager.setPlayer(MainActivity.appPlayer);
|
playerNotificationManager.setPlayer(MainActivity.appPlayer);
|
||||||
|
@ -2006,6 +2042,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
intentFilter.addAction(ACTION_OPEN_REWARDS_PAGE);
|
intentFilter.addAction(ACTION_OPEN_REWARDS_PAGE);
|
||||||
intentFilter.addAction(ACTION_PUBLISH_SUCCESSFUL);
|
intentFilter.addAction(ACTION_PUBLISH_SUCCESSFUL);
|
||||||
intentFilter.addAction(ACTION_SAVE_SHARED_USER_STATE);
|
intentFilter.addAction(ACTION_SAVE_SHARED_USER_STATE);
|
||||||
|
intentFilter.addAction(LbrynetMessagingService.ACTION_NOTIFICATION_RECEIVED);
|
||||||
requestsReceiver = new BroadcastReceiver() {
|
requestsReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
@ -2026,6 +2063,25 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
saveSharedUserState();
|
saveSharedUserState();
|
||||||
} else if (ACTION_PUBLISH_SUCCESSFUL.equalsIgnoreCase(action)) {
|
} else if (ACTION_PUBLISH_SUCCESSFUL.equalsIgnoreCase(action)) {
|
||||||
openPublishesOnSuccessfulPublish();
|
openPublishesOnSuccessfulPublish();
|
||||||
|
} else if (LbrynetMessagingService.ACTION_NOTIFICATION_RECEIVED.equalsIgnoreCase(action)) {
|
||||||
|
handleNotificationReceived(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleNotificationReceived(Intent intent) {
|
||||||
|
loadUnreadNotificationsCount();
|
||||||
|
|
||||||
|
if (notificationListAdapter != null) {
|
||||||
|
LbryNotification lnotification = new LbryNotification();
|
||||||
|
lnotification.setTitle(intent.getStringExtra("title"));
|
||||||
|
lnotification.setDescription(intent.getStringExtra("body"));
|
||||||
|
lnotification.setTargetUrl(intent.getStringExtra("url"));
|
||||||
|
lnotification.setTimestamp(new Date(intent.getLongExtra("timestamp", System.currentTimeMillis())));
|
||||||
|
// show at the top
|
||||||
|
notificationListAdapter.insertNotification(lnotification, 0);
|
||||||
|
findViewById(R.id.notification_list_empty_container).setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
loadRemoteNotifications();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2071,13 +2127,48 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
setBackgroundTint(Color.RED).setTextColor(Color.WHITE).show();
|
setBackgroundTint(Color.RED).setTextColor(Color.WHITE).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void showNotifications() {
|
||||||
|
clearWunderbarFocus(findViewById(R.id.wunderbar));
|
||||||
|
findViewById(R.id.notifications_container).setVisibility(View.VISIBLE);
|
||||||
|
((ImageView) findViewById(R.id.notifications_toggle_icon)).setColorFilter(ContextCompat.getColor(this, R.color.lbryGreen));
|
||||||
|
if (notificationListAdapter == null) {
|
||||||
|
loadRemoteNotifications();
|
||||||
|
}
|
||||||
|
markNotificationsRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hideNotifications() {
|
||||||
|
((ImageView) findViewById(R.id.notifications_toggle_icon)).setColorFilter(ContextCompat.getColor(this, R.color.actionBarForeground));
|
||||||
|
findViewById(R.id.notifications_container).setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markNotificationsRead() {
|
||||||
|
(new AsyncTask<Void, Void, Void>() {
|
||||||
|
protected Void doInBackground(Void... params) {
|
||||||
|
try {
|
||||||
|
SQLiteDatabase db = dbHelper.getWritableDatabase();
|
||||||
|
DatabaseHelper.markNotificationsRead(db);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
protected void onPostExecute(Void result) {
|
||||||
|
loadUnreadNotificationsCount();
|
||||||
|
}
|
||||||
|
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
|
|
||||||
if (findViewById(R.id.url_suggestions_container).getVisibility() == View.VISIBLE) {
|
if (findViewById(R.id.url_suggestions_container).getVisibility() == View.VISIBLE) {
|
||||||
clearWunderbarFocus(findViewById(R.id.wunderbar));
|
clearWunderbarFocus(findViewById(R.id.wunderbar));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (findViewById(R.id.notifications_container).getVisibility() == View.VISIBLE) {
|
||||||
|
hideNotifications();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (backPressInterceptor != null && backPressInterceptor.onBackPressed()) {
|
if (backPressInterceptor != null && backPressInterceptor.onBackPressed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -3051,6 +3142,90 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void displayUnreadNotificationCount(int count) {
|
||||||
|
String text = count > 99 ? "99+" : String.valueOf(count);
|
||||||
|
|
||||||
|
TextView badge = findViewById(R.id.notifications_badge_count);
|
||||||
|
badge.setVisibility(count > 0 ? View.VISIBLE : View.INVISIBLE);
|
||||||
|
badge.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadUnreadNotificationsCount() {
|
||||||
|
(new AsyncTask<Void, Void, Integer>() {
|
||||||
|
@Override
|
||||||
|
protected Integer doInBackground(Void... params) {
|
||||||
|
try {
|
||||||
|
SQLiteDatabase db = dbHelper.getReadableDatabase();
|
||||||
|
return DatabaseHelper.getUnreadNotificationsCount(db);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected void onPostExecute(Integer count) {
|
||||||
|
displayUnreadNotificationCount(count);
|
||||||
|
}
|
||||||
|
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadRemoteNotifications() {
|
||||||
|
findViewById(R.id.notification_list_empty_container).setVisibility(View.GONE);
|
||||||
|
NotificationListTask task = new NotificationListTask(this, findViewById(R.id.notifications_progress), new NotificationListTask.ListNotificationsHandler() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<LbryNotification> notifications) {
|
||||||
|
loadLocalNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception exception) {
|
||||||
|
// pass
|
||||||
|
Log.e(TAG, "error loading remote notifications", exception);
|
||||||
|
loadLocalNotifications();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadLocalNotifications() {
|
||||||
|
(new AsyncTask<Void, Void, List<LbryNotification>>() {
|
||||||
|
protected void onPreExecute() {
|
||||||
|
findViewById(R.id.notification_list_empty_container).setVisibility(View.GONE);
|
||||||
|
findViewById(R.id.notifications_progress).setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected List<LbryNotification> doInBackground(Void... params) {
|
||||||
|
List<LbryNotification> notifications = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
SQLiteDatabase db = dbHelper.getReadableDatabase();
|
||||||
|
notifications = DatabaseHelper.getNotifications(db);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
return notifications;
|
||||||
|
}
|
||||||
|
protected void onPostExecute(List<LbryNotification> notifications) {
|
||||||
|
findViewById(R.id.notification_list_empty_container).setVisibility(notifications.size() == 0 ? View.VISIBLE : View.GONE);
|
||||||
|
findViewById(R.id.notifications_progress).setVisibility(View.GONE);
|
||||||
|
notificationListAdapter = new NotificationListAdapter(notifications, MainActivity.this);
|
||||||
|
notificationListAdapter.setClickListener(new NotificationListAdapter.NotificationClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onNotificationClicked(LbryNotification notification) {
|
||||||
|
LbryUri target = LbryUri.tryParse(notification.getTargetUrl());
|
||||||
|
if (target != null) {
|
||||||
|
if (target.isChannel()) {
|
||||||
|
openChannelUrl(notification.getTargetUrl());
|
||||||
|
} else {
|
||||||
|
openFileUrl(notification.getTargetUrl());
|
||||||
|
}
|
||||||
|
hideNotifications();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
((RecyclerView) findViewById(R.id.notifications_list)).setAdapter(notificationListAdapter);
|
||||||
|
}
|
||||||
|
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
private void checkSyncedWallet() {
|
private void checkSyncedWallet() {
|
||||||
String password = Utils.getSecureValue(SECURE_VALUE_KEY_SAVED_PASSWORD, this, Lbry.KEYSTORE);
|
String password = Utils.getSecureValue(SECURE_VALUE_KEY_SAVED_PASSWORD, this, Lbry.KEYSTORE);
|
||||||
// Just check if the current user has a synced wallet, no need to do anything else here
|
// Just check if the current user has a synced wallet, no need to do anything else here
|
||||||
|
|
|
@ -193,6 +193,10 @@ public class ClaimListAdapter extends RecyclerView.Adapter<ClaimListAdapter.View
|
||||||
protected TextView fileSizeView;
|
protected TextView fileSizeView;
|
||||||
protected ProgressBar downloadProgressView;
|
protected ProgressBar downloadProgressView;
|
||||||
protected TextView deviceView;
|
protected TextView deviceView;
|
||||||
|
|
||||||
|
protected View loadingImagePlaceholder;
|
||||||
|
protected View loadingTextPlaceholder1;
|
||||||
|
protected View loadingTextPlaceholder2;
|
||||||
public ViewHolder(View v) {
|
public ViewHolder(View v) {
|
||||||
super(v);
|
super(v);
|
||||||
feeContainer = v.findViewById(R.id.claim_fee_container);
|
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);
|
fileSizeView = v.findViewById(R.id.claim_file_size);
|
||||||
downloadProgressView = v.findViewById(R.id.claim_download_progress);
|
downloadProgressView = v.findViewById(R.id.claim_download_progress);
|
||||||
deviceView = v.findViewById(R.id.claim_view_device);
|
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.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.repostInfoView.setVisibility(isRepost && type != VIEW_TYPE_FEATURED ? View.VISIBLE : View.GONE);
|
||||||
vh.repostChannelView.setText(isRepost ? original.getSigningChannel().getName() : null);
|
vh.repostChannelView.setText(isRepost ? original.getSigningChannel().getName() : null);
|
||||||
vh.repostChannelView.setOnClickListener(new View.OnClickListener() {
|
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);
|
vh.noThumbnailView.setVisibility(Helper.isNullOrEmpty(thumbnailUrl) ? View.VISIBLE : View.GONE);
|
||||||
Helper.setIconViewBackgroundColor(vh.noThumbnailView, bgColor, false, context);
|
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()) {
|
if (type == VIEW_TYPE_FEATURED && item.isUnresolved()) {
|
||||||
vh.durationView.setVisibility(View.GONE);
|
vh.durationView.setVisibility(View.GONE);
|
||||||
vh.titleView.setText("Nothing here. Publish something!");
|
vh.titleView.setText("Nothing here. Publish something!");
|
||||||
|
|
|
@ -61,6 +61,16 @@ public class CommentListAdapter extends RecyclerView.Adapter<CommentListAdapter.
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getPositionForComment(String commentHash) {
|
||||||
|
for (int i = 0; i < items.size(); i++) {
|
||||||
|
if (commentHash.equalsIgnoreCase(items.get(i).getId())) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
return items != null ? items.size() : 0;
|
return items != null ? items.size() : 0;
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
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;
|
||||||
|
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
|
||||||
|
private NotificationClickListener clickListener;
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private int customizeMode;
|
||||||
|
|
||||||
|
public NotificationListAdapter(List<LbryNotification> notifications, Context context) {
|
||||||
|
this.context = context;
|
||||||
|
this.items = new ArrayList<>(notifications);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getItemCount() {
|
||||||
|
return items != null ? items.size() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insertNotification(LbryNotification notification, int index) {
|
||||||
|
if (!items.contains(notification)) {
|
||||||
|
items.add(index, notification);
|
||||||
|
}
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNotification(LbryNotification notification) {
|
||||||
|
if (!items.contains(notification)) {
|
||||||
|
items.add(notification);
|
||||||
|
}
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTags(List<LbryNotification> notifications) {
|
||||||
|
for (LbryNotification notification : notifications) {
|
||||||
|
if (!items.contains(notification)) {
|
||||||
|
items.add(notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public NotificationListAdapter.ViewHolder onCreateViewHolder(ViewGroup root, int viewType) {
|
||||||
|
View v = LayoutInflater.from(context).inflate(R.layout.list_item_notification, root, false);
|
||||||
|
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);
|
||||||
|
vh.titleView.setText(notification.getTitle());
|
||||||
|
vh.titleView.setVisibility(!Helper.isNullOrEmpty(notification.getTitle()) ? View.VISIBLE : View.GONE);
|
||||||
|
vh.bodyView.setText(notification.getDescription());
|
||||||
|
vh.timeView.setText(DateUtils.getRelativeTimeSpanString(
|
||||||
|
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) {
|
||||||
|
if (clickListener != null) {
|
||||||
|
clickListener.onNotificationClicked(notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface NotificationClickListener {
|
||||||
|
void onNotificationClicked(LbryNotification notification);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,6 @@ import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
import android.opengl.Visibility;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
|
@ -13,16 +12,16 @@ import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import io.lbry.browser.exceptions.LbryUriException;
|
|
||||||
import io.lbry.browser.model.Tag;
|
import io.lbry.browser.model.Tag;
|
||||||
import io.lbry.browser.model.UrlSuggestion;
|
import io.lbry.browser.model.UrlSuggestion;
|
||||||
import io.lbry.browser.model.ViewHistory;
|
import io.lbry.browser.model.ViewHistory;
|
||||||
|
import io.lbry.browser.model.lbryinc.LbryNotification;
|
||||||
import io.lbry.browser.model.lbryinc.Subscription;
|
import io.lbry.browser.model.lbryinc.Subscription;
|
||||||
import io.lbry.browser.utils.Helper;
|
import io.lbry.browser.utils.Helper;
|
||||||
import io.lbry.browser.utils.LbryUri;
|
import io.lbry.browser.utils.LbryUri;
|
||||||
|
|
||||||
public class DatabaseHelper extends SQLiteOpenHelper {
|
public class DatabaseHelper extends SQLiteOpenHelper {
|
||||||
public static final int DATABASE_VERSION = 2;
|
public static final int DATABASE_VERSION = 5;
|
||||||
public static final String DATABASE_NAME = "LbryApp.db";
|
public static final String DATABASE_NAME = "LbryApp.db";
|
||||||
private static DatabaseHelper instance;
|
private static DatabaseHelper instance;
|
||||||
|
|
||||||
|
@ -48,7 +47,18 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
||||||
", thumbnail_url TEXT" +
|
", thumbnail_url TEXT" +
|
||||||
", release_time INTEGER " +
|
", release_time INTEGER " +
|
||||||
", device TEXT" +
|
", device TEXT" +
|
||||||
", timestamp TEXT NOT NULL)"
|
", timestamp TEXT NOT NULL)",
|
||||||
|
"CREATE TABLE notifications (" +
|
||||||
|
" id INTEGER PRIMARY KEY NOT NULL" +
|
||||||
|
", remote_id INTEGER NOT NULL" +
|
||||||
|
", title TEXT" +
|
||||||
|
", 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 = {
|
private static final String[] SQL_CREATE_INDEXES = {
|
||||||
"CREATE UNIQUE INDEX idx_subscription_url ON subscriptions (url)",
|
"CREATE UNIQUE INDEX idx_subscription_url ON subscriptions (url)",
|
||||||
|
@ -56,13 +66,36 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
||||||
"CREATE UNIQUE INDEX idx_url_history_url ON url_history (url)",
|
"CREATE UNIQUE INDEX idx_url_history_url ON url_history (url)",
|
||||||
"CREATE UNIQUE INDEX idx_tag_name ON tags (name)",
|
"CREATE UNIQUE INDEX idx_tag_name ON tags (name)",
|
||||||
"CREATE UNIQUE INDEX idx_view_history_url_device ON view_history (url, device)",
|
"CREATE UNIQUE INDEX idx_view_history_url_device ON view_history (url, device)",
|
||||||
"CREATE INDEX idx_view_history_device ON view_history (device)"
|
"CREATE INDEX idx_view_history_device ON view_history (device)",
|
||||||
|
"CREATE UNIQUE INDEX idx_notification_remote_id ON notifications (remote_id)",
|
||||||
|
"CREATE INDEX idx_notification_timestamp ON notifications (timestamp)"
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final String[] SQL_V1_V2_UPGRADE = {
|
private static final String[] SQL_V1_V2_UPGRADE = {
|
||||||
"ALTER TABLE view_history ADD COLUMN currency TEXT"
|
"ALTER TABLE view_history ADD COLUMN currency TEXT"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static final String[] SQL_V2_V3_UPGRADE = {
|
||||||
|
"CREATE TABLE notifications (" +
|
||||||
|
" id INTEGER PRIMARY KEY NOT NULL" +
|
||||||
|
", title TEXT" +
|
||||||
|
", description TEXT" +
|
||||||
|
", thumbnail_url TEXT" +
|
||||||
|
", target_url TEXT" +
|
||||||
|
", is_read INTEGER DEFAULT 0 NOT NULL" +
|
||||||
|
", timestamp TEXT NOT NULL)",
|
||||||
|
"CREATE INDEX idx_notification_timestamp ON notifications (timestamp)"
|
||||||
|
};
|
||||||
|
|
||||||
|
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)"
|
||||||
|
};
|
||||||
|
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 (?, ?)";
|
private static final String SQL_INSERT_SUBSCRIPTION = "REPLACE INTO subscriptions (channel_name, url) VALUES (?, ?)";
|
||||||
private static final String SQL_CLEAR_SUBSCRIPTIONS = "DELETE FROM subscriptions";
|
private static final String SQL_CLEAR_SUBSCRIPTIONS = "DELETE FROM subscriptions";
|
||||||
private static final String SQL_DELETE_SUBSCRIPTION = "DELETE FROM subscriptions WHERE url = ?";
|
private static final String SQL_DELETE_SUBSCRIPTION = "DELETE FROM subscriptions WHERE url = ?";
|
||||||
|
@ -73,6 +106,11 @@ 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_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_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, 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";
|
||||||
|
|
||||||
private static final String SQL_INSERT_VIEW_HISTORY =
|
private static final String SQL_INSERT_VIEW_HISTORY =
|
||||||
"REPLACE INTO view_history (url, claim_id, claim_name, cost, currency, title, publisher_claim_id, publisher_name, publisher_title, thumbnail_url, device, release_time, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
"REPLACE INTO view_history (url, claim_id, claim_name, cost, currency, title, publisher_claim_id, publisher_name, publisher_title, thumbnail_url, device, release_time, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||||
private static final String SQL_GET_VIEW_HISTORY =
|
private static final String SQL_GET_VIEW_HISTORY =
|
||||||
|
@ -111,6 +149,21 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
||||||
db.execSQL(sql);
|
db.execSQL(sql);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (oldVersion < 3) {
|
||||||
|
for (String sql : SQL_V2_V3_UPGRADE) {
|
||||||
|
db.execSQL(sql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (oldVersion < 4) {
|
||||||
|
for (String sql : SQL_V3_V4_UPGRADE) {
|
||||||
|
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) {
|
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
|
||||||
|
@ -251,4 +304,60 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
||||||
return subscriptions;
|
return subscriptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void createOrUpdateNotification(LbryNotification notification, SQLiteDatabase db) {
|
||||||
|
db.execSQL(SQL_INSERT_NOTIFICATION, new Object[] {
|
||||||
|
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())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public static List<LbryNotification> getNotifications(SQLiteDatabase db) {
|
||||||
|
List<LbryNotification> notifications = new ArrayList<>();
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
cursor = db.rawQuery(SQL_GET_NOTIFICATIONS, null);
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
LbryNotification notification = new LbryNotification();
|
||||||
|
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(columnIndex++)));
|
||||||
|
} catch (ParseException ex) {
|
||||||
|
// invalid timestamp (which shouldn't happen). Skip this item
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
notifications.add(notification);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Helper.closeCursor(cursor);
|
||||||
|
}
|
||||||
|
return notifications;
|
||||||
|
}
|
||||||
|
public static int getUnreadNotificationsCount(SQLiteDatabase db) {
|
||||||
|
int count = 0;
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
cursor = db.rawQuery(SQL_GET_UNREAD_NOTIFICATIONS_COUNT, null);
|
||||||
|
if (cursor.moveToNext()) {
|
||||||
|
count = cursor.getInt(0);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Helper.closeCursor(cursor);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
public static void markNotificationsRead(SQLiteDatabase db) {
|
||||||
|
db.execSQL(SQL_MARK_NOTIFICATIONS_READ);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ public class Claim {
|
||||||
@EqualsAndHashCode.Include
|
@EqualsAndHashCode.Include
|
||||||
private boolean placeholder;
|
private boolean placeholder;
|
||||||
private boolean placeholderAnonymous;
|
private boolean placeholderAnonymous;
|
||||||
|
private boolean loadingPlaceholder;
|
||||||
private boolean featured;
|
private boolean featured;
|
||||||
private boolean unresolved; // used for featured
|
private boolean unresolved; // used for featured
|
||||||
private String address;
|
private String address;
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package io.lbry.browser.model.lbryinc;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class LbryNotification {
|
||||||
|
private long id;
|
||||||
|
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;
|
||||||
|
}
|
|
@ -45,10 +45,12 @@ public class CommentListTask extends AsyncTask<Void, Void, List<Comment>> {
|
||||||
options.put("claim_id", claim);
|
options.put("claim_id", claim);
|
||||||
options.put("page", page);
|
options.put("page", page);
|
||||||
options.put("page_size", pageSize);
|
options.put("page_size", pageSize);
|
||||||
|
options.put("hidden", false);
|
||||||
options.put("include_replies", false);
|
options.put("include_replies", false);
|
||||||
options.put("is_channel_signature_valid", true);
|
options.put("is_channel_signature_valid", true);
|
||||||
|
options.put("skip_validation", true);
|
||||||
options.put("visible", true);
|
options.put("visible", true);
|
||||||
options.put("hidden", false);
|
|
||||||
|
|
||||||
JSONObject result = (JSONObject) Lbry.genericApiCall(Lbry.METHOD_COMMENT_LIST, options);
|
JSONObject result = (JSONObject) Lbry.genericApiCall(Lbry.METHOD_COMMENT_LIST, options);
|
||||||
JSONArray items = result.getJSONArray("items");
|
JSONArray items = result.getJSONArray("items");
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
package io.lbry.browser.tasks.lbryinc;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import io.lbry.browser.MainActivity;
|
||||||
|
import io.lbry.browser.data.DatabaseHelper;
|
||||||
|
import io.lbry.browser.exceptions.LbryioRequestException;
|
||||||
|
import io.lbry.browser.exceptions.LbryioResponseException;
|
||||||
|
import io.lbry.browser.model.lbryinc.LbryNotification;
|
||||||
|
import io.lbry.browser.utils.Helper;
|
||||||
|
import io.lbry.browser.utils.Lbryio;
|
||||||
|
|
||||||
|
public class NotificationListTask extends AsyncTask<Void, Void, List<LbryNotification>> {
|
||||||
|
private Context context;
|
||||||
|
private ListNotificationsHandler handler;
|
||||||
|
private ProgressBar progressBar;
|
||||||
|
private Exception error;
|
||||||
|
|
||||||
|
public NotificationListTask(Context context, ProgressBar progressBar, ListNotificationsHandler handler) {
|
||||||
|
this.context = context;
|
||||||
|
this.progressBar = progressBar;
|
||||||
|
this.handler = handler;
|
||||||
|
}
|
||||||
|
protected void onPreExecute() {
|
||||||
|
Helper.setViewVisibility(progressBar, View.VISIBLE);
|
||||||
|
}
|
||||||
|
protected List<LbryNotification> doInBackground(Void... params) {
|
||||||
|
List<LbryNotification> notifications = new ArrayList<>();
|
||||||
|
SQLiteDatabase db = null;
|
||||||
|
try {
|
||||||
|
JSONArray array = (JSONArray) Lbryio.parseResponse(Lbryio.call("notification", "list", context));
|
||||||
|
if (array != null) {
|
||||||
|
for (int i = 0; i < array.length(); i++) {
|
||||||
|
JSONObject item = array.getJSONObject(i);
|
||||||
|
if (item.has("notification_parameters")) {
|
||||||
|
LbryNotification notification = new LbryNotification();
|
||||||
|
|
||||||
|
JSONObject notificationParams = item.getJSONObject("notification_parameters");
|
||||||
|
if (notificationParams.has("device")) {
|
||||||
|
JSONObject device = notificationParams.getJSONObject("device");
|
||||||
|
notification.setTitle(Helper.getJSONString("title", null, device));
|
||||||
|
notification.setDescription(Helper.getJSONString("text", null, device));
|
||||||
|
notification.setTargetUrl(Helper.getJSONString("target", null, device));
|
||||||
|
}
|
||||||
|
if (notificationParams.has("dynamic") && !notificationParams.isNull("dynamic")) {
|
||||||
|
JSONObject dynamic = notificationParams.getJSONObject("dynamic");
|
||||||
|
if (dynamic.has("channelURI")) {
|
||||||
|
String channelUrl = Helper.getJSONString("channelURI", null, dynamic);
|
||||||
|
if (!Helper.isNullOrEmpty(channelUrl)) {
|
||||||
|
notification.setTargetUrl(channelUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dynamic.has("hash") && "comment".equalsIgnoreCase(Helper.getJSONString("notification_rule", null, item))) {
|
||||||
|
notification.setTargetUrl(String.format("%s?comment_hash=%s", notification.getTargetUrl(), dynamic.getString("hash")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
notification.setTimestamp(dateFormat.parse(Helper.getJSONString("created_at", dateFormat.format(new Date()), item)));
|
||||||
|
} catch (ParseException ex) {
|
||||||
|
notification.setTimestamp(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notification.getRemoteId() > 0 && !Helper.isNullOrEmpty(notification.getDescription())) {
|
||||||
|
notifications.add(notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context instanceof MainActivity) {
|
||||||
|
db = ((MainActivity) context).getDbHelper().getWritableDatabase();
|
||||||
|
for (LbryNotification notification : notifications) {
|
||||||
|
DatabaseHelper.createOrUpdateNotification(notification, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ClassCastException | LbryioRequestException | LbryioResponseException | JSONException | IllegalStateException ex) {
|
||||||
|
error = ex;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return notifications;
|
||||||
|
}
|
||||||
|
protected void onPostExecute(List<LbryNotification> notifications) {
|
||||||
|
Helper.setViewVisibility(progressBar, View.GONE);
|
||||||
|
if (handler != null) {
|
||||||
|
if (notifications != null) {
|
||||||
|
handler.onSuccess(notifications);
|
||||||
|
} else {
|
||||||
|
handler.onError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ListNotificationsHandler {
|
||||||
|
void onSuccess(List<LbryNotification> notifications);
|
||||||
|
void onError(Exception exception);
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,6 +38,7 @@ import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.widget.AppCompatSpinner;
|
import androidx.appcompat.widget.AppCompatSpinner;
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.widget.NestedScrollView;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
@ -233,6 +234,9 @@ public class FileViewFragment extends BaseFragment implements
|
||||||
private View inlineChannelCreatorProgress;
|
private View inlineChannelCreatorProgress;
|
||||||
private MaterialButton inlineChannelCreatorCreateButton;
|
private MaterialButton inlineChannelCreatorCreateButton;
|
||||||
|
|
||||||
|
// if this is set, scroll to the specific comment on load
|
||||||
|
private String commentHash;
|
||||||
|
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||||
ViewGroup container, Bundle savedInstanceState) {
|
ViewGroup container, Bundle savedInstanceState) {
|
||||||
View root = inflater.inflate(R.layout.fragment_file_view, container, false);
|
View root = inflater.inflate(R.layout.fragment_file_view, container, false);
|
||||||
|
@ -357,11 +361,29 @@ public class FileViewFragment extends BaseFragment implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (params.containsKey("url")) {
|
if (params.containsKey("url")) {
|
||||||
newUrl = params.get("url").toString();
|
LbryUri newLbryUri = LbryUri.tryParse(params.get("url").toString());
|
||||||
|
if (newLbryUri != null) {
|
||||||
|
newUrl = newLbryUri.toString();
|
||||||
|
String qs = newLbryUri.getQueryString();
|
||||||
|
if (!Helper.isNullOrEmpty(qs)) {
|
||||||
|
String[] qsPairs = qs.split("&");
|
||||||
|
for (String pair : qsPairs) {
|
||||||
|
String[] parts = pair.split("=");
|
||||||
|
if (parts.length < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ("comment_hash".equalsIgnoreCase(parts[0])) {
|
||||||
|
commentHash = parts[1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (claim == null || !newUrl.equalsIgnoreCase(currentUrl)) {
|
if (claim == null || !newUrl.equalsIgnoreCase(currentUrl)) {
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if (currentUrl != null) {
|
} else if (currentUrl != null) {
|
||||||
updateRequired = true;
|
updateRequired = true;
|
||||||
} else if (context instanceof MainActivity) {
|
} else if (context instanceof MainActivity) {
|
||||||
|
@ -567,7 +589,6 @@ public class FileViewFragment extends BaseFragment implements
|
||||||
}
|
}
|
||||||
checkOwnClaim();
|
checkOwnClaim();
|
||||||
fetchChannels();
|
fetchChannels();
|
||||||
checkAndLoadComments();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getStreamingUrl() {
|
private String getStreamingUrl() {
|
||||||
|
@ -709,7 +730,6 @@ public class FileViewFragment extends BaseFragment implements
|
||||||
} else {
|
} else {
|
||||||
onSdkReady();
|
onSdkReady();
|
||||||
}
|
}
|
||||||
checkCommentSdkInitializing();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onStop() {
|
public void onStop() {
|
||||||
|
@ -1513,7 +1533,6 @@ public class FileViewFragment extends BaseFragment implements
|
||||||
private void checkAndLoadComments() {
|
private void checkAndLoadComments() {
|
||||||
View root = getView();
|
View root = getView();
|
||||||
if (root != null) {
|
if (root != null) {
|
||||||
checkCommentSdkInitializing();
|
|
||||||
RecyclerView commentsList = root.findViewById(R.id.file_view_comments_list);
|
RecyclerView commentsList = root.findViewById(R.id.file_view_comments_list);
|
||||||
if (commentsList == null || commentsList.getAdapter() == null || commentsList.getAdapter().getItemCount() == 0) {
|
if (commentsList == null || commentsList.getAdapter() == null || commentsList.getAdapter().getItemCount() == 0) {
|
||||||
loadComments();
|
loadComments();
|
||||||
|
@ -1521,14 +1540,6 @@ public class FileViewFragment extends BaseFragment implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkCommentSdkInitializing() {
|
|
||||||
View root = getView();
|
|
||||||
if (root != null) {
|
|
||||||
TextView commentsSDKInitializing = root.findViewById(R.id.file_view_comments_sdk_initializing);
|
|
||||||
Helper.setViewVisibility(commentsSDKInitializing, Lbry.SDK_READY ? View.GONE : View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showUnsupportedView() {
|
private void showUnsupportedView() {
|
||||||
View root = getView();
|
View root = getView();
|
||||||
if (root != null) {
|
if (root != null) {
|
||||||
|
@ -2075,10 +2086,21 @@ public class FileViewFragment extends BaseFragment implements
|
||||||
// reset the list view
|
// reset the list view
|
||||||
View root = getView();
|
View root = getView();
|
||||||
if (claim != null && root != null) {
|
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 title = claim.getTitle();
|
||||||
String claimId = claim.getClaimId();
|
String claimId = claim.getClaimId();
|
||||||
ProgressBar relatedLoading = root.findViewById(R.id.file_view_related_content_progress);
|
ProgressBar relatedLoading = root.findViewById(R.id.file_view_related_content_progress);
|
||||||
Context context = getContext();
|
|
||||||
boolean canShowMatureContent = false;
|
boolean canShowMatureContent = false;
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
@ -2098,10 +2120,14 @@ public class FileViewFragment extends BaseFragment implements
|
||||||
|
|
||||||
Context ctx = getContext();
|
Context ctx = getContext();
|
||||||
if (ctx != null) {
|
if (ctx != null) {
|
||||||
relatedContentAdapter = new ClaimListAdapter(filteredClaims, ctx);
|
relatedContentAdapter.setItems(filteredClaims);
|
||||||
relatedContentAdapter.setListener(new ClaimListAdapter.ClaimListItemListener() {
|
relatedContentAdapter.setListener(new ClaimListAdapter.ClaimListItemListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClaimClicked(Claim claim) {
|
public void onClaimClicked(Claim claim) {
|
||||||
|
if (claim.isLoadingPlaceholder()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (context instanceof MainActivity) {
|
if (context instanceof MainActivity) {
|
||||||
MainActivity activity = (MainActivity) context;
|
MainActivity activity = (MainActivity) context;
|
||||||
if (claim.getName().startsWith("@")) {
|
if (claim.getName().startsWith("@")) {
|
||||||
|
@ -2123,6 +2149,10 @@ public class FileViewFragment extends BaseFragment implements
|
||||||
v.findViewById(R.id.file_view_no_related_content),
|
v.findViewById(R.id.file_view_no_related_content),
|
||||||
relatedContentAdapter == null || relatedContentAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
|
relatedContentAdapter == null || relatedContentAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if related content loads before comment, this will affect the scroll position
|
||||||
|
// so just ensure that we are at the correct position
|
||||||
|
scrollToCommentHash();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2137,9 +2167,9 @@ public class FileViewFragment extends BaseFragment implements
|
||||||
|
|
||||||
private void loadComments() {
|
private void loadComments() {
|
||||||
View root = getView();
|
View root = getView();
|
||||||
ProgressBar relatedLoading = root.findViewById(R.id.file_view_comments_progress);
|
ProgressBar commentsLoading = root.findViewById(R.id.file_view_comments_progress);
|
||||||
if (claim != null && root != null) {
|
if (claim != null && root != null) {
|
||||||
CommentListTask task = new CommentListTask(1, 500, claim.getClaimId(), relatedLoading, new CommentListHandler() {
|
CommentListTask task = new CommentListTask(1, 200, claim.getClaimId(), commentsLoading, new CommentListHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Comment> comments, boolean hasReachedEnd) {
|
public void onSuccess(List<Comment> comments, boolean hasReachedEnd) {
|
||||||
Context ctx = getContext();
|
Context ctx = getContext();
|
||||||
|
@ -2167,6 +2197,7 @@ public class FileViewFragment extends BaseFragment implements
|
||||||
relatedContentList.setAdapter(commentListAdapter);
|
relatedContentList.setAdapter(commentListAdapter);
|
||||||
commentListAdapter.notifyDataSetChanged();
|
commentListAdapter.notifyDataSetChanged();
|
||||||
|
|
||||||
|
scrollToCommentHash();
|
||||||
checkNoComments();
|
checkNoComments();
|
||||||
resolveCommentPosters();
|
resolveCommentPosters();
|
||||||
}
|
}
|
||||||
|
@ -2181,6 +2212,20 @@ public class FileViewFragment extends BaseFragment implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void scrollToCommentHash() {
|
||||||
|
View root = getView();
|
||||||
|
// check for the position of commentHash if set
|
||||||
|
if (root != null && !Helper.isNullOrEmpty(commentHash) && commentListAdapter != null && commentListAdapter.getItemCount() > 0) {
|
||||||
|
RecyclerView commentList = root.findViewById(R.id.file_view_comments_list);
|
||||||
|
int position = commentListAdapter.getPositionForComment(commentHash);
|
||||||
|
if (position > -1) {
|
||||||
|
NestedScrollView scrollView = root.findViewById(R.id.file_view_scroll_view);
|
||||||
|
scrollView.requestChildFocus(commentList, commentList);
|
||||||
|
commentList.getLayoutManager().scrollToPosition(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void checkNoComments() {
|
private void checkNoComments() {
|
||||||
View root = getView();
|
View root = getView();
|
||||||
if (root != null) {
|
if (root != null) {
|
||||||
|
@ -2194,7 +2239,7 @@ public class FileViewFragment extends BaseFragment implements
|
||||||
long st = System.currentTimeMillis();;
|
long st = System.currentTimeMillis();;
|
||||||
List<String> urlsToResolve = new ArrayList<>(commentListAdapter.getClaimUrlsToResolve());
|
List<String> urlsToResolve = new ArrayList<>(commentListAdapter.getClaimUrlsToResolve());
|
||||||
if (urlsToResolve.size() > 0) {
|
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
|
@Override
|
||||||
public void onSuccess(List<Claim> claims) {
|
public void onSuccess(List<Claim> claims) {
|
||||||
if (commentListAdapter != null) {
|
if (commentListAdapter != null) {
|
||||||
|
|
|
@ -70,6 +70,7 @@ public final class Helper {
|
||||||
public static final String METHOD_GET = "GET";
|
public static final String METHOD_GET = "GET";
|
||||||
public static final String METHOD_POST = "POST";
|
public static final String METHOD_POST = "POST";
|
||||||
public static final String ISO_DATE_FORMAT_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS";
|
public static final String ISO_DATE_FORMAT_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS";
|
||||||
|
public static final String ISO_DATE_FORMAT_JSON = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||||
public static final String SDK_AMOUNT_FORMAT = "0.0#######";
|
public static final String SDK_AMOUNT_FORMAT = "0.0#######";
|
||||||
public static final MediaType FORM_MEDIA_TYPE = MediaType.parse("application/x-www-form-urlencoded");
|
public static final MediaType FORM_MEDIA_TYPE = MediaType.parse("application/x-www-form-urlencoded");
|
||||||
public static final MediaType JSON_MEDIA_TYPE = MediaType.get("application/json; charset=utf-8");
|
public static final MediaType JSON_MEDIA_TYPE = MediaType.get("application/json; charset=utf-8");
|
||||||
|
|
11
app/src/main/res/drawable-anydpi/ic_notifications.xml
Normal file
11
app/src/main/res/drawable-anydpi/ic_notifications.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="#FFFFFF"
|
||||||
|
android:alpha="0.8">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z"/>
|
||||||
|
</vector>
|
BIN
app/src/main/res/drawable-hdpi/ic_notifications.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_notifications.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 273 B |
BIN
app/src/main/res/drawable-mdpi/ic_notifications.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_notifications.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 203 B |
BIN
app/src/main/res/drawable-xhdpi/ic_notifications.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_notifications.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 333 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_notifications.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_notifications.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 477 B |
6
app/src/main/res/drawable/bg_notification_badge.xml
Normal file
6
app/src/main/res/drawable/bg_notification_badge.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="@color/red" />
|
||||||
|
<corners android:radius="16dp" />
|
||||||
|
</shape>
|
|
@ -28,20 +28,22 @@
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:layout_marginRight="24dp">
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/wunderbar"
|
android:id="@+id/wunderbar"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
android:drawableLeft="@drawable/ic_search"
|
android:drawableLeft="@drawable/ic_search"
|
||||||
android:drawablePadding="8dp"
|
android:drawablePadding="8dp"
|
||||||
android:drawableTint="@color/actionBarForeground"
|
android:drawableTint="@color/actionBarForeground"
|
||||||
android:fontFamily="@font/inter"
|
android:fontFamily="@font/inter"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginRight="24dp"
|
||||||
android:hint="@string/uri_placeholder"
|
android:hint="@string/uri_placeholder"
|
||||||
android:imeOptions="actionGo"
|
android:imeOptions="actionGo"
|
||||||
android:inputType="textNoSuggestions"
|
android:inputType="textNoSuggestions"
|
||||||
|
android:paddingRight="36dp"
|
||||||
android:selectAllOnFocus="true"
|
android:selectAllOnFocus="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textFontWeight="300"
|
android:textFontWeight="300"
|
||||||
|
@ -52,6 +54,7 @@
|
||||||
android:background="?attr/selectableItemBackground"
|
android:background="?attr/selectableItemBackground"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginRight="24dp"
|
||||||
android:layout_alignParentRight="true"
|
android:layout_alignParentRight="true"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:visibility="gone">
|
android:visibility="gone">
|
||||||
|
@ -61,6 +64,39 @@
|
||||||
android:src="@drawable/ic_close"
|
android:src="@drawable/ic_close"
|
||||||
android:tint="@color/actionBarForeground" />
|
android:tint="@color/actionBarForeground" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/wunderbar_notifications"
|
||||||
|
android:clickable="true"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_centerVertical="true">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/notifications_toggle_icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:src="@drawable/ic_notifications"
|
||||||
|
android:tint="@color/actionBarForeground" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/notifications_badge_count"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_notification_badge"
|
||||||
|
android:fontFamily="@font/inter"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:minWidth="12dp"
|
||||||
|
android:paddingLeft="2dp"
|
||||||
|
android:paddingRight="2dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginLeft="24dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="10sp"
|
||||||
|
android:visibility="invisible"
|
||||||
|
/>
|
||||||
|
</RelativeLayout>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
</androidx.appcompat.widget.Toolbar>
|
</androidx.appcompat.widget.Toolbar>
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
|
@ -33,6 +33,53 @@
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/notifications_container"
|
||||||
|
android:background="@color/pageBackground"
|
||||||
|
android:elevation="6dp"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone">
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/notifications_progress"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/notification_list_empty_container"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:padding="36dp"
|
||||||
|
android:visibility="gone">
|
||||||
|
<ImageView
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_width="160dp"
|
||||||
|
android:layout_height="300dp"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:src="@drawable/gerbil_happy" />
|
||||||
|
<TextView
|
||||||
|
android:text="@string/no_notifications"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:fontFamily="@font/inter"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textAlignment="center" />
|
||||||
|
</LinearLayout>
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/notifications_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
/>
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
<include layout="@layout/floating_wallet_balance" />
|
<include layout="@layout/floating_wallet_balance" />
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
|
|
|
@ -721,19 +721,6 @@
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/file_view_comments_sdk_initializing"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginLeft="16dp"
|
|
||||||
android:layout_marginRight="16dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:fontFamily="@font/inter"
|
|
||||||
android:text="@string/sdk_initializing_comments"
|
|
||||||
android:textFontWeight="300"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/file_view_comments_list"
|
android:id="@+id/file_view_comments_list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
50
app/src/main/res/layout/list_item_notification.xml
Normal file
50
app/src/main/res/layout/list_item_notification.xml
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:layout_marginBottom="8dp">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="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: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>
|
|
@ -138,6 +138,13 @@
|
||||||
android:src="@drawable/ic_check"
|
android:src="@drawable/ic_check"
|
||||||
android:tint="@color/nextLbryGreen" />
|
android:tint="@color/nextLbryGreen" />
|
||||||
</RelativeLayout>
|
</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>
|
</RelativeLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
@ -146,6 +153,22 @@
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_marginLeft="16dp"
|
android:layout_marginLeft="16dp"
|
||||||
android:layout_toRightOf="@id/claim_media_container">
|
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
|
<TextView
|
||||||
android:id="@+id/claim_vanity_url"
|
android:id="@+id/claim_vanity_url"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
|
@ -603,6 +603,9 @@
|
||||||
<string name="cannot_find_lbrynet_log">The lbrynet.log file could not be found.</string>
|
<string name="cannot_find_lbrynet_log">The lbrynet.log file could not be found.</string>
|
||||||
<string name="cannot_share_lbrynet_log">The lbrynet.log file cannot be shared due to permission restrictions.</string>
|
<string name="cannot_share_lbrynet_log">The lbrynet.log file cannot be shared due to permission restrictions.</string>
|
||||||
|
|
||||||
|
<!-- Notifications -->
|
||||||
|
<string name="no_notifications">It\'s quiet here! New notifications will be displayed when you receive them.</string>
|
||||||
|
|
||||||
<!-- Font Awesome -->
|
<!-- Font Awesome -->
|
||||||
<string name="fa_gift" translatable="false"></string>
|
<string name="fa_gift" translatable="false"></string>
|
||||||
<string name="fa_lock" translatable="false"></string>
|
<string name="fa_lock" translatable="false"></string>
|
||||||
|
@ -626,4 +629,7 @@
|
||||||
<string name="fa_mobile_alt" translatable="false"></string>
|
<string name="fa_mobile_alt" translatable="false"></string>
|
||||||
<string name="fa_repost" translatable="false"></string>
|
<string name="fa_repost" translatable="false"></string>
|
||||||
<string name="fa_folder_open" translatable="false"></string>
|
<string name="fa_folder_open" translatable="false"></string>
|
||||||
|
|
||||||
|
<string name="fa_asterisk" translatable="false"></string>
|
||||||
|
<string name="fa_comment_alt" translatable="false"></string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
app:iconSpaceReserved="false">
|
app:iconSpaceReserved="false">
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
app:key="io.lbry.browser.preference.userinterface.BackgroundPlayback"
|
app:key="io.lbry.browser.preference.userinterface.BackgroundPlayback"
|
||||||
|
app:defaultValue="true"
|
||||||
app:title="@string/enable_background_playback"
|
app:title="@string/enable_background_playback"
|
||||||
app:iconSpaceReserved="false" />
|
app:iconSpaceReserved="false" />
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
|
@ -27,6 +28,11 @@
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:title="@string/notifications"
|
android:title="@string/notifications"
|
||||||
app:iconSpaceReserved="false">
|
app:iconSpaceReserved="false">
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
app:key="io.lbry.browser.preference.notifications.Comments"
|
||||||
|
app:title="@string/comments"
|
||||||
|
app:defaultValue="true"
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
app:key="io.lbry.browser.preference.notifications.Subscriptions"
|
app:key="io.lbry.browser.preference.notifications.Subscriptions"
|
||||||
app:title="@string/subscriptions"
|
app:title="@string/subscriptions"
|
||||||
|
|
Loading…
Reference in a new issue