Pin watching now works in the background.

captchafix
Florens Douwes 11 years ago
parent f756a6e692
commit 39d974f91c
  1. 2
      Chan/res/values/strings.xml
  2. 4
      Chan/src/org/floens/chan/ChanApplication.java
  3. 87
      Chan/src/org/floens/chan/activity/BaseActivity.java
  4. 164
      Chan/src/org/floens/chan/activity/BoardActivity.java
  5. 56
      Chan/src/org/floens/chan/adapter/PinnedAdapter.java
  6. 6
      Chan/src/org/floens/chan/adapter/PostAdapter.java
  7. 106
      Chan/src/org/floens/chan/loader/Loader.java
  8. 47
      Chan/src/org/floens/chan/manager/PinnedManager.java
  9. 12
      Chan/src/org/floens/chan/manager/ThreadManager.java
  10. 38
      Chan/src/org/floens/chan/model/Pin.java
  11. 67
      Chan/src/org/floens/chan/service/PinnedService.java
  12. 56
      Chan/src/org/floens/chan/watch/PinWatcher.java
  13. 33
      Chan/src/org/floens/chan/watch/WatchNotifier.java

@ -46,7 +46,7 @@
<string name="drawer_open">Open drawer</string> <string name="drawer_open">Open drawer</string>
<string name="drawer_close">Close drawer</string> <string name="drawer_close">Close drawer</string>
<string name="drawer_pinned">Pinned threads</string> <string name="drawer_pinned">Pinned threads</string>
<string name="drawer_pinned_change_title">Enter title</string> <string name="drawer_pinned_change_title">Edit title</string>
<string name="one_reply">reply</string> <string name="one_reply">reply</string>
<string name="multiple_replies">replies</string> <string name="multiple_replies">replies</string>

@ -4,9 +4,11 @@ import org.floens.chan.database.DatabaseManager;
import org.floens.chan.manager.BoardManager; import org.floens.chan.manager.BoardManager;
import org.floens.chan.manager.PinnedManager; import org.floens.chan.manager.PinnedManager;
import org.floens.chan.manager.ReplyManager; import org.floens.chan.manager.ReplyManager;
import org.floens.chan.service.PinnedService;
import org.floens.chan.utils.IconCache; import org.floens.chan.utils.IconCache;
import android.app.Application; import android.app.Application;
import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.StrictMode; import android.os.StrictMode;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
@ -70,7 +72,7 @@ public class ChanApplication extends Application {
new PinnedManager(this); new PinnedManager(this);
new ReplyManager(this); new ReplyManager(this);
// startService(new Intent(this, PinnedService.class)); startService(new Intent(this, PinnedService.class));
} }
} }

