Add bookmark clearing

multisite
Floens 9 years ago
parent 28a2f5cb4c
commit 674a1804e6
  1. 56
      Clover/app/src/main/java/org/floens/chan/core/manager/WatchManager.java
  2. 15
      Clover/app/src/main/java/org/floens/chan/core/model/Pin.java
  3. 57
      Clover/app/src/main/java/org/floens/chan/ui/adapter/PinAdapter.java
  4. 33
      Clover/app/src/main/java/org/floens/chan/ui/controller/DrawerController.java
  5. 12
      Clover/app/src/main/res/layout/cell_header.xml
  6. 8
      Clover/app/src/main/res/values/strings.xml

@ -168,8 +168,11 @@ public class WatchManager {
} }
} }
pin.order = pins.size(); if (pin.order < 0) {
pin.order = pins.size();
}
pins.add(pin); pins.add(pin);
applyOrder();
databaseManager.runTaskSync(databasePinManager.createPin(pin)); databaseManager.runTaskSync(databasePinManager.createPin(pin));
updateState(); updateState();
@ -186,6 +189,7 @@ public class WatchManager {
databaseManager.runTaskSync(databasePinManager.deletePin(pin)); databaseManager.runTaskSync(databasePinManager.deletePin(pin));
// Update the new orders // Update the new orders
applyOrder();
updatePinsInDatabase(); updatePinsInDatabase();
updateState(); updateState();
@ -223,6 +227,14 @@ public class WatchManager {
return null; return null;
} }
public void reorder(List<Pin> pins) {
for (int i = 0; i < pins.size(); i++) {
Pin pin = pins.get(i);
pin.order = i;
}
updatePinsInDatabase();
}
public List<Pin> getAllPins() { public List<Pin> getAllPins() {
return pins; return pins;
} }
@ -305,6 +317,40 @@ public class WatchManager {
} }
} }
// Clear all non watching pins or all pins
// Returns a list of pins that can later be given to addAll
// to undo the clearing
public List<Pin> clearPins(boolean all) {
List<Pin> toRemove = new ArrayList<>();
for (int i = 0; i < pins.size(); i++) {
Pin pin = pins.get(i);
if (all || !pin.watching) {
toRemove.add(pin);
}
}
List<Pin> undo = new ArrayList<>(toRemove.size());
for (int i = 0; i < toRemove.size(); i++) {
Pin pin = toRemove.get(i);
undo.add(pin.copy());
}
for (int i = 0; i < toRemove.size(); i++) {
Pin pin = toRemove.get(i);
deletePin(pin);
}
return undo;
}
public void addAll(List<Pin> pins) {
Collections.sort(pins, SORT_PINS);
for (int i = 0; i < pins.size(); i++) {
Pin pin = pins.get(i);
createPin(pin);
}
}
// Called when the user changes the watch enabled preference // Called when the user changes the watch enabled preference
public void onWatchEnabledChanged(boolean watchEnabled) { public void onWatchEnabledChanged(boolean watchEnabled) {
updateState(watchEnabled, isBackgroundWatchingSettingEnabled()); updateState(watchEnabled, isBackgroundWatchingSettingEnabled());
@ -329,6 +375,14 @@ public class WatchManager {
return pinWatchers.get(pin); return pinWatchers.get(pin);
} }
private void applyOrder() {
Collections.sort(pins, SORT_PINS);
for (int i = 0; i < pins.size(); i++) {
Pin pin = pins.get(i);
pin.order = i;
}
}
private boolean createPinWatcher(Pin pin) { private boolean createPinWatcher(Pin pin) {
if (!pinWatchers.containsKey(pin)) { if (!pinWatchers.containsKey(pin)) {
pinWatchers.put(pin, new PinWatcher(pin)); pinWatchers.put(pin, new PinWatcher(pin));

@ -70,4 +70,19 @@ public class Pin {
return Math.max(0, quoteNewCount - quoteLastCount); return Math.max(0, quoteNewCount - quoteLastCount);
} }
} }
public Pin copy() {
Pin copy = new Pin();
copy.loadable = loadable;
copy.watching = watching;
copy.watchLastCount = watchLastCount;
copy.watchNewCount = watchNewCount;
copy.quoteLastCount = quoteLastCount;
copy.quoteNewCount = quoteNewCount;
copy.isError = isError;
copy.thumbnailUrl = thumbnailUrl;
copy.order = order;
copy.archived = archived;
return copy;
}
} }

