From e61f8fcc566a12065de4cb6a642381a185a067ff Mon Sep 17 00:00:00 2001 From: Floens Date: Mon, 6 Oct 2014 19:20:47 +0200 Subject: [PATCH] Improve notifications Redo WatchNotifier code Use string xml files Add color option Add sound option Add quote only option --- .../org/floens/chan/core/ChanPreferences.java | 13 ++ .../floens/chan/core/loader/ChanParser.java | 6 +- .../java/org/floens/chan/core/model/Pin.java | 12 +- .../java/org/floens/chan/core/model/Post.java | 8 +- .../floens/chan/core/watch/PinWatcher.java | 79 ++++--- .../floens/chan/ui/activity/BaseActivity.java | 2 +- .../ui/activity/WatchSettingsActivity.java | 54 ++++- .../floens/chan/ui/adapter/PinnedAdapter.java | 2 +- .../floens/chan/ui/service/WatchNotifier.java | 203 ++++++++++-------- .../java/org/floens/chan/utils/Utils.java | 8 + .../app/src/main/res/values/string_arrays.xml | 68 ++++++ Clover/app/src/main/res/values/strings.xml | 45 ++-- .../app/src/main/res/values/watch_strings.xml | 41 ++++ .../app/src/main/res/xml/preference_watch.xml | 27 +++ 14 files changed, 405 insertions(+), 163 deletions(-) create mode 100644 Clover/app/src/main/res/values/string_arrays.xml create mode 100644 Clover/app/src/main/res/values/watch_strings.xml diff --git a/Clover/app/src/main/java/org/floens/chan/core/ChanPreferences.java b/Clover/app/src/main/java/org/floens/chan/core/ChanPreferences.java index d7783d30..69790391 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/ChanPreferences.java +++ b/Clover/app/src/main/java/org/floens/chan/core/ChanPreferences.java @@ -103,6 +103,19 @@ public class ChanPreferences { return Integer.parseInt(number); } + public static String getWatchNotifyMode() { + return p().getString("preference_watch_notify_mode", "all"); + } + + public static String getWatchSound() { + return p().getString("preference_watch_sound", "quotes"); + } + + public static long getWatchLed() { + String raw = p().getString("preference_watch_led", "ffffffff"); + return Long.parseLong(raw, 16); + } + public static boolean getVideoAutoPlay() { return getImageAutoLoad() && !getVideoExternal() && p().getBoolean("preference_autoplay", false); } diff --git a/Clover/app/src/main/java/org/floens/chan/core/loader/ChanParser.java b/Clover/app/src/main/java/org/floens/chan/core/loader/ChanParser.java index 3400bc41..1d01934a 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/loader/ChanParser.java +++ b/Clover/app/src/main/java/org/floens/chan/core/loader/ChanParser.java @@ -53,11 +53,7 @@ import java.util.regex.Pattern; public class ChanParser { private static final Pattern colorPattern = Pattern.compile("color:#([0-9a-fA-F]*)"); - private static ChanParser instance; - - static { - instance = new ChanParser(); - } + private static ChanParser instance = new ChanParser(); public static ChanParser getInstance() { return instance; diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/Pin.java b/Clover/app/src/main/java/org/floens/chan/core/model/Pin.java index bad02683..07407917 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/model/Pin.java +++ b/Clover/app/src/main/java/org/floens/chan/core/model/Pin.java @@ -41,10 +41,10 @@ public class Pin { public int watchNewCount = -1; @DatabaseField - public int quoteLastCount = 0; + public int quoteLastCount = -1; @DatabaseField - public int quoteNewCount = 0; + public int quoteNewCount = -1; @DatabaseField public boolean isError = false; @@ -54,7 +54,7 @@ public class Pin { private PinWatcher pinWatcher; - public int getNewPostsCount() { + public int getNewPostCount() { if (watchLastCount < 0 || watchNewCount < 0) { return 0; } else { @@ -63,7 +63,11 @@ public class Pin { } public int getNewQuoteCount() { - return Math.max(0, quoteNewCount - quoteLastCount); + if (quoteNewCount < 0 || quoteLastCount < 0) { + return 0; + } else { + return Math.max(0, quoteNewCount - quoteLastCount); + } } public PinWatcher getPinWatcher() { diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/Post.java b/Clover/app/src/main/java/org/floens/chan/core/model/Post.java index 2677838c..0038e595 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/model/Post.java +++ b/Clover/app/src/main/java/org/floens/chan/core/model/Post.java @@ -123,14 +123,8 @@ public class Post { if (isOP && (replies < 0 || images < 0)) return false; - if (ext != null) { + if (filename != null && ext != null && imageWidth > 0 && imageHeight > 0 && tim >= 0) { hasImage = true; - } - - if (hasImage) { - if (filename == null || ext == null || imageWidth <= 0 || imageHeight <= 0 || tim < 0) - return false; - imageUrl = ChanUrls.getImageUrl(board, Long.toString(tim), ext); filename = Parser.unescapeEntities(filename, false); diff --git a/Clover/app/src/main/java/org/floens/chan/core/watch/PinWatcher.java b/Clover/app/src/main/java/org/floens/chan/core/watch/PinWatcher.java index 81541d69..e02e911d 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/watch/PinWatcher.java +++ b/Clover/app/src/main/java/org/floens/chan/core/watch/PinWatcher.java @@ -37,6 +37,7 @@ public class PinWatcher implements Loader.LoaderListener { private Loader loader; private final List posts = new ArrayList<>(); + private final List quotes = new ArrayList<>(); private boolean wereNewQuotes = false; private boolean wereNewPosts = false; @@ -63,28 +64,25 @@ public class PinWatcher implements Loader.LoaderListener { if (pin.watchNewCount >= 0) { pin.watchLastCount = pin.watchNewCount; } + wereNewPosts = false; - pin.quoteLastCount = pin.quoteNewCount; + if (pin.quoteNewCount >= 0) { + pin.quoteLastCount = pin.quoteNewCount; + } wereNewQuotes = false; - wereNewPosts = false; } - public List getNewPosts() { + public List getUnviewedPosts() { if (posts.size() == 0) { return posts; } else { - return posts.subList(Math.max(0, posts.size() - pin.getNewPostsCount()), posts.size()); + return posts.subList(Math.max(0, posts.size() - pin.getNewPostCount()), posts.size()); } } - /* Currently not used - public List getNewQuotes() { - if (posts.size() == 0) { - return posts; - } else { - return posts.subList(Math.max(0, posts.size() - pin.getNewQuoteCount()), posts.size()); - } - }*/ + public List getUnviewedQuotes() { + return quotes.subList(Math.max(0, quotes.size() - pin.getNewQuoteCount()), quotes.size()); + } public boolean getWereNewQuotes() { if (wereNewQuotes) { @@ -133,33 +131,60 @@ public class PinWatcher implements Loader.LoaderListener { pin.thumbnailUrl = loader.getOP().thumbnailUrl; } + for (Post post : result) { + post.title = pin.loadable.title; + } + + // Populate posts list posts.clear(); posts.addAll(result); - int lastQuoteNewCount = pin.quoteNewCount; - int lastWatchNewCount = pin.watchNewCount; + // Populate quotes list + quotes.clear(); - if (pin.watchLastCount < 0) - pin.watchLastCount = result.size(); + // Get list of saved replies from this thread + List savedReplies = new ArrayList<>(); + for (Post item : result) { +// saved.title = pin.loadable.title; - pin.watchNewCount = result.size(); + if (item.isSavedReply) { + savedReplies.add(item); + } + } - // Get list of saved posts - int total = 0; - for (Post saved : result) { - if (saved.isSavedReply) { - total += saved.repliesFrom.size(); + // Now get a list of posts that have a quote to a saved reply + for (Post post : result) { + for (Post saved : savedReplies) { + if (post.repliesTo.contains(saved.no)) { + quotes.add(post); + } } } - pin.quoteNewCount = total; + boolean isFirstLoad = pin.watchNewCount < 0 || pin.quoteNewCount < 0; + + // If it was more than before processing + int lastWatchNewCount = pin.watchNewCount; + int lastQuoteNewCount = pin.quoteNewCount; - if (pin.quoteNewCount > lastQuoteNewCount) { - wereNewQuotes = true; + if (isFirstLoad) { + pin.watchLastCount = posts.size(); + pin.quoteLastCount = quotes.size(); } - if (pin.watchNewCount > lastWatchNewCount) { - wereNewPosts = true; + pin.watchNewCount = posts.size(); + pin.quoteNewCount = quotes.size(); + + if (!isFirstLoad) { + // There were new posts after processing + if (pin.watchNewCount > lastWatchNewCount) { + wereNewPosts = true; + } + + // There were new quotes after processing + if (pin.quoteNewCount > lastQuoteNewCount) { + wereNewQuotes = true; + } } if (Logger.debugEnabled()) { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/BaseActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/BaseActivity.java index 09f529c1..3cf59e53 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/BaseActivity.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/activity/BaseActivity.java @@ -214,7 +214,7 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene int count = 0; boolean color = false; for (Pin p : list) { - count += p.getNewPostsCount(); + count += p.getNewPostCount(); if (p.getNewQuoteCount() > 0) { color = true; } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/WatchSettingsActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/WatchSettingsActivity.java index d397c001..c18b7053 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/WatchSettingsActivity.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/activity/WatchSettingsActivity.java @@ -23,7 +23,6 @@ import android.app.FragmentTransaction; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; @@ -147,7 +146,6 @@ public class WatchSettingsActivity extends Activity implements OnCheckedChangeLi } updateListSummary(backgroundTimeout, currentValue); - // Timeout is reset when board activity is started backgroundTimeout.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { @@ -156,7 +154,55 @@ public class WatchSettingsActivity extends Activity implements OnCheckedChangeLi } }); - ((CheckBoxPreference) findPreference("preference_watch_background_enabled")).setOnPreferenceChangeListener(new OnPreferenceChangeListener() { + final ListPreference notifyMode = (ListPreference) findPreference("preference_watch_notify_mode"); + String currentNotifyMode = notifyMode.getValue(); + if (currentNotifyMode == null) { + notifyMode.setValue((String) notifyMode.getEntryValues()[0]); + currentNotifyMode = notifyMode.getValue(); + } + updateListSummary(notifyMode, currentNotifyMode); + + notifyMode.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + updateListSummary(notifyMode, newValue.toString()); + return true; + } + }); + + final ListPreference sound = (ListPreference) findPreference("preference_watch_sound"); + String currentSound = sound.getValue(); + if (currentSound == null) { + sound.setValue((String) sound.getEntryValues()[0]); + currentSound = sound.getValue(); + } + updateListSummary(sound, currentSound); + + sound.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + updateListSummary(sound, newValue.toString()); + return true; + } + }); + + final ListPreference led = (ListPreference) findPreference("preference_watch_led"); + String currentLed = led.getValue(); + if (currentLed == null) { + led.setValue((String) led.getEntryValues()[0]); + currentLed = led.getValue(); + } + updateListSummary(led, currentLed); + + led.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + updateListSummary(led, newValue.toString()); + return true; + } + }); + + findPreference("preference_watch_background_enabled").setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(final Preference preference, final Object newValue) { ChanApplication.getWatchManager().onBackgroundWatchingChanged((Boolean) newValue); @@ -170,7 +216,5 @@ public class WatchSettingsActivity extends Activity implements OnCheckedChangeLi int index = backgroundTimeout.findIndexOfValue(value); backgroundTimeout.setSummary(backgroundTimeout.getEntries()[index]); } - } - } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/adapter/PinnedAdapter.java b/Clover/app/src/main/java/org/floens/chan/ui/adapter/PinnedAdapter.java index fd60ac76..749258de 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/adapter/PinnedAdapter.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/adapter/PinnedAdapter.java @@ -156,7 +156,7 @@ public class PinnedAdapter extends BaseAdapter { if (pin.isError) { countView.setText("Err"); } else { - int count = pin.getNewPostsCount(); + int count = pin.getNewPostCount(); String total = Integer.toString(count); if (count > 999) { total = "1k+"; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/service/WatchNotifier.java b/Clover/app/src/main/java/org/floens/chan/ui/service/WatchNotifier.java index def798bc..49b21361 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/service/WatchNotifier.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/service/WatchNotifier.java @@ -23,17 +23,18 @@ import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; -import android.media.RingtoneManager; import android.os.IBinder; import android.support.v4.app.NotificationCompat; import org.floens.chan.ChanApplication; import org.floens.chan.R; +import org.floens.chan.core.ChanPreferences; import org.floens.chan.core.manager.WatchManager; import org.floens.chan.core.model.Pin; import org.floens.chan.core.model.Post; import org.floens.chan.core.watch.PinWatcher; import org.floens.chan.ui.activity.BoardActivity; +import org.floens.chan.utils.Utils; import java.util.ArrayList; import java.util.Collections; @@ -43,6 +44,7 @@ import java.util.List; public class WatchNotifier extends Service { private static final String TAG = "WatchNotifier"; private static final int NOTIFICATION_ID = 1; + private static final PostAgeComparer POST_AGE_COMPARER = new PostAgeComparer(); private NotificationManager nm; private WatchManager wm; @@ -59,7 +61,7 @@ public class WatchNotifier extends Service { nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); wm = ChanApplication.getWatchManager(); - startForeground(NOTIFICATION_ID, getIdleNotification()); + startForeground(NOTIFICATION_ID, createNotification()); } @Override @@ -80,12 +82,7 @@ public class WatchNotifier extends Service { } public void updateNotification() { - Notification notification = createNotification(); - if (notification != null) { - nm.notify(NOTIFICATION_ID, notification); - } else { - nm.notify(NOTIFICATION_ID, getIdleNotification()); - } + nm.notify(NOTIFICATION_ID, createNotification()); } public void pausePins() { @@ -93,69 +90,89 @@ public class WatchNotifier extends Service { } private Notification createNotification() { - List watchingPins = wm.getWatchingPins(); + boolean notifyQuotesOnly = ChanPreferences.getWatchNotifyMode().equals("quotes"); + boolean soundQuotesOnly = ChanPreferences.getWatchSound().equals("quotes"); + List list = new ArrayList<>(); + List listQuoting = new ArrayList<>(); List pins = new ArrayList<>(); - int newPostsCount = 0; - int newQuotesCount = 0; - List posts = new ArrayList<>(); - boolean makeSound = false; - boolean show = false; - boolean wereNewPosts = false; - - for (Pin pin : watchingPins) { + List subjectPins = new ArrayList<>(); + + boolean ticker = false; + boolean sound = false; + + for (Pin pin : wm.getWatchingPins()) { PinWatcher watcher = pin.getPinWatcher(); if (watcher == null || pin.isError) continue; - boolean add = false; + pins.add(pin); - if (pin.getNewPostsCount() > 0) { + if (notifyQuotesOnly) { + list.addAll(watcher.getUnviewedQuotes()); + listQuoting.addAll(watcher.getUnviewedQuotes()); + if (watcher.getWereNewQuotes()) { + ticker = true; + sound = true; + } + if (pin.getNewQuoteCount() > 0) { + subjectPins.add(pin); + } + } else { + list.addAll(watcher.getUnviewedPosts()); + listQuoting.addAll(watcher.getUnviewedQuotes()); if (watcher.getWereNewPosts()) { - wereNewPosts = true; + ticker = true; + if (!soundQuotesOnly) { + sound = true; + } } - newPostsCount += pin.getNewPostsCount(); - for (Post p : watcher.getNewPosts()) { - p.title = pin.loadable.title; - posts.add(p); + if (watcher.getWereNewQuotes()) { + sound = true; + } + if (pin.getNewPostCount() > 0) { + subjectPins.add(pin); } - - show = true; - add = true; - } - - if (pin.getNewQuoteCount() > 0) { - newQuotesCount += pin.getNewQuoteCount(); - show = true; - add = true; - } - - if (watcher.getWereNewQuotes()) { - makeSound = true; } + } - if (add) { - pins.add(pin); - } + if (ChanApplication.getInstance().getApplicationInForeground()) { + ticker = false; + sound = false; } - if (show) { - String title = newPostsCount + " new post" + (newPostsCount != 1 ? "s" : ""); - if (newQuotesCount > 0) { - title += ", " + newQuotesCount + " quoting you"; - } + return notifyAboutPosts(pins, subjectPins, list, listQuoting, notifyQuotesOnly, ticker, sound); + } + + private Notification notifyAboutPosts(List pins, List subjectPins, List list, List listQuoting, + boolean notifyQuotesOnly, boolean makeTicker, boolean makeSound) { + String title = getResources().getQuantityString(R.plurals.watch_title, pins.size(), pins.size()); - String tickerText = title + " in "; - if (pins.size() == 1) { - tickerText += pins.get(0).loadable.title; + if (list.size() == 0) { + // Idle notification + String message = getString(R.string.watch_idle); + return getNotificationFor(null, title, message, -1, null, false, false, null); + } else { + // New posts notification + String message; + List notificationList; + if (notifyQuotesOnly) { + message = getResources().getQuantityString(R.plurals.watch_new_quotes, listQuoting.size(), listQuoting.size()); + notificationList = listQuoting; } else { - tickerText += pins.size() + " thread" + (pins.size() != 1 ? "s" : ""); + notificationList = list; + if (listQuoting.size() > 0) { + message = getResources().getQuantityString(R.plurals.watch_new_quoting, list.size(), list.size(), listQuoting.size()); + } else { + message = getResources().getQuantityString(R.plurals.watch_new, list.size(), list.size()); + } } - Collections.sort(posts, new PostAgeComparer()); - + Collections.sort(notificationList, POST_AGE_COMPARER); List lines = new ArrayList<>(); - for (Post post : posts) { + for (Post post : notificationList) { + CharSequence prefix = Utils.ellipsize(post.title, 18); + CharSequence comment; if (post.comment.length() == 0) { comment = "(image)"; @@ -163,48 +180,62 @@ public class WatchNotifier extends Service { comment = post.comment; } - if (pins.size() > 1) { - lines.add(post.title + ": " + comment); - } else { - lines.add(comment); - } + lines.add(prefix + ": " + comment); } - Pin targetPin = null; - if (pins.size() == 1) { - targetPin = pins.get(0); + Pin subject = null; + if (subjectPins.size() == 1) { + subject = subjectPins.get(0); } - boolean showTickerText = !ChanApplication.getInstance().getApplicationInForeground() && wereNewPosts; - return getNotificationFor(showTickerText ? tickerText : null, title, tickerText, newPostsCount, lines, makeSound, targetPin); - } else { - return null; - } - } - - private Notification getIdleNotification() { - List watchingPins = wm.getWatchingPins(); - int s = watchingPins.size(); - String message = "Watching " + s + " thread" + (s != 1 ? "s" : ""); + String ticker = null; + if (makeTicker) { + ticker = message; + } - return getNotificationFor(null, message, message, -1, null, false, null); + return getNotificationFor(ticker, title, message, -1, lines, makeTicker, makeSound, subject); + } } + /** + * Create a notification with the supplied parameters. + * The style of the big notification is InboxStyle, a list of text. + * + * @param tickerText The tickertext to show, or null if no tickertext should be shown. + * @param contentTitle The title of the notification + * @param contentText The content of the small notification + * @param contentNumber The contentInfo and number, or -1 if not shown + * @param expandedLines A list of lines for the big notification, or null if not shown + * @param makeSound Should the notification make a sound + * @param target The target pin, or null to open the pinned pane on tap + * @return + */ @SuppressWarnings("deprecation") - private Notification getNotificationFor(String tickerText, String title, String content, int count, - List lines, boolean makeSound, Pin targetPin) { - + private Notification getNotificationFor(String tickerText, String contentTitle, String contentText, int contentNumber, + List expandedLines, boolean light, boolean makeSound, Pin target) { Intent intent = new Intent(this, BoardActivity.class); intent.setAction(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - intent.putExtra("pin_id", targetPin == null ? -1 : targetPin.id); + intent.putExtra("pin_id", target == null ? -1 : target.id); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); NotificationCompat.Builder builder = new NotificationCompat.Builder(this); + if (makeSound) { + builder.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE); + } + + if (light) { + long watchLed = ChanPreferences.getWatchLed(); + if (watchLed >= 0) { + builder.setLights((int) watchLed, 1000, 1000); + } + } + + builder.setContentIntent(pendingIntent); if (tickerText != null) { @@ -212,12 +243,12 @@ public class WatchNotifier extends Service { } builder.setTicker(tickerText); - builder.setContentTitle(title); - builder.setContentText(content); + builder.setContentTitle(contentTitle); + builder.setContentText(contentText); - if (count >= 0) { - builder.setContentInfo(Integer.toString(count)); - builder.setNumber(count); + if (contentNumber >= 0) { + builder.setContentInfo(Integer.toString(contentNumber)); + builder.setNumber(contentNumber); } builder.setSmallIcon(R.drawable.ic_stat_notify); @@ -231,16 +262,12 @@ public class WatchNotifier extends Service { builder.addAction(R.drawable.ic_action_pause, getString(R.string.watch_pause_pins), pauseWatchingPending); - if (makeSound) { - builder.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)); - } - - if (lines != null) { + if (expandedLines != null) { NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(); - for (CharSequence line : lines.subList(Math.max(0, lines.size() - 10), lines.size())) { + for (CharSequence line : expandedLines.subList(Math.max(0, expandedLines.size() - 10), expandedLines.size())) { style.addLine(line); } - style.setBigContentTitle(title); + style.setBigContentTitle(contentTitle); builder.setStyle(style); } diff --git a/Clover/app/src/main/java/org/floens/chan/utils/Utils.java b/Clover/app/src/main/java/org/floens/chan/utils/Utils.java index 185c65c4..6bbef463 100644 --- a/Clover/app/src/main/java/org/floens/chan/utils/Utils.java +++ b/Clover/app/src/main/java/org/floens/chan/utils/Utils.java @@ -119,4 +119,12 @@ public class Utils { String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i"); return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); } + + public static CharSequence ellipsize(CharSequence text, int max) { + if (text.length() <= max) { + return text; + } else { + return text.subSequence(0, max) + "\u2026"; + } + } } diff --git a/Clover/app/src/main/res/values/string_arrays.xml b/Clover/app/src/main/res/values/string_arrays.xml new file mode 100644 index 00000000..6bcbef7d --- /dev/null +++ b/Clover/app/src/main/res/values/string_arrays.xml @@ -0,0 +1,68 @@ + + + + light + dark + black + + + + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + + + + 60 + 120 + 180 + 300 + 600 + 1800 + 3600 + + + + all + quotes + + + + -1 + ffffffff + ffff0000 + ffffff00 + ff00ff00 + ff00ffff + ff0000ff + ffff00ff + + + + all + quotes + + diff --git a/Clover/app/src/main/res/values/strings.xml b/Clover/app/src/main/res/values/strings.xml index 7bf1b3b9..f0ea4521 100644 --- a/Clover/app/src/main/res/values/strings.xml +++ b/Clover/app/src/main/res/values/strings.xml @@ -160,11 +160,6 @@ along with this program. If not, see . Dark Black - - light - dark - black - Font size @@ -179,18 +174,6 @@ along with this program. If not, see . 18 19 - - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 - 18 - 19 - Board mode @@ -250,14 +233,26 @@ along with this program. If not, see . 30 minutes 60 minutes - - 60 - 120 - 180 - 300 - 600 - 1800 - 3600 + Notify about + + All posts + Only posts quoting you + + Notification light + + None + White + Red + Yellow + Green + Cyan + Blue + Purple + + Notification sound and vibrate + + All posts + Only posts quoting you 4chan pass enables you to post without filling in CAPTCHAs diff --git a/Clover/app/src/main/res/values/watch_strings.xml b/Clover/app/src/main/res/values/watch_strings.xml new file mode 100644 index 00000000..c0c07f25 --- /dev/null +++ b/Clover/app/src/main/res/values/watch_strings.xml @@ -0,0 +1,41 @@ + + + + Watching one thread + Watching %d threads + + + No new posts + + + %d new post + %d new posts + + + + %d new post quoting you + %d new posts quoting you + + + + %1$d new post, %2$d quoting you + %1$d new posts, %2$d quoting you + + + diff --git a/Clover/app/src/main/res/xml/preference_watch.xml b/Clover/app/src/main/res/xml/preference_watch.xml index 85267bcc..9c18cfca 100644 --- a/Clover/app/src/main/res/xml/preference_watch.xml +++ b/Clover/app/src/main/res/xml/preference_watch.xml @@ -38,4 +38,31 @@ along with this program. If not, see . android:key="preference_watch_background_timeout" android:title="@string/watch_background_timeout_description" /> + + + + + + \ No newline at end of file