Added watch settings

captchafix
Florens Douwes 11 years ago
parent 39d974f91c
commit 900fdb6ce4
  1. 8
      Chan/AndroidManifest.xml
  2. 11
      Chan/res/menu/watch_settings.xml
  3. 8
      Chan/res/values/strings.xml
  4. 61
      Chan/res/xml/preference.xml
  5. 3
      Chan/src/org/floens/chan/ChanApplication.java
  6. 108
      Chan/src/org/floens/chan/activity/WatchSettingsActivity.java
  7. 10
      Chan/src/org/floens/chan/fragment/SettingsFragment.java
  8. 2
      Chan/src/org/floens/chan/loader/Loader.java
  9. 89
      Chan/src/org/floens/chan/manager/ThreadManager.java
  10. 70
      Chan/src/org/floens/chan/service/PinnedService.java
  11. 18
      Chan/src/org/floens/chan/utils/ChanPreferences.java
  12. 8
      Chan/src/org/floens/chan/utils/ScrollerRunnable.java
  13. 5
      Chan/src/org/floens/chan/utils/Utils.java
  14. 18
      Chan/src/org/floens/chan/watch/WatchNotifier.java

@ -53,6 +53,14 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="org.floens.chan.activity.BoardActivity" />
</activity>
<activity
android:name="org.floens.chan.activity.WatchSettingsActivity"
android:label="@string/preference_watch_settings"
android:parentActivityName="org.floens.chan.activity.BoardActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.floens.chan.activity.BoardActivity" />
</activity>
<activity
android:name="org.floens.chan.activity.AboutActivity"
android:label="@string/preference_about"

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/enable_watch_switch"
android:title=""
android:actionViewClass="android.widget.Switch"
android:showAsAction="always">
</item>
</menu>

@ -88,6 +88,8 @@
<string name="delete_fail">Error deleting post</string>
<string name="delete_image_only">Only delete the image</string>
<string name="preference_watch_settings">Thread watcher</string>
<string name="preference_general">General</string>
<string name="preference_open_link_confirmation">Ask before opening links</string>
<string name="preference_default_name">Default name</string>
@ -100,4 +102,10 @@
<string name="open_link_confirmation">Open link?</string>
<string name="watch_summary_disabled">Off</string>
<string name="watch_summary_enabled">Watching pinned threads</string>
<string name="watch_info_text">
To view the thread watcher options, turn the thread watcher on.
</string>
</resources>

@ -1,57 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceCategory
android:title="@string/preference_general" >
<Preference
android:title="@string/board_edit" >
android:key="watch_settings"
android:title="@string/preference_watch_settings" >
<intent
android:action="android.intent.action.VIEW"
android:targetPackage="org.floens.chan"
android:targetClass="org.floens.chan.activity.BoardEditor" />
android:targetClass="org.floens.chan.activity.WatchSettingsActivity"
android:targetPackage="org.floens.chan" />
</Preference>
<Preference android:title="@string/board_edit" >
<intent
android:action="android.intent.action.VIEW"
android:targetClass="org.floens.chan.activity.BoardEditor"
android:targetPackage="org.floens.chan" />
</Preference>
<PreferenceCategory android:title="@string/preference_general" >
<CheckBoxPreference
android:title="@string/preference_open_link_confirmation"
android:defaultValue="true"
android:key="preference_open_link_confirmation"
android:defaultValue="true" />
android:title="@string/preference_open_link_confirmation" />
<EditTextPreference
android:title="@string/preference_default_name"
android:key="preference_default_name" />
android:key="preference_default_name"
android:title="@string/preference_default_name" />
<EditTextPreference
android:title="@string/preference_default_email"
android:key="preference_default_email" />
android:key="preference_default_email"
android:title="@string/preference_default_email" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/preference_about"
android:key="group_about" >
android:key="group_about"
android:title="@string/preference_about" >
<Preference
android:title="@string/preference_licenses"
android:key="about_licences"
android:summary="@string/preference_licences_summary"
android:key="about_licences" />
android:title="@string/preference_licenses" />
<Preference
android:title="Chan"
android:key="about_version" />
android:key="about_version"
android:title="Chan" />
<Preference
android:title="@string/preference_developer"
android:key="about_developer" >
android:key="about_developer"
android:title="@string/preference_developer" >
<intent
android:action="android.intent.action.VIEW"
android:targetPackage="org.floens.chan"
android:targetClass="org.floens.chan.activity.DeveloperActivity" />
android:targetClass="org.floens.chan.activity.DeveloperActivity"
android:targetPackage="org.floens.chan" />
</Preference>
</PreferenceCategory>
</PreferenceScreen>
</PreferenceScreen>

