Improve notifications

Redo WatchNotifier code
Use string xml files
Add color option
Add sound option
Add quote only option
captchafix
Floens 11 years ago
parent 87b154272e
commit e61f8fcc56
  1. 13
      Clover/app/src/main/java/org/floens/chan/core/ChanPreferences.java
  2. 6
      Clover/app/src/main/java/org/floens/chan/core/loader/ChanParser.java
  3. 10
      Clover/app/src/main/java/org/floens/chan/core/model/Pin.java
  4. 8
      Clover/app/src/main/java/org/floens/chan/core/model/Post.java
  5. 71
      Clover/app/src/main/java/org/floens/chan/core/watch/PinWatcher.java
  6. 2
      Clover/app/src/main/java/org/floens/chan/ui/activity/BaseActivity.java
  7. 54
      Clover/app/src/main/java/org/floens/chan/ui/activity/WatchSettingsActivity.java
  8. 2
      Clover/app/src/main/java/org/floens/chan/ui/adapter/PinnedAdapter.java
  9. 195
      Clover/app/src/main/java/org/floens/chan/ui/service/WatchNotifier.java
  10. 8
      Clover/app/src/main/java/org/floens/chan/utils/Utils.java
  11. 68
      Clover/app/src/main/res/values/string_arrays.xml
  12. 45
      Clover/app/src/main/res/values/strings.xml
  13. 41
      Clover/app/src/main/res/values/watch_strings.xml
  14. 27
      Clover/app/src/main/res/xml/preference_watch.xml

@ -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);
}

@ -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;

@ -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,8 +63,12 @@ public class Pin {
}
public int getNewQuoteCount() {
if (quoteNewCount < 0 || quoteLastCount < 0) {
return 0;
} else {
return Math.max(0, quoteNewCount - quoteLastCount);
}
}
public PinWatcher getPinWatcher() {
return pinWatcher;

@ -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);

@ -37,6 +37,7 @@ public class PinWatcher implements Loader.LoaderListener {
private Loader loader;
private final List<Post> posts = new ArrayList<>();
private final List<Post> 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;
if (pin.quoteNewCount >= 0) {
pin.quoteLastCount = pin.quoteNewCount;
}
wereNewQuotes = false;
wereNewPosts = false;
}
public List<Post> getNewPosts() {
public List<Post> 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<Post> getNewQuotes() {
if (posts.size() == 0) {
return posts;
} else {
return posts.subList(Math.max(0, posts.size() - pin.getNewQuoteCount()), posts.size());
public List<Post> getUnviewedQuotes() {
return quotes.subList(Math.max(0, quotes.size() - pin.getNewQuoteCount()), quotes.size());
}
}*/
public boolean getWereNewQuotes() {
if (wereNewQuotes) {
@ -133,35 +131,62 @@ 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<Post> 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 (pin.quoteNewCount > lastQuoteNewCount) {
wereNewQuotes = true;
// If it was more than before processing
int lastWatchNewCount = pin.watchNewCount;
int lastQuoteNewCount = pin.quoteNewCount;
if (isFirstLoad) {
pin.watchLastCount = posts.size();
pin.quoteLastCount = quotes.size();
}
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()) {
Logger.d(TAG, String.format("postlast=%d postnew=%d werenewposts=%b quotelast=%d quotenew=%d werenewquotes=%b",
pin.watchLastCount, pin.watchNewCount, wereNewPosts, pin.quoteLastCount, pin.quoteNewCount, wereNewQuotes));

@ -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;
}

@ -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]);
}
}
}

@ -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+";

