Threads hava auto reload now. The bottom reload view is still a bit buggy.

captchafix
Florens Douwes 12 years ago
parent 2c5cd1bedb
commit c62c353f8d
  1. 21
      Chan/AndroidManifest.xml
  2. 2
      Chan/res/layout/activity_base.xml
  3. 4
      Chan/src/org/floens/chan/ChanApplication.java
  4. 27
      Chan/src/org/floens/chan/activity/BaseActivity.java
  5. 8
      Chan/src/org/floens/chan/activity/BoardActivity.java
  6. 11
      Chan/src/org/floens/chan/adapter/PinnedAdapter.java
  7. 66
      Chan/src/org/floens/chan/adapter/PostAdapter.java
  8. 24
      Chan/src/org/floens/chan/fragment/ThreadFragment.java
  9. 13
      Chan/src/org/floens/chan/manager/PinnedManager.java
  10. 75
      Chan/src/org/floens/chan/manager/ThreadManager.java
  11. 17
      Chan/src/org/floens/chan/view/PostView.java
  12. 77
      Chan/src/org/floens/chan/view/ThreadWatchCounterView.java
  13. 2
      Chan/src/org/floens/chan/watch/PinWatcher.java
  14. 114
      Chan/src/org/floens/chan/watch/WatchLogic.java

@ -28,10 +28,11 @@
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.nfc.action.NDEF_DISCOVERED"></action>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="4chan.org" android:scheme="http" android:pathPrefix="/" />
<data android:host="4chan.org" android:scheme="http" android:pathPrefix="/" />
<data android:host="4chan.org" android:scheme="https" android:pathPrefix="/" />
<data android:host="www.4chan.org" android:scheme="http" android:pathPrefix="/" />
<data android:host="www.4chan.org" android:scheme="https" android:pathPrefix="/" />
@ -52,25 +53,25 @@
android:name="org.floens.chan.activity.SettingsActivity"
android:label="@string/action_settings"
android:parentActivityName="org.floens.chan.activity.BoardActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.floens.chan.activity.BoardActivity" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.floens.chan.activity.BoardActivity" />
</activity>
<activity
android:name="org.floens.chan.activity.AboutActivity"
android:label="@string/about"
android:parentActivityName="org.floens.chan.activity.BoardActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.floens.chan.activity.BoardActivity" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.floens.chan.activity.BoardActivity" />
</activity>
<activity
android:name="org.floens.chan.activity.BoardEditor"
android:label="@string/board_edit"
android:parentActivityName="org.floens.chan.activity.BoardActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.floens.chan.activity.BoardActivity" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.floens.chan.activity.BoardActivity" />
</activity>
<activity
android:name="org.floens.chan.imageview.activity.ImageViewActivity"

@ -17,7 +17,7 @@
<FrameLayout
android:id="@+id/right_pane"
android:layout_width="340dp"
android:layout_width="320dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#FFE5E5E5" />

@ -4,11 +4,9 @@ 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;
@ -78,7 +76,7 @@ public class ChanApplication extends Application {
new PinnedManager(this);
new ReplyManager(this);
startService(new Intent(this, PinnedService.class));
// startService(new Intent(this, PinnedService.class));
}
}