@ -8,7 +8,6 @@ import org.floens.chan.service.PinnedService;
import org.floens.chan.utils.IconCache;
import android.app.Application;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.StrictMode;
import android.preference.PreferenceManager;
@ -72,7 +71,7 @@ public class ChanApplication extends Application {
new PinnedManager(this);
new ReplyManager(this);
startService(new Intent(this, PinnedService.class));
PinnedService.startStopAccordingToSettings(this);
}
}

@ -0,0 +1,108 @@
package org.floens.chan.activity;
import org.floens.chan.R;
import org.floens.chan.utils.ChanPreferences;
import org.floens.chan.utils.Utils;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceFragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.TextView;
public class WatchSettingsActivity extends Activity implements OnCheckedChangeListener {
private Switch watchSwitch;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.watch_settings, menu);
watchSwitch = (Switch) menu.findItem(R.id.enable_watch_switch).getActionView();
watchSwitch.setOnCheckedChangeListener(this);
watchSwitch.setPadding(0, 0, Utils.dp(this, 20), 0);
setEnabled(ChanPreferences.getWatchEnabled());
return true;
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
setEnabled(isChecked);
}
private void setEnabled(boolean enabled) {
if (enabled) {
FragmentTransaction t = getFragmentManager().beginTransaction();
t.replace(android.R.id.content, new WatchSettingsFragment());
t.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
t.commit();
} else {
FragmentTransaction t = getFragmentManager().beginTransaction();
t.replace(android.R.id.content, TextFragment.newInstance(R.string.watch_info_text));
t.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
t.commit();
}
watchSwitch.setChecked(enabled);
ChanPreferences.setWatchEnabled(enabled);
watchSwitch.setEnabled(false);
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
watchSwitch.setEnabled(true);
}
}, 500);
}
public static class TextFragment extends Fragment {
public static TextFragment newInstance(int textResource) {
TextFragment f = new TextFragment();
Bundle bundle = new Bundle();
bundle.putInt("text_resource", textResource);
f.setArguments(bundle);
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup group, Bundle savedInstanceState) {
LinearLayout container = new LinearLayout(inflater.getContext());
int p = Utils.dp(inflater.getContext(), 20);
container.setPadding(p, p, p, p);
TextView text = new TextView(inflater.getContext());
text.setTextSize(20);
text.setText(getArguments().getInt("text_resource"));
container.setGravity(Gravity.CENTER);
container.addView(text);
return container;
}
}
public static class WatchSettingsFragment extends PreferenceFragment {
}
}

