From c62c353f8dc985526c7ef4060ccc8fb88232a2bf Mon Sep 17 00:00:00 2001 From: Florens Douwes Date: Fri, 28 Feb 2014 17:14:19 +0100 Subject: [PATCH] Threads hava auto reload now. The bottom reload view is still a bit buggy. --- Chan/AndroidManifest.xml | 21 ++-- Chan/res/layout/activity_base.xml | 2 +- Chan/src/org/floens/chan/ChanApplication.java | 4 +- .../floens/chan/activity/BaseActivity.java | 27 ++--- .../floens/chan/activity/BoardActivity.java | 8 +- .../floens/chan/adapter/PinnedAdapter.java | 11 ++ .../org/floens/chan/adapter/PostAdapter.java | 66 +++++++--- .../floens/chan/fragment/ThreadFragment.java | 24 +++- .../floens/chan/manager/PinnedManager.java | 13 -- .../floens/chan/manager/ThreadManager.java | 75 +++++++++++- Chan/src/org/floens/chan/view/PostView.java | 17 ++- .../chan/view/ThreadWatchCounterView.java | 77 ++++++++++++ .../src/org/floens/chan/watch/PinWatcher.java | 2 - .../src/org/floens/chan/watch/WatchLogic.java | 114 ++++++++++++++++++ 14 files changed, 385 insertions(+), 76 deletions(-) create mode 100644 Chan/src/org/floens/chan/view/ThreadWatchCounterView.java create mode 100644 Chan/src/org/floens/chan/watch/WatchLogic.java diff --git a/Chan/AndroidManifest.xml b/Chan/AndroidManifest.xml index 0d594f59..0ae9d23e 100644 --- a/Chan/AndroidManifest.xml +++ b/Chan/AndroidManifest.xml @@ -28,10 +28,11 @@ + - + @@ -52,25 +53,25 @@ android:name="org.floens.chan.activity.SettingsActivity" android:label="@string/action_settings" android:parentActivityName="org.floens.chan.activity.BoardActivity" > - + - + - + diff --git a/Chan/src/org/floens/chan/ChanApplication.java b/Chan/src/org/floens/chan/ChanApplication.java index e0894f28..6eefa18e 100644 --- a/Chan/src/org/floens/chan/ChanApplication.java +++ b/Chan/src/org/floens/chan/ChanApplication.java @@ -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)); } } diff --git a/Chan/src/org/floens/chan/activity/BaseActivity.java b/Chan/src/org/floens/chan/activity/BaseActivity.java index 742b5ec2..5d069a1a 100644 --- a/Chan/src/org/floens/chan/activity/BaseActivity.java +++ b/Chan/src/org/floens/chan/activity/BaseActivity.java @@ -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) { diff --git a/Chan/src/org/floens/chan/activity/BoardActivity.java b/Chan/src/org/floens/chan/activity/BoardActivity.java index bcc16e09..335b86f7 100644 --- a/Chan/src/org/floens/chan/activity/BoardActivity.java +++ b/Chan/src/org/floens/chan/activity/BoardActivity.java @@ -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)) { diff --git a/Chan/src/org/floens/chan/adapter/PinnedAdapter.java b/Chan/src/org/floens/chan/adapter/PinnedAdapter.java index 0ea8b011..8444b383 100644 --- a/Chan/src/org/floens/chan/adapter/PinnedAdapter.java +++ b/Chan/src/org/floens/chan/adapter/PinnedAdapter.java @@ -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 { 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); diff --git a/Chan/src/org/floens/chan/adapter/PostAdapter.java b/Chan/src/org/floens/chan/adapter/PostAdapter.java index 31651a3d..e78b8ce4 100644 --- a/Chan/src/org/floens/chan/adapter/PostAdapter.java +++ b/Chan/src/org/floens/chan/adapter/PostAdapter.java @@ -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 postList = new ArrayList(); 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 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 list) { + List newPosts = new ArrayList(); + + 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(); } diff --git a/Chan/src/org/floens/chan/fragment/ThreadFragment.java b/Chan/src/org/floens/chan/fragment/ThreadFragment.java index 14c11542..8e3157fb 100644 --- a/Chan/src/org/floens/chan/fragment/ThreadFragment.java +++ b/Chan/src/org/floens/chan/fragment/ThreadFragment.java @@ -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 diff --git a/Chan/src/org/floens/chan/manager/PinnedManager.java b/Chan/src/org/floens/chan/manager/PinnedManager.java index 10ef76b3..4095a5d8 100644 --- a/Chan/src/org/floens/chan/manager/PinnedManager.java +++ b/Chan/src/org/floens/chan/manager/PinnedManager.java @@ -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 diff --git a/Chan/src/org/floens/chan/manager/ThreadManager.java b/Chan/src/org/floens/chan/manager/ThreadManager.java index e75e9c56..78034855 100644 --- a/Chan/src/org/floens/chan/manager/ThreadManager.java +++ b/Chan/src/org/floens/chan/manager/ThreadManager.java @@ -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> popupQueue = new ArrayList>(); 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 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(); + } } } } diff --git a/Chan/src/org/floens/chan/view/PostView.java b/Chan/src/org/floens/chan/view/PostView.java index f567897a..c3674f1f 100644 --- a/Chan/src/org/floens/chan/view/PostView.java +++ b/Chan/src/org/floens/chan/view/PostView.java @@ -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); diff --git a/Chan/src/org/floens/chan/view/ThreadWatchCounterView.java b/Chan/src/org/floens/chan/view/ThreadWatchCounterView.java new file mode 100644 index 00000000..f60e0ccd --- /dev/null +++ b/Chan/src/org/floens/chan/view/ThreadWatchCounterView.java @@ -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); + } + } + } +} + + + + diff --git a/Chan/src/org/floens/chan/watch/PinWatcher.java b/Chan/src/org/floens/chan/watch/PinWatcher.java index 152d2ad4..8a986681 100644 --- a/Chan/src/org/floens/chan/watch/PinWatcher.java +++ b/Chan/src/org/floens/chan/watch/PinWatcher.java @@ -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; diff --git a/Chan/src/org/floens/chan/watch/WatchLogic.java b/Chan/src/org/floens/chan/watch/WatchLogic.java new file mode 100644 index 00000000..67692ac0 --- /dev/null +++ b/Chan/src/org/floens/chan/watch/WatchLogic.java @@ -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(); + } +} + +