diff --git a/Chan/res/values/strings.xml b/Chan/res/values/strings.xml
index e8876632..b2144f34 100644
--- a/Chan/res/values/strings.xml
+++ b/Chan/res/values/strings.xml
@@ -46,7 +46,7 @@
Open drawer
Close drawer
Pinned threads
- Enter title
+ Edit title
reply
replies
diff --git a/Chan/src/org/floens/chan/ChanApplication.java b/Chan/src/org/floens/chan/ChanApplication.java
index 36116b76..c4da0ed0 100644
--- a/Chan/src/org/floens/chan/ChanApplication.java
+++ b/Chan/src/org/floens/chan/ChanApplication.java
@@ -4,9 +4,11 @@ import org.floens.chan.database.DatabaseManager;
import org.floens.chan.manager.BoardManager;
import org.floens.chan.manager.PinnedManager;
import org.floens.chan.manager.ReplyManager;
+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;
@@ -70,7 +72,7 @@ public class ChanApplication extends Application {
new PinnedManager(this);
new ReplyManager(this);
-// startService(new Intent(this, PinnedService.class));
+ startService(new Intent(this, PinnedService.class));
}
}
diff --git a/Chan/src/org/floens/chan/activity/BaseActivity.java b/Chan/src/org/floens/chan/activity/BaseActivity.java
index 5d069a1a..0474bf7b 100644
--- a/Chan/src/org/floens/chan/activity/BaseActivity.java
+++ b/Chan/src/org/floens/chan/activity/BaseActivity.java
@@ -7,6 +7,7 @@ import org.floens.chan.animation.SwipeDismissListViewTouchListener.DismissCallba
import org.floens.chan.manager.PinnedManager;
import org.floens.chan.model.Pin;
import org.floens.chan.model.Post;
+import org.floens.chan.utils.Logger;
import android.app.Activity;
import android.app.AlertDialog;
@@ -38,22 +39,22 @@ import android.widget.ShareActionProvider;
public abstract class BaseActivity extends Activity implements PanelSlideListener, PinnedManager.PinListener {
private final static int ACTION_OPEN_URL = 1;
-
+
protected PinnedAdapter pinnedAdapter;
protected DrawerLayout pinDrawer;
protected ListView pinDrawerView;
protected ActionBarDrawerToggle pinDrawerListener;
-
+
protected SlidingPaneLayout threadPane;
-
+
private ShareActionProvider shareActionProvider;
-
+
/**
* Called when a post has been clicked in the pinned drawer
* @param post
*/
abstract public void openPin(Pin post);
-
+
/**
* Called when a post has been clicked in the listview
* @param post
@@ -63,25 +64,25 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
+
setContentView(R.layout.activity_base);
-
+
pinDrawer = (DrawerLayout) findViewById(R.id.drawer_layout);
initDrawer();
-
+
threadPane = (SlidingPaneLayout) findViewById(R.id.pane_container);
initPane();
-
+
PinnedManager.getInstance().addPinListener(this);
}
-
+
@Override
protected void onDestroy() {
super.onDestroy();
-
+
PinnedManager.getInstance().removePinListener(this);
}
-
+
private void initPane() {
threadPane.setPanelSlideListener(this);
threadPane.setParallaxDistance(200);
@@ -89,21 +90,21 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
threadPane.setSliderFadeColor(0xcce5e5e5);
threadPane.openPane();
}
-
+
protected void initDrawer() {
if (pinDrawerListener == null) {
return;
}
-
+
pinDrawer.setDrawerListener(pinDrawerListener);
pinDrawer.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
-
+
pinDrawerView = (ListView)findViewById(R.id.left_drawer);
-
+
pinnedAdapter = new PinnedAdapter(getActionBar().getThemedContext(), 0); // Get the dark theme, not the light one
pinnedAdapter.reload();
pinDrawerView.setAdapter(pinnedAdapter);
-
+
pinDrawerView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
@@ -112,19 +113,19 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
openPin(pin);
}
});
-
+
pinDrawerView.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView> parent, View view, int position, long id) {
Pin post = pinnedAdapter.getItem(position);
if (post == null || post.type == Pin.Type.HEADER) return false;
-
+
changePinTitle(post);
-
+
return true;
}
});
-
+
SwipeDismissListViewTouchListener touchListener = new SwipeDismissListViewTouchListener(pinDrawerView,
new DismissCallbacks() {
@Override
@@ -139,24 +140,26 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
return pinnedAdapter.getItem(position).type != Pin.Type.HEADER;
}
});
-
+
pinDrawerView.setOnTouchListener(touchListener);
pinDrawerView.setOnScrollListener(touchListener.makeScrollListener());
}
-
+
@Override
public void onPinsChanged() {
pinnedAdapter.reload();
+ pinDrawerView.invalidate();
+ Logger.test("onPinsChanged");
}
public void addPin(Pin pin) {
PinnedManager.getInstance().add(pin);
}
-
+
public void removePin(Pin pin) {
PinnedManager.getInstance().remove(pin);
}
-
+
public void updatePin(Pin pin) {
PinnedManager.getInstance().update(pin);
}
@@ -166,13 +169,13 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
text.setSingleLine();
text.setText(pin.loadable.title);
text.setSelectAllOnFocus(true);
-
+
AlertDialog dialog = new AlertDialog.Builder(this)
.setPositiveButton(R.string.change, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface d, int which) {
String value = text.getText().toString();
-
+
if (!TextUtils.isEmpty(value)) {
pin.loadable.title = value;
updatePin(pin);
@@ -187,9 +190,9 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
.setTitle(R.string.drawer_pinned_change_title)
.setView(text)
.create();
-
+
text.requestFocus();
-
+
dialog.setOnShowListener(new OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
@@ -197,7 +200,7 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
imm.showSoftInput(text, 0);
}
});
-
+
dialog.show();
}
@@ -208,18 +211,18 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
startActivity(new Intent(this, SettingsActivity.class));
return true;
}
-
+
return super.onOptionsItemSelected(item);
}
-
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.base, menu);
shareActionProvider = (ShareActionProvider) menu.findItem(R.id.action_share).getActionProvider();
-
+
return true;
}
-
+
@Override
public void onPanelClosed(View view) {
}
@@ -231,7 +234,7 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
@Override
public void onPanelSlide(View view, float offset) {
}
-
+
/**
* Set the url that Android Beam and the share action will send.
* @param url
@@ -247,11 +250,11 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
e.printStackTrace();
return;
}
-
+
NdefMessage message = new NdefMessage(new NdefRecord[] {record});
adapter.setNdefPushMessage(message, this);
}
-
+
if (shareActionProvider != null) {
Intent share = new Intent(android.content.Intent.ACTION_SEND);
share.putExtra(android.content.Intent.EXTRA_TEXT, url);
@@ -259,7 +262,7 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
shareActionProvider.setShareIntent(share);
}
}
-
+
/**
* Let the user choose between all activities that can open the url.
* This is done to prevent "open in browser" opening the url in our own app.
@@ -267,20 +270,20 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
*/
public void showUrlOpenPicker(String url) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
-
+
Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
pickIntent.putExtra(Intent.EXTRA_INTENT, intent);
-
+
startActivityForResult(pickIntent, ACTION_OPEN_URL);
}
-
+
/**
* Used for showUrlOpenPicker
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
-
+
if (requestCode == ACTION_OPEN_URL && resultCode == RESULT_OK && data != null) {
data.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(data);
diff --git a/Chan/src/org/floens/chan/activity/BoardActivity.java b/Chan/src/org/floens/chan/activity/BoardActivity.java
index 1d2a85d1..1556aeec 100644
--- a/Chan/src/org/floens/chan/activity/BoardActivity.java
+++ b/Chan/src/org/floens/chan/activity/BoardActivity.java
@@ -34,44 +34,44 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
private ThreadFragment boardFragment;
private ThreadFragment threadFragment;
private boolean boardSetByIntent = false;
-
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
+
boardLoadable.mode = Loadable.Mode.BOARD;
threadLoadable.mode = Loadable.Mode.THREAD;
-
+
boardFragment = ThreadFragment.newInstance(this);
threadFragment = ThreadFragment.newInstance(this);
-
+
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.left_pane, boardFragment);
ft.replace(R.id.right_pane, threadFragment);
ft.commitAllowingStateLoss();
-
+
updateActionBarState();
-
+
final ActionBar actionBar = getActionBar();
actionBar.setListNavigationCallbacks(
new ArrayAdapter(
- actionBar.getThemedContext(),
+ actionBar.getThemedContext(),
R.layout.board_select_spinner,
android.R.id.text1,
BoardManager.getInstance().getMyBoardsKeys()
), this);
-
+
Intent startIntent = getIntent();
Uri startUri = startIntent.getData();
-
+
if (savedInstanceState != null) {
boardLoadable.readFromBundle(this, "board", savedInstanceState);
boardLoadable.no = 0;
boardLoadable.listViewIndex = 0;
boardLoadable.listViewTop = 0;
-
+
threadLoadable.readFromBundle(this, "thread", savedInstanceState);
-
+
setNavigationFromBoardValue(boardLoadable.board);
startLoadingThread(threadLoadable);
} else if (startUri != null) {
@@ -80,41 +80,41 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
getActionBar().setSelectedNavigationItem(0);
}
}
-
+
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
-
+
boardLoadable.writeToBundle(this, "board", outState);
threadLoadable.writeToBundle(this, "thread", outState);
}
-
+
@Override
protected void onStart() {
super.onStart();
-
+
PinnedService.onActivityStart();
}
-
+
@Override
protected void onStop() {
super.onStop();
-
+
PinnedService.onActivityStop();
}
-
+
@Override
protected void onPause() {
super.onPause();
-
+
PinnedManager.getInstance().updateAll();
}
-
+
@Override
protected void initDrawer() {
- pinDrawerListener = new ActionBarDrawerToggle(this, pinDrawer,
+ pinDrawerListener = new ActionBarDrawerToggle(this, pinDrawer,
R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) {};
-
+
super.initDrawer();
}
@@ -124,16 +124,16 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
boardLoadable = new Loadable(BoardManager.getInstance().getMyBoardsValues().get(position));
startLoadingBoard(boardLoadable);
}
-
+
boardSetByIntent = false;
-
+
return true;
}
-
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
-
+
return true;
}
@@ -147,7 +147,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
@Override
public void openPin(Pin pin) {
startLoadingThread(pin.loadable);
-
+
pinDrawer.closeDrawer(pinDrawerView);
}
@@ -161,7 +161,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
super.onPostCreate(savedInstanceState);
pinDrawerListener.syncState();
}
-
+
@Override
public void onBackPressed() {
if (threadPane.isOpen()) {
@@ -170,10 +170,16 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
threadPane.openPane();
}
}
-
+
+ @Override
+ public void updatePin(Pin pin) {
+ super.updatePin(pin);
+ updateActionBarState();
+ }
+
private void updateActionBarState() {
final ActionBar actionBar = getActionBar();
-
+
if (threadPane.isSlideable()) {
if (threadPane.isOpen()) {
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
@@ -185,51 +191,51 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
actionBar.setTitle(threadLoadable.title);
pinDrawerListener.setDrawerIndicatorEnabled(false);
}
-
+
actionBar.setDisplayHomeAsUpEnabled(true);
} else {
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
pinDrawerListener.setDrawerIndicatorEnabled(true);
actionBar.setTitle(threadLoadable.title);
-
+
actionBar.setDisplayHomeAsUpEnabled(true);
}
-
+
actionBar.setDisplayShowTitleEnabled(true);
-
+
invalidateOptionsMenu();
}
-
+
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
boolean open = threadPane.isOpen();
boolean slidable = threadPane.isSlideable();
-
+
setMenuItemEnabled(menu.findItem(R.id.action_reload_board), slidable && open);
setMenuItemEnabled(menu.findItem(R.id.action_reload_thread), slidable && !open);
setMenuItemEnabled(menu.findItem(R.id.action_reload_tablet), !slidable);
-
+
setMenuItemEnabled(menu.findItem(R.id.action_pin), !slidable || !open);
-
+
setMenuItemEnabled(menu.findItem(R.id.action_reply), slidable);
setMenuItemEnabled(menu.findItem(R.id.action_reply_tablet), !slidable);
-
+
return super.onPrepareOptionsMenu(menu);
}
-
+
private void setMenuItemEnabled(MenuItem item, boolean enabled) {
if (item != null) {
item.setVisible(enabled);
item.setEnabled(enabled);
}
}
-
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (pinDrawerListener.onOptionsItemSelected(item)) {
return true;
}
-
+
switch(item.getItemId()) {
case R.id.action_reload_board:
case R.id.action_reload_tablet_board:
@@ -248,22 +254,22 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
return true;
case R.id.action_reply_board:
boardFragment.openReply();
-
+
return true;
case R.id.action_reply_thread:
threadFragment.openReply();
-
+
return true;
case R.id.action_pin:
if (threadFragment.hasLoader()) {
Pin pin = new Pin();
pin.loadable = threadLoadable;
-
+
addPin(pin);
-
+
pinDrawer.openDrawer(pinDrawerView);
}
-
+
return true;
case R.id.action_open_browser:
if (threadPane.isOpen()) {
@@ -273,14 +279,14 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
showUrlOpenPicker(ChanUrls.getThreadUrlDesktop(threadLoadable.board, threadLoadable.no));
}
}
-
+
return true;
case android.R.id.home:
threadPane.openPane();
-
+
return true;
}
-
+
return super.onOptionsItemSelected(item);
}
@@ -293,63 +299,63 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
public void onPanelOpened(View view) {
updateActionBarState();
}
-
+
private void startLoadingBoard(Loadable loadable) {
if (loadable.mode == Loadable.Mode.INVALID) return;
-
- this.boardLoadable = loadable;
-
+
+ boardLoadable = loadable;
+
boardFragment.bindLoadable(loadable);
boardFragment.requestData();
-
+
setShareUrl(ChanUrls.getBoardUrlDesktop(loadable.board));
-
+
updateActionBarState();
}
-
+
private void startLoadingThread(Loadable loadable) {
if (loadable.mode == Loadable.Mode.INVALID) return;
-
+
Pin pin = PinnedManager.getInstance().findPinByLoadable(loadable);
if (pin != null) {
// Use the loadable from the pin.
- // This way we can store the listview position in the pin loadable,
+ // This way we can store the listview position in the pin loadable,
// and not in a separate loadable instance.
- loadable = pin.loadable;
+ loadable = pin.loadable;
}
-
+
threadLoadable = loadable;
-
+
threadFragment.bindLoadable(loadable);
threadFragment.requestData();
-
+
setShareUrl(ChanUrls.getThreadUrlDesktop(loadable.board, loadable.no));
-
+
if (TextUtils.isEmpty(loadable.title)) {
loadable.title = "/" + loadable.board + "/" + loadable.no;
}
-
+
threadPane.closePane();
-
+
updateActionBarState();
}
-
+
/**
* Handle opening from an external url.
* @param startUri
*/
private void handleIntentURI(Uri startUri) {
List parts = startUri.getPathSegments();
-
+
if (parts.size() == 1) {
// Board mode
- String rawBoard = parts.get(0);
-
+ String rawBoard = parts.get(0);
+
if (BoardManager.getInstance().getBoardExists(rawBoard)) {
boardSetByIntent = true;
-
+
startLoadingBoard(new Loadable(rawBoard));
-
+
ActionBar actionBar = getActionBar();
if (!setNavigationFromBoardValue(rawBoard)) {
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
@@ -357,7 +363,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
String value = BoardManager.getInstance().getBoardKey(rawBoard);
actionBar.setTitle(value == null ? ("/" + rawBoard + "/") : value);
}
-
+
} else {
handleIntentURIFallback(startUri.toString());
}
@@ -366,14 +372,14 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
// First load a board and then start another activity opening the thread
String rawBoard = parts.get(0);
int no = -1;
-
+
try {
no = Integer.parseInt(parts.get(2));
} catch (NumberFormatException e) {}
-
+
if (no >= 0 && BoardManager.getInstance().getBoardExists(rawBoard)) {
boardSetByIntent = true;
-
+
startLoadingBoard(new Loadable(rawBoard));
startLoadingThread(new Loadable(rawBoard, no));
} else {
@@ -384,7 +390,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
showUrlOpenPicker(startUri.toString());
}
}
-
+
private void handleIntentURIFallback(final String url) {
new AlertDialog.Builder(this)
.setTitle(R.string.open_unknown_title)
@@ -407,7 +413,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
.create()
.show();
}
-
+
/**
* Set the visual selector to the board. If the user has not set the board as a favorite,
* return false.
@@ -423,7 +429,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
break;
}
}
-
+
if (foundIndex >= 0) {
getActionBar().setSelectedNavigationItem(foundIndex);
return true;
diff --git a/Chan/src/org/floens/chan/adapter/PinnedAdapter.java b/Chan/src/org/floens/chan/adapter/PinnedAdapter.java
index 8444b383..4e75f0c0 100644
--- a/Chan/src/org/floens/chan/adapter/PinnedAdapter.java
+++ b/Chan/src/org/floens/chan/adapter/PinnedAdapter.java
@@ -19,40 +19,38 @@ import android.widget.TextView;
public class PinnedAdapter extends ArrayAdapter {
private final HashMap idMap;
private int idCounter;
- private View.OnTouchListener listener;
-
+
public PinnedAdapter(Context context, int resId) {
super(context, resId, new ArrayList());
-
+
idMap = new HashMap();
}
-
- public void setTouchListener(View.OnTouchListener listener) {
- this.listener = listener;
- }
-
+
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
+
LinearLayout view = null;
-
+
Pin item = getItem(position);
-
+
if (item.type == Pin.Type.HEADER) {
view = (LinearLayout) inflater.inflate(R.layout.pin_item_header, null);
-
+
((TextView) view.findViewById(R.id.drawer_item_header)).setText(R.string.drawer_pinned);
} else {
view = (LinearLayout) inflater.inflate(R.layout.pin_item, null);
-
+
((TextView) view.findViewById(R.id.drawer_item_text)).setText(item.loadable.title);
-
- int count = Math.max(0, item.watchNewCount - item.watchLastCount);
- String total = Integer.toString(Math.min(count, 999));
-
+
+ int count = item.getNewPostCount();
+ String total = Integer.toString(count);
+ if (count > 999) {
+ total = "1k+";
+ }
+
((TextView) view.findViewById(R.id.drawer_item_count)).setText(total);
-
+
FrameLayout frameLayout = (FrameLayout) view.findViewById(R.id.drawer_item_count_container);
// if (Math.random() < 0.5d) {
frameLayout.setBackgroundResource(R.drawable.pin_icon_blue);
@@ -60,38 +58,36 @@ public class PinnedAdapter extends ArrayAdapter {
// frameLayout.setBackgroundResource(R.drawable.pin_icon_red);
// }
}
-
- if (listener != null) {
- view.setOnTouchListener(listener);
- }
-
+
return view;
}
-
+
public void reload() {
clear();
-
+
Pin header = new Pin();
header.type = Pin.Type.HEADER;
add(header);
-
+
addAll(PinnedManager.getInstance().getPins());
+
+ notifyDataSetChanged();
}
-
+
@Override
public void remove(Pin item) {
super.remove(item);
idMap.remove(item);
notifyDataSetChanged();
}
-
+
@Override
public void add(Pin item) {
idMap.put(item, ++idCounter);
super.add(item);
notifyDataSetChanged();
}
-
+
@Override
public boolean hasStableIds() {
return true;
@@ -100,7 +96,7 @@ public class PinnedAdapter extends ArrayAdapter {
@Override
public long getItemId(int position) {
if (position < 0 || position >= getCount()) return -1;
-
+
Pin item = getItem(position);
if (item == null) {
return -1;
diff --git a/Chan/src/org/floens/chan/adapter/PostAdapter.java b/Chan/src/org/floens/chan/adapter/PostAdapter.java
index 4483fdaa..baf99c75 100644
--- a/Chan/src/org/floens/chan/adapter/PostAdapter.java
+++ b/Chan/src/org/floens/chan/adapter/PostAdapter.java
@@ -28,6 +28,7 @@ public class PostAdapter extends BaseAdapter {
private boolean endOfLine;
private int count = 0;
private final List postList = new ArrayList();
+ private long lastViewedTime = 0;
public PostAdapter(Context activity, ThreadManager threadManager, ListView listView) {
context = activity;
@@ -62,6 +63,11 @@ public class PostAdapter extends BaseAdapter {
}
if (position >= count) {
+ if (System.currentTimeMillis() - lastViewedTime > 10000L) {
+ lastViewedTime = System.currentTimeMillis();
+ threadManager.bottomPostViewed();
+ }
+
return createThreadEndView();
} else {
PostView postView = null;
diff --git a/Chan/src/org/floens/chan/loader/Loader.java b/Chan/src/org/floens/chan/loader/Loader.java
index 5fb384f6..61cc59aa 100644
--- a/Chan/src/org/floens/chan/loader/Loader.java
+++ b/Chan/src/org/floens/chan/loader/Loader.java
@@ -9,6 +9,7 @@ import org.floens.chan.model.Post;
import org.floens.chan.utils.Logger;
import android.os.Handler;
+import android.os.Looper;
import android.util.SparseArray;
import com.android.volley.Response;
@@ -17,26 +18,26 @@ import com.android.volley.VolleyError;
public class Loader {
private static final String TAG = "Loader";
- private static final Handler handler = new Handler();
-
- private static final int[] watchTimeouts = {10, 15, 20, 30, 60, 90, 120, 180, 240, 300};
-
+ private static final Handler handler = new Handler(Looper.getMainLooper());
+
+ private static final int[] watchTimeouts = {5, 10, 15, 20, 30, 60, 90, 120, 180, 240, 300};
+
private final List listeners = new ArrayList();
private final Loadable loadable;
private final SparseArray postsById = new SparseArray();
-
+
private boolean destroyed = false;
private ChanReaderRequest request;
-
+
private int currentTimeout;
private int lastPostCount;
private long lastLoadTime;
private Runnable pendingRunnable;
-
+
public Loader(Loadable loadable) {
this.loadable = loadable;
}
-
+
/**
* Add a LoaderListener
* @param l the listener to add
@@ -44,7 +45,7 @@ public class Loader {
public void addListener(LoaderListener l) {
listeners.add(l);
}
-
+
/**
* Remove a LoaderListener
* @param l the listener to remove
@@ -60,81 +61,87 @@ public class Loader {
return false;
}
}
-
+
public void requestData() {
if (request != null) {
request.cancel();
}
-
+
if (loadable.isBoardMode()) {
loadable.no = 0;
loadable.listViewIndex = 0;
loadable.listViewTop = 0;
}
-
+
+ currentTimeout = 0;
+
request = getData(loadable);
}
-
+
public void requestNextData() {
if (loadable.isBoardMode()) {
loadable.no++;
-
+
if (request != null) {
request.cancel();
}
-
+
request = getData(loadable);
} else if (loadable.isThreadMode()) {
if (request != null) {
return;
}
-
+
clearTimer();
-
+
request = getData(loadable);
}
}
-
+
public void requestNextDataResetTimer() {
currentTimeout = 0;
requestNextData();
}
-
+
/**
* @return Returns if this loader is currently loading
*/
public boolean isLoading() {
return request != null;
}
-
+
public Post findPostById(int id) {
return postsById.get(id);
}
-
+
public Loadable getLoadable() {
return loadable;
}
-
+
public void onStart() {
if (loadable.isThreadMode()) {
requestNextDataResetTimer();
}
}
-
+
public void onStop() {
clearTimer();
}
-
+
public long getTimeUntilReload() {
- long waitTime = watchTimeouts[currentTimeout] * 1000L;
- return lastLoadTime + waitTime - System.currentTimeMillis();
+ if (request != null) {
+ return 0L;
+ } else {
+ long waitTime = watchTimeouts[currentTimeout] * 1000L;
+ return lastLoadTime + waitTime - System.currentTimeMillis();
+ }
}
-
+
private void setTimer(int postCount) {
if (pendingRunnable != null) {
clearTimer();
}
-
+
if (pendingRunnable == null) {
pendingRunnable = new Runnable() {
@Override
@@ -142,36 +149,35 @@ public class Loader {
pendingRunnableCallback();
};
};
-
+
+ lastPostCount = postCount;
+
if (postCount > lastPostCount) {
currentTimeout = 0;
} else {
currentTimeout = Math.min(watchTimeouts.length - 1, currentTimeout + 1);
}
-
- lastPostCount = postCount;
-
+
handler.postDelayed(pendingRunnable, watchTimeouts[currentTimeout] * 1000L);
}
}
-
+
private void clearTimer() {
if (pendingRunnable != null) {
handler.removeCallbacks(pendingRunnable);
pendingRunnable = null;
- lastLoadTime = 0;
}
}
-
+
private void pendingRunnableCallback() {
Logger.d(TAG, "pending callback");
pendingRunnable = null;
requestNextData();
}
-
+
private ChanReaderRequest getData(Loadable loadable) {
Logger.i(TAG, "Requested " + loadable.board + ", " + loadable.no);
-
+
ChanReaderRequest request = ChanReaderRequest.newInstance(loadable, new Response.Listener>() {
@Override
public void onResponse(List list) {
@@ -185,47 +191,49 @@ public class Loader {
onError(error);
}
});
-
+
ChanApplication.getVolleyRequestQueue().add(request);
-
+
return request;
}
-
+
private void onData(List result) {
if (destroyed) return;
-
+
+ Logger.test("ondata in loader");
+
postsById.clear();
for (Post post : result) {
postsById.append(post.no, post);
}
-
+
for (LoaderListener l : listeners) {
l.onData(result, loadable.isBoardMode());
}
-
+
lastLoadTime = System.currentTimeMillis();
if (loadable.isThreadMode()) {
setTimer(result.size());
}
}
-
+
private void onError(VolleyError error) {
if (destroyed) return;
-
+
Logger.e(TAG, "Error loading " + error.getMessage(), error);
-
+
// 404 with more pages already loaded means endofline
if ((error instanceof ServerError) && loadable.isBoardMode() && loadable.no > 0) {
error = new EndOfLineException();
}
-
+
for (LoaderListener l : listeners) {
l.onError(error);
}
-
+
clearTimer();
}
-
+
public static interface LoaderListener {
public void onData(List result, boolean append);
public void onError(VolleyError error);
diff --git a/Chan/src/org/floens/chan/manager/PinnedManager.java b/Chan/src/org/floens/chan/manager/PinnedManager.java
index 38e66253..19b11beb 100644
--- a/Chan/src/org/floens/chan/manager/PinnedManager.java
+++ b/Chan/src/org/floens/chan/manager/PinnedManager.java
@@ -11,27 +11,27 @@ import android.content.Context;
public class PinnedManager {
private static PinnedManager instance;
-
+
private final List listeners = new ArrayList();
private final List pins;
-
+
public PinnedManager(Context context) {
instance = this;
pins = DatabaseManager.getInstance().getPinned();
}
-
+
public static PinnedManager getInstance() {
return instance;
}
-
+
public void addPinListener(PinListener l) {
listeners.add(l);
}
-
+
public void removePinListener(PinListener l) {
listeners.remove(l);
}
-
+
/**
* Look for a pin that has an loadable that is equal to the supplied loadable.
* @param other
@@ -43,14 +43,14 @@ public class PinnedManager {
return pin;
}
}
-
+
return null;
}
-
+
public List getPins() {
return pins;
}
-
+
/**
* Add a pin
* @param pin
@@ -63,15 +63,15 @@ public class PinnedManager {
return false;
}
}
-
+
pins.add(pin);
DatabaseManager.getInstance().addPin(pin);
-
+
onPinsChanged();
-
+
return true;
}
-
+
/**
* Remove a pin
* @param pin
@@ -79,22 +79,23 @@ public class PinnedManager {
public void remove(Pin pin) {
pins.remove(pin);
DatabaseManager.getInstance().removePin(pin);
-
+ pin.destroy();
+
onPinsChanged();
}
-
+
/**
* Update the pin in the database
* @param pin
*/
public void update(Pin pin) {
DatabaseManager.getInstance().updatePin(pin);
-
+
onPinsChanged();
}
-
+
/**
- * Updates all the pins to the database.
+ * Updates all the pins to the database.
* This will run in a new thread because it can be an expensive operation.
* (this will be an huge headache later on when we get concurrent problems)
*/
@@ -106,19 +107,19 @@ public class PinnedManager {
}
}).start();
}
-
+
public void onPinViewed(Pin pin) {
- pin.watchLastCount = pin.watchNewCount;
-
+ pin.onViewed();
+
onPinsChanged();
}
-
+
public void onPinsChanged() {
for (PinListener l : listeners) {
l.onPinsChanged();
}
}
-
+
public static interface PinListener {
public void onPinsChanged();
}
diff --git a/Chan/src/org/floens/chan/manager/ThreadManager.java b/Chan/src/org/floens/chan/manager/ThreadManager.java
index de91b906..212ec13b 100644
--- a/Chan/src/org/floens/chan/manager/ThreadManager.java
+++ b/Chan/src/org/floens/chan/manager/ThreadManager.java
@@ -13,6 +13,7 @@ import org.floens.chan.loader.LoaderPool;
import org.floens.chan.manager.ReplyManager.DeleteListener;
import org.floens.chan.manager.ReplyManager.DeleteResponse;
import org.floens.chan.model.Loadable;
+import org.floens.chan.model.Pin;
import org.floens.chan.model.Post;
import org.floens.chan.model.PostLinkable;
import org.floens.chan.model.SavedReply;
@@ -93,6 +94,15 @@ public class ThreadManager implements Loader.LoaderListener {
highlightedPost = null;
}
+ public void bottomPostViewed() {
+ if (loader != null && loader.getLoadable().isThreadMode()) {
+ Pin pin = PinnedManager.getInstance().findPinByLoadable(loader.getLoadable());
+ if (pin != null) {
+ PinnedManager.getInstance().onPinViewed(pin);
+ }
+ }
+ }
+
public void requestData() {
if (loader != null) {
loader.requestData();
@@ -398,7 +408,7 @@ public class ThreadManager implements Loader.LoaderListener {
FragmentTransaction ft = activity.getFragmentManager().beginTransaction();
ft.add(popup, "postPopup");
- ft.commit();
+ ft.commitAllowingStateLoss();
currentPopupFragment = popup;
}
diff --git a/Chan/src/org/floens/chan/model/Pin.java b/Chan/src/org/floens/chan/model/Pin.java
index c9257ce2..c2608053 100644
--- a/Chan/src/org/floens/chan/model/Pin.java
+++ b/Chan/src/org/floens/chan/model/Pin.java
@@ -10,33 +10,51 @@ public class Pin {
// Database stuff
@DatabaseField(generatedId = true)
private int id;
-
+
@DatabaseField(canBeNull = false, foreign = true)
public Loadable loadable = new Loadable("", -1);
-
+
// ListView Stuff
/** Header is used to display a static header in the drawer listview. */
public Type type = Type.THREAD;
public static enum Type {
- HEADER,
+ HEADER,
THREAD
};
-
+
// PinnedService stuff
public PinWatcher pinWatcher;
-
+
@DatabaseField
public int watchLastCount;
-
+
@DatabaseField
public int watchNewCount;
-
+
public void updateWatch() {
if (pinWatcher == null) {
-// pinWatcher = new PinWatcher(this);
+ pinWatcher = new PinWatcher(this);
+ }
+
+ pinWatcher.update();
+ }
+
+ public int getNewPostCount() {
+ if (pinWatcher != null) {
+ return pinWatcher.getNewPostCount();
+ } else {
+ return 0;
+ }
+ }
+
+ public void onViewed() {
+ watchLastCount = watchNewCount;
+ }
+
+ public void destroy() {
+ if (pinWatcher != null) {
+ pinWatcher.destroy();
}
-
-// pinWatcher.update();
}
}
diff --git a/Chan/src/org/floens/chan/service/PinnedService.java b/Chan/src/org/floens/chan/service/PinnedService.java
index 34bf7959..9ab02d81 100644
--- a/Chan/src/org/floens/chan/service/PinnedService.java
+++ b/Chan/src/org/floens/chan/service/PinnedService.java
@@ -2,13 +2,11 @@ package org.floens.chan.service;
import java.util.List;
-import org.floens.chan.R;
import org.floens.chan.manager.PinnedManager;
import org.floens.chan.model.Pin;
import org.floens.chan.utils.Logger;
+import org.floens.chan.watch.WatchNotifier;
-import android.app.Notification;
-import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
@@ -23,6 +21,7 @@ public class PinnedService extends Service {
private Thread loadThread;
private boolean running = true;
+ private final WatchNotifier watchNotifier;
public static void onActivityStart() {
Logger.test("onActivityStart");
@@ -34,6 +33,10 @@ public class PinnedService extends Service {
activityInForeground = false;
}
+ public PinnedService() {
+ watchNotifier = new WatchNotifier(this);
+ }
+
@Override
public void onCreate() {
super.onCreate();
@@ -49,37 +52,39 @@ public class PinnedService extends Service {
}
private void start() {
- loadThread = new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e1) {
- e1.printStackTrace();
- }
-
- while (running) {
- doUpdates();
-
- long timeout = activityInForeground ? FOREGROUND_INTERVAL : BACKGROUND_INTERVAL;
-
- try {
- Thread.sleep(timeout);
- } catch (InterruptedException e) {
- e.printStackTrace();
+ running = true;
+
+ if (loadThread == null) {
+ loadThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while (running) {
+ update();
+
+ long timeout = activityInForeground ? FOREGROUND_INTERVAL : BACKGROUND_INTERVAL;
+
+ try {
+ Thread.sleep(timeout);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
}
+
+ loadThread = null;
}
- }
- });
+ });
- loadThread.start();
+ loadThread.start();
+ }
}
- private void doUpdates() {
+ private void update() {
List pins = PinnedManager.getInstance().getPins();
for (Pin pin : pins) {
pin.updateWatch();
}
+
+ watchNotifier.update();
}
public static void callOnPinsChanged() {
@@ -91,18 +96,6 @@ public class PinnedService extends Service {
});
}
- @SuppressWarnings("deprecation")
- private void showNotification(String text) {
- NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
-
- Notification.Builder builder = new Notification.Builder(this);
- builder.setTicker(text);
- builder.setContentTitle(text);
- builder.setContentText(text);
- builder.setSmallIcon(R.drawable.ic_stat_notify);
-
- nm.notify(1, builder.getNotification());
- }
@Override
public IBinder onBind(Intent intent) {
diff --git a/Chan/src/org/floens/chan/watch/PinWatcher.java b/Chan/src/org/floens/chan/watch/PinWatcher.java
index 875c9b23..71523501 100644
--- a/Chan/src/org/floens/chan/watch/PinWatcher.java
+++ b/Chan/src/org/floens/chan/watch/PinWatcher.java
@@ -3,6 +3,7 @@ package org.floens.chan.watch;
import java.util.List;
import org.floens.chan.loader.Loader;
+import org.floens.chan.loader.LoaderPool;
import org.floens.chan.model.Pin;
import org.floens.chan.model.Post;
import org.floens.chan.service.PinnedService;
@@ -12,44 +13,63 @@ import com.android.volley.VolleyError;
public class PinWatcher implements Loader.LoaderListener {
private static final String TAG = "PinWatcher";
-
+
private final Pin pin;
- private Loader loader;
-
+ private final Loader loader;
+
private long startTime;
+ private boolean isError = false;
public PinWatcher(Pin pin) {
this.pin = pin;
-
-// loader = new Loader(this);
+
+ loader = LoaderPool.getInstance().obtain(pin.loadable, this);
}
-
+
public void destroy() {
-
+ LoaderPool.getInstance().release(loader, this);
+ }
+
+ public void update() {
+ Logger.test("PinWatcher update");
+
+ if (!isError) {
+ if (loader.getTimeUntilReload() < -1000000L) {
+ Logger.test("Here: " + loader.getTimeUntilReload());
+ }
+
+ if (loader.getTimeUntilReload() < 0L) {
+ loader.requestNextDataResetTimer();
+ }
+ }
+ }
+
+ public int getNewPostCount() {
+ if (pin.watchLastCount <= 0) {
+ return 0;
+ } else {
+ return Math.max(0, pin.watchNewCount - pin.watchLastCount);
+ }
}
@Override
public void onError(VolleyError error) {
Logger.test("PinWatcher onError: ", error);
+ isError = true;
}
-
+
@Override
public void onData(List result, boolean append) {
int count = result.size();
-
- Logger.test("PinWatcher onData");
- Logger.test("Post size: " + count);
-
+
+ Logger.test("PinWatcher onData, Post size: " + count);
+
if (pin.watchLastCount <= 0) {
pin.watchLastCount = pin.watchNewCount;
}
-
+
pin.watchNewCount = count;
-
- Logger.test("Load time: " + (System.currentTimeMillis() - startTime) + "ms");
-
+
PinnedService.callOnPinsChanged();
}
-
-
}
diff --git a/Chan/src/org/floens/chan/watch/WatchNotifier.java b/Chan/src/org/floens/chan/watch/WatchNotifier.java
new file mode 100644
index 00000000..d87b0b51
--- /dev/null
+++ b/Chan/src/org/floens/chan/watch/WatchNotifier.java
@@ -0,0 +1,33 @@
+package org.floens.chan.watch;
+
+import org.floens.chan.R;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+
+public class WatchNotifier {
+ private final int NOTIFICATION_ID = 1;
+
+ private final Context context;
+
+ public WatchNotifier(Context context) {
+ this.context = context;
+ }
+
+ public void update() {
+ showNotification("Update!");
+ }
+
+ private void showNotification(String text) {
+ NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ Notification.Builder builder = new Notification.Builder(context);
+ builder.setTicker(text);
+ builder.setContentTitle(text);
+ builder.setContentText(text);
+ builder.setSmallIcon(R.drawable.ic_stat_notify);
+
+ nm.notify(NOTIFICATION_ID, builder.getNotification());
+ }
+}