@ -72,6 +72,16 @@ public class SettingsFragment extends PreferenceFragment {
updateDeveloperPreference();
}
@Override
public void onResume() {
super.onResume();
final Preference watchPreference = findPreference("watch_settings");
if (watchPreference != null) {
watchPreference.setSummary(ChanPreferences.getWatchEnabled() ? R.string.watch_summary_enabled : R.string.watch_summary_disabled);
}
}
private void updateDeveloperPreference() {
if (ChanPreferences.getDeveloper()) {
((PreferenceGroup) findPreference("group_about")).addPreference(developerPreference);

@ -200,8 +200,6 @@ public class Loader {
private void onData(List<Post> result) {
if (destroyed) return;
Logger.test("ondata in loader");
postsById.clear();
for (Post post : result) {
postsById.append(post.no, post);

@ -38,10 +38,9 @@ import android.widget.Toast;
import com.android.volley.VolleyError;
/**
* All PostView's need to have this referenced.
* This manages some things like pages, starting and stopping of loading,
* handling linkables, replies popups etc.
* onDestroy, onStart and onStop must be called from the activity/fragment
* All PostView's need to have this referenced. This manages some things like
* pages, starting and stopping of loading, handling linkables, replies popups
* etc. onDestroy, onStart and onStop must be called from the activity/fragment
*/
public class ThreadManager implements Loader.LoaderListener {
private static final String TAG = "ThreadManager";
@ -54,8 +53,8 @@ public class ThreadManager implements Loader.LoaderListener {
private Loader loader;
public ThreadManager(Activity context, final ThreadManagerListener listener) {
activity = context;
public ThreadManager(Activity activity, final ThreadManagerListener listener) {
this.activity = activity;
threadManagerListener = listener;
}
@ -137,12 +136,14 @@ public class ThreadManager implements Loader.LoaderListener {
}
public Post findPostById(int id) {
if (loader == null) return null;
if (loader == null)
return null;
return loader.findPostById(id);
}
public Loadable getLoadable() {
if (loader == null) return null;
if (loader == null)
return null;
return loader.getLoadable();
}
@ -205,7 +206,8 @@ public class ThreadManager implements Loader.LoaderListener {
}
public void openReply(boolean startInActivity) {
if (loader == null) return;
if (loader == null)
return;
if (startInActivity) {
ReplyActivity.setLoadable(loader.getLoadable());
@ -268,24 +270,23 @@ public class ThreadManager implements Loader.LoaderListener {
text += "\nCapcode: " + post.capcode;
}
AlertDialog dialog = new AlertDialog.Builder(activity)
.setTitle(R.string.post_info)
.setMessage(text)
AlertDialog dialog = new AlertDialog.Builder(activity).setTitle(R.string.post_info).setMessage(text)
.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.create();
}).create();
dialog.show();
}
/**
* When the user clicks a post:
* a. when there's one linkable, open the linkable.
* b. when there's more than one linkable, show the user multiple options to select from.
* @param post The post that was clicked.
* When the user clicks a post: a. when there's one linkable, open the
* linkable. b. when there's more than one linkable, show the user multiple
* options to select from.
*
* @param post
* The post that was clicked.
*/
public void showPostLinkables(Post post) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
@ -329,7 +330,9 @@ public class ThreadManager implements Loader.LoaderListener {
/**
* Handle when a linkable has been clicked.
* @param linkable the selected linkable.
*
* @param linkable
* the selected linkable.
*/
private void handleLinkableSelected(final PostLinkable linkable) {
if (linkable.type == PostLinkable.Type.QUOTE) {
@ -341,16 +344,12 @@ public class ThreadManager implements Loader.LoaderListener {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
}).setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
openLink(linkable);
}
})
.setTitle(R.string.open_link_confirmation)
.setMessage(linkable.value)
.create();
}).setTitle(R.string.open_link_confirmation).setMessage(linkable.value).create();
dialog.show();
} else {
@ -360,9 +359,11 @@ public class ThreadManager implements Loader.LoaderListener {
}
/**
* When a linkable to a post has been clicked,
* show a dialog with the referenced post in it.
* @param linkable the clicked linkable.
* When a linkable to a post has been clicked, show a dialog with the
* referenced post in it.
*
* @param linkable
* the clicked linkable.
*/
private void showPostReply(PostLinkable linkable) {
String value = linkable.value;
@ -390,14 +391,17 @@ public class ThreadManager implements Loader.LoaderListener {
/**
* Open an url.
* @param linkable Linkable with an url.
*
* @param linkable
* Linkable with an url.
*/
private void openLink(PostLinkable linkable) {
activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(linkable.value)));
}
private void showPostsRepliesFragment(List<Post> list) {
// Post popups are now queued up, more than 32 popups on top of each other makes the system crash!
// Post popups are now queued up, more than 32 popups on top of each
// other makes the system crash!
popupQueue.add(list);
if (currentPopupFragment != null) {
@ -414,7 +418,8 @@ public class ThreadManager implements Loader.LoaderListener {
}
public void onPostRepliesPop() {
if (popupQueue.size() == 0) return;
if (popupQueue.size() == 0)
return;
popupQueue.remove(popupQueue.size() - 1);
@ -442,30 +447,26 @@ public class ThreadManager implements Loader.LoaderListener {
int padding = activity.getResources().getDimensionPixelSize(R.dimen.general_padding);
view.setPadding(padding, padding, padding, padding);
new AlertDialog.Builder(activity)
.setTitle(R.string.delete_confirm)
.setView(view)
new AlertDialog.Builder(activity).setTitle(R.string.delete_confirm).setView(view)
.setPositiveButton(R.string.delete, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
doDeletePost(post, view.isChecked());
}
})
.setNegativeButton(R.string.cancel, new OnClickListener() {
}).setNegativeButton(R.string.cancel, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.show();
}).show();
}
private void doDeletePost(Post post, boolean onlyImageDelete) {
SavedReply reply = DatabaseManager.getInstance().getSavedReply(post.board, post.no);
if (reply == null) {
/*reply = new SavedReply();
reply.board = "g";
reply.no = 1234;
reply.password = "boom";*/
/*
* reply = new SavedReply(); reply.board = "g"; reply.no = 1234;
* reply.password = "boom";
*/
return;
}
@ -501,9 +502,13 @@ public class ThreadManager implements Loader.LoaderListener {
public interface ThreadManagerListener {
public void onThreadLoaded(List<Post> result, boolean append);
public void onThreadLoadError(VolleyError error);
public void onOPClicked(Post post);
public void onThumbnailClicked(Post post);
public void onScrollTo(Post post);
}
}

@ -4,19 +4,23 @@ import java.util.List;
import org.floens.chan.manager.PinnedManager;
import org.floens.chan.model.Pin;
import org.floens.chan.utils.ChanPreferences;
import org.floens.chan.utils.Logger;
import org.floens.chan.watch.WatchNotifier;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.widget.Toast;
public class PinnedService extends Service {
private static final long FOREGROUND_INTERVAL = 10000L;
private static final long BACKGROUND_INTERVAL = 60000L;
private static PinnedService instance;
private static boolean activityInForeground = false;
private Thread loadThread;
@ -33,6 +37,43 @@ public class PinnedService extends Service {
activityInForeground = false;
}
public static void startStopAccordingToSettings(Context context) {
if (ChanPreferences.getWatchEnabled()) {
if (!getRunning()) {
enable(context);
}
} else {
if (getRunning()) {
disable(context);
}
}
}
public static void enable(Context context) {
if (!getRunning()) {
context.startService(new Intent(context, PinnedService.class));
}
}
public static void disable(Context context) {
if (getRunning()) {
context.stopService(new Intent(context, PinnedService.class));
}
}
public static boolean getRunning() {
return instance != null;
}
public static void callOnPinsChanged() {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
PinnedManager.getInstance().onPinsChanged();
}
});
}
public PinnedService() {
watchNotifier = new WatchNotifier(this);
}
@ -41,17 +82,25 @@ public class PinnedService extends Service {
public void onCreate() {
super.onCreate();
start();
instance = this;
startThread();
}
@Override
public void onDestroy() {
super.onDestroy();
instance = null;
running = false;
if (loadThread != null) {
loadThread.interrupt();
Toast.makeText(getApplicationContext(), "Service thread interrupted", Toast.LENGTH_SHORT).show();
}
}
private void start() {
private void startThread() {
running = true;
if (loadThread == null) {
@ -63,18 +112,19 @@ public class PinnedService extends Service {
long timeout = activityInForeground ? FOREGROUND_INTERVAL : BACKGROUND_INTERVAL;
if (!running) return;
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
}
loadThread = null;
}
});
loadThread.start();
Toast.makeText(getApplicationContext(), "Service thread started", Toast.LENGTH_SHORT).show();
}
}
@ -87,16 +137,6 @@ public class PinnedService extends Service {
watchNotifier.update();
}
public static void callOnPinsChanged() {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
PinnedManager.getInstance().onPinsChanged();
}
});
}
@Override
public IBinder onBind(Intent intent) {
return null;

@ -1,6 +1,7 @@
package org.floens.chan.utils;
import org.floens.chan.ChanApplication;
import org.floens.chan.service.PinnedService;
public class ChanPreferences {
public static boolean getOpenLinkConfirmation() {
@ -26,4 +27,21 @@ public class ChanPreferences {
public static String getImageSaveDirectory() {
return "Chan";
}
public static boolean getWatchEnabled() {
return ChanApplication.getPreferences().getBoolean("preference_watch_enabled", true);
}
/**
* This also calls startStopAccordingToSettings on the PinnedService to
* start/stop the service as needed.
*
* @param enabled
*/
public static void setWatchEnabled(boolean enabled) {
if (getWatchEnabled() != enabled) {
ChanApplication.getPreferences().edit().putBoolean("preference_watch_enabled", enabled).commit();
PinnedService.startStopAccordingToSettings(ChanApplication.getInstance());
}
}
}

@ -5,8 +5,6 @@ import android.view.ViewConfiguration;
import android.widget.ListView;
public class ScrollerRunnable implements Runnable {
private static final int SCROLL_DURATION = 1;
private static final int MOVE_DOWN_POS = 1;
private static final int MOVE_UP_POS = 2;
@ -28,12 +26,12 @@ public class ScrollerRunnable implements Runnable {
final int firstPos = mList.getFirstVisiblePosition();
final int lastPos = firstPos + mList.getChildCount() - 1;
int viewTravelCount = 0;
// int viewTravelCount = 0;
if (position <= firstPos) {
viewTravelCount = firstPos - position + 1;
// viewTravelCount = firstPos - position + 1;
mMode = MOVE_UP_POS;
} else if (position >= lastPos) {
viewTravelCount = position - lastPos + 1;
// viewTravelCount = position - lastPos + 1;
mMode = MOVE_DOWN_POS;
} else {
// Already on screen, nothing to do

@ -5,9 +5,14 @@ import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.util.TypedValue;
import android.view.View;
public class Utils {
public static int dp(Context context, int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14, context.getResources().getDisplayMetrics());
}
/**
* Sets the android.R.attr.selectableItemBackground as background drawable on the view.
* @param view

@ -1,6 +1,10 @@
package org.floens.chan.watch;
import java.util.List;
import org.floens.chan.R;
import org.floens.chan.manager.PinnedManager;
import org.floens.chan.model.Pin;
import android.app.Notification;
import android.app.NotificationManager;
@ -16,7 +20,18 @@ public class WatchNotifier {
}
public void update() {
showNotification("Update!");
List<Pin> pins = PinnedManager.getInstance().getPins();
int count = 0;
int pinCount = 0;
for (Pin pin : pins) {
count += pin.getNewPostCount();
pinCount++;
}
showNotification(count + " new posts in " + pinCount + " threads");
// showNotification("WatchNotifier update");
}
private void showNotification(String text) {
@ -27,6 +42,7 @@ public class WatchNotifier {
builder.setContentTitle(text);
builder.setContentText(text);
builder.setSmallIcon(R.drawable.ic_stat_notify);
builder.setOnlyAlertOnce(false);
nm.notify(NOTIFICATION_ID, builder.getNotification());
}

Loading…
Cancel
Save