From 746d442051328b79f6273a7fbc8dd9d4a685b3bb Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Fri, 9 Oct 2020 06:56:13 +0100 Subject: [PATCH 01/13] per-channel subscribe notification toggle --- .../java/io/lbry/browser/MainActivity.java | 3 +- .../io/lbry/browser/data/DatabaseHelper.java | 27 +++++-- .../browser/model/lbryinc/Subscription.java | 8 ++- .../browser/tasks/MergeSubscriptionsTask.java | 4 +- .../tasks/lbryinc/ChannelSubscribeTask.java | 4 +- .../tasks/lbryinc/FetchSubscriptionsTask.java | 3 +- .../browser/ui/channel/ChannelFragment.java | 45 +++++++++++- .../ui/findcontent/FileViewFragment.java | 43 +++++++++++- .../java/io/lbry/browser/utils/Lbryio.java | 18 +++++ app/src/main/res/layout/fragment_channel.xml | 18 +++++ .../main/res/layout/fragment_file_view.xml | 70 ++++++++++++------- app/src/main/res/values/strings.xml | 2 + 12 files changed, 203 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/io/lbry/browser/MainActivity.java b/app/src/main/java/io/lbry/browser/MainActivity.java index fceb7dda..2bd3538b 100644 --- a/app/src/main/java/io/lbry/browser/MainActivity.java +++ b/app/src/main/java/io/lbry/browser/MainActivity.java @@ -2713,11 +2713,12 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener JSONObject item = array.getJSONObject(i); String claimId = item.getString("claim_id"); String channelName = item.getString("channel_name"); + boolean isNotificationsDisabled = item.getBoolean("is_notifications_disabled"); LbryUri url = new LbryUri(); url.setChannelName(channelName); url.setClaimId(claimId); - subscriptions.add(new Subscription(channelName, url.toString())); + subscriptions.add(new Subscription(channelName, url.toString(), isNotificationsDisabled)); subUrls.add(url.toString()); } Lbryio.subscriptions = subscriptions; diff --git a/app/src/main/java/io/lbry/browser/data/DatabaseHelper.java b/app/src/main/java/io/lbry/browser/data/DatabaseHelper.java index 9aece297..1d1a18f6 100644 --- a/app/src/main/java/io/lbry/browser/data/DatabaseHelper.java +++ b/app/src/main/java/io/lbry/browser/data/DatabaseHelper.java @@ -21,13 +21,13 @@ import io.lbry.browser.utils.Helper; import io.lbry.browser.utils.LbryUri; public class DatabaseHelper extends SQLiteOpenHelper { - public static final int DATABASE_VERSION = 7; + public static final int DATABASE_VERSION = 8; public static final String DATABASE_NAME = "LbryApp.db"; private static DatabaseHelper instance; private static final String[] SQL_CREATE_TABLES = { // local subscription store - "CREATE TABLE subscriptions (url TEXT PRIMARY KEY NOT NULL, channel_name TEXT NOT NULL)", + "CREATE TABLE subscriptions (url TEXT PRIMARY KEY NOT NULL, channel_name TEXT NOT NULL, is_notifications_disabled INTEGER DEFAULT 0 NOT NULL)", // url entry / suggestion history "CREATE TABLE url_history (id INTEGER PRIMARY KEY NOT NULL, value TEXT NOT NULL, url TEXT, type INTEGER NOT NULL, timestamp TEXT NOT NULL)", // tags (known and followed) @@ -105,11 +105,15 @@ public class DatabaseHelper extends SQLiteOpenHelper { "CREATE TABLE shuffle_watched (id INTEGER PRIMARY KEY NOT NULL, claim_id TEXT NOT NULL)", "CREATE UNIQUE INDEX idx_shuffle_watched_claim ON shuffle_watched (claim_id)" }; + private static final String[] SQL_V7_V8_UPGRADE = { + "AlTER TABLE subscriptions ADD COLUMN is_notifications_disabled INTEGER DEFAULT 0 NOT NULL" + }; - 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, is_notifications_disabled) VALUES (?, ?, ?)"; + private static final String SQL_UPDATE_SUBSCRIPTION_NOTIFICATION = "UPDATE subscriptions SET is_notification_disabled = ? WHERE url = ?"; 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_GET_SUBSCRIPTIONS = "SELECT channel_name, url FROM subscriptions"; + private static final String SQL_GET_SUBSCRIPTIONS = "SELECT channel_name, url, is_notifications_disabled FROM subscriptions"; private static final String SQL_INSERT_URL_HISTORY = "REPLACE INTO url_history (value, url, type, timestamp) VALUES (?, ?, ?, ?)"; private static final String SQL_CLEAR_URL_HISTORY = "DELETE FROM url_history"; @@ -188,6 +192,11 @@ public class DatabaseHelper extends SQLiteOpenHelper { db.execSQL(sql); } } + if (oldVersion < 8) { + for (String sql : SQL_V7_V8_UPGRADE) { + db.execSQL(sql); + } + } } public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { @@ -304,7 +313,14 @@ public class DatabaseHelper extends SQLiteOpenHelper { } public static void createOrUpdateSubscription(Subscription subscription, SQLiteDatabase db) { - db.execSQL(SQL_INSERT_SUBSCRIPTION, new Object[] { subscription.getChannelName(), subscription.getUrl() }); + db.execSQL(SQL_INSERT_SUBSCRIPTION, new Object[] { + subscription.getChannelName(), + subscription.getUrl(), + subscription.isNotificationsDisabled() ? 1 : 0 + }); + } + public static void setSubscriptionNotificationDisabled(boolean flag, String url, SQLiteDatabase db) { + db.execSQL(SQL_UPDATE_SUBSCRIPTION_NOTIFICATION, new Object[] { flag ? 1 : 0, url }); } public static void deleteSubscription(Subscription subscription, SQLiteDatabase db) { db.execSQL(SQL_DELETE_SUBSCRIPTION, new Object[] { subscription.getUrl() }); @@ -321,6 +337,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { Subscription subscription = new Subscription(); subscription.setChannelName(cursor.getString(0)); subscription.setUrl(cursor.getString(1)); + subscription.setNotificationsDisabled(cursor.getInt(2) == 1); subscriptions.add(subscription); } } finally { diff --git a/app/src/main/java/io/lbry/browser/model/lbryinc/Subscription.java b/app/src/main/java/io/lbry/browser/model/lbryinc/Subscription.java index 30f2412a..6d302e52 100644 --- a/app/src/main/java/io/lbry/browser/model/lbryinc/Subscription.java +++ b/app/src/main/java/io/lbry/browser/model/lbryinc/Subscription.java @@ -11,17 +11,21 @@ public class Subscription { @Getter @Setter private String url; + @Getter + @Setter + private boolean isNotificationsDisabled; public Subscription() { } - public Subscription(String channelName, String url) { + public Subscription(String channelName, String url, boolean isNotificationsDisabled) { this.channelName = channelName; this.url = url; + this.isNotificationsDisabled = isNotificationsDisabled; } public static Subscription fromClaim(Claim claim) { - return new Subscription(claim.getName(), claim.getPermanentUrl()); + return new Subscription(claim.getName(), claim.getPermanentUrl(), false); } public String toString() { return url; diff --git a/app/src/main/java/io/lbry/browser/tasks/MergeSubscriptionsTask.java b/app/src/main/java/io/lbry/browser/tasks/MergeSubscriptionsTask.java index 2d516e13..842c381e 100644 --- a/app/src/main/java/io/lbry/browser/tasks/MergeSubscriptionsTask.java +++ b/app/src/main/java/io/lbry/browser/tasks/MergeSubscriptionsTask.java @@ -76,13 +76,15 @@ public class MergeSubscriptionsTask extends AsyncTask { options.put("claim_id", channelClaimId); if (!isUnsubscribing) { options.put("channel_name", subscription.getChannelName()); + options.put("notifications_disabled", String.valueOf(subscription.isNotificationsDisabled()).toLowerCase()); } String action = isUnsubscribing ? "delete" : "new"; - Lbryio.call("subscription", action, options, context); - + Object response = Lbryio.parseResponse(Lbryio.call("subscription", action, options, context)); if (!isUnsubscribing) { Lbryio.addSubscription(subscription); } else { diff --git a/app/src/main/java/io/lbry/browser/tasks/lbryinc/FetchSubscriptionsTask.java b/app/src/main/java/io/lbry/browser/tasks/lbryinc/FetchSubscriptionsTask.java index da886beb..e3c2a1f8 100644 --- a/app/src/main/java/io/lbry/browser/tasks/lbryinc/FetchSubscriptionsTask.java +++ b/app/src/main/java/io/lbry/browser/tasks/lbryinc/FetchSubscriptionsTask.java @@ -49,11 +49,12 @@ public class FetchSubscriptionsTask extends AsyncTask()); commentChannelSpinnerAdapter.addPlaceholder(false); @@ -2524,13 +2558,18 @@ public class FileViewFragment extends BaseFragment implements private void checkIsFollowing() { if (claim != null && claim.getSigningChannel() != null) { boolean isFollowing = Lbryio.isFollowing(claim.getSigningChannel()); + boolean notificationsDisabled = Lbryio.isNotificationsDisabled(claim.getSigningChannel()); Context context = getContext(); View root = getView(); if (context != null && root != null) { OutlineIconView iconFollow = root.findViewById(R.id.file_view_icon_follow); SolidIconView iconUnfollow = root.findViewById(R.id.file_view_icon_unfollow); - Helper.setViewVisibility(iconFollow, !isFollowing ? View.VISIBLE: View.INVISIBLE); - Helper.setViewVisibility(iconUnfollow, isFollowing ? View.VISIBLE : View.INVISIBLE); + SolidIconView iconBell = root.findViewById(R.id.file_view_icon_bell); + Helper.setViewVisibility(iconFollow, !isFollowing ? View.VISIBLE: View.GONE); + Helper.setViewVisibility(iconUnfollow, isFollowing ? View.VISIBLE : View.GONE); + Helper.setViewVisibility(iconBell, isFollowing ? View.VISIBLE : View.GONE); + + iconBell.setText(notificationsDisabled ? R.string.fa_bell : R.string.fa_bell_slash); } } } diff --git a/app/src/main/java/io/lbry/browser/utils/Lbryio.java b/app/src/main/java/io/lbry/browser/utils/Lbryio.java index 796ac65d..51f99eb4 100644 --- a/app/src/main/java/io/lbry/browser/utils/Lbryio.java +++ b/app/src/main/java/io/lbry/browser/utils/Lbryio.java @@ -333,6 +333,15 @@ public final class Lbryio { } } + public static void updateSubscriptionNotificationsDisabled(Subscription subscription) { + synchronized (lock) { + int index = subscriptions.indexOf(subscription); + if (index > -1) { + subscriptions.get(index).setNotificationsDisabled(subscription.isNotificationsDisabled()); + } + } + } + public static void addSubscription(Subscription subscription) { synchronized (lock) { if (!subscriptions.contains(subscription)) { @@ -364,6 +373,15 @@ public final class Lbryio { public static boolean isFollowing(Claim claim) { return subscriptions.contains(Subscription.fromClaim(claim)); } + public static boolean isNotificationsDisabled(Claim claim) { + Subscription sub = Subscription.fromClaim(claim); + int index = subscriptions.indexOf(sub); + if (index > -1) { + Subscription actual = subscriptions.get(subscriptions.indexOf(sub)); + return actual.isNotificationsDisabled(); + } + return false; + } public static void updateRewardsLists(List rewards) { synchronized (lock) { diff --git a/app/src/main/res/layout/fragment_channel.xml b/app/src/main/res/layout/fragment_channel.xml index 9cd5b5a1..84797cb4 100644 --- a/app/src/main/res/layout/fragment_channel.xml +++ b/app/src/main/res/layout/fragment_channel.xml @@ -224,9 +224,27 @@ android:layout_width="24dp" android:layout_height="24dp" android:text="@string/fa_heart_broken" + android:textColor="@color/foreground" android:textSize="20dp" android:visibility="invisible"/> + + + diff --git a/app/src/main/res/layout/fragment_file_view.xml b/app/src/main/res/layout/fragment_file_view.xml index 97fa2df9..d62cbbfc 100644 --- a/app/src/main/res/layout/fragment_file_view.xml +++ b/app/src/main/res/layout/fragment_file_view.xml @@ -503,7 +503,6 @@ android:id="@+id/file_view_publisher_area" android:layout_width="match_parent" android:layout_height="wrap_content"> - + android:layout_toStartOf="@id/file_view_publisher_area_actions"> - + android:layout_alignParentEnd="true"> + - + + + + Content Website reposted + You will receive all notifications + You will not receive notifications for this channel %1$s follower %1$s followers -- 2.45.2 From 64bd54032226094e755b40d9ebfef0cf27325a76 Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Fri, 9 Oct 2020 07:38:47 +0100 Subject: [PATCH 02/13] In-app notification options (#1023) * pull-down to refresh in-app notifications * support deleting notifications --- .../java/io/lbry/browser/MainActivity.java | 128 +++++++++++++++++- .../adapter/NotificationListAdapter.java | 73 +++++++++- .../io/lbry/browser/data/DatabaseHelper.java | 15 ++ .../tasks/lbryinc/NotificationDeleteTask.java | 33 +++++ app/src/main/res/layout/content_main.xml | 17 ++- .../res/layout/list_item_notification.xml | 14 ++ app/src/main/res/menu/menu_notification.xml | 10 ++ app/src/main/res/values/strings.xml | 4 + 8 files changed, 282 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/io/lbry/browser/tasks/lbryinc/NotificationDeleteTask.java create mode 100644 app/src/main/res/menu/menu_notification.xml diff --git a/app/src/main/java/io/lbry/browser/MainActivity.java b/app/src/main/java/io/lbry/browser/MainActivity.java index 2bd3538b..7283ce8f 100644 --- a/app/src/main/java/io/lbry/browser/MainActivity.java +++ b/app/src/main/java/io/lbry/browser/MainActivity.java @@ -11,6 +11,7 @@ import android.content.BroadcastReceiver; import android.content.ClipData; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; @@ -36,6 +37,7 @@ import android.text.style.TypefaceSpan; import android.util.Base64; import android.util.Log; import android.view.KeyEvent; +import android.view.MenuItem; import android.view.View; import android.view.Menu; import android.view.WindowManager; @@ -75,7 +77,9 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatDelegate; +import androidx.appcompat.view.ActionMode; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.core.app.ActivityCompat; import androidx.core.app.NotificationCompat; @@ -98,6 +102,7 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; @@ -142,6 +147,7 @@ import io.lbry.browser.listener.FilePickerListener; import io.lbry.browser.listener.PIPModeListener; import io.lbry.browser.listener.ScreenOrientationListener; import io.lbry.browser.listener.SdkStatusListener; +import io.lbry.browser.listener.SelectionModeListener; import io.lbry.browser.listener.StoragePermissionListener; import io.lbry.browser.listener.WalletBalanceListener; import io.lbry.browser.model.Claim; @@ -165,6 +171,7 @@ import io.lbry.browser.tasks.lbryinc.FetchRewardsTask; import io.lbry.browser.tasks.LighthouseAutoCompleteTask; import io.lbry.browser.tasks.MergeSubscriptionsTask; import io.lbry.browser.tasks.claim.ResolveTask; +import io.lbry.browser.tasks.lbryinc.NotificationDeleteTask; import io.lbry.browser.tasks.lbryinc.NotificationListTask; import io.lbry.browser.tasks.lbryinc.NotificationUpdateTask; import io.lbry.browser.tasks.localdata.FetchRecentUrlHistoryTask; @@ -209,7 +216,9 @@ import lombok.Setter; import lombok.SneakyThrows; import okhttp3.OkHttpClient; -public class MainActivity extends AppCompatActivity implements SdkStatusListener, SharedPreferences.OnSharedPreferenceChangeListener { +public class MainActivity extends AppCompatActivity implements SdkStatusListener, + SharedPreferences.OnSharedPreferenceChangeListener, + ActionMode.Callback, SelectionModeListener { private static final String CHANNEL_ID_PLAYBACK = "io.lbry.browser.LBRY_PLAYBACK_CHANNEL"; private static final int PLAYBACK_NOTIFICATION_ID = 3; private static final String SPECIAL_URL_PREFIX = "lbry://?"; @@ -244,6 +253,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener public static boolean startingPermissionRequest = false; public static boolean startingSignInFlowActivity = false; + private ActionMode actionMode; private BillingClient billingClient; @Getter private boolean enteringPIPMode = false; @@ -366,6 +376,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener private MediaSessionCompat mediaSession; private boolean receivedStopService; private ActionBarDrawerToggle toggle; + private SwipeRefreshLayout notificationsSwipeContainer; private SyncSetTask syncSetTask = null; private List pendingSyncSetQueue; @Getter @@ -581,6 +592,16 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener } }); + notificationsSwipeContainer = findViewById(R.id.notifications_list_swipe_container); + notificationsSwipeContainer.setColorSchemeResources(R.color.nextLbryGreen); + notificationsSwipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + notificationsSwipeContainer.setRefreshing(true); + loadRemoteNotifications(false); + } + }); + findViewById(R.id.global_now_playing_card).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -1874,6 +1895,103 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener } } + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + this.actionMode = mode; + if (isDarkMode()) { + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + } + + actionMode.getMenuInflater().inflate(R.menu.menu_notification, menu); + return true; + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return true; + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + if (R.id.action_delete == item.getItemId()) { + if (notificationListAdapter != null && notificationListAdapter.getSelectedCount() > 0) { + + final List selectedNotifications = new ArrayList<>(notificationListAdapter.getSelectedItems()); + String message = getResources().getQuantityString(R.plurals.confirm_delete_notifications, selectedNotifications.size()); + AlertDialog.Builder builder = new AlertDialog.Builder(this). + setTitle(R.string.delete_selection). + setMessage(message) + .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + handleDeleteSelectedNotifications(selectedNotifications); + } + }).setNegativeButton(R.string.no, null); + builder.show(); + return true; + } + } + return false; + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + if (notificationListAdapter != null) { + notificationListAdapter.clearSelectedItems(); + notificationListAdapter.setInSelectionMode(false); + notificationListAdapter.notifyDataSetChanged(); + } + if (isDarkMode()) { + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + } + this.actionMode = null; + } + + @Override + public void onEnterSelectionMode() { + startSupportActionMode(this); + } + + @Override + public void onExitSelectionMode() { + if (actionMode != null) { + actionMode.finish(); + } + } + + @Override + public void onItemSelectionToggled() { + if (actionMode != null) { + actionMode.setTitle(notificationListAdapter != null ? String.valueOf(notificationListAdapter.getSelectedCount()) : ""); + actionMode.invalidate(); + } + } + + private void handleDeleteSelectedNotifications(List notifications) { + List remoteIds = new ArrayList<>(); + for (LbryNotification notification : notifications) { + remoteIds.add(notification.getRemoteId()); + } + (new AsyncTask() { + protected Void doInBackground(Void... params) { + try { + SQLiteDatabase db = dbHelper.getWritableDatabase(); + DatabaseHelper.deleteNotifications(notifications, db); + } catch (Exception ex) { + // pass + } + return null; + } + }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + new NotificationDeleteTask(remoteIds).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + if (notificationListAdapter != null) { + notificationListAdapter.removeNotifications(notifications); + } + if (actionMode != null) { + actionMode.finish(); + } + } + private class PlayerNotificationDescriptionAdapter implements PlayerNotificationManager.MediaDescriptionAdapter { @Override @@ -3391,6 +3509,10 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener if (markRead && findViewById(R.id.notifications_container).getVisibility() == View.VISIBLE) { markNotificationsRead(); } + + if (notificationsSwipeContainer != null) { + notificationsSwipeContainer.setRefreshing(false); + } } @Override @@ -3398,6 +3520,9 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener // pass Log.e(TAG, "error loading remote notifications", exception); loadLocalNotifications(); + if (notificationsSwipeContainer != null) { + notificationsSwipeContainer.setRefreshing(false); + } } }); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -3427,6 +3552,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener if (notificationListAdapter == null) { notificationListAdapter = new NotificationListAdapter(notifications, MainActivity.this); + notificationListAdapter.setSelectionModeListener(MainActivity.this); ((RecyclerView) findViewById(R.id.notifications_list)).setAdapter(notificationListAdapter); } else { notificationListAdapter.addNotifications(notifications); diff --git a/app/src/main/java/io/lbry/browser/adapter/NotificationListAdapter.java b/app/src/main/java/io/lbry/browser/adapter/NotificationListAdapter.java index cfdba94c..3865eb5b 100644 --- a/app/src/main/java/io/lbry/browser/adapter/NotificationListAdapter.java +++ b/app/src/main/java/io/lbry/browser/adapter/NotificationListAdapter.java @@ -14,6 +14,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; +import com.google.android.material.snackbar.Snackbar; import java.util.ArrayList; import java.util.Calendar; @@ -24,6 +25,7 @@ import java.util.List; import java.util.TimeZone; import io.lbry.browser.R; +import io.lbry.browser.listener.SelectionModeListener; import io.lbry.browser.model.Claim; import io.lbry.browser.model.lbryinc.LbryNotification; import io.lbry.browser.ui.controls.SolidIconView; @@ -43,15 +45,19 @@ public class NotificationListAdapter extends RecyclerView.Adapter items; + private List selectedItems; @Setter private NotificationClickListener clickListener; @Getter @Setter - private int customizeMode; + private boolean inSelectionMode; + @Setter + private SelectionModeListener selectionModeListener; public NotificationListAdapter(List notifications, Context context) { this.context = context; this.items = new ArrayList<>(notifications); + this.selectedItems = new ArrayList<>(); Collections.sort(items, Collections.reverseOrder(new LbryNotification())); } @@ -62,6 +68,7 @@ public class NotificationListAdapter extends RecyclerView.Adapter getSelectedItems() { + return this.selectedItems; + } + public int getSelectedCount() { + return selectedItems != null ? selectedItems.size() : 0; + } + public void clearSelectedItems() { + this.selectedItems.clear(); + } + public boolean isNotificationSelected(LbryNotification notification) { + return selectedItems.contains(notification); + } public void insertNotification(LbryNotification notification, int index) { if (!items.contains(notification)) { @@ -90,6 +110,12 @@ public class NotificationListAdapter extends RecyclerView.Adapter notifications) { + for (LbryNotification notification : notifications) { + items.remove(notification); + } + notifyDataSetChanged(); + } public List getAuthorUrls() { List urls = new ArrayList<>(); @@ -158,9 +184,8 @@ public class NotificationListAdapter extends RecyclerView.Adapter notifications, SQLiteDatabase db) { + StringBuilder sb = new StringBuilder("DELETE FROM notifications WHERE remote_id IN ("); + List remoteIds = new ArrayList<>(); + String delim = ""; + for (int i = 0; i < notifications.size(); i++) { + remoteIds.add(String.valueOf(notifications.get(i).getRemoteId())); + sb.append(delim).append("?"); + delim = ","; + } + sb.append(")"); + + String sql = sb.toString(); + db.execSQL(sql, remoteIds.toArray()); + } public static int getUnreadNotificationsCount(SQLiteDatabase db) { int count = 0; Cursor cursor = null; diff --git a/app/src/main/java/io/lbry/browser/tasks/lbryinc/NotificationDeleteTask.java b/app/src/main/java/io/lbry/browser/tasks/lbryinc/NotificationDeleteTask.java new file mode 100644 index 00000000..42e23391 --- /dev/null +++ b/app/src/main/java/io/lbry/browser/tasks/lbryinc/NotificationDeleteTask.java @@ -0,0 +1,33 @@ +package io.lbry.browser.tasks.lbryinc; + +import android.os.AsyncTask; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.lbry.browser.exceptions.LbryioRequestException; +import io.lbry.browser.exceptions.LbryioResponseException; +import io.lbry.browser.utils.Helper; +import io.lbry.browser.utils.Lbryio; + +public class NotificationDeleteTask extends AsyncTask { + private List ids; + + public NotificationDeleteTask(List ids) { + this.ids = ids; + } + + protected Boolean doInBackground(Void... params) { + Map options = new HashMap<>(); + options.put("notification_ids", Helper.joinL(ids, ",")); + + try { + Object result = Lbryio.parseResponse(Lbryio.call("notification", "delete", options, null)); + return "ok".equalsIgnoreCase(result.toString()); + } catch (LbryioResponseException | LbryioRequestException ex) { + // pass + } + return false; + } +} diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml index ed30a9f0..0edffd26 100644 --- a/app/src/main/res/layout/content_main.xml +++ b/app/src/main/res/layout/content_main.xml @@ -71,13 +71,18 @@ android:textSize="16sp" android:textAlignment="center" /> - + android:layout_height="match_parent"> + + diff --git a/app/src/main/res/layout/list_item_notification.xml b/app/src/main/res/layout/list_item_notification.xml index a8c8254e..ed5fbd00 100644 --- a/app/src/main/res/layout/list_item_notification.xml +++ b/app/src/main/res/layout/list_item_notification.xml @@ -28,6 +28,20 @@ android:layout_width="48dp" android:layout_height="48dp" android:visibility="invisible" /> + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f9bccf2d..411eda82 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -631,6 +631,10 @@ It\'s quiet here! New notifications will be displayed when you receive them. + + Are you sure you want to remove the selected notification? + Are you sure you want to remove the selected notifications? + -- 2.45.2 From ff8ffda3c65a63bd2c47713217b1bf0d92c16753 Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Fri, 9 Oct 2020 07:39:46 +0100 Subject: [PATCH 03/13] bumpversion 0.16.6 --> 0.16.7 --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 6ac82f87..df1d1857 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { applicationId "io.lbry.browser" minSdkVersion 21 targetSdkVersion 29 - versionCode 1606 - versionName "0.16.6" + versionCode 1607 + versionName "0.16.7" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } -- 2.45.2 From 53d22dd22d2fbf1110fb65feaf153537de0b4010 Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Fri, 9 Oct 2020 07:43:38 +0100 Subject: [PATCH 04/13] remove debug logs --- app/src/main/java/io/lbry/browser/MainActivity.java | 1 - app/src/main/java/io/lbry/browser/tasks/CommentListTask.java | 2 -- 2 files changed, 3 deletions(-) diff --git a/app/src/main/java/io/lbry/browser/MainActivity.java b/app/src/main/java/io/lbry/browser/MainActivity.java index 7283ce8f..bd3dfb2e 100644 --- a/app/src/main/java/io/lbry/browser/MainActivity.java +++ b/app/src/main/java/io/lbry/browser/MainActivity.java @@ -3241,7 +3241,6 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener // TODO: Broadcast startup status changes JSONObject startupStatus = status.getJSONObject("startup_status"); - android.util.Log.d(TAG, startupStatus.toString(2)); sdkReady = startupStatus.getBoolean("file_manager") && startupStatus.getBoolean("wallet"); } } catch (ConnectException | JSONException ex) { diff --git a/app/src/main/java/io/lbry/browser/tasks/CommentListTask.java b/app/src/main/java/io/lbry/browser/tasks/CommentListTask.java index cb135060..bb08073e 100644 --- a/app/src/main/java/io/lbry/browser/tasks/CommentListTask.java +++ b/app/src/main/java/io/lbry/browser/tasks/CommentListTask.java @@ -52,7 +52,6 @@ public class CommentListTask extends AsyncTask> { options.put("visible", true); JSONObject result = (JSONObject) Lbry.genericApiCall(Lbry.METHOD_COMMENT_LIST, options); - android.util.Log.d("LbryMain", result.toString(2)); JSONArray items = result.getJSONArray("items"); List children = new ArrayList<>(); @@ -78,7 +77,6 @@ public class CommentListTask extends AsyncTask> { } } catch (Exception ex) { error = ex; - android.util.Log.d("LbryMain", ex.toString(), ex); } return comments; } -- 2.45.2 From 0d185c6db38bb616c6a4b065128469cac9f8a8c9 Mon Sep 17 00:00:00 2001 From: yuval Date: Sat, 10 Oct 2020 16:58:29 +0300 Subject: [PATCH 05/13] Fix issue #1025 Increased size of currency selection spinner in publish form fragment Now spinner is in proper size to display "LBRY Credits" default value --- app/src/main/res/layout/fragment_publish_form.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/fragment_publish_form.xml b/app/src/main/res/layout/fragment_publish_form.xml index cff1ad36..14898fa9 100644 --- a/app/src/main/res/layout/fragment_publish_form.xml +++ b/app/src/main/res/layout/fragment_publish_form.xml @@ -316,7 +316,7 @@ Date: Mon, 12 Oct 2020 11:47:45 +0100 Subject: [PATCH 06/13] do not open lbry.*/$/verify links in the app --- app/src/main/AndroidManifest.xml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 306751c9..cc655ddf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -68,10 +68,18 @@ - - - - + + + + + + + + + + + + -- 2.45.2 From 5f850685d6d0eb38d99efb229b36301ae9630456 Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Mon, 12 Oct 2020 11:48:24 +0100 Subject: [PATCH 07/13] bumpversion 0.16.7 --> 0.16.8 --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index df1d1857..7d88afb1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { applicationId "io.lbry.browser" minSdkVersion 21 targetSdkVersion 29 - versionCode 1607 - versionName "0.16.7" + versionCode 1608 + versionName "0.16.8" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } -- 2.45.2 From f9887cffae0535b993ebe34aab729607b07858a3 Mon Sep 17 00:00:00 2001 From: yuval Date: Tue, 13 Oct 2020 23:17:58 +0300 Subject: [PATCH 08/13] Fix issue #1030 Dismiss all active dialog fragment when entering PiP mode So that PiP window contain the video only --- app/src/main/java/io/lbry/browser/MainActivity.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/src/main/java/io/lbry/browser/MainActivity.java b/app/src/main/java/io/lbry/browser/MainActivity.java index bd3dfb2e..96f11f79 100644 --- a/app/src/main/java/io/lbry/browser/MainActivity.java +++ b/app/src/main/java/io/lbry/browser/MainActivity.java @@ -92,6 +92,7 @@ import androidx.core.view.GravityCompat; import androidx.core.view.OnApplyWindowInsetsListener; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; +import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; @@ -1032,6 +1033,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener //findViewById(R.id.global_sdk_initializing_status).setVisibility(View.GONE); findViewById(R.id.app_bar_main_container).setFitsSystemWindows(true); hideActionBar(); + dismissActiveDialogs(); for (PIPModeListener listener : pipModeListeners) { listener.onEnterPIPMode(); @@ -1044,6 +1046,15 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener pipPlayerContainer.setVisibility(View.VISIBLE); playerReassigned = true; } + + private void dismissActiveDialogs() { + for( Fragment fragment: getSupportFragmentManager().getFragments() ){ + if (fragment instanceof DialogFragment){ + ((DialogFragment) fragment).dismiss(); + } + } + } + private void renderFullMode() { if (!inFullscreenMode) { showActionBar(); -- 2.45.2 From 3d48fa574189c30398cb12bc154530f1e7e38f79 Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Wed, 14 Oct 2020 15:09:59 +0100 Subject: [PATCH 09/13] also check pending when checking if item is a placeholder --- .../main/java/io/lbry/browser/adapter/ClaimListAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/io/lbry/browser/adapter/ClaimListAdapter.java b/app/src/main/java/io/lbry/browser/adapter/ClaimListAdapter.java index 93b7958d..be907737 100644 --- a/app/src/main/java/io/lbry/browser/adapter/ClaimListAdapter.java +++ b/app/src/main/java/io/lbry/browser/adapter/ClaimListAdapter.java @@ -428,9 +428,9 @@ public class ClaimListAdapter extends RecyclerView.Adapter Date: Wed, 14 Oct 2020 15:11:36 +0100 Subject: [PATCH 10/13] make credits translatable --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 411eda82..674f3198 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -316,7 +316,7 @@ <a href="https://lbry.com/faq/how-to-backup-wallet#android">Manual backup</a> <a href="https://lbry.com/faq/how-to-backup-wallet#sync">Sync FAQ</a> 0 - Credits + Credits Account Recommended A lbry.tv account allows you to earn rewards, backup your wallet, and keep everything in sync. -- 2.45.2 From 897bfdaffd5f3f356f149579ca06969a5bf84410 Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Wed, 14 Oct 2020 15:16:45 +0100 Subject: [PATCH 11/13] fix build error --- app/src/main/java/io/lbry/browser/adapter/ClaimListAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/lbry/browser/adapter/ClaimListAdapter.java b/app/src/main/java/io/lbry/browser/adapter/ClaimListAdapter.java index be907737..616432c6 100644 --- a/app/src/main/java/io/lbry/browser/adapter/ClaimListAdapter.java +++ b/app/src/main/java/io/lbry/browser/adapter/ClaimListAdapter.java @@ -428,7 +428,7 @@ public class ClaimListAdapter extends RecyclerView.Adapter Date: Wed, 14 Oct 2020 15:30:59 +0100 Subject: [PATCH 12/13] use credits icon on wallet balance page --- .../main/res/layout/card_wallet_balance.xml | 60 ++++++++----------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/app/src/main/res/layout/card_wallet_balance.xml b/app/src/main/res/layout/card_wallet_balance.xml index 15a22b27..c9d6a01f 100644 --- a/app/src/main/res/layout/card_wallet_balance.xml +++ b/app/src/main/res/layout/card_wallet_balance.xml @@ -38,6 +38,12 @@ android:layout_height="wrap_content" android:orientation="horizontal" android:layout_above="@id/wallet_balance_usd_value"> + - + - + - + - Date: Thu, 15 Oct 2020 01:49:53 +0200 Subject: [PATCH 13/13] Use '%23' instead of '#' when rendering #hashtag style text from markdown --- .../browser/ui/findcontent/FileViewFragment.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/java/io/lbry/browser/ui/findcontent/FileViewFragment.java b/app/src/main/java/io/lbry/browser/ui/findcontent/FileViewFragment.java index de9ce019..bc66d751 100644 --- a/app/src/main/java/io/lbry/browser/ui/findcontent/FileViewFragment.java +++ b/app/src/main/java/io/lbry/browser/ui/findcontent/FileViewFragment.java @@ -92,6 +92,8 @@ import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import io.lbry.browser.MainActivity; import io.lbry.browser.R; @@ -2072,7 +2074,17 @@ public class FileViewFragment extends BaseFragment implements @Override public void onSuccess(String text) { String html = buildMarkdownHtml(text); + if (webView != null) { + // Due to a change to Chrome, WebView only displays '#' -and everything after it- + // if it is '%23' instead. Problem appears in text like '#2' or #hashtags. + Pattern pattern = Pattern.compile("#(\\S+)"); + Matcher matcher = pattern.matcher(html); + + if (matcher.find()) { + html = html.replaceAll(pattern.toString(), "&%2335;" + matcher.group(1)); + } + webView.loadData(html, "text/html", "utf-8"); } } -- 2.45.2