@ -82,6 +82,14 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
PinnedManager.getInstance().removePinListener(this);
}
private void initPane() {
threadPane.setPanelSlideListener(this);
threadPane.setParallaxDistance(200);
threadPane.setShadowResource(R.drawable.panel_shadow);
threadPane.setSliderFadeColor(0xcce5e5e5);
threadPane.openPane();
}
protected void initDrawer() {
if (pinDrawerListener == null) {
return;
@ -92,7 +100,8 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
pinDrawerView = (ListView)findViewById(R.id.left_drawer);
pinnedAdapter = PinnedManager.getInstance().getAdapter();
pinnedAdapter = new PinnedAdapter(getActionBar().getThemedContext(), 0); // Get the dark theme, not the light one
pinnedAdapter.reload();
pinDrawerView.setAdapter(pinnedAdapter);
pinDrawerView.setOnItemClickListener(new OnItemClickListener() {
@ -135,33 +144,21 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene
pinDrawerView.setOnScrollListener(touchListener.makeScrollListener());
}
private void initPane() {
threadPane.setPanelSlideListener(this);
threadPane.setParallaxDistance(200);
threadPane.setShadowResource(R.drawable.panel_shadow);
threadPane.setSliderFadeColor(0xcce5e5e5);
threadPane.openPane();
}
@Override
public void onPinsChanged() {
pinnedAdapter.notifyDataSetChanged();
pinnedAdapter.reload();
}
public void addPin(Pin pin) {
if (PinnedManager.getInstance().add(pin)) {
pinnedAdapter.add(pin);
}
PinnedManager.getInstance().add(pin);
}
public void removePin(Pin pin) {
PinnedManager.getInstance().remove(pin);
pinnedAdapter.remove(pin);
}
public void updatePin(Pin pin) {
PinnedManager.getInstance().update(pin);
pinnedAdapter.notifyDataSetChanged();
}
private void changePinTitle(final Pin pin) {

@ -171,7 +171,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
pinDrawerListener.setDrawerIndicatorEnabled(true);
actionBar.setTitle(threadLoadable.title);
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setDisplayHomeAsUpEnabled(true);
}
actionBar.setDisplayShowTitleEnabled(true);
@ -183,7 +183,7 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
public boolean onPrepareOptionsMenu(Menu menu) {
boolean open = threadPane.isOpen();
setMenuItemEnabled(menu.findItem(R.id.action_pin), !open);
setMenuItemEnabled(menu.findItem(R.id.action_pin), !threadPane.isSlideable() || !open);
return super.onPrepareOptionsMenu(menu);
}
@ -306,9 +306,9 @@ public class BoardActivity extends BaseActivity implements ActionBar.OnNavigatio
String rawBoard = parts.get(0);
if (BoardManager.getInstance().getBoardExists(rawBoard)) {
boardLoadable.board = rawBoard;
boardSetByIntent = true;
startLoadingBoard(boardLoadable);
startLoadingBoard(new Loadable(rawBoard));
ActionBar actionBar = getActionBar();
if (!setNavigationFromBoardValue(rawBoard)) {

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import org.floens.chan.R;
import org.floens.chan.manager.PinnedManager;
import org.floens.chan.model.Pin;
import android.content.Context;
@ -67,6 +68,16 @@ public class PinnedAdapter extends ArrayAdapter<Pin> {
return view;
}
public void reload() {
clear();
Pin header = new Pin();
header.type = Pin.Type.HEADER;
add(header);
addAll(PinnedManager.getInstance().getPins());
}
@Override
public void remove(Pin item) {
super.remove(item);

@ -7,9 +7,11 @@ import org.floens.chan.R;
import org.floens.chan.manager.ThreadManager;
import org.floens.chan.model.Post;
import org.floens.chan.view.PostView;
import org.floens.chan.view.ThreadWatchCounterView;
import android.content.Context;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
@ -19,7 +21,7 @@ import android.widget.TextView;
public class PostAdapter extends BaseAdapter {
private final Context activity;
private final Context context;
private final ThreadManager threadManager;
private final ListView listView;
private boolean endOfLine;
@ -27,14 +29,14 @@ public class PostAdapter extends BaseAdapter {
private final List<Post> postList = new ArrayList<Post>();
public PostAdapter(Context activity, ThreadManager threadManager, ListView listView) {
this.activity = activity;
this.context = activity;
this.threadManager = threadManager;
this.listView = listView;
}
@Override
public int getCount() {
if (threadManager.getLoadable().isBoardMode()) {
if (threadManager.getLoadable().isBoardMode() || threadManager.getLoadable().isThreadMode()) {
return count + 1;
} else {
return count;
@ -53,21 +55,13 @@ public class PostAdapter extends BaseAdapter {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (position >= getCount() - 1 && !endOfLine) {
if (position >= getCount() - 1 && !endOfLine && threadManager.getLoadable().isBoardMode()) {
// Try to load more posts
threadManager.loadMore();
}
if (position >= count) {
if (endOfLine) {
TextView textView = new TextView(activity);
textView.setText(activity.getString(R.string.end_of_line));
int padding = activity.getResources().getDimensionPixelSize(R.dimen.general_padding);
textView.setPadding(padding, padding, padding, padding);
return textView;
} else {
return new ProgressBar(activity);
}
return createThreadEndView();
} else {
PostView postView = null;
@ -75,22 +69,58 @@ public class PostAdapter extends BaseAdapter {
if (convertView != null && convertView instanceof PostView) {
postView = (PostView) convertView;
} else {
postView = new PostView(activity);
postView = new PostView(context);
}
postView.setPost(postList.get(position), threadManager);
} else {
Log.e("Chan", "PostAdapter: Invalid index: " + position + ", size: " + postList.size() + ", count: " + count);
return new View(activity);
}
return postView;
}
}
public void addToList(List<Post> list){
count += list.size();
postList.addAll(list);
private View createThreadEndView() {
if (threadManager.getWatchLogic() != null) {
ThreadWatchCounterView view = new ThreadWatchCounterView(context);
view.init(threadManager, listView);
int padding = context.getResources().getDimensionPixelSize(R.dimen.general_padding);
view.setPadding(padding, padding, padding, padding);
view.setGravity(Gravity.CENTER);
return view;
} else {
if (endOfLine) {
TextView textView = new TextView(context);
textView.setText(context.getString(R.string.end_of_line));
int padding = context.getResources().getDimensionPixelSize(R.dimen.general_padding);
textView.setPadding(padding, padding, padding, padding);
return textView;
} else {
return new ProgressBar(context);
}
}
}
public void addToList(List<Post> list) {
List<Post> newPosts = new ArrayList<Post>();
for (Post newPost : list) {
boolean have = false;
for (Post havePost : postList) {
if (havePost.no == newPost.no) {
have = true;
break;
}
}
if (!have) {
newPosts.add(newPost);
}
}
postList.addAll(newPosts);
count += newPosts.size();
notifyDataSetChanged();
}

@ -51,7 +51,29 @@ public class ThreadFragment extends Fragment implements ThreadListener {
public void onDestroy() {
super.onDestroy();
stopLoading();
if (threadManager != null) {
stopLoading();
threadManager.onDestroy();
}
}
@Override
public void onResume() {
super.onResume();
if (threadManager != null) {
threadManager.onResume();
}
}
@Override
public void onPause() {
super.onPause();
if (threadManager != null) {
threadManager.onPause();
}
}
@Override

@ -3,7 +3,6 @@ package org.floens.chan.manager;
import java.util.ArrayList;
import java.util.List;
import org.floens.chan.adapter.PinnedAdapter;
import org.floens.chan.database.DatabaseManager;
import org.floens.chan.model.Loadable;
import org.floens.chan.model.Pin;
@ -36,18 +35,6 @@ public class PinnedManager {
listeners.remove(l);
}
public PinnedAdapter getAdapter() {
PinnedAdapter adapter = new PinnedAdapter(context, 0);
Pin header = new Pin();
header.type = Pin.Type.HEADER;
adapter.add(header);
adapter.addAll(pins);
return adapter;
}
/**
* Look for a pin that has an loadable that is equal to the supplied loadable.
* @param other

@ -13,6 +13,9 @@ import org.floens.chan.model.Post;
import org.floens.chan.model.PostLinkable;
import org.floens.chan.net.ThreadLoader;
import org.floens.chan.utils.ChanPreferences;
import org.floens.chan.utils.Logger;
import org.floens.chan.watch.WatchLogic;
import org.floens.chan.watch.WatchLogic.WatchListener;
import android.app.Activity;
import android.app.AlertDialog;
@ -38,13 +41,15 @@ import com.android.volley.VolleyError;
* All PostView's need to have this referenced.
* This manages some things like pages, starting and stopping of loading,
* handling linkables, replies popups etc.
* onDestroy, onPause and onResume must be called from the activity/fragment
*/
public class ThreadManager implements ThreadLoader.ThreadLoaderListener {
public class ThreadManager implements ThreadLoader.ThreadLoaderListener, WatchListener {
private final Activity activity;
private final ThreadLoader threadLoader;
private final ThreadManager.ThreadListener threadListener;
private Loadable loadable;
private boolean endOfLine = false;
private WatchLogic watchLogic;
private final List<List<Post>> popupQueue = new ArrayList<List<Post>>();
private PostRepliesFragment currentPopupFragment;
@ -56,13 +61,52 @@ public class ThreadManager implements ThreadLoader.ThreadLoaderListener {
threadLoader = new ThreadLoader(this);
}
public void onDestroy() {
if (watchLogic != null) {
watchLogic.destroy();
}
}
public void onPause() {
if (watchLogic != null) {
watchLogic.stopTimer();
}
}
public void onResume() {
if (watchLogic != null) {
watchLogic.startTimer();
}
}
@Override
public void onWatchReloadRequested() {
Logger.test("ThreadManager reload requested");
if (!threadLoader.isLoading()) {
threadLoader.start(loadable);
}
}
public WatchLogic getWatchLogic() {
return watchLogic;
}
@Override
public void onError(VolleyError error) {
threadListener.onThreadLoadError(error);
if (watchLogic != null) {
watchLogic.stopTimer();
}
}
@Override
public void onData(List<Post> result) {
if (watchLogic != null) {
watchLogic.onLoaded(result.size());
}
threadListener.onThreadLoaded(result);
}
@ -80,18 +124,33 @@ public class ThreadManager implements ThreadLoader.ThreadLoaderListener {
public void startLoading(Loadable loadable) {
this.loadable = loadable;
threadLoader.start(loadable);
Pin pin = PinnedManager.getInstance().findPinByLoadable(loadable);
if (pin != null) {
PinnedManager.getInstance().onPinViewed(pin);
}
if (watchLogic != null) {
watchLogic.destroy();
watchLogic = null;
}
if (loadable.isThreadMode()) {
watchLogic = new WatchLogic(this);
watchLogic.startTimer();
}
}
public void stop() {
threadLoader.stop();
endOfLine = false;
if (watchLogic != null) {
watchLogic.destroy();
watchLogic = null;
}
}
public void reload() {
@ -111,11 +170,19 @@ public class ThreadManager implements ThreadLoader.ThreadLoaderListener {
}
public void loadMore() {
if (threadLoader.isLoading()) return;
if (loadable == null) {
Log.e("Chan", "ThreadManager: loadable null");
} else {
if (!endOfLine) {
threadLoader.loadMore();
if (loadable.isBoardMode()) {
if (!endOfLine) {
threadLoader.loadMore();
}
} else if (loadable.isThreadMode()) {
if (watchLogic != null) {
watchLogic.loadNow();
}
}
}
}

@ -40,6 +40,7 @@ public class PostView extends LinearLayout implements View.OnClickListener, View
private Post post;
private boolean isBuild = false;
private LinearLayout full;
private NetworkImageView imageView;
private TextView titleView;
private TextView commentView;
@ -217,13 +218,15 @@ public class PostView extends LinearLayout implements View.OnClickListener, View
countryView.setImageUrl(ChanUrls.getCountryFlagUrl(post.country), ChanApplication.getImageLoader());
} else {
countryView.setVisibility(View.GONE);
countryView.setImageUrl(null, null);
}
if (post.isOP && manager.getLoadable().isBoardMode()) {
setClickable(true);
setFocusable(true);
full.setClickable(true);
full.setFocusable(true);
full.setOnClickListener(this);
ViewUtils.setPressedDrawable(this);
ViewUtils.setPressedDrawable(full);
}
}
@ -244,7 +247,7 @@ public class PostView extends LinearLayout implements View.OnClickListener, View
int iconHeight = resources.getDimensionPixelSize(R.dimen.post_icon_height);
int imageSize = resources.getDimensionPixelSize(R.dimen.thumbnail_size);
LinearLayout full = new LinearLayout(context);
full = new LinearLayout(context);
full.setLayoutParams(matchParams);
full.setOrientation(HORIZONTAL);
@ -302,10 +305,14 @@ public class PostView extends LinearLayout implements View.OnClickListener, View
right.addView(commentView, matchWrapParams);
repliesCountView = new TextView(context);
// Set the drawable before the padding, because setting the background resets the padding
// This behavior differs with 4.4 / 4.1
ViewUtils.setPressedDrawable(repliesCountView);
repliesCountView.setTextColor(Color.argb(255, 100, 100, 100));
repliesCountView.setPadding(postPadding, postPadding, postPadding, postPadding);
repliesCountView.setTextSize(14);
ViewUtils.setPressedDrawable(repliesCountView);
right.addView(repliesCountView, wrapParams);

@ -0,0 +1,77 @@
package org.floens.chan.view;
import org.floens.chan.manager.ThreadManager;
import org.floens.chan.watch.WatchLogic;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
public class ThreadWatchCounterView extends TextView {
private boolean detached = false;
public ThreadWatchCounterView(Context activity) {
super(activity);
}
public ThreadWatchCounterView(Context activity, AttributeSet attbs) {
super(activity, attbs);
}
public ThreadWatchCounterView(Context activity, AttributeSet attbs, int style) {
super(activity, attbs, style);
}
public void init(final ThreadManager threadManager, final ListView listView) {
updateCounterText(threadManager);
postInvalidateDelayed(1000);
postDelayed(new Runnable() {
@Override
public void run() {
if (!detached) {
updateCounterText(threadManager);
// TODO: This sometimes fails to recreate this view
listView.invalidateViews();
}
}
}, 1000);
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
threadManager.loadMore();
}
});
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
setOnClickListener(null);
detached = true;
}
private void updateCounterText(ThreadManager threadManager) {
WatchLogic logic = threadManager.getWatchLogic();
if (logic != null) {
int time = Math.round(logic.timeLeft() / 1000f);
if (time <= 0) {
setText("Loading");
} else {
setText("Loading in " + time);
}
}
}
}

@ -12,8 +12,6 @@ import org.floens.chan.utils.Logger;
import com.android.volley.VolleyError;
public class PinWatcher implements ThreadLoader.ThreadLoaderListener {
private static final int[] timeouts = {10, 15, 20, 30, 60, 90, 120, 180, 240, 300};
private final ThreadLoader watchLoader;
private final Loadable watchLoadable;

@ -0,0 +1,114 @@
package org.floens.chan.watch;
import java.util.Timer;
import java.util.TimerTask;
import org.floens.chan.utils.Logger;
public class WatchLogic {
private static final int[] timeouts = {10, 15, 20, 30, 60, 90, 120, 180, 240, 300};
private WatchListener listener;
private int selectedTimeout;
private long lastLoadTime;
private int lastPostCount;
private Timer timer = new Timer();
public WatchLogic(WatchListener listener) {
this.listener = listener;
}
public void destroy() {
this.listener = null;
clearTimer();
Logger.test("WatchLogic destroy()");
}
/**
* Starts the timer from the beginning
*/
public void startTimer() {
Logger.test("WatchLogic timer start");
lastLoadTime = now();
selectedTimeout = 0;
scheduleTimer();
}
public void stopTimer() {
Logger.test("WatchLogic timer paused");
clearTimer();
}
public void loadNow() {
clearTimer();
lastLoadTime = 0;
selectedTimeout = -1; // so that the next timeout will be the first one
if (listener != null) {
listener.onWatchReloadRequested();
}
}
/**
* Call this to notify of new posts.
* @param wereNewPosts set this to true when there were new posts, false otherwise
*/
public void onLoaded(int postCount) {
Logger.test("WatchLogic onLoaded: " + (postCount > lastPostCount));
if (postCount > lastPostCount) {
selectedTimeout = 0;
} else {
selectedTimeout = Math.min(timeouts.length - 1, selectedTimeout + 1);
}
lastLoadTime = now();
lastPostCount = postCount;
scheduleTimer();
}
/**
* Time time in ms left before a reload is necessary.
* @return
*/
public long timeLeft() {
long waitTime = timeouts[Math.max(0, selectedTimeout)] * 1000L;
return lastLoadTime + waitTime - now();
}
private void scheduleTimer() {
clearTimer();
timer.schedule(new TimerTask() {
@Override
public void run() {
if (listener != null) {
listener.onWatchReloadRequested();
}
}
}, Math.max(0, timeLeft()));
}
/**
* Clear all the scheduled runnables
*/
private void clearTimer() {
timer.cancel();
timer.purge();
timer = new Timer();
}
private long now() {
return System.currentTimeMillis();
}
public interface WatchListener {
public void onWatchReloadRequested();
}
}
Loading…
Cancel
Save