@ -26,7 +26,9 @@ import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import org.floens.chan.Chan;
import org.floens.chan.R; import org.floens.chan.R;
import org.floens.chan.core.manager.WatchManager;
import org.floens.chan.core.model.Pin; import org.floens.chan.core.model.Pin;
import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.core.settings.ChanSettings;
import org.floens.chan.ui.helper.PostHelper; import org.floens.chan.ui.helper.PostHelper;
@ -34,6 +36,8 @@ import org.floens.chan.ui.view.ThumbnailView;
import org.floens.chan.utils.AndroidUtils; import org.floens.chan.utils.AndroidUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List; import java.util.List;
import static org.floens.chan.ui.theme.ThemeHelper.theme; import static org.floens.chan.ui.theme.ThemeHelper.theme;
@ -44,6 +48,10 @@ import static org.floens.chan.utils.AndroidUtils.setRoundItemBackground;
import static org.floens.chan.utils.AndroidUtils.sp; import static org.floens.chan.utils.AndroidUtils.sp;
public class PinAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { public class PinAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public enum HeaderAction {
SETTINGS, CLEAR, CLEAR_ALL
}
private static final int PIN_OFFSET = 3; private static final int PIN_OFFSET = 3;
private static final int TYPE_HEADER = 0; private static final int TYPE_HEADER = 0;
@ -51,11 +59,20 @@ public class PinAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_LINK = 2; private static final int TYPE_LINK = 2;
private static final int TYPE_DIVIDER = 3; private static final int TYPE_DIVIDER = 3;
private static final Comparator<Pin> SORT_PINS = new Comparator<Pin>() {
@Override
public int compare(Pin lhs, Pin rhs) {
return lhs.order - rhs.order;
}
};
private final WatchManager watchManager;
private final Callback callback; private final Callback callback;
private List<Pin> pins = new ArrayList<>(); private List<Pin> pins = new ArrayList<>();
private Pin highlighted; private Pin highlighted;
public PinAdapter(Callback callback) { public PinAdapter(Callback callback) {
watchManager = Chan.getWatchManager();
this.callback = callback; this.callback = callback;
setHasStableIds(true); setHasStableIds(true);
} }
@ -120,7 +137,8 @@ public class PinAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
case TYPE_HEADER: case TYPE_HEADER:
HeaderHolder headerHolder = (HeaderHolder) holder; HeaderHolder headerHolder = (HeaderHolder) holder;
headerHolder.text.setText(R.string.drawer_pinned); headerHolder.text.setText(R.string.drawer_pinned);
theme().settingsDrawable.apply(headerHolder.image); theme().clearDrawable.apply(headerHolder.clear);
theme().settingsDrawable.apply(headerHolder.settings);
break; break;
case TYPE_PIN: case TYPE_PIN:
@ -179,16 +197,19 @@ public class PinAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public void onPinsChanged(List<Pin> pins) { public void onPinsChanged(List<Pin> pins) {
this.pins.clear(); this.pins.clear();
this.pins.addAll(pins); this.pins.addAll(pins);
Collections.sort(pins, SORT_PINS);
notifyDataSetChanged(); notifyDataSetChanged();
} }
public void onPinAdded(Pin pin) { public void onPinAdded(Pin pin) {
pins.add(pin); pins.add(pin);
Collections.sort(pins, SORT_PINS);
notifyDataSetChanged(); notifyDataSetChanged();
} }
public void onPinRemoved(Pin pin) { public void onPinRemoved(Pin pin) {
pins.remove(pin); pins.remove(pin);
Collections.sort(pins, SORT_PINS);
notifyDataSetChanged(); notifyDataSetChanged();
} }
@ -264,10 +285,8 @@ public class PinAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
} }
private void applyOrder() { private void applyOrder() {
for (int i = 0; i < pins.size(); i++) { watchManager.reorder(pins);
Pin pin = pins.get(i); notifyDataSetChanged();
pin.order = i;
}
} }
public class PinViewHolder extends RecyclerView.ViewHolder { public class PinViewHolder extends RecyclerView.ViewHolder {
@ -323,18 +342,34 @@ public class PinAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public class HeaderHolder extends RecyclerView.ViewHolder { public class HeaderHolder extends RecyclerView.ViewHolder {
private TextView text; private TextView text;
private ImageView image; private ImageView clear;
private ImageView settings;
public HeaderHolder(View itemView) { public HeaderHolder(View itemView) {
super(itemView); super(itemView);
text = (TextView) itemView.findViewById(R.id.text); text = (TextView) itemView.findViewById(R.id.text);
text.setTypeface(ROBOTO_MEDIUM); text.setTypeface(ROBOTO_MEDIUM);
image = (ImageView) itemView.findViewById(R.id.image); clear = (ImageView) itemView.findViewById(R.id.clear);
setRoundItemBackground(image); setRoundItemBackground(clear);
image.setOnClickListener(new View.OnClickListener() { clear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
callback.onHeaderClicked(HeaderHolder.this, HeaderAction.CLEAR);
}
});
clear.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
callback.onHeaderClicked(HeaderHolder.this, HeaderAction.CLEAR_ALL);
return true;
}
});
settings = (ImageView) itemView.findViewById(R.id.settings);
setRoundItemBackground(settings);
settings.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
callback.onHeaderClicked(HeaderHolder.this); callback.onHeaderClicked(HeaderHolder.this, HeaderAction.SETTINGS);
} }
}); });
} }
@ -392,7 +427,7 @@ public class PinAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
void onWatchCountClicked(Pin pin); void onWatchCountClicked(Pin pin);
void onHeaderClicked(HeaderHolder holder); void onHeaderClicked(HeaderHolder holder, HeaderAction headerAction);
void onPinRemoved(Pin pin); void onPinRemoved(Pin pin);

