Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete messages by swiping and add snackbar for undoing deletion. #99

Merged
merged 13 commits into from
Feb 18, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
leopoldsedev marked this conversation as resolved.
Show resolved Hide resolved
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
Expand All @@ -25,7 +25,7 @@
import io.noties.markwon.movement.MovementMethodPlugin;
import java.util.List;

public class ListMessageAdapter extends BaseAdapter {
public class ListMessageAdapter extends RecyclerView.Adapter<ListMessageAdapter.ViewHolder> {

private Context content;
private Picasso picasso;
Expand Down Expand Up @@ -56,34 +56,25 @@ public class ListMessageAdapter extends BaseAdapter {
.build();
}

void items(List<MessageWithImage> items) {
this.items = items;
public MessageWithImage getItem(int position) {
return items.get(position);
}

@Override
public int getCount() {
return items.size();
void items(List<MessageWithImage> items) {
this.items = items;
}

@NonNull
@Override
public MessageWithImage getItem(int position) {
return items.get(position);
}
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(content).inflate(R.layout.message_item, parent, false);

@Override
public long getItemId(int position) {
return getItem(position).message.getId();
ViewHolder holder = new ViewHolder(view);
return holder;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
final View view;
if (convertView == null) {
view = LayoutInflater.from(content).inflate(R.layout.message_item, parent, false);
} else {
view = convertView;
}
ViewHolder holder = new ViewHolder(view);
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
final MessageWithImage message = items.get(position);
if (Extras.useMarkdown(message.message)) {
holder.message.setAutoLinkMask(0);
Expand All @@ -102,8 +93,17 @@ public View getView(int position, View convertView, ViewGroup parent) {
? Utils.dateToRelative(message.message.getDate())
: "?");
holder.delete.setOnClickListener((ignored) -> delete.delete(message.message));
}

return view;
@Override
public int getItemCount() {
return items.size();
}

@Override
public long getItemId(int position) {
MessageWithImage currentItem = items.get(position);
return currentItem.message.getId();
}

static class ViewHolder extends RecyclerView.ViewHolder {
Expand Down
156 changes: 138 additions & 18 deletions app/src/main/java/com/github/gotify/messages/MessagesActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,31 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Canvas;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.ViewFlipper;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import butterknife.BindView;
import butterknife.ButterKnife;
Expand Down Expand Up @@ -66,7 +74,7 @@
import static java.util.Collections.emptyList;

public class MessagesActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener, AbsListView.OnScrollListener {
implements NavigationView.OnNavigationItemSelectedListener {

private BroadcastReceiver receiver =
new BroadcastReceiver() {
Expand All @@ -90,7 +98,7 @@ public void onReceive(Context context, Intent intent) {
NavigationView navigationView;

@BindView(R.id.messages_view)
ListView messagesView;
RecyclerView messagesView;

@BindView(R.id.swipe_refresh)
SwipeRefreshLayout swipeRefreshLayout;
Expand Down Expand Up @@ -137,9 +145,21 @@ protected void onCreate(Bundle savedInstanceState) {

messages = new MessageFacade(client.createService(MessageApi.class), appsHolder);

messagesView.setOnScrollListener(this);
messagesView.setAdapter(
new ListMessageAdapter(this, settings, picasso, emptyList(), this::delete));
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
DividerItemDecoration dividerItemDecoration =
new DividerItemDecoration(
messagesView.getContext(), layoutManager.getOrientation());
ListMessageAdapter adapter =
new ListMessageAdapter(this, settings, picasso, emptyList(), this::delete);

messagesView.addItemDecoration(dividerItemDecoration);
messagesView.setHasFixedSize(true);
messagesView.setLayoutManager(layoutManager);
messagesView.addOnScrollListener(new MessageListOnScrollListener());
messagesView.setAdapter(adapter);

ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new SwipeToDeleteCallback(adapter));
itemTouchHelper.attachToRecyclerView(messagesView);

swipeRefreshLayout.setOnRefreshListener(this::onRefresh);
drawer.addDrawerListener(
Expand Down Expand Up @@ -341,18 +361,118 @@ protected void onDestroy() {
picasso.shutdown();
}

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {}
private class SwipeToDeleteCallback extends ItemTouchHelper.SimpleCallback {
private ListMessageAdapter adapter;
private Drawable icon;
private final ColorDrawable background;

@Override
public void onScroll(
AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem + visibleItemCount > totalItemCount - 15
&& totalItemCount != 0
&& messages.canLoadMore(appId)) {
if (!isLoadMore) {
isLoadMore = true;
new LoadMore().execute(appId);
public SwipeToDeleteCallback(ListMessageAdapter adapter) {
super(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);
this.adapter = adapter;

int backgroundColorId =
ContextCompat.getColor(MessagesActivity.this, R.color.swipeBackground);
int iconColorId = ContextCompat.getColor(MessagesActivity.this, R.color.swipeIcon);

Drawable drawable =
ContextCompat.getDrawable(MessagesActivity.this, R.drawable.ic_delete);
if (drawable != null) {
leopoldsedev marked this conversation as resolved.
Show resolved Hide resolved
icon = DrawableCompat.wrap(drawable.mutate());
DrawableCompat.setTint(icon, iconColorId);
}

background = new ColorDrawable(backgroundColorId);
}

@Override
public boolean onMove(
@NonNull RecyclerView recyclerView,
@NonNull RecyclerView.ViewHolder viewHolder,
@NonNull RecyclerView.ViewHolder target) {
return false;
}

@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
Message message = adapter.getItem(position).message;
MessagesActivity.this.delete(message);
}

@Override
public void onChildDraw(
@NonNull Canvas c,
@NonNull RecyclerView recyclerView,
@NonNull RecyclerView.ViewHolder viewHolder,
float dX,
float dY,
int actionState,
boolean isCurrentlyActive) {
View itemView = viewHolder.itemView;

int iconHeight = itemView.getHeight() / 3;
double scale = iconHeight / (double) icon.getIntrinsicHeight();
int iconWidth = (int) (icon.getIntrinsicWidth() * scale);

int iconMarginLeftRight = 50;
int iconMarginTopBottom = (itemView.getHeight() - iconHeight) / 2;
int iconTop = itemView.getTop() + iconMarginTopBottom;
int iconBottom = itemView.getBottom() - iconMarginTopBottom;

if (dX > 0) {
// Swiping to the right
int iconLeft = itemView.getLeft() + iconMarginLeftRight;
int iconRight = itemView.getLeft() + iconMarginLeftRight + iconWidth;
icon.setBounds(iconLeft, iconTop, iconRight, iconBottom);

background.setBounds(
itemView.getLeft(),
itemView.getTop(),
itemView.getLeft() + ((int) dX),
itemView.getBottom());
} else if (dX < 0) {
// Swiping to the left
int iconLeft = itemView.getRight() - iconMarginLeftRight - iconWidth;
int iconRight = itemView.getRight() - iconMarginLeftRight;
icon.setBounds(iconLeft, iconTop, iconRight, iconBottom);

background.setBounds(
itemView.getRight() + ((int) dX),
itemView.getTop(),
itemView.getRight(),
itemView.getBottom());
} else {
// View is unswiped
icon.setBounds(0, 0, 0, 0);
background.setBounds(0, 0, 0, 0);
}

background.draw(c);
icon.draw(c);

super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
}

private class MessageListOnScrollListener extends RecyclerView.OnScrollListener {
@Override
public void onScrollStateChanged(RecyclerView view, int scrollState) {}

@Override
public void onScrolled(RecyclerView view, int dx, int dy) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) view.getLayoutManager();
if (linearLayoutManager != null) {
int lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
int totalItemCount = view.getAdapter().getItemCount();

if (lastVisibleItem > totalItemCount - 15
&& totalItemCount != 0
&& messages.canLoadMore(appId)) {
if (!isLoadMore) {
isLoadMore = true;
new LoadMore().execute(appId);
}
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/res/layout/activity_messages.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@
android:layout_width="match_parent"
android:layout_height="match_parent" >

<ListView
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/messages_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</ListView>
</androidx.recyclerview.widget.RecyclerView>

<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/no_messages"
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/res/layout/message_item.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
android:padding="10dp"
android:background="?android:colorBackground">

<TextView
android:id="@+id/message_date"
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@
<color name="colorPrimaryDark">#3867d6</color>
<color name="colorAccent">#1c49b4</color>
<color name="icons">#434343</color>
<color name="swipeBackground">#E91E1E</color>
leopoldsedev marked this conversation as resolved.
Show resolved Hide resolved
<color name="swipeIcon">#FFFFFF</color>
</resources>