@ -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<Pin> watchingPins = wm.getWatchingPins();
boolean notifyQuotesOnly = ChanPreferences.getWatchNotifyMode().equals("quotes");
boolean soundQuotesOnly = ChanPreferences.getWatchSound().equals("quotes");
List<Post> list = new ArrayList<>();
List<Post> listQuoting = new ArrayList<>();
List<Pin> pins = new ArrayList<>();
int newPostsCount = 0;
int newQuotesCount = 0;
List<Post> posts = new ArrayList<>();
boolean makeSound = false;
boolean show = false;
boolean wereNewPosts = false;
for (Pin pin : watchingPins) {
List<Pin> 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 (watcher.getWereNewPosts()) {
wereNewPosts = true;
if (notifyQuotesOnly) {
list.addAll(watcher.getUnviewedQuotes());
listQuoting.addAll(watcher.getUnviewedQuotes());
if (watcher.getWereNewQuotes()) {
ticker = true;
sound = true;
}
newPostsCount += pin.getNewPostsCount();
for (Post p : watcher.getNewPosts()) {
p.title = pin.loadable.title;
posts.add(p);
if (pin.getNewQuoteCount() > 0) {
subjectPins.add(pin);
}
show = true;
add = true;
} else {
list.addAll(watcher.getUnviewedPosts());
listQuoting.addAll(watcher.getUnviewedQuotes());
if (watcher.getWereNewPosts()) {
ticker = true;
if (!soundQuotesOnly) {
sound = true;
}
if (pin.getNewQuoteCount() > 0) {
newQuotesCount += pin.getNewQuoteCount();
show = true;
add = true;
}
if (watcher.getWereNewQuotes()) {
makeSound = true;
sound = true;
}
if (pin.getNewPostCount() > 0) {
subjectPins.add(pin);
}
if (add) {
pins.add(pin);
}
}
if (show) {
String title = newPostsCount + " new post" + (newPostsCount != 1 ? "s" : "");
if (newQuotesCount > 0) {
title += ", " + newQuotesCount + " quoting you";
if (ChanApplication.getInstance().getApplicationInForeground()) {
ticker = false;
sound = false;
}
String tickerText = title + " in ";
if (pins.size() == 1) {
tickerText += pins.get(0).loadable.title;
} else {
tickerText += pins.size() + " thread" + (pins.size() != 1 ? "s" : "");
return notifyAboutPosts(pins, subjectPins, list, listQuoting, notifyQuotesOnly, ticker, sound);
}
Collections.sort(posts, new PostAgeComparer());
private Notification notifyAboutPosts(List<Pin> pins, List<Pin> subjectPins, List<Post> list, List<Post> listQuoting,
boolean notifyQuotesOnly, boolean makeTicker, boolean makeSound) {
String title = getResources().getQuantityString(R.plurals.watch_title, pins.size(), pins.size());
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<Post> notificationList;
if (notifyQuotesOnly) {
message = getResources().getQuantityString(R.plurals.watch_new_quotes, listQuoting.size(), listQuoting.size());
notificationList = listQuoting;
} else {
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(notificationList, POST_AGE_COMPARER);
List<CharSequence> 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;
String ticker = null;
if (makeTicker) {
ticker = message;
}
}
private Notification getIdleNotification() {
List<Pin> watchingPins = wm.getWatchingPins();
int s = watchingPins.size();
String message = "Watching " + s + " thread" + (s != 1 ? "s" : "");
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<CharSequence> lines, boolean makeSound, Pin targetPin) {
private Notification getNotificationFor(String tickerText, String contentTitle, String contentText, int contentNumber,
List<CharSequence> 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);
}

@ -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";
}
}
}

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?><!--
Clover - 4chan browser https://github.com/Floens/Clover/
Copyright (C) 2014 Floens
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<string-array name="preference_themes_values">
<item>light</item>
<item>dark</item>
<item>black</item>
</string-array>
<string-array name="preference_fonts_values">
<item>10</item>
<item>11</item>
<item>12</item>
<item>13</item>
<item>14</item>
<item>15</item>
<item>16</item>
<item>17</item>
<item>18</item>
<item>19</item>
</string-array>
<string-array name="watch_background_timeouts_int">
<item>60</item>
<item>120</item>
<item>180</item>
<item>300</item>
<item>600</item>
<item>1800</item>
<item>3600</item>
</string-array>
<string-array name="watch_notify_modes_values">
<item>all</item>
<item>quotes</item>
</string-array>
<string-array name="watch_leds_values">
<item>-1</item>
<item>ffffffff</item>
<item>ffff0000</item>
<item>ffffff00</item>
<item>ff00ff00</item>
<item>ff00ffff</item>
<item>ff0000ff</item>
<item>ffff00ff</item>
</string-array>
<string-array name="watch_sounds_values">
<item>all</item>
<item>quotes</item>
</string-array>
</resources>

@ -160,11 +160,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<item>Dark</item>
<item>Black</item>
</string-array>
<string-array name="preference_themes_values">
<item>light</item>
<item>dark</item>
<item>black</item>
</string-array>
<string name="preference_font">Font size</string>
<string-array name="preference_fonts">
@ -179,18 +174,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<item>18</item>
<item>19</item>
</string-array>
<string-array name="preference_fonts_values">
<item>10</item>
<item>11</item>
<item>12</item>
<item>13</item>
<item>14</item>
<item>15</item>
<item>16</item>
<item>17</item>
<item>18</item>
<item>19</item>
</string-array>
<string name="preference_board_mode">Board mode</string>
<string-array name="preference_board_modes">
@ -250,14 +233,26 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<item>30 minutes</item>
<item>60 minutes</item>
</string-array>
<string-array name="watch_background_timeouts_int">
<item>60</item>
<item>120</item>
<item>180</item>
<item>300</item>
<item>600</item>
<item>1800</item>
<item>3600</item>
<string name="watch_notify_mode">Notify about</string>
<string-array name="watch_notify_modes">
<item>All posts</item>
<item>Only posts quoting you</item>
</string-array>
<string name="watch_led">Notification light</string>
<string-array name="watch_leds">
<item>None</item>
<item>White</item>
<item>Red</item>
<item>Yellow</item>
<item>Green</item>
<item>Cyan</item>
<item>Blue</item>
<item>Purple</item>
</string-array>
<string name="watch_sound">Notification sound and vibrate</string>
<string-array name="watch_sounds">
<item>All posts</item>
<item>Only posts quoting you</item>
</string-array>
<string name="pass_info_text">4chan pass enables you to post without filling in CAPTCHAs</string>

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?><!--
Clover - 4chan browser https://github.com/Floens/Clover/
Copyright (C) 2014 Floens
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<plurals name="watch_title">
<item quantity="one">Watching one thread</item>
<item quantity="other">Watching %d threads</item>
</plurals>
<string name="watch_idle">No new posts</string>
<plurals name="watch_new">
<item quantity="one">%d new post</item>
<item quantity="other">%d new posts</item>
</plurals>
<plurals name="watch_new_quotes">
<item quantity="one">%d new post quoting you</item>
<item quantity="other">%d new posts quoting you</item>
</plurals>
<plurals name="watch_new_quoting">
<item quantity="one">%1$d new post, %2$d quoting you</item>
<item quantity="other">%1$d new posts, %2$d quoting you</item>
</plurals>
</resources>

@ -38,4 +38,31 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:key="preference_watch_background_timeout"
android:title="@string/watch_background_timeout_description" />
<ListPreference
android:defaultValue="all"
android:dependency="preference_watch_background_enabled"
android:dialogTitle="@string/watch_notify_mode"
android:entries="@array/watch_notify_modes"
android:entryValues="@array/watch_notify_modes_values"
android:key="preference_watch_notify_mode"
android:title="@string/watch_notify_mode" />
<ListPreference
android:defaultValue="quotes"
android:dependency="preference_watch_background_enabled"
android:dialogTitle="@string/watch_sound"
android:entries="@array/watch_sounds"
android:entryValues="@array/watch_sounds_values"
android:key="preference_watch_sound"
android:title="@string/watch_sound" />
<ListPreference
android:defaultValue="ffffffff"
android:dependency="preference_watch_background_enabled"
android:dialogTitle="@string/watch_led"
android:entries="@array/watch_leds"
android:entryValues="@array/watch_leds_values"
android:key="preference_watch_led"
android:title="@string/watch_led" />
</PreferenceScreen>
Loading…
Cancel
Save