In-app notification options #1023
8 changed files with 282 additions and 12 deletions
|
@ -11,6 +11,7 @@ import android.content.BroadcastReceiver;
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
@ -36,6 +37,7 @@ import android.text.style.TypefaceSpan;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
@ -75,7 +77,9 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.ActionBarDrawerToggle;
|
import androidx.appcompat.app.ActionBarDrawerToggle;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatDelegate;
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
|
import androidx.appcompat.view.ActionMode;
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
|
@ -98,6 +102,7 @@ import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
import org.java_websocket.client.WebSocketClient;
|
import org.java_websocket.client.WebSocketClient;
|
||||||
import org.java_websocket.handshake.ServerHandshake;
|
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.PIPModeListener;
|
||||||
import io.lbry.browser.listener.ScreenOrientationListener;
|
import io.lbry.browser.listener.ScreenOrientationListener;
|
||||||
import io.lbry.browser.listener.SdkStatusListener;
|
import io.lbry.browser.listener.SdkStatusListener;
|
||||||
|
import io.lbry.browser.listener.SelectionModeListener;
|
||||||
import io.lbry.browser.listener.StoragePermissionListener;
|
import io.lbry.browser.listener.StoragePermissionListener;
|
||||||
import io.lbry.browser.listener.WalletBalanceListener;
|
import io.lbry.browser.listener.WalletBalanceListener;
|
||||||
import io.lbry.browser.model.Claim;
|
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.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.NotificationDeleteTask;
|
||||||
import io.lbry.browser.tasks.lbryinc.NotificationListTask;
|
import io.lbry.browser.tasks.lbryinc.NotificationListTask;
|
||||||
import io.lbry.browser.tasks.lbryinc.NotificationUpdateTask;
|
import io.lbry.browser.tasks.lbryinc.NotificationUpdateTask;
|
||||||
import io.lbry.browser.tasks.localdata.FetchRecentUrlHistoryTask;
|
import io.lbry.browser.tasks.localdata.FetchRecentUrlHistoryTask;
|
||||||
|
@ -209,7 +216,9 @@ import lombok.Setter;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import okhttp3.OkHttpClient;
|
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 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;
|
||||||
private static final String SPECIAL_URL_PREFIX = "lbry://?";
|
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 startingPermissionRequest = false;
|
||||||
public static boolean startingSignInFlowActivity = false;
|
public static boolean startingSignInFlowActivity = false;
|
||||||
|
|
||||||
|
private ActionMode actionMode;
|
||||||
private BillingClient billingClient;
|
private BillingClient billingClient;
|
||||||
@Getter
|
@Getter
|
||||||
private boolean enteringPIPMode = false;
|
private boolean enteringPIPMode = false;
|
||||||
|
@ -366,6 +376,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
private MediaSessionCompat mediaSession;
|
private MediaSessionCompat mediaSession;
|
||||||
private boolean receivedStopService;
|
private boolean receivedStopService;
|
||||||
private ActionBarDrawerToggle toggle;
|
private ActionBarDrawerToggle toggle;
|
||||||
|
private SwipeRefreshLayout notificationsSwipeContainer;
|
||||||
private SyncSetTask syncSetTask = null;
|
private SyncSetTask syncSetTask = null;
|
||||||
private List<WalletSync> pendingSyncSetQueue;
|
private List<WalletSync> pendingSyncSetQueue;
|
||||||
@Getter
|
@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() {
|
findViewById(R.id.global_now_playing_card).setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
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<LbryNotification> 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<LbryNotification> notifications) {
|
||||||
|
List<Long> remoteIds = new ArrayList<>();
|
||||||
|
for (LbryNotification notification : notifications) {
|
||||||
|
remoteIds.add(notification.getRemoteId());
|
||||||
|
}
|
||||||
|
(new AsyncTask<Void, Void, Void>() {
|
||||||
|
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 {
|
private class PlayerNotificationDescriptionAdapter implements PlayerNotificationManager.MediaDescriptionAdapter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -3390,6 +3508,10 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
if (markRead && findViewById(R.id.notifications_container).getVisibility() == View.VISIBLE) {
|
if (markRead && findViewById(R.id.notifications_container).getVisibility() == View.VISIBLE) {
|
||||||
markNotificationsRead();
|
markNotificationsRead();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (notificationsSwipeContainer != null) {
|
||||||
|
notificationsSwipeContainer.setRefreshing(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -3397,6 +3519,9 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
// pass
|
// pass
|
||||||
Log.e(TAG, "error loading remote notifications", exception);
|
Log.e(TAG, "error loading remote notifications", exception);
|
||||||
loadLocalNotifications();
|
loadLocalNotifications();
|
||||||
|
if (notificationsSwipeContainer != null) {
|
||||||
|
notificationsSwipeContainer.setRefreshing(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||||
|
@ -3426,6 +3551,7 @@ public class MainActivity extends AppCompatActivity implements SdkStatusListener
|
||||||
|
|
||||||
if (notificationListAdapter == null) {
|
if (notificationListAdapter == null) {
|
||||||
notificationListAdapter = new NotificationListAdapter(notifications, MainActivity.this);
|
notificationListAdapter = new NotificationListAdapter(notifications, MainActivity.this);
|
||||||
|
notificationListAdapter.setSelectionModeListener(MainActivity.this);
|
||||||
((RecyclerView) findViewById(R.id.notifications_list)).setAdapter(notificationListAdapter);
|
((RecyclerView) findViewById(R.id.notifications_list)).setAdapter(notificationListAdapter);
|
||||||
} else {
|
} else {
|
||||||
notificationListAdapter.addNotifications(notifications);
|
notificationListAdapter.addNotifications(notifications);
|
||||||
|
|
|
@ -14,6 +14,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
import com.bumptech.glide.request.RequestOptions;
|
import com.bumptech.glide.request.RequestOptions;
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
@ -24,6 +25,7 @@ import java.util.List;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import io.lbry.browser.R;
|
import io.lbry.browser.R;
|
||||||
|
import io.lbry.browser.listener.SelectionModeListener;
|
||||||
import io.lbry.browser.model.Claim;
|
import io.lbry.browser.model.Claim;
|
||||||
import io.lbry.browser.model.lbryinc.LbryNotification;
|
import io.lbry.browser.model.lbryinc.LbryNotification;
|
||||||
import io.lbry.browser.ui.controls.SolidIconView;
|
import io.lbry.browser.ui.controls.SolidIconView;
|
||||||
|
@ -43,15 +45,19 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi
|
||||||
|
|
||||||
private Context context;
|
private Context context;
|
||||||
private List<LbryNotification> items;
|
private List<LbryNotification> items;
|
||||||
|
private List<LbryNotification> selectedItems;
|
||||||
@Setter
|
@Setter
|
||||||
private NotificationClickListener clickListener;
|
private NotificationClickListener clickListener;
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private int customizeMode;
|
private boolean inSelectionMode;
|
||||||
|
@Setter
|
||||||
|
private SelectionModeListener selectionModeListener;
|
||||||
|
|
||||||
public NotificationListAdapter(List<LbryNotification> notifications, Context context) {
|
public NotificationListAdapter(List<LbryNotification> notifications, Context context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.items = new ArrayList<>(notifications);
|
this.items = new ArrayList<>(notifications);
|
||||||
|
this.selectedItems = new ArrayList<>();
|
||||||
Collections.sort(items, Collections.reverseOrder(new LbryNotification()));
|
Collections.sort(items, Collections.reverseOrder(new LbryNotification()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +68,7 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi
|
||||||
protected TextView timeView;
|
protected TextView timeView;
|
||||||
protected SolidIconView iconView;
|
protected SolidIconView iconView;
|
||||||
protected ImageView thumbnailView;
|
protected ImageView thumbnailView;
|
||||||
|
protected View selectedOverlayView;
|
||||||
public ViewHolder(View v) {
|
public ViewHolder(View v) {
|
||||||
super(v);
|
super(v);
|
||||||
layoutView = v.findViewById(R.id.notification_layout);
|
layoutView = v.findViewById(R.id.notification_layout);
|
||||||
|
@ -70,12 +77,25 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi
|
||||||
timeView = v.findViewById(R.id.notification_time);
|
timeView = v.findViewById(R.id.notification_time);
|
||||||
iconView = v.findViewById(R.id.notification_icon);
|
iconView = v.findViewById(R.id.notification_icon);
|
||||||
thumbnailView = v.findViewById(R.id.notification_author_thumbnail);
|
thumbnailView = v.findViewById(R.id.notification_author_thumbnail);
|
||||||
|
selectedOverlayView = v.findViewById(R.id.notification_selected_overlay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
return items != null ? items.size() : 0;
|
return items != null ? items.size() : 0;
|
||||||
}
|
}
|
||||||
|
public List<LbryNotification> 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) {
|
public void insertNotification(LbryNotification notification, int index) {
|
||||||
if (!items.contains(notification)) {
|
if (!items.contains(notification)) {
|
||||||
|
@ -90,6 +110,12 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi
|
||||||
}
|
}
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
public void removeNotifications(List<LbryNotification> notifications) {
|
||||||
|
for (LbryNotification notification : notifications) {
|
||||||
|
items.remove(notification);
|
||||||
|
}
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
public List<String> getAuthorUrls() {
|
public List<String> getAuthorUrls() {
|
||||||
List<String> urls = new ArrayList<>();
|
List<String> urls = new ArrayList<>();
|
||||||
|
@ -158,9 +184,8 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(NotificationListAdapter.ViewHolder vh, int position) {
|
public void onBindViewHolder(NotificationListAdapter.ViewHolder vh, int position) {
|
||||||
LbryNotification notification = items.get(position);
|
LbryNotification notification = items.get(position);
|
||||||
|
|
||||||
|
|
||||||
vh.layoutView.setBackgroundColor(ContextCompat.getColor(context, notification.isSeen() ? android.R.color.transparent : R.color.nextLbryGreenSemiTransparent));
|
vh.layoutView.setBackgroundColor(ContextCompat.getColor(context, notification.isSeen() ? android.R.color.transparent : R.color.nextLbryGreenSemiTransparent));
|
||||||
|
vh.selectedOverlayView.setVisibility(isNotificationSelected(notification) ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
vh.titleView.setVisibility(!Helper.isNullOrEmpty(notification.getTitle()) ? View.VISIBLE : View.GONE);
|
vh.titleView.setVisibility(!Helper.isNullOrEmpty(notification.getTitle()) ? View.VISIBLE : View.GONE);
|
||||||
vh.titleView.setText(notification.getTitle());
|
vh.titleView.setText(notification.getTitle());
|
||||||
|
@ -181,11 +206,49 @@ public class NotificationListAdapter extends RecyclerView.Adapter<NotificationLi
|
||||||
vh.itemView.setOnClickListener(new View.OnClickListener() {
|
vh.itemView.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
|
if (inSelectionMode) {
|
||||||
|
toggleSelectedNotification(notification);
|
||||||
|
} else {
|
||||||
if (clickListener != null) {
|
if (clickListener != null) {
|
||||||
clickListener.onNotificationClicked(notification);
|
clickListener.onNotificationClicked(notification);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
vh.itemView.setOnLongClickListener(new View.OnLongClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onLongClick(View view) {
|
||||||
|
if (!inSelectionMode) {
|
||||||
|
inSelectionMode = true;
|
||||||
|
if (selectionModeListener != null) {
|
||||||
|
selectionModeListener.onEnterSelectionMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toggleSelectedNotification(notification);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toggleSelectedNotification(LbryNotification notification) {
|
||||||
|
if (selectedItems.contains(notification)) {
|
||||||
|
selectedItems.remove(notification);
|
||||||
|
} else {
|
||||||
|
selectedItems.add(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectionModeListener != null) {
|
||||||
|
selectionModeListener.onItemSelectionToggled();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedItems.size() == 0) {
|
||||||
|
inSelectionMode = false;
|
||||||
|
if (selectionModeListener != null) {
|
||||||
|
selectionModeListener.onExitSelectionMode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getLocalNotificationTime(LbryNotification notification) {
|
private long getLocalNotificationTime(LbryNotification notification) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.sql.SQLInput;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -372,6 +373,20 @@ public class DatabaseHelper extends SQLiteOpenHelper {
|
||||||
}
|
}
|
||||||
return notifications;
|
return notifications;
|
||||||
}
|
}
|
||||||
|
public static void deleteNotifications(List<LbryNotification> notifications, SQLiteDatabase db) {
|
||||||
|
StringBuilder sb = new StringBuilder("DELETE FROM notifications WHERE remote_id IN (");
|
||||||
|
List<Object> 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) {
|
public static int getUnreadNotificationsCount(SQLiteDatabase db) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
|
|
|
@ -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<Void, Void, Boolean> {
|
||||||
|
private List<Long> ids;
|
||||||
|
|
||||||
|
public NotificationDeleteTask(List<Long> ids) {
|
||||||
|
this.ids = ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Boolean doInBackground(Void... params) {
|
||||||
|
Map<String, String> 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -71,6 +71,10 @@
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:textAlignment="center" />
|
android:textAlignment="center" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
android:id="@+id/notifications_list_swipe_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/notifications_list"
|
android:id="@+id/notifications_list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -78,6 +82,7 @@
|
||||||
android:paddingTop="8dp"
|
android:paddingTop="8dp"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
/>
|
/>
|
||||||
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<include layout="@layout/floating_wallet_balance" />
|
<include layout="@layout/floating_wallet_balance" />
|
||||||
|
|
|
@ -28,6 +28,20 @@
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
android:visibility="invisible" />
|
android:visibility="invisible" />
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:background="@drawable/bg_channel_overlay_icon"
|
||||||
|
android:id="@+id/notification_selected_overlay"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:visibility="gone">
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:src="@drawable/ic_check"
|
||||||
|
android:tint="@color/nextLbryGreen" />
|
||||||
|
</RelativeLayout>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
|
|
10
app/src/main/res/menu/menu_notification.xml
Normal file
10
app/src/main/res/menu/menu_notification.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_delete"
|
||||||
|
android:icon="@drawable/ic_delete"
|
||||||
|
android:title="@string/delete"
|
||||||
|
app:iconTint="@color/actionBarForeground"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
</menu>
|
|
@ -628,6 +628,10 @@
|
||||||
|
|
||||||
<!-- Notifications -->
|
<!-- Notifications -->
|
||||||
<string name="no_notifications">It\'s quiet here! New notifications will be displayed when you receive them.</string>
|
<string name="no_notifications">It\'s quiet here! New notifications will be displayed when you receive them.</string>
|
||||||
|
<plurals name="confirm_delete_notifications">
|
||||||
|
<item quantity="one">Are you sure you want to remove the selected notification?</item>
|
||||||
|
<item quantity="other">Are you sure you want to remove the selected notifications?</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
<!-- Font Awesome -->
|
<!-- Font Awesome -->
|
||||||
<string name="fa_gift" translatable="false"></string>
|
<string name="fa_gift" translatable="false"></string>
|
||||||
|
|
Loading…
Reference in a new issue