@ -151,20 +151,43 @@ public class DrawerController extends Controller implements PinAdapter.Callback,
} }
@Override @Override
public void onHeaderClicked(PinAdapter.HeaderHolder holder) { public void onHeaderClicked(PinAdapter.HeaderHolder holder, PinAdapter.HeaderAction headerAction) {
openController(new WatchSettingsController(context)); if (headerAction == PinAdapter.HeaderAction.SETTINGS) {
openController(new WatchSettingsController(context));
} else if (headerAction == PinAdapter.HeaderAction.CLEAR || headerAction == PinAdapter.HeaderAction.CLEAR_ALL) {
boolean all = headerAction == PinAdapter.HeaderAction.CLEAR_ALL;
final List<Pin> pins = watchManager.clearPins(all);
if (!pins.isEmpty()) {
String text = context.getResources().getQuantityString(R.plurals.bookmark, pins.size(), pins.size());
//noinspection WrongConstant
Snackbar snackbar = Snackbar.make(drawerLayout, context.getString(R.string.drawer_pins_cleared, text), 4000);
fixSnackbarText(context, snackbar);
snackbar.setAction(R.string.undo, new View.OnClickListener() {
@Override
public void onClick(View v) {
watchManager.addAll(pins);
}
});
snackbar.show();
} else {
int text = watchManager.getAllPins().isEmpty() ? R.string.drawer_pins_non_cleared : R.string.drawer_pins_non_cleared_try_all;
Snackbar snackbar = Snackbar.make(drawerLayout, text, Snackbar.LENGTH_LONG);
fixSnackbarText(context, snackbar);
snackbar.show();
}
}
} }
@Override @Override
public void onPinRemoved(final Pin pin) { public void onPinRemoved(Pin pin) {
final Pin undoPin = pin.copy();
watchManager.deletePin(pin); watchManager.deletePin(pin);
Snackbar snackbar = Snackbar.make(drawerLayout, context.getString(R.string.drawer_pin_removed, pin.loadable.title), Snackbar.LENGTH_LONG); Snackbar snackbar = Snackbar.make(drawerLayout, context.getString(R.string.drawer_pin_removed, pin.loadable.title), Snackbar.LENGTH_LONG);
fixSnackbarText(context, snackbar); fixSnackbarText(context, snackbar);
snackbar.setAction(R.string.undo, new View.OnClickListener() { snackbar.setAction(R.string.undo, new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
watchManager.createPin(pin); watchManager.createPin(undoPin);
} }
}); });
snackbar.show(); snackbar.show();

@ -35,7 +35,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:textSize="14sp" /> android:textSize="14sp" />
<ImageView <ImageView
android:id="@+id/image" android:id="@+id/clear"
android:layout_width="56dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:scaleType="center"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/settings"
android:layout_width="56dp" android:layout_width="56dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"

@ -98,6 +98,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<item quantity="other">%d boards</item> <item quantity="other">%d boards</item>
</plurals> </plurals>
<plurals name="bookmark">
<item quantity="one">%d bookmark</item>
<item quantity="other">%d bookmarks</item>
</plurals>
<string name="card_stats">%1$dR %2$dI</string> <string name="card_stats">%1$dR %2$dI</string>
<string name="action_view_album">View album</string> <string name="action_view_album">View album</string>
@ -236,6 +241,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<string name="drawer_catalog">Catalog</string> <string name="drawer_catalog">Catalog</string>
<string name="drawer_pinned">Bookmarked threads</string> <string name="drawer_pinned">Bookmarked threads</string>
<string name="drawer_pin_removed">Removed \'%1$s\'</string> <string name="drawer_pin_removed">Removed \'%1$s\'</string>
<string name="drawer_pins_cleared">%1$s cleared</string>
<string name="drawer_pins_non_cleared">No bookmarks cleared.</string>
<string name="drawer_pins_non_cleared_try_all">No bookmarks cleared. Hold to remove all.</string>
<string name="post_highlight_id">Highlight ID</string> <string name="post_highlight_id">Highlight ID</string>
<string name="post_highlight_tripcode">Highlight tripcode</string> <string name="post_highlight_tripcode">Highlight tripcode</string>

Loading…
Cancel
Save