@ -7,6 +7,7 @@ import org.floens.chan.animation.SwipeDismissListViewTouchListener.DismissCallba
import org.floens.chan.manager.PinnedManager; import org.floens.chan.manager.PinnedManager;
import org.floens.chan.model.Pin; import org.floens.chan.model.Pin;
import org.floens.chan.model.Post; import org.floens.chan.model.Post;
import org.floens.chan.utils.Logger;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
@ -38,22 +39,22 @@ import android.widget.ShareActionProvider;
public abstract class BaseActivity extends Activity implements PanelSlideListener, PinnedManager.PinListener { public abstract class BaseActivity extends Activity implements PanelSlideListener, PinnedManager.PinListener {
private final static int ACTION_OPEN_URL = 1; private final static int ACTION_OPEN_URL = 1;
protected PinnedAdapter pinnedAdapter; protected PinnedAdapter pinnedAdapter;
protected DrawerLayout pinDrawer; protected DrawerLayout pinDrawer;
protected ListView pinDrawerView; protected ListView pinDrawerView;
protected ActionBarDrawerToggle pinDrawerListener; protected ActionBarDrawerToggle pinDrawerListener;
protected SlidingPaneLayout threadPane; protected SlidingPaneLayout threadPane;
private ShareActionProvider shareActionProvider; private ShareActionProvider shareActionProvider;
/** /**
* Called when a post has been clicked in the pinned drawer * Called when a post has been clicked in the pinned drawer
* @param post * @param post
*/ */
abstract public void openPin(Pin post); abstract public void openPin(Pin post);
/** /**
* Called when a post has been clicked in the listview * Called when a post has been clicked in the listview
* @param post * @param post
@ -63,25 +64,25 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base); setContentView(R.layout.activity_base);
pinDrawer = (DrawerLayout) findViewById(R.id.drawer_layout); pinDrawer = (DrawerLayout) findViewById(R.id.drawer_layout);
initDrawer(); initDrawer();
threadPane = (SlidingPaneLayout) findViewById(R.id.pane_container); threadPane = (SlidingPaneLayout) findViewById(R.id.pane_container);
initPane(); initPane();
PinnedManager.getInstance().addPinListener(this); PinnedManager.getInstance().addPinListener(this);
} }
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
PinnedManager.getInstance().removePinListener(this); PinnedManager.getInstance().removePinListener(this);
} }
private void initPane() { private void initPane() {
threadPane.setPanelSlideListener(this); threadPane.setPanelSlideListener(this);
threadPane.setParallaxDistance(200); threadPane.setParallaxDistance(200);
@ -89,21 +90,21 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
threadPane.setSliderFadeColor(0xcce5e5e5); threadPane.setSliderFadeColor(0xcce5e5e5);
threadPane.openPane(); threadPane.openPane();
} }
protected void initDrawer() { protected void initDrawer() {
if (pinDrawerListener == null) { if (pinDrawerListener == null) {
return; return;
} }
pinDrawer.setDrawerListener(pinDrawerListener); pinDrawer.setDrawerListener(pinDrawerListener);
pinDrawer.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); pinDrawer.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
pinDrawerView = (ListView)findViewById(R.id.left_drawer); pinDrawerView = (ListView)findViewById(R.id.left_drawer);
pinnedAdapter = new PinnedAdapter(getActionBar().getThemedContext(), 0); // Get the dark theme, not the light one pinnedAdapter = new PinnedAdapter(getActionBar().getThemedContext(), 0); // Get the dark theme, not the light one
pinnedAdapter.reload(); pinnedAdapter.reload();
pinDrawerView.setAdapter(pinnedAdapter); pinDrawerView.setAdapter(pinnedAdapter);
pinDrawerView.setOnItemClickListener(new OnItemClickListener() { pinDrawerView.setOnItemClickListener(new OnItemClickListener() {
@Override @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 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); openPin(pin);
} }
}); });
pinDrawerView.setOnItemLongClickListener(new OnItemLongClickListener() { pinDrawerView.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override @Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Pin post = pinnedAdapter.getItem(position); Pin post = pinnedAdapter.getItem(position);
if (post == null || post.type == Pin.Type.HEADER) return false; if (post == null || post.type == Pin.Type.HEADER) return false;
changePinTitle(post); changePinTitle(post);
return true; return true;
} }
}); });
SwipeDismissListViewTouchListener touchListener = new SwipeDismissListViewTouchListener(pinDrawerView, SwipeDismissListViewTouchListener touchListener = new SwipeDismissListViewTouchListener(pinDrawerView,
new DismissCallbacks() { new DismissCallbacks() {
@Override @Override
@ -139,24 +140,26 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
return pinnedAdapter.getItem(position).type != Pin.Type.HEADER; return pinnedAdapter.getItem(position).type != Pin.Type.HEADER;
} }
}); });
pinDrawerView.setOnTouchListener(touchListener); pinDrawerView.setOnTouchListener(touchListener);
pinDrawerView.setOnScrollListener(touchListener.makeScrollListener()); pinDrawerView.setOnScrollListener(touchListener.makeScrollListener());
} }
@Override @Override
public void onPinsChanged() { public void onPinsChanged() {
pinnedAdapter.reload(); pinnedAdapter.reload();
pinDrawerView.invalidate();
Logger.test("onPinsChanged");
} }
public void addPin(Pin pin) { public void addPin(Pin pin) {
PinnedManager.getInstance().add(pin); PinnedManager.getInstance().add(pin);
} }
public void removePin(Pin pin) { public void removePin(Pin pin) {
PinnedManager.getInstance().remove(pin); PinnedManager.getInstance().remove(pin);
} }
public void updatePin(Pin pin) { public void updatePin(Pin pin) {
PinnedManager.getInstance().update(pin); PinnedManager.getInstance().update(pin);
} }
@ -166,13 +169,13 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
text.setSingleLine(); text.setSingleLine();
text.setText(pin.loadable.title); text.setText(pin.loadable.title);
text.setSelectAllOnFocus(true); text.setSelectAllOnFocus(true);
AlertDialog dialog = new AlertDialog.Builder(this) AlertDialog dialog = new AlertDialog.Builder(this)
.setPositiveButton(R.string.change, new DialogInterface.OnClickListener() { .setPositiveButton(R.string.change, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface d, int which) { public void onClick(DialogInterface d, int which) {
String value = text.getText().toString(); String value = text.getText().toString();
if (!TextUtils.isEmpty(value)) { if (!TextUtils.isEmpty(value)) {
pin.loadable.title = value; pin.loadable.title = value;
updatePin(pin); updatePin(pin);
@ -187,9 +190,9 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
.setTitle(R.string.drawer_pinned_change_title) .setTitle(R.string.drawer_pinned_change_title)
.setView(text) .setView(text)
.create(); .create();
text.requestFocus(); text.requestFocus();
dialog.setOnShowListener(new OnShowListener() { dialog.setOnShowListener(new OnShowListener() {
@Override @Override
public void onShow(DialogInterface dialog) { public void onShow(DialogInterface dialog) {
@ -197,7 +200,7 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
imm.showSoftInput(text, 0); imm.showSoftInput(text, 0);
} }
}); });
dialog.show(); dialog.show();
} }
@ -208,18 +211,18 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
startActivity(new Intent(this, SettingsActivity.class)); startActivity(new Intent(this, SettingsActivity.class));
return true; return true;
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.base, menu); getMenuInflater().inflate(R.menu.base, menu);
shareActionProvider = (ShareActionProvider) menu.findItem(R.id.action_share).getActionProvider(); shareActionProvider = (ShareActionProvider) menu.findItem(R.id.action_share).getActionProvider();
return true; return true;
} }
@Override @Override
public void onPanelClosed(View view) { public void onPanelClosed(View view) {
} }
@ -231,7 +234,7 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
@Override @Override
public void onPanelSlide(View view, float offset) { public void onPanelSlide(View view, float offset) {
} }
/** /**
* Set the url that Android Beam and the share action will send. * Set the url that Android Beam and the share action will send.
* @param url * @param url
@ -247,11 +250,11 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
e.printStackTrace(); e.printStackTrace();
return; return;
} }
NdefMessage message = new NdefMessage(new NdefRecord[] {record}); NdefMessage message = new NdefMessage(new NdefRecord[] {record});
adapter.setNdefPushMessage(message, this); adapter.setNdefPushMessage(message, this);
} }
if (shareActionProvider != null) { if (shareActionProvider != null) {
Intent share = new Intent(android.content.Intent.ACTION_SEND); Intent share = new Intent(android.content.Intent.ACTION_SEND);
share.putExtra(android.content.Intent.EXTRA_TEXT, url); share.putExtra(android.content.Intent.EXTRA_TEXT, url);
@ -259,7 +262,7 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
shareActionProvider.setShareIntent(share); shareActionProvider.setShareIntent(share);
} }
} }
/** /**
* Let the user choose between all activities that can open the url. * 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. * 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) { public void showUrlOpenPicker(String url) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY); Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
pickIntent.putExtra(Intent.EXTRA_INTENT, intent); pickIntent.putExtra(Intent.EXTRA_INTENT, intent);
startActivityForResult(pickIntent, ACTION_OPEN_URL); startActivityForResult(pickIntent, ACTION_OPEN_URL);
} }
/** /**
* Used for showUrlOpenPicker * Used for showUrlOpenPicker
*/ */
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ACTION_OPEN_URL && resultCode == RESULT_OK && data != null) { if (requestCode == ACTION_OPEN_URL && resultCode == RESULT_OK && data != null) {
data.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); data.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(data); startActivity(data);

@ -34,44 +34,44 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
private ThreadFragment boardFragment; private ThreadFragment boardFragment;
private ThreadFragment threadFragment; private ThreadFragment threadFragment;
private boolean boardSetByIntent = false; private boolean boardSetByIntent = false;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
boardLoadable.mode = Loadable.Mode.BOARD; boardLoadable.mode = Loadable.Mode.BOARD;
threadLoadable.mode = Loadable.Mode.THREAD; threadLoadable.mode = Loadable.Mode.THREAD;
boardFragment = ThreadFragment.newInstance(this); boardFragment = ThreadFragment.newInstance(this);
threadFragment = ThreadFragment.newInstance(this); threadFragment = ThreadFragment.newInstance(this);
FragmentTransaction ft = getFragmentManager().beginTransaction(); FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.left_pane, boardFragment); ft.replace(R.id.left_pane, boardFragment);
ft.replace(R.id.right_pane, threadFragment); ft.replace(R.id.right_pane, threadFragment);
ft.commitAllowingStateLoss(); ft.commitAllowingStateLoss();
updateActionBarState(); updateActionBarState();
final ActionBar actionBar = getActionBar(); final ActionBar actionBar = getActionBar();
actionBar.setListNavigationCallbacks( actionBar.setListNavigationCallbacks(
new ArrayAdapter<String>( new ArrayAdapter<String>(
actionBar.getThemedContext(), actionBar.getThemedContext(),
R.layout.board_select_spinner, R.layout.board_select_spinner,
android.R.id.text1, android.R.id.text1,
BoardManager.getInstance().getMyBoardsKeys() BoardManager.getInstance().getMyBoardsKeys()
), this); ), this);
Intent startIntent = getIntent(); Intent startIntent = getIntent();
Uri startUri = startIntent.getData(); Uri startUri = startIntent.getData();
if (savedInstanceState != null) { if (savedInstanceState != null) {
boardLoadable.readFromBundle(this, "board", savedInstanceState); boardLoadable.readFromBundle(this, "board", savedInstanceState);
boardLoadable.no = 0; boardLoadable.no = 0;
boardLoadable.listViewIndex = 0; boardLoadable.listViewIndex = 0;
boardLoadable.listViewTop = 0; boardLoadable.listViewTop = 0;
threadLoadable.readFromBundle(this, "thread", savedInstanceState); threadLoadable.readFromBundle(this, "thread", savedInstanceState);
setNavigationFromBoardValue(boardLoadable.board); setNavigationFromBoardValue(boardLoadable.board);
startLoadingThread(threadLoadable); startLoadingThread(threadLoadable);
} else if (startUri != null) { } else if (startUri != null) {
@ -80,41 +80,41 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
getActionBar().setSelectedNavigationItem(0); getActionBar().setSelectedNavigationItem(0);
} }
} }
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
boardLoadable.writeToBundle(this, "board", outState); boardLoadable.writeToBundle(this, "board", outState);
threadLoadable.writeToBundle(this, "thread", outState); threadLoadable.writeToBundle(this, "thread", outState);
} }
@Override @Override
protected void onStart() { protected void onStart() {
super.onStart(); super.onStart();
PinnedService.onActivityStart(); PinnedService.onActivityStart();
} }
@Override @Override
protected void onStop() { protected void onStop() {
super.onStop(); super.onStop();
PinnedService.onActivityStop(); PinnedService.onActivityStop();
} }
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
PinnedManager.getInstance().updateAll(); PinnedManager.getInstance().updateAll();
} }
@Override @Override
protected void initDrawer() { 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) {}; R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) {};
super.initDrawer(); super.initDrawer();
} }
@ -124,16 +124,16 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
boardLoadable = new Loadable(BoardManager.getInstance().getMyBoardsValues().get(position)); boardLoadable = new Loadable(BoardManager.getInstance().getMyBoardsValues().get(position));
startLoadingBoard(boardLoadable); startLoadingBoard(boardLoadable);
} }
boardSetByIntent = false; boardSetByIntent = false;
return true; return true;
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu); super.onCreateOptionsMenu(menu);
return true; return true;
} }
@ -147,7 +147,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
@Override @Override
public void openPin(Pin pin) { public void openPin(Pin pin) {
startLoadingThread(pin.loadable); startLoadingThread(pin.loadable);
pinDrawer.closeDrawer(pinDrawerView); pinDrawer.closeDrawer(pinDrawerView);
} }
@ -161,7 +161,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
super.onPostCreate(savedInstanceState); super.onPostCreate(savedInstanceState);
pinDrawerListener.syncState(); pinDrawerListener.syncState();
} }
@Override @Override
public void onBackPressed() { public void onBackPressed() {
if (threadPane.isOpen()) { if (threadPane.isOpen()) {
@ -170,10 +170,16 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
threadPane.openPane(); threadPane.openPane();
} }
} }
@Override
public void updatePin(Pin pin) {
super.updatePin(pin);
updateActionBarState();
}
private void updateActionBarState() { private void updateActionBarState() {
final ActionBar actionBar = getActionBar(); final ActionBar actionBar = getActionBar();
if (threadPane.isSlideable()) { if (threadPane.isSlideable()) {
if (threadPane.isOpen()) { if (threadPane.isOpen()) {
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
@ -185,51 +191,51 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
actionBar.setTitle(threadLoadable.title); actionBar.setTitle(threadLoadable.title);
pinDrawerListener.setDrawerIndicatorEnabled(false); pinDrawerListener.setDrawerIndicatorEnabled(false);
} }
actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setDisplayHomeAsUpEnabled(true);
} else { } else {
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
pinDrawerListener.setDrawerIndicatorEnabled(true); pinDrawerListener.setDrawerIndicatorEnabled(true);
actionBar.setTitle(threadLoadable.title); actionBar.setTitle(threadLoadable.title);
actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setDisplayHomeAsUpEnabled(true);
} }
actionBar.setDisplayShowTitleEnabled(true); actionBar.setDisplayShowTitleEnabled(true);
invalidateOptionsMenu(); invalidateOptionsMenu();
} }
@Override @Override
public boolean onPrepareOptionsMenu(Menu menu) { public boolean onPrepareOptionsMenu(Menu menu) {
boolean open = threadPane.isOpen(); boolean open = threadPane.isOpen();
boolean slidable = threadPane.isSlideable(); boolean slidable = threadPane.isSlideable();
setMenuItemEnabled(menu.findItem(R.id.action_reload_board), slidable && open); 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_thread), slidable && !open);
setMenuItemEnabled(menu.findItem(R.id.action_reload_tablet), !slidable); setMenuItemEnabled(menu.findItem(R.id.action_reload_tablet), !slidable);
setMenuItemEnabled(menu.findItem(R.id.action_pin), !slidable || !open); setMenuItemEnabled(menu.findItem(R.id.action_pin), !slidable || !open);
setMenuItemEnabled(menu.findItem(R.id.action_reply), slidable); setMenuItemEnabled(menu.findItem(R.id.action_reply), slidable);
setMenuItemEnabled(menu.findItem(R.id.action_reply_tablet), !slidable); setMenuItemEnabled(menu.findItem(R.id.action_reply_tablet), !slidable);
return super.onPrepareOptionsMenu(menu); return super.onPrepareOptionsMenu(menu);
} }
private void setMenuItemEnabled(MenuItem item, boolean enabled) { private void setMenuItemEnabled(MenuItem item, boolean enabled) {
if (item != null) { if (item != null) {
item.setVisible(enabled); item.setVisible(enabled);
item.setEnabled(enabled); item.setEnabled(enabled);
} }
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (pinDrawerListener.onOptionsItemSelected(item)) { if (pinDrawerListener.onOptionsItemSelected(item)) {
return true; return true;
} }
switch(item.getItemId()) { switch(item.getItemId()) {
case R.id.action_reload_board: case R.id.action_reload_board:
case R.id.action_reload_tablet_board: case R.id.action_reload_tablet_board:
@ -248,22 +254,22 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
return true; return true;
case R.id.action_reply_board: case R.id.action_reply_board:
boardFragment.openReply(); boardFragment.openReply();
return true; return true;
case R.id.action_reply_thread: case R.id.action_reply_thread:
threadFragment.openReply(); threadFragment.openReply();
return true; return true;
case R.id.action_pin: case R.id.action_pin:
if (threadFragment.hasLoader()) { if (threadFragment.hasLoader()) {
Pin pin = new Pin(); Pin pin = new Pin();
pin.loadable = threadLoadable; pin.loadable = threadLoadable;
addPin(pin); addPin(pin);
pinDrawer.openDrawer(pinDrawerView); pinDrawer.openDrawer(pinDrawerView);
} }
return true; return true;
case R.id.action_open_browser: case R.id.action_open_browser:
if (threadPane.isOpen()) { if (threadPane.isOpen()) {
@ -273,14 +279,14 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
showUrlOpenPicker(ChanUrls.getThreadUrlDesktop(threadLoadable.board, threadLoadable.no)); showUrlOpenPicker(ChanUrls.getThreadUrlDesktop(threadLoadable.board, threadLoadable.no));
} }
} }
return true; return true;
case android.R.id.home: case android.R.id.home:
threadPane.openPane(); threadPane.openPane();
return true; return true;
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@ -293,63 +299,63 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
public void onPanelOpened(View view) { public void onPanelOpened(View view) {
updateActionBarState(); updateActionBarState();
} }
private void startLoadingBoard(Loadable loadable) { private void startLoadingBoard(Loadable loadable) {
if (loadable.mode == Loadable.Mode.INVALID) return; if (loadable.mode == Loadable.Mode.INVALID) return;
this.boardLoadable = loadable; boardLoadable = loadable;
boardFragment.bindLoadable(loadable); boardFragment.bindLoadable(loadable);
boardFragment.requestData(); boardFragment.requestData();
setShareUrl(ChanUrls.getBoardUrlDesktop(loadable.board)); setShareUrl(ChanUrls.getBoardUrlDesktop(loadable.board));
updateActionBarState(); updateActionBarState();
} }
private void startLoadingThread(Loadable loadable) { private void startLoadingThread(Loadable loadable) {
if (loadable.mode == Loadable.Mode.INVALID) return; if (loadable.mode == Loadable.Mode.INVALID) return;
Pin pin = PinnedManager.getInstance().findPinByLoadable(loadable); Pin pin = PinnedManager.getInstance().findPinByLoadable(loadable);
if (pin != null) { if (pin != null) {
// Use the loadable from the pin. // 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. // and not in a separate loadable instance.
loadable = pin.loadable; loadable = pin.loadable;
} }
threadLoadable = loadable; threadLoadable = loadable;
threadFragment.bindLoadable(loadable); threadFragment.bindLoadable(loadable);
threadFragment.requestData(); threadFragment.requestData();
setShareUrl(ChanUrls.getThreadUrlDesktop(loadable.board, loadable.no)); setShareUrl(ChanUrls.getThreadUrlDesktop(loadable.board, loadable.no));
if (TextUtils.isEmpty(loadable.title)) { if (TextUtils.isEmpty(loadable.title)) {
loadable.title = "/" + loadable.board + "/" + loadable.no; loadable.title = "/" + loadable.board + "/" + loadable.no;
} }
threadPane.closePane(); threadPane.closePane();
updateActionBarState(); updateActionBarState();
} }
/** /**
* Handle opening from an external url. * Handle opening from an external url.
* @param startUri * @param startUri
*/ */
private void handleIntentURI(Uri startUri) { private void handleIntentURI(Uri startUri) {
List<String> parts = startUri.getPathSegments(); List<String> parts = startUri.getPathSegments();
if (parts.size() == 1) { if (parts.size() == 1) {
// Board mode // Board mode
String rawBoard = parts.get(0); String rawBoard = parts.get(0);
if (BoardManager.getInstance().getBoardExists(rawBoard)) { if (BoardManager.getInstance().getBoardExists(rawBoard)) {
boardSetByIntent = true; boardSetByIntent = true;
startLoadingBoard(new Loadable(rawBoard)); startLoadingBoard(new Loadable(rawBoard));
ActionBar actionBar = getActionBar(); ActionBar actionBar = getActionBar();
if (!setNavigationFromBoardValue(rawBoard)) { if (!setNavigationFromBoardValue(rawBoard)) {
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
@ -357,7 +363,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
String value = BoardManager.getInstance().getBoardKey(rawBoard); String value = BoardManager.getInstance().getBoardKey(rawBoard);
actionBar.setTitle(value == null ? ("/" + rawBoard + "/") : value); actionBar.setTitle(value == null ? ("/" + rawBoard + "/") : value);
} }
} else { } else {
handleIntentURIFallback(startUri.toString()); 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 // First load a board and then start another activity opening the thread
String rawBoard = parts.get(0); String rawBoard = parts.get(0);
int no = -1; int no = -1;
try { try {
no = Integer.parseInt(parts.get(2)); no = Integer.parseInt(parts.get(2));
} catch (NumberFormatException e) {} } catch (NumberFormatException e) {}
if (no >= 0 && BoardManager.getInstance().getBoardExists(rawBoard)) { if (no >= 0 && BoardManager.getInstance().getBoardExists(rawBoard)) {
boardSetByIntent = true; boardSetByIntent = true;
startLoadingBoard(new Loadable(rawBoard)); startLoadingBoard(new Loadable(rawBoard));
startLoadingThread(new Loadable(rawBoard, no)); startLoadingThread(new Loadable(rawBoard, no));
} else { } else {
@ -384,7 +390,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
showUrlOpenPicker(startUri.toString()); showUrlOpenPicker(startUri.toString());
} }
} }
private void handleIntentURIFallback(final String url) { private void handleIntentURIFallback(final String url) {
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setTitle(R.string.open_unknown_title) .setTitle(R.string.open_unknown_title)
@ -407,7 +413,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
.create() .create()
.show(); .show();
} }
/** /**
* Set the visual selector to the board. If the user has not set the board as a favorite, * Set the visual selector to the board. If the user has not set the board as a favorite,
* return false. * return false.
@ -423,7 +429,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
break; break;
} }
} }
if (foundIndex >= 0) { if (foundIndex >= 0) {
getActionBar().setSelectedNavigationItem(foundIndex); getActionBar().setSelectedNavigationItem(foundIndex);
return true; return true;

@ -19,40 +19,38 @@ import android.widget.TextView;
public class PinnedAdapter extends ArrayAdapter<Pin> { public class PinnedAdapter extends ArrayAdapter<Pin> {
private final HashMap<Pin, Integer> idMap; private final HashMap<Pin, Integer> idMap;
private int idCounter; private int idCounter;
private View.OnTouchListener listener;
public PinnedAdapter(Context context, int resId) { public PinnedAdapter(Context context, int resId) {
super(context, resId, new ArrayList<Pin>()); super(context, resId, new ArrayList<Pin>());
idMap = new HashMap<Pin, Integer>(); idMap = new HashMap<Pin, Integer>();
} }
public void setTouchListener(View.OnTouchListener listener) {
this.listener = listener;
}
@Override @Override
public View getView(int position, View convertView, ViewGroup parent) { public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout view = null; LinearLayout view = null;
Pin item = getItem(position); Pin item = getItem(position);
if (item.type == Pin.Type.HEADER) { if (item.type == Pin.Type.HEADER) {
view = (LinearLayout) inflater.inflate(R.layout.pin_item_header, null); view = (LinearLayout) inflater.inflate(R.layout.pin_item_header, null);
((TextView) view.findViewById(R.id.drawer_item_header)).setText(R.string.drawer_pinned); ((TextView) view.findViewById(R.id.drawer_item_header)).setText(R.string.drawer_pinned);
} else { } else {
view = (LinearLayout) inflater.inflate(R.layout.pin_item, null); view = (LinearLayout) inflater.inflate(R.layout.pin_item, null);
((TextView) view.findViewById(R.id.drawer_item_text)).setText(item.loadable.title); ((TextView) view.findViewById(R.id.drawer_item_text)).setText(item.loadable.title);
int count = Math.max(0, item.watchNewCount - item.watchLastCount); int count = item.getNewPostCount();
String total = Integer.toString(Math.min(count, 999)); String total = Integer.toString(count);
if (count > 999) {
total = "1k+";
}
((TextView) view.findViewById(R.id.drawer_item_count)).setText(total); ((TextView) view.findViewById(R.id.drawer_item_count)).setText(total);
FrameLayout frameLayout = (FrameLayout) view.findViewById(R.id.drawer_item_count_container); FrameLayout frameLayout = (FrameLayout) view.findViewById(R.id.drawer_item_count_container);
// if (Math.random() < 0.5d) { // if (Math.random() < 0.5d) {
frameLayout.setBackgroundResource(R.drawable.pin_icon_blue); frameLayout.setBackgroundResource(R.drawable.pin_icon_blue);
@ -60,38 +58,36 @@ public class PinnedAdapter extends ArrayAdapter<Pin> {
// frameLayout.setBackgroundResource(R.drawable.pin_icon_red); // frameLayout.setBackgroundResource(R.drawable.pin_icon_red);
// } // }
} }
if (listener != null) {
view.setOnTouchListener(listener);
}
return view; return view;
} }
public void reload() { public void reload() {
clear(); clear();
Pin header = new Pin(); Pin header = new Pin();
header.type = Pin.Type.HEADER; header.type = Pin.Type.HEADER;
add(header); add(header);
addAll(PinnedManager.getInstance().getPins()); addAll(PinnedManager.getInstance().getPins());
notifyDataSetChanged();
} }
@Override @Override
public void remove(Pin item) { public void remove(Pin item) {
super.remove(item); super.remove(item);
idMap.remove(item); idMap.remove(item);
notifyDataSetChanged(); notifyDataSetChanged();
} }
@Override @Override
public void add(Pin item) { public void add(Pin item) {
idMap.put(item, ++idCounter); idMap.put(item, ++idCounter);
super.add(item); super.add(item);
notifyDataSetChanged(); notifyDataSetChanged();
} }
@Override @Override
public boolean hasStableIds() { public boolean hasStableIds() {
return true; return true;
@ -100,7 +96,7 @@ public class PinnedAdapter extends ArrayAdapter<Pin> {
@Override @Override
public long getItemId(int position) { public long getItemId(int position) {
if (position < 0 || position >= getCount()) return -1; if (position < 0 || position >= getCount()) return -1;
Pin item = getItem(position); Pin item = getItem(position);
if (item == null) { if (item == null) {
return -1; return -1;

@ -28,6 +28,7 @@ public class PostAdapter extends BaseAdapter {
private boolean endOfLine; private boolean endOfLine;
private int count = 0; private int count = 0;
private final List<Post> postList = new ArrayList<Post>(); private final List<Post> postList = new ArrayList<Post>();
private long lastViewedTime = 0;
public PostAdapter(Context activity, ThreadManager threadManager, ListView listView) { public PostAdapter(Context activity, ThreadManager threadManager, ListView listView) {
context = activity; context = activity;
@ -62,6 +63,11 @@ public class PostAdapter extends BaseAdapter {
} }
if (position >= count) { if (position >= count) {
if (System.currentTimeMillis() - lastViewedTime > 10000L) {
lastViewedTime = System.currentTimeMillis();
threadManager.bottomPostViewed();
}
return createThreadEndView(); return createThreadEndView();
} else { } else {
PostView postView = null; PostView postView = null;

@ -9,6 +9,7 @@ import org.floens.chan.model.Post;
import org.floens.chan.utils.Logger; import org.floens.chan.utils.Logger;
import android.os.Handler; import android.os.Handler;
import android.os.Looper;
import android.util.SparseArray; import android.util.SparseArray;
import com.android.volley.Response; import com.android.volley.Response;
@ -17,26 +18,26 @@ import com.android.volley.VolleyError;
public class Loader { public class Loader {
private static final String TAG = "Loader"; private static final String TAG = "Loader";
private static final Handler handler = new Handler(); private static final Handler handler = new Handler(Looper.getMainLooper());
private static final int[] watchTimeouts = {10, 15, 20, 30, 60, 90, 120, 180, 240, 300}; private static final int[] watchTimeouts = {5, 10, 15, 20, 30, 60, 90, 120, 180, 240, 300};
private final List<LoaderListener> listeners = new ArrayList<LoaderListener>(); private final List<LoaderListener> listeners = new ArrayList<LoaderListener>();
private final Loadable loadable; private final Loadable loadable;
private final SparseArray<Post> postsById = new SparseArray<Post>(); private final SparseArray<Post> postsById = new SparseArray<Post>();
private boolean destroyed = false; private boolean destroyed = false;
private ChanReaderRequest request; private ChanReaderRequest request;
private int currentTimeout; private int currentTimeout;
private int lastPostCount; private int lastPostCount;
private long lastLoadTime; private long lastLoadTime;
private Runnable pendingRunnable; private Runnable pendingRunnable;
public Loader(Loadable loadable) { public Loader(Loadable loadable) {
this.loadable = loadable; this.loadable = loadable;
} }
/** /**
* Add a LoaderListener * Add a LoaderListener
* @param l the listener to add * @param l the listener to add
@ -44,7 +45,7 @@ public class Loader {
public void addListener(LoaderListener l) { public void addListener(LoaderListener l) {
listeners.add(l); listeners.add(l);
} }
/** /**
* Remove a LoaderListener * Remove a LoaderListener
* @param l the listener to remove * @param l the listener to remove
@ -60,81 +61,87 @@ public class Loader {
return false; return false;
} }
} }
public void requestData() { public void requestData() {
if (request != null) { if (request != null) {
request.cancel(); request.cancel();
} }
if (loadable.isBoardMode()) { if (loadable.isBoardMode()) {
loadable.no = 0; loadable.no = 0;
loadable.listViewIndex = 0; loadable.listViewIndex = 0;
loadable.listViewTop = 0; loadable.listViewTop = 0;
} }
currentTimeout = 0;
request = getData(loadable); request = getData(loadable);
} }
public void requestNextData() { public void requestNextData() {
if (loadable.isBoardMode()) { if (loadable.isBoardMode()) {
loadable.no++; loadable.no++;
if (request != null) { if (request != null) {
request.cancel(); request.cancel();
} }
request = getData(loadable); request = getData(loadable);
} else if (loadable.isThreadMode()) { } else if (loadable.isThreadMode()) {
if (request != null) { if (request != null) {
return; return;
} }
clearTimer(); clearTimer();
request = getData(loadable); request = getData(loadable);
} }
} }
public void requestNextDataResetTimer() { public void requestNextDataResetTimer() {
currentTimeout = 0; currentTimeout = 0;
requestNextData(); requestNextData();
} }
/** /**
* @return Returns if this loader is currently loading * @return Returns if this loader is currently loading
*/ */
public boolean isLoading() { public boolean isLoading() {
return request != null; return request != null;
} }
public Post findPostById(int id) { public Post findPostById(int id) {
return postsById.get(id); return postsById.get(id);
} }
public Loadable getLoadable() { public Loadable getLoadable() {
return loadable; return loadable;
} }
public void onStart() { public void onStart() {
if (loadable.isThreadMode()) { if (loadable.isThreadMode()) {
requestNextDataResetTimer(); requestNextDataResetTimer();
} }
} }
public void onStop() { public void onStop() {
clearTimer(); clearTimer();
} }
public long getTimeUntilReload() { public long getTimeUntilReload() {
long waitTime = watchTimeouts[currentTimeout] * 1000L; if (request != null) {
return lastLoadTime + waitTime - System.currentTimeMillis(); return 0L;
} else {
long waitTime = watchTimeouts[currentTimeout] * 1000L;
return lastLoadTime + waitTime - System.currentTimeMillis();
}
} }
private void setTimer(int postCount) { private void setTimer(int postCount) {
if (pendingRunnable != null) { if (pendingRunnable != null) {
clearTimer(); clearTimer();
} }
if (pendingRunnable == null) { if (pendingRunnable == null) {
pendingRunnable = new Runnable() { pendingRunnable = new Runnable() {
@Override @Override
@ -142,36 +149,35 @@ public class Loader {
pendingRunnableCallback(); pendingRunnableCallback();
}; };
}; };
lastPostCount = postCount;
if (postCount > lastPostCount) { if (postCount > lastPostCount) {
currentTimeout = 0; currentTimeout = 0;
} else { } else {
currentTimeout = Math.min(watchTimeouts.length - 1, currentTimeout + 1); currentTimeout = Math.min(watchTimeouts.length - 1, currentTimeout + 1);
} }
lastPostCount = postCount;
handler.postDelayed(pendingRunnable, watchTimeouts[currentTimeout] * 1000L); handler.postDelayed(pendingRunnable, watchTimeouts[currentTimeout] * 1000L);
} }
} }
private void clearTimer() { private void clearTimer() {
if (pendingRunnable != null) { if (pendingRunnable != null) {
handler.removeCallbacks(pendingRunnable); handler.removeCallbacks(pendingRunnable);
pendingRunnable = null; pendingRunnable = null;
lastLoadTime = 0;
} }
} }
private void pendingRunnableCallback() { private void pendingRunnableCallback() {
Logger.d(TAG, "pending callback"); Logger.d(TAG, "pending callback");
pendingRunnable = null; pendingRunnable = null;
requestNextData(); requestNextData();
} }
private ChanReaderRequest getData(Loadable loadable) { private ChanReaderRequest getData(Loadable loadable) {
Logger.i(TAG, "Requested " + loadable.board + ", " + loadable.no); Logger.i(TAG, "Requested " + loadable.board + ", " + loadable.no);
ChanReaderRequest request = ChanReaderRequest.newInstance(loadable, new Response.Listener<List<Post>>() { ChanReaderRequest request = ChanReaderRequest.newInstance(loadable, new Response.Listener<List<Post>>() {
@Override @Override
public void onResponse(List<Post> list) { public void onResponse(List<Post> list) {
@ -185,47 +191,49 @@ public class Loader {
onError(error); onError(error);
} }
}); });
ChanApplication.getVolleyRequestQueue().add(request); ChanApplication.getVolleyRequestQueue().add(request);
return request; return request;
} }
private void onData(List<Post> result) { private void onData(List<Post> result) {
if (destroyed) return; if (destroyed) return;
Logger.test("ondata in loader");
postsById.clear(); postsById.clear();
for (Post post : result) { for (Post post : result) {
postsById.append(post.no, post); postsById.append(post.no, post);
} }
for (LoaderListener l : listeners) { for (LoaderListener l : listeners) {
l.onData(result, loadable.isBoardMode()); l.onData(result, loadable.isBoardMode());
} }
lastLoadTime = System.currentTimeMillis(); lastLoadTime = System.currentTimeMillis();
if (loadable.isThreadMode()) { if (loadable.isThreadMode()) {
setTimer(result.size()); setTimer(result.size());
} }
} }
private void onError(VolleyError error) { private void onError(VolleyError error) {
if (destroyed) return; if (destroyed) return;
Logger.e(TAG, "Error loading " + error.getMessage(), error); Logger.e(TAG, "Error loading " + error.getMessage(), error);
// 404 with more pages already loaded means endofline // 404 with more pages already loaded means endofline
if ((error instanceof ServerError) && loadable.isBoardMode() && loadable.no > 0) { if ((error instanceof ServerError) && loadable.isBoardMode() && loadable.no > 0) {
error = new EndOfLineException(); error = new EndOfLineException();
} }
for (LoaderListener l : listeners) { for (LoaderListener l : listeners) {
l.onError(error); l.onError(error);
} }
clearTimer(); clearTimer();
} }
public static interface LoaderListener { public static interface LoaderListener {
public void onData(List<Post> result, boolean append); public void onData(List<Post> result, boolean append);
public void onError(VolleyError error); public void onError(VolleyError error);

@ -11,27 +11,27 @@ import android.content.Context;
public class PinnedManager { public class PinnedManager {
private static PinnedManager instance; private static PinnedManager instance;
private final List<PinListener> listeners = new ArrayList<PinListener>(); private final List<PinListener> listeners = new ArrayList<PinListener>();
private final List<Pin> pins; private final List<Pin> pins;
public PinnedManager(Context context) { public PinnedManager(Context context) {
instance = this; instance = this;
pins = DatabaseManager.getInstance().getPinned(); pins = DatabaseManager.getInstance().getPinned();
} }
public static PinnedManager getInstance() { public static PinnedManager getInstance() {
return instance; return instance;
} }
public void addPinListener(PinListener l) { public void addPinListener(PinListener l) {
listeners.add(l); listeners.add(l);
} }
public void removePinListener(PinListener l) { public void removePinListener(PinListener l) {
listeners.remove(l); listeners.remove(l);
} }
/** /**
* Look for a pin that has an loadable that is equal to the supplied loadable. * Look for a pin that has an loadable that is equal to the supplied loadable.
* @param other * @param other
@ -43,14 +43,14 @@ public class PinnedManager {
return pin; return pin;
} }
} }
return null; return null;
} }
public List<Pin> getPins() { public List<Pin> getPins() {
return pins; return pins;
} }
/** /**
* Add a pin * Add a pin
* @param pin * @param pin
@ -63,15 +63,15 @@ public class PinnedManager {
return false; return false;
} }
} }
pins.add(pin); pins.add(pin);
DatabaseManager.getInstance().addPin(pin); DatabaseManager.getInstance().addPin(pin);
onPinsChanged(); onPinsChanged();
return true; return true;
} }
/** /**
* Remove a pin * Remove a pin
* @param pin * @param pin
@ -79,22 +79,23 @@ public class PinnedManager {
public void remove(Pin pin) { public void remove(Pin pin) {
pins.remove(pin); pins.remove(pin);
DatabaseManager.getInstance().removePin(pin); DatabaseManager.getInstance().removePin(pin);
pin.destroy();
onPinsChanged(); onPinsChanged();
} }
/** /**
* Update the pin in the database * Update the pin in the database
* @param pin * @param pin
*/ */
public void update(Pin pin) { public void update(Pin pin) {
DatabaseManager.getInstance().updatePin(pin); DatabaseManager.getInstance().updatePin(pin);
onPinsChanged(); 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 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) * (this will be an huge headache later on when we get concurrent problems)
*/ */
@ -106,19 +107,19 @@ public class PinnedManager {
} }
}).start(); }).start();
} }
public void onPinViewed(Pin pin) { public void onPinViewed(Pin pin) {
pin.watchLastCount = pin.watchNewCount; pin.onViewed();
onPinsChanged(); onPinsChanged();
} }
public void onPinsChanged() { public void onPinsChanged() {
for (PinListener l : listeners) { for (PinListener l : listeners) {
l.onPinsChanged(); l.onPinsChanged();
} }
} }
public static interface PinListener { public static interface PinListener {
public void onPinsChanged(); public void onPinsChanged();
} }

@ -13,6 +13,7 @@ import org.floens.chan.loader.LoaderPool;
import org.floens.chan.manager.ReplyManager.DeleteListener; import org.floens.chan.manager.ReplyManager.DeleteListener;
import org.floens.chan.manager.ReplyManager.DeleteResponse; import org.floens.chan.manager.ReplyManager.DeleteResponse;
import org.floens.chan.model.Loadable; import org.floens.chan.model.Loadable;
import org.floens.chan.model.Pin;
import org.floens.chan.model.Post; import org.floens.chan.model.Post;
import org.floens.chan.model.PostLinkable; import org.floens.chan.model.PostLinkable;
import org.floens.chan.model.SavedReply; import org.floens.chan.model.SavedReply;
@ -93,6 +94,15 @@ public class ThreadManager implements Loader.LoaderListener {
highlightedPost = null; 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() { public void requestData() {
if (loader != null) { if (loader != null) {
loader.requestData(); loader.requestData();
@ -398,7 +408,7 @@ public class ThreadManager implements Loader.LoaderListener {
FragmentTransaction ft = activity.getFragmentManager().beginTransaction(); FragmentTransaction ft = activity.getFragmentManager().beginTransaction();
ft.add(popup, "postPopup"); ft.add(popup, "postPopup");
ft.commit(); ft.commitAllowingStateLoss();
currentPopupFragment = popup; currentPopupFragment = popup;
} }

@ -10,33 +10,51 @@ public class Pin {
// Database stuff // Database stuff
@DatabaseField(generatedId = true) @DatabaseField(generatedId = true)
private int id; private int id;
@DatabaseField(canBeNull = false, foreign = true) @DatabaseField(canBeNull = false, foreign = true)
public Loadable loadable = new Loadable("", -1); public Loadable loadable = new Loadable("", -1);
// ListView Stuff // ListView Stuff
/** Header is used to display a static header in the drawer listview. */ /** Header is used to display a static header in the drawer listview. */
public Type type = Type.THREAD; public Type type = Type.THREAD;
public static enum Type { public static enum Type {
HEADER, HEADER,
THREAD THREAD
}; };
// PinnedService stuff // PinnedService stuff
public PinWatcher pinWatcher; public PinWatcher pinWatcher;
@DatabaseField @DatabaseField
public int watchLastCount; public int watchLastCount;
@DatabaseField @DatabaseField
public int watchNewCount; public int watchNewCount;
public void updateWatch() { public void updateWatch() {
if (pinWatcher == null) { 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();
} }
} }

@ -2,13 +2,11 @@ package org.floens.chan.service;
import java.util.List; import java.util.List;
import org.floens.chan.R;
import org.floens.chan.manager.PinnedManager; import org.floens.chan.manager.PinnedManager;
import org.floens.chan.model.Pin; import org.floens.chan.model.Pin;
import org.floens.chan.utils.Logger; 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.app.Service;
import android.content.Intent; import android.content.Intent;
import android.os.Handler; import android.os.Handler;
@ -23,6 +21,7 @@ public class PinnedService extends Service {
private Thread loadThread; private Thread loadThread;
private boolean running = true; private boolean running = true;
private final WatchNotifier watchNotifier;
public static void onActivityStart() { public static void onActivityStart() {
Logger.test("onActivityStart"); Logger.test("onActivityStart");
@ -34,6 +33,10 @@ public class PinnedService extends Service {
activityInForeground = false; activityInForeground = false;
} }
public PinnedService() {
watchNotifier = new WatchNotifier(this);
}
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
@ -49,37 +52,39 @@ public class PinnedService extends Service {
} }
private void start() { private void start() {
loadThread = new Thread(new Runnable() { running = true;
@Override
public void run() { if (loadThread == null) {
try { loadThread = new Thread(new Runnable() {
Thread.sleep(2000); @Override
} catch (InterruptedException e1) { public void run() {
e1.printStackTrace(); while (running) {
} update();
while (running) { long timeout = activityInForeground ? FOREGROUND_INTERVAL : BACKGROUND_INTERVAL;
doUpdates();
try {
long timeout = activityInForeground ? FOREGROUND_INTERVAL : BACKGROUND_INTERVAL; Thread.sleep(timeout);
} catch (InterruptedException e) {
try { e.printStackTrace();
Thread.sleep(timeout); }
} catch (InterruptedException e) {
e.printStackTrace();
} }
loadThread = null;
} }
} });
});
loadThread.start(); loadThread.start();
}
} }
private void doUpdates() { private void update() {
List<Pin> pins = PinnedManager.getInstance().getPins(); List<Pin> pins = PinnedManager.getInstance().getPins();
for (Pin pin : pins) { for (Pin pin : pins) {
pin.updateWatch(); pin.updateWatch();
} }
watchNotifier.update();
} }
public static void callOnPinsChanged() { 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 @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {

@ -3,6 +3,7 @@ package org.floens.chan.watch;
import java.util.List; import java.util.List;
import org.floens.chan.loader.Loader; import org.floens.chan.loader.Loader;
import org.floens.chan.loader.LoaderPool;
import org.floens.chan.model.Pin; import org.floens.chan.model.Pin;
import org.floens.chan.model.Post; import org.floens.chan.model.Post;
import org.floens.chan.service.PinnedService; import org.floens.chan.service.PinnedService;
@ -12,44 +13,63 @@ import com.android.volley.VolleyError;
public class PinWatcher implements Loader.LoaderListener { public class PinWatcher implements Loader.LoaderListener {
private static final String TAG = "PinWatcher"; private static final String TAG = "PinWatcher";
private final Pin pin; private final Pin pin;
private Loader loader; private final Loader loader;
private long startTime; private long startTime;
private boolean isError = false;
public PinWatcher(Pin pin) { public PinWatcher(Pin pin) {
this.pin = pin; this.pin = pin;
// loader = new Loader(this); loader = LoaderPool.getInstance().obtain(pin.loadable, this);
} }
public void destroy() { 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 @Override
public void onError(VolleyError error) { public void onError(VolleyError error) {
Logger.test("PinWatcher onError: ", error); Logger.test("PinWatcher onError: ", error);
isError = true;
} }
@Override @Override
public void onData(List<Post> result, boolean append) { public void onData(List<Post> result, boolean append) {
int count = result.size(); int count = result.size();
Logger.test("PinWatcher onData"); Logger.test("PinWatcher onData, Post size: " + count);
Logger.test("Post size: " + count);
if (pin.watchLastCount <= 0) { if (pin.watchLastCount <= 0) {
pin.watchLastCount = pin.watchNewCount; pin.watchLastCount = pin.watchNewCount;
} }
pin.watchNewCount = count; pin.watchNewCount = count;
Logger.test("Load time: " + (System.currentTimeMillis() - startTime) + "ms");
PinnedService.callOnPinsChanged(); PinnedService.callOnPinsChanged();
} }
} }

@ -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());
}
}
Loading…
Cancel
Save