Made pins clickable and highlight when open

filtering
Floens 10 years ago
parent e3519915b2
commit ad02acaefe
  1. 29
      Clover/app/src/main/java/org/floens/chan/ChanApplication.java
  2. 8
      Clover/app/src/main/java/org/floens/chan/controller/NavigationController.java
  3. 6
      Clover/app/src/main/java/org/floens/chan/core/loader/LoaderPool.java
  4. 49
      Clover/app/src/main/java/org/floens/chan/core/manager/WatchManager.java
  5. 13
      Clover/app/src/main/java/org/floens/chan/core/model/Pin.java
  6. 15
      Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java
  7. 14
      Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java
  8. 7
      Clover/app/src/main/java/org/floens/chan/core/watch/PinWatcher.java
  9. 17
      Clover/app/src/main/java/org/floens/chan/ui/activity/BaseActivity.java
  10. 111
      Clover/app/src/main/java/org/floens/chan/ui/activity/BoardActivity.java
  11. 106
      Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java
  12. 125
      Clover/app/src/main/java/org/floens/chan/ui/adapter/PinAdapter.java
  13. 2
      Clover/app/src/main/java/org/floens/chan/ui/adapter/PinnedAdapter.java
  14. 13
      Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java
  15. 2
      Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java
  16. 37
      Clover/app/src/main/java/org/floens/chan/ui/controller/RootNavigationController.java
  17. 4
      Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java
  18. 54
      Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java
  19. 3
      Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java
  20. 14
      Clover/app/src/main/java/org/floens/chan/ui/toolbar/NavigationItem.java
  21. 13
      Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java
  22. 4
      Clover/app/src/main/java/org/floens/chan/ui/toolbar/ToolbarMenuItem.java
  23. 13
      Clover/app/src/main/java/org/floens/chan/utils/AndroidUtils.java
  24. 2
      Clover/app/src/main/res/layout/cell_header.xml
  25. 15
      Clover/app/src/main/res/layout/cell_pin.xml

@ -40,8 +40,8 @@ import org.floens.chan.utils.Logger;
import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import de.greenrobot.event.EventBus;
public class ChanApplication extends Application {
private static final String TAG = "ChanApplication";
@ -62,7 +62,6 @@ public class ChanApplication extends Application {
private static DatabaseManager databaseManager;
private static FileCache fileCache;
private List<ForegroundChangedListener> foregroundChangedListeners = new ArrayList<>();
private int activityForegroundCounter = 0;
public ChanApplication() {
@ -152,9 +151,7 @@ public class ChanApplication extends Application {
activityForegroundCounter++;
if (getApplicationInForeground() != lastForeground) {
for (ForegroundChangedListener listener : foregroundChangedListeners) {
listener.onForegroundChanged(getApplicationInForeground());
}
EventBus.getDefault().post(new ForegroundChangedMessage(getApplicationInForeground()));
}
}
@ -167,9 +164,7 @@ public class ChanApplication extends Application {
}
if (getApplicationInForeground() != lastForeground) {
for (ForegroundChangedListener listener : foregroundChangedListeners) {
listener.onForegroundChanged(getApplicationInForeground());
}
EventBus.getDefault().post(new ForegroundChangedMessage(getApplicationInForeground()));
}
}
@ -177,14 +172,6 @@ public class ChanApplication extends Application {
return activityForegroundCounter > 0;
}
public void addForegroundChangedListener(ForegroundChangedListener listener) {
foregroundChangedListeners.add(listener);
}
public void removeForegroundChangedListener(ForegroundChangedListener listener) {
foregroundChangedListeners.remove(listener);
}
private void cleanupOutdated() {
File ionCacheFolder = new File(getCacheDir() + "/ion");
if (ionCacheFolder.exists() && ionCacheFolder.isDirectory()) {
@ -202,7 +189,11 @@ public class ChanApplication extends Application {
}
}
public interface ForegroundChangedListener {
void onForegroundChanged(boolean foreground);
public static class ForegroundChangedMessage {
public boolean inForeground;
public ForegroundChangedMessage(boolean inForeground) {
this.inForeground = inForeground;
}
}
}

@ -137,6 +137,14 @@ public abstract class NavigationController extends Controller implements Control
protected void controllerPopped(Controller controller) {
}
public Controller getTop() {
if (controllerList.size() > 0) {
return controllerList.get(controllerList.size() - 1);
} else {
return null;
}
}
@Override
public void onControllerTransitionCompleted(ControllerTransition transition) {
ControllerLogic.finishTransition(transition);

@ -25,15 +25,11 @@ import java.util.Map;
public class LoaderPool {
// private static final String TAG = "LoaderPool";
private static LoaderPool instance;
private static LoaderPool instance = new LoaderPool();
private static Map<Loadable, ChanLoader> loaders = new HashMap<>();
public static LoaderPool getInstance() {
if (instance == null) {
instance = new LoaderPool();
}
return instance;
}

@ -39,12 +39,11 @@ import java.util.concurrent.TimeUnit;
import de.greenrobot.event.EventBus;
public class WatchManager implements ChanApplication.ForegroundChangedListener {
public class WatchManager {
private static final String TAG = "WatchManager";
private static final int FOREGROUND_TIME = 5;
private final Context context;
private final List<PinListener> listeners = new ArrayList<>();
private final List<Pin> pins;
private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
private PendingTimer pendingTimer;
@ -54,7 +53,7 @@ public class WatchManager implements ChanApplication.ForegroundChangedListener {
pins = ChanApplication.getDatabaseManager().getPinned();
ChanApplication.getInstance().addForegroundChangedListener(this);
EventBus.getDefault().register(this);
updateTimerState(true);
updateNotificationServiceState();
@ -189,19 +188,20 @@ public class WatchManager implements ChanApplication.ForegroundChangedListener {
ChanApplication.getDatabaseManager().updatePins(pins);
}
public void addPinListener(PinListener l) {
listeners.add(l);
public void toggleWatch(Pin pin) {
pin.watching = !pin.watching;
EventBus.getDefault().post(new PinChangedMessage(pin));
ChanApplication.getWatchManager().onPinsChanged();
ChanApplication.getWatchManager().invokeLoadNow();
}
public void removePinListener(PinListener l) {
listeners.remove(l);
public void pinWatcherUpdated(Pin pin) {
EventBus.getDefault().post(new PinChangedMessage(pin));
onPinsChanged();
}
public void onPinsChanged() {
for (PinListener l : listeners) {
l.onPinsChanged();
}
updateTimerState(false);
updateNotificationServiceState();
updatePinWatchers();
@ -231,21 +231,26 @@ public class WatchManager implements ChanApplication.ForegroundChangedListener {
}
}
public void onEvent(ChanApplication.ForegroundChangedMessage message) {
updateNotificationServiceState();
updateTimerState(true);
}
public void onWatchEnabledChanged(boolean watchEnabled) {
updateNotificationServiceState(watchEnabled, getWatchBackgroundEnabled());
updateTimerState(watchEnabled, getWatchBackgroundEnabled(), false);
updatePinWatchers(watchEnabled);
for (Pin pin : getPins()) {
EventBus.getDefault().post(new PinChangedMessage(pin));
}
}
public void onBackgroundWatchingChanged(boolean backgroundEnabled) {
updateNotificationServiceState(getTimerEnabled(), backgroundEnabled);
updateTimerState(getTimerEnabled(), backgroundEnabled, false);
}
@Override
public void onForegroundChanged(final boolean foreground) {
updateNotificationServiceState();
updateTimerState(true);
for (Pin pin : getPins()) {
EventBus.getDefault().post(new PinChangedMessage(pin));
}
}
private boolean getTimerEnabled() {
@ -294,7 +299,7 @@ public class WatchManager implements ChanApplication.ForegroundChangedListener {
setTimer(invokeLoadNow ? 1 : FOREGROUND_TIME);
} else {
if (backgroundEnabled) {
setTimer(ChanSettings.getWatchBackgroundTimeout());
setTimer(Integer.parseInt(ChanSettings.watchBackgroundTimeout.get()));
} else {
if (pendingTimer != null) {
pendingTimer.cancel();
@ -343,16 +348,14 @@ public class WatchManager implements ChanApplication.ForegroundChangedListener {
pendingTimer = null;
for (Pin pin : getWatchingPins()) {
pin.update();
if (pin.update()) {
EventBus.getDefault().post(new PinChangedMessage(pin));
}
}
updateTimerState(false);
}
public interface PinListener {
void onPinsChanged();
}
public static class PinAddedMessage {
public Pin pin;

@ -20,7 +20,6 @@ package org.floens.chan.core.model;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import org.floens.chan.ChanApplication;
import org.floens.chan.core.watch.PinWatcher;
@DatabaseTable
@ -80,10 +79,8 @@ public class Pin {
}
}
public void update() {
if (pinWatcher != null && watching) {
pinWatcher.update();
}
public boolean update() {
return pinWatcher != null && watching && pinWatcher.update();
}
public void createWatcher() {
@ -98,10 +95,4 @@ public class Pin {
pinWatcher = null;
}
}
public void toggleWatch() {
watching = !watching;
ChanApplication.getWatchManager().onPinsChanged();
ChanApplication.getWatchManager().invokeLoadNow();
}
}

@ -55,16 +55,13 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt
}
public void bindLoadable(Loadable loadable) {
if (chanLoader == null) {
if (!loadable.equals(this.loadable)) {
if (this.loadable != null) {
unbindLoadable();
}
this.loadable = loadable;
chanLoader = LoaderPool.getInstance().obtain(loadable, this);
if (!loadable.equals(this.loadable)) {
if (chanLoader != null) {
unbindLoadable();
}
this.loadable = loadable;
chanLoader = LoaderPool.getInstance().obtain(loadable, this);
}
}

@ -91,9 +91,19 @@ public class ChanSettings {
anonymizeIds = new BooleanSetting(p, "preference_anonymize_ids", false);
repliesButtonsBottom = new BooleanSetting(p, "preference_buttons_bottom", false);
watchEnabled = new BooleanSetting(p, "preference_watch_enabled", false);
watchEnabled = new BooleanSetting(p, "preference_watch_enabled", false, new Setting.SettingCallback<Boolean>() {
@Override
public void onValueChange(Setting setting, Boolean value) {
ChanApplication.getWatchManager().onWatchEnabledChanged(value);
}
});
watchCountdown = new BooleanSetting(p, "preference_watch_countdown", false);
watchBackground = new BooleanSetting(p, "preference_watch_background_enabled", false);
watchBackground = new BooleanSetting(p, "preference_watch_background_enabled", false, new Setting.SettingCallback<Boolean>() {
@Override
public void onValueChange(Setting setting, Boolean value) {
ChanApplication.getWatchManager().onBackgroundWatchingChanged(value);
}
});
watchBackgroundTimeout = new StringSetting(p, "preference_watch_background_timeout", "60");
watchNotifyMode = new StringSetting(p, "preference_watch_notify_mode", "all");
watchSound = new StringSetting(p, "preference_watch_sound", "all");

@ -55,9 +55,12 @@ public class PinWatcher implements ChanLoader.ChanLoaderCallback {
}
}
public void update() {
public boolean update() {
if (!pin.isError) {
chanLoader.loadMoreIfTime();
return true;
} else {
return false;
}
}
@ -192,7 +195,7 @@ public class PinWatcher implements ChanLoader.ChanLoaderCallback {
AndroidUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
ChanApplication.getWatchManager().onPinsChanged();
ChanApplication.getWatchManager().pinWatcherUpdated(pin);
}
});
}

@ -45,21 +45,20 @@ import android.widget.ListView;
import org.floens.chan.ChanApplication;
import org.floens.chan.R;
import org.floens.chan.core.manager.WatchManager;
import org.floens.chan.core.model.ChanThread;
import org.floens.chan.core.model.Loadable;
import org.floens.chan.core.model.Pin;
import org.floens.chan.core.model.Post;
import org.floens.chan.ui.animation.SwipeDismissListViewTouchListener;
import org.floens.chan.ui.animation.SwipeDismissListViewTouchListener.DismissCallbacks;
import org.floens.chan.ui.ThemeActivity;
import org.floens.chan.ui.adapter.PinnedAdapter;
import org.floens.chan.ui.animation.SwipeDismissListViewTouchListener;
import org.floens.chan.ui.animation.SwipeDismissListViewTouchListener.DismissCallbacks;
import org.floens.chan.utils.AndroidUtils;
import org.floens.chan.utils.ThemeHelper;
import static org.floens.chan.utils.AndroidUtils.dp;
public abstract class BaseActivity extends ThemeActivity implements PanelSlideListener, WatchManager.PinListener {
public abstract class BaseActivity extends ThemeActivity implements PanelSlideListener {
public static boolean doRestartOnResume = false;
private final static int ACTION_OPEN_URL = 1;
@ -110,8 +109,6 @@ public abstract class BaseActivity extends ThemeActivity implements PanelSlideLi
threadPane = (SlidingPaneLayout) findViewById(R.id.pane_container);
initPane();
ChanApplication.getWatchManager().addPinListener(this);
updateIcon();
}
@ -127,8 +124,6 @@ public abstract class BaseActivity extends ThemeActivity implements PanelSlideLi
@Override
protected void onDestroy() {
super.onDestroy();
ChanApplication.getWatchManager().removePinListener(this);
}
@Override
@ -212,12 +207,6 @@ public abstract class BaseActivity extends ThemeActivity implements PanelSlideLi
pinDrawerView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
}
@Override
public void onPinsChanged() {
pinnedAdapter.reload();
updateIcon();
}
private void updateIcon() {
/*List<Pin> list = ChanApplication.getWatchManager().getWatchingPins();
if (list.size() > 0) {

@ -17,115 +17,8 @@
*/
package org.floens.chan.ui.activity;
import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.ViewGroup;
import org.floens.chan.controller.Controller;
import org.floens.chan.ui.controller.BrowseController;
import org.floens.chan.ui.controller.RootNavigationController;
import org.floens.chan.utils.ThemeHelper;
import java.util.ArrayList;
import java.util.List;
/**
* StartActivity
* <p/>
* (Not actually called StartActivity because then the launcher icon would disappear.
* Instead it's called like the old launcher activity, BoardActivity.)
* The old starting activity. This exists so that the launcher icon does not disappear.
*/
public class BoardActivity extends Activity {
private static final String TAG = "StartActivity";
private ViewGroup contentView;
private List<Controller> stack = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.getInstance().reloadPostViewColors(this);
contentView = (ViewGroup) findViewById(android.R.id.content);
RootNavigationController rootNavigationController = new RootNavigationController(this);
rootNavigationController.onCreate();
setContentView(rootNavigationController.view);
addController(rootNavigationController);
rootNavigationController.pushController(new BrowseController(this), false);
rootNavigationController.onShow();
// Prevent overdraw
// Do this after setContentView, or the decor creating will reset the background to a default non-null drawable
getWindow().setBackgroundDrawable(null);
}
public void addController(Controller controller) {
stack.add(controller);
}
public void removeController(Controller controller) {
stack.remove(controller);
}
public ViewGroup getContentView() {
return contentView;
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
for (Controller controller : stack) {
controller.onConfigurationChanged(newConfig);
}
}
@Override
public void onBackPressed() {
if (!stackTop().onBack()) {
// Don't destroy the view, let Android do that or it'll create artifacts
stackTop().onHide();
super.onBackPressed();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
stackTop().onDestroy();
}
private Controller stackTop() {
return stack.get(stack.size() - 1);
}
/*
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// http://stackoverflow.com/a/7748416/1001608
// Possible work around for market launches. See http://code.google.com/p/android/issues/detail?id=2373
// for more details. Essentially, the market launches the main activity on top of other activities.
// we never want this to happen. Instead, we check if we are the root and if not, we finish.
if (!isTaskRoot()) {
final Intent intent = getIntent();
final String intentAction = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
Logger.w(TAG, "StartActivity is not the root. Finishing StartActivity instead of launching.");
finish();
return;
}
}
Intent intent = new Intent(this, ChanActivity.class);
startActivity(intent);
finish();
}*/
public class BoardActivity extends StartActivity {
}

@ -0,0 +1,106 @@
package org.floens.chan.ui.activity;
import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.ViewGroup;
import org.floens.chan.ChanApplication;
import org.floens.chan.controller.Controller;
import org.floens.chan.ui.controller.BrowseController;
import org.floens.chan.ui.controller.RootNavigationController;
import org.floens.chan.utils.ThemeHelper;
import java.util.ArrayList;
import java.util.List;
public class StartActivity extends Activity {
private static final String TAG = "StartActivity";
private ViewGroup contentView;
private List<Controller> stack = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.getInstance().reloadPostViewColors(this);
contentView = (ViewGroup) findViewById(android.R.id.content);
RootNavigationController rootNavigationController = new RootNavigationController(this);
rootNavigationController.onCreate();
setContentView(rootNavigationController.view);
addController(rootNavigationController);
rootNavigationController.pushController(new BrowseController(this), false);
rootNavigationController.onShow();
// Prevent overdraw
// Do this after setContentView, or the decor creating will reset the background to a default non-null drawable
getWindow().setBackgroundDrawable(null);
}
public void addController(Controller controller) {
stack.add(controller);
}
public void removeController(Controller controller) {
stack.remove(controller);
}
public ViewGroup getContentView() {
return contentView;
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
for (Controller controller : stack) {
controller.onConfigurationChanged(newConfig);
}
}
@Override
public void onBackPressed() {
if (!stackTop().onBack()) {
// Don't destroy the view, let Android do that or it'll create artifacts
stackTop().onHide();
super.onBackPressed();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
stackTop().onDestroy();
}
@Override
protected void onStart() {
super.onStart();
ChanApplication.getInstance().activityEnteredForeground();
}
@Override
protected void onStop() {
super.onStop();
ChanApplication.getInstance().activityEnteredBackground();
}
@Override
protected void onPause() {
super.onPause();
ChanApplication.getWatchManager().updateDatabase();
}
private Controller stackTop() {
return stack.get(stack.size() - 1);
}
}

@ -1,5 +1,6 @@
package org.floens.chan.ui.adapter;
import android.os.Build;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
@ -10,16 +11,21 @@ import android.widget.TextView;
import org.floens.chan.ChanApplication;
import org.floens.chan.R;
import org.floens.chan.core.model.Pin;
import org.floens.chan.core.settings.ChanSettings;
import org.floens.chan.ui.helper.SwipeListener;
import org.floens.chan.ui.view.ThumbnailView;
import org.floens.chan.utils.AndroidUtils;
import java.util.ArrayList;
import java.util.List;
import static org.floens.chan.utils.AndroidUtils.ROBOTO_MEDIUM;
import static org.floens.chan.utils.AndroidUtils.dp;
import static org.floens.chan.utils.AndroidUtils.getAttrDrawable;
public class PinAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements SwipeListener.Callback {
private static final int PIN_OFFSET = 3;
private static final int TYPE_HEADER = 0;
private static final int TYPE_PIN = 1;
private static final int TYPE_LINK = 2;
@ -52,10 +58,10 @@ public class PinAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> im
((PinHeaderHolder) holder).text.setText(R.string.drawer_pinned);
break;
case TYPE_PIN:
final Pin pin = pins.get(position - 3);
final Pin pin = pins.get(position - PIN_OFFSET);
PinViewHolder pinHolder = (PinViewHolder) holder;
pinHolder.textView.setText(pin.loadable.title);
pinHolder.image.setUrl(pin.thumbnailUrl, dp(40), dp(40));
updatePinViewHolder(pinHolder, pin);
break;
case TYPE_LINK:
LinkHolder linkHolder = (LinkHolder) holder;
@ -75,12 +81,12 @@ public class PinAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> im
@Override
public int getItemCount() {
return pins.size() + 3;
return pins.size() + PIN_OFFSET;
}
@Override
public long getItemId(int position) {
position -= 3;
position -= PIN_OFFSET;
if (position >= 0 && position < pins.size()) {
return pins.get(position).id + 10;
} else {
@ -109,7 +115,7 @@ public class PinAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> im
public void onPinAdded(Pin pin) {
pins.add(pin);
notifyItemInserted(pins.size() - 1 + 3);
notifyItemInserted(pins.size() - 1 + PIN_OFFSET);
}
public void onPinRemoved(Pin pin) {
@ -120,12 +126,24 @@ public class PinAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> im
} else {
int location = pins.indexOf(pin);
pins.remove(pin);
notifyItemRemoved(location + 3);
notifyItemRemoved(location + PIN_OFFSET);
}
}
public void onPinChanged(Pin pin) {
notifyItemChanged(pins.indexOf(pin) + 3);
public void onPinChanged(RecyclerView recyclerView, Pin pin) {
PinViewHolder holder = (PinViewHolder) recyclerView.findViewHolderForAdapterPosition(pins.indexOf(pin) + PIN_OFFSET);
if (holder != null) {
updatePinViewHolder(holder, pin);
}
}
public void updateHighlighted(RecyclerView recyclerView) {
for (int i = 0; i < pins.size(); i++) {
PinViewHolder holder = (PinViewHolder) recyclerView.findViewHolderForAdapterPosition(i + PIN_OFFSET);
if (holder != null) {
updatePinViewHolder(holder, pins.get(i));
}
}
}
@Override
@ -135,7 +153,7 @@ public class PinAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> im
@Override
public void removeItem(int position) {
ChanApplication.getWatchManager().removePin(pins.get(position - 3));
ChanApplication.getWatchManager().removePin(pins.get(position - PIN_OFFSET));
}
@Override
@ -145,8 +163,8 @@ public class PinAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> im
@Override
public void moveItem(int from, int to) {
Pin item = pins.remove(from - 3);
pins.add(to - 3, item);
Pin item = pins.remove(from - PIN_OFFSET);
pins.add(to - PIN_OFFSET, item);
notifyItemMoved(from, to);
}
@ -154,21 +172,88 @@ public class PinAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> im
public void movingDone() {
}
public void updatePinViewHolder(PinViewHolder holder, Pin pin) {
holder.textView.setText(pin.loadable.title);
holder.image.setUrl(pin.thumbnailUrl, dp(40), dp(40));
if (ChanSettings.watchEnabled.get()) {
String count;
if (pin.isError) {
count = "Err";
} else {
int c = pin.getNewPostCount();
if (c > 999) {
count = "1k+";
} else {
count = Integer.toString(c);
}
}
holder.watchCountText.setVisibility(View.VISIBLE);
holder.watchCountText.setText(count);
if (!pin.watching) {
holder.watchCountText.setTextColor(0xff898989); // TODO material colors
} else if (pin.getNewQuoteCount() > 0) {
holder.watchCountText.setTextColor(0xffFF4444);
} else {
holder.watchCountText.setTextColor(0xff33B5E5);
}
// The 16dp padding now belongs to the counter, for a bigger touch area
holder.textView.setPadding(holder.textView.getPaddingLeft(), holder.textView.getPaddingTop(),
0, holder.textView.getPaddingBottom());
holder.watchCountText.setPadding(dp(16), holder.watchCountText.getPaddingTop(),
holder.watchCountText.getPaddingRight(), holder.watchCountText.getPaddingBottom());
} else {
// The 16dp padding now belongs to the textview, for better ellipsize
holder.watchCountText.setVisibility(View.GONE);
holder.textView.setPadding(holder.textView.getPaddingLeft(), holder.textView.getPaddingTop(),
dp(16), holder.textView.getPaddingBottom());
}
boolean highlighted = callback.isHighlighted(pin);
if (highlighted && !holder.highlighted) {
holder.itemView.setBackgroundColor(0x22000000);
holder.highlighted = true;
} else if (!highlighted && holder.highlighted) {
//noinspection deprecation
holder.itemView.setBackgroundDrawable(AndroidUtils.getAttrDrawable(holder.itemView.getContext(), android.R.attr.selectableItemBackground));
holder.highlighted = false;
}
}
public class PinViewHolder extends RecyclerView.ViewHolder {
private boolean highlighted;
private ThumbnailView image;
private TextView textView;
private TextView watchCountText;
public PinViewHolder(View pinCell) {
super(pinCell);
image = (ThumbnailView) pinCell.findViewById(R.id.thumb);
public PinViewHolder(View itemView) {
super(itemView);
image = (ThumbnailView) itemView.findViewById(R.id.thumb);
image.setCircular(true);
textView = (TextView) pinCell.findViewById(R.id.text);
textView = (TextView) itemView.findViewById(R.id.text);
textView.setTypeface(ROBOTO_MEDIUM);
watchCountText = (TextView) itemView.findViewById(R.id.watch_count);
watchCountText.setTypeface(ROBOTO_MEDIUM);
pinCell.setOnClickListener(new View.OnClickListener() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
watchCountText.setBackground(getAttrDrawable(itemView.getContext(), android.R.attr.selectableItemBackgroundBorderless));
} else {
watchCountText.setBackgroundResource(R.drawable.gray_background_selector);
}
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
callback.onPinClicked(pins.get(getAdapterPosition() - 3));
callback.onPinClicked(pins.get(getAdapterPosition() - PIN_OFFSET));
}
});
watchCountText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
callback.onWatchCountClicked(pins.get(getAdapterPosition() - PIN_OFFSET));
}
});
}
@ -205,5 +290,9 @@ public class PinAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> im
public interface Callback {
void onPinClicked(Pin pin);
void onWatchCountClicked(Pin pin);
boolean isHighlighted(Pin pin);
}
}

@ -178,7 +178,7 @@ public class PinnedAdapter extends BaseAdapter {
countContainer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pin.toggleWatch();
// pin.toggleWatch();
}
});

@ -31,6 +31,7 @@ import org.floens.chan.chan.ChanUrls;
import org.floens.chan.core.manager.BoardManager;
import org.floens.chan.core.model.Board;
import org.floens.chan.core.model.Loadable;
import org.floens.chan.core.model.Pin;
import org.floens.chan.ui.layout.ThreadLayout;
import org.floens.chan.ui.toolbar.ToolbarMenu;
import org.floens.chan.ui.toolbar.ToolbarMenuItem;
@ -41,7 +42,7 @@ import org.floens.chan.utils.AndroidUtils;
import java.util.ArrayList;
import java.util.List;
public class BrowseController extends ThreadController implements ToolbarMenuItem.ToolbarMenuItemCallback, ThreadLayout.ThreadLayoutCallback, FloatingMenu.FloatingMenuCallback, BoardManager.BoardChangeListener {
public class BrowseController extends ThreadController implements ToolbarMenuItem.ToolbarMenuItemCallback, ThreadLayout.ThreadLayoutCallback, FloatingMenu.FloatingMenuCallback, BoardManager.BoardChangeListener, RootNavigationController.DrawerCallbacks {
private static final int REFRESH_ID = 1;
private static final int POST_ID = 2;
private static final int SEARCH_ID = 101;
@ -144,6 +145,16 @@ public class BrowseController extends ThreadController implements ToolbarMenuIte
loadBoards();
}
@Override
public void onPinClicked(Pin pin) {
openThread(pin.loadable);
}
@Override
public boolean isPinCurrent(Pin pin) {
return false;
}
private void loadBoard(Board board) {
Loadable loadable = new Loadable(board.value);
loadable.mode = Loadable.Mode.CATALOG;

@ -217,7 +217,7 @@ public class ImageViewerController extends Controller implements View.OnClickLis
public void setTitle(PostImage postImage) {
navigationItem.title = postImage.filename;
toolbar.updateTitle(navigationItem);
navigationItem.updateTitle();
}
public void scrollTo(PostImage postImage) {

@ -28,6 +28,7 @@ import android.widget.FrameLayout;
import org.floens.chan.ChanApplication;
import org.floens.chan.R;
import org.floens.chan.controller.Controller;
import org.floens.chan.controller.ControllerTransition;
import org.floens.chan.controller.NavigationController;
import org.floens.chan.core.manager.WatchManager;
import org.floens.chan.core.model.Pin;
@ -121,9 +122,37 @@ public class RootNavigationController extends NavigationController implements Pi
setDrawerEnabled(controller.navigationItem.hasDrawer);
}
@Override
public void onControllerTransitionCompleted(ControllerTransition transition) {
super.onControllerTransitionCompleted(transition);
updateHighlighted();
}
public void updateHighlighted() {
pinAdapter.updateHighlighted(recyclerView);
}
@Override
public void onPinClicked(Pin pin) {
Controller top = getTop();
if (top instanceof DrawerCallbacks) {
((DrawerCallbacks) top).onPinClicked(pin);
drawerLayout.closeDrawer(Gravity.LEFT);
pinAdapter.updateHighlighted(recyclerView);
}
}
public boolean isHighlighted(Pin pin) {
Controller top = getTop();
if (top instanceof DrawerCallbacks) {
return ((DrawerCallbacks) top).isPinCurrent(pin);
}
return false;
}
@Override
public void onWatchCountClicked(Pin pin) {
ChanApplication.getWatchManager().toggleWatch(pin);
}
public void onEvent(WatchManager.PinAddedMessage message) {
@ -136,7 +165,7 @@ public class RootNavigationController extends NavigationController implements Pi
}
public void onEvent(WatchManager.PinChangedMessage message) {
pinAdapter.onPinChanged(message.pin);
pinAdapter.onPinChanged(recyclerView, message.pin);
}
private void setDrawerEnabled(boolean enabled) {
@ -153,4 +182,10 @@ public class RootNavigationController extends NavigationController implements Pi
return false;
}
}
public interface DrawerCallbacks {
void onPinClicked(Pin pin);
boolean isPinCurrent(Pin pin);
}
}

@ -49,4 +49,8 @@ public abstract class ThreadController extends Controller implements ThreadLayou
public void scrollTo(PostImage postImage) {
threadLayout.getPresenter().scrollTo(postImage);
}
@Override
public void onShowPosts() {
}
}

@ -26,6 +26,7 @@ import org.floens.chan.R;
import org.floens.chan.chan.ChanUrls;
import org.floens.chan.core.manager.WatchManager;
import org.floens.chan.core.model.Loadable;
import org.floens.chan.core.model.Pin;
import org.floens.chan.ui.layout.ThreadLayout;
import org.floens.chan.ui.toolbar.ToolbarMenu;
import org.floens.chan.ui.toolbar.ToolbarMenuItem;
@ -36,7 +37,7 @@ import java.util.Arrays;
import de.greenrobot.event.EventBus;
public class ViewThreadController extends ThreadController implements ThreadLayout.ThreadLayoutCallback, ToolbarMenuItem.ToolbarMenuItemCallback {
public class ViewThreadController extends ThreadController implements ThreadLayout.ThreadLayoutCallback, ToolbarMenuItem.ToolbarMenuItemCallback, RootNavigationController.DrawerCallbacks {
private static final int POST_ID = 1;
private static final int PIN_ID = 2;
private static final int REFRESH_ID = 101;
@ -62,11 +63,7 @@ public class ViewThreadController extends ThreadController implements ThreadLayo
view.setBackgroundColor(0xffffffff);
threadLayout.getPresenter().bindLoadable(loadable);
threadLayout.getPresenter().requestData();
navigationItem.hasDrawer = true;
navigationItem.title = loadable.title;
navigationItem.menu = new ToolbarMenu(context);
navigationItem.menu.addItem(new ToolbarMenuItem(context, this, POST_ID, R.drawable.ic_action_write));
@ -77,7 +74,15 @@ public class ViewThreadController extends ThreadController implements ThreadLayo
new FloatingMenuItem(SHARE_ID, context.getString(R.string.action_share))
));
setPinIconState(threadLayout.getPresenter().isPinned());
loadLoadable(loadable);
}
@Override
public void onShow() {
super.onShow();
if (navigationController instanceof RootNavigationController) {
((RootNavigationController)navigationController).updateHighlighted();
}
}
@Override
@ -100,14 +105,17 @@ public class ViewThreadController extends ThreadController implements ThreadLayo
}
@Override
public void openThread(Loadable threadLoadable) {
public void openThread(final Loadable threadLoadable) {
// TODO implement, scroll to post and fix title
new AlertDialog.Builder(context)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int which) {
// threadManagerListener.onOpenThread(thread, link.postId);
loadLoadable(threadLoadable);
if (navigationController instanceof RootNavigationController) {
((RootNavigationController)navigationController).updateHighlighted();
}
}
})
.setTitle(R.string.open_thread_confirmation)
@ -115,6 +123,36 @@ public class ViewThreadController extends ThreadController implements ThreadLayo
.show();
}
@Override
public void onPinClicked(Pin pin) {
loadLoadable(pin.loadable);
}
@Override
public boolean isPinCurrent(Pin pin) {
return pin.loadable.equals(loadable);
}
private void loadLoadable(Loadable loadable) {
if (!loadable.equals(threadLayout.getPresenter().getLoadable())) {
this.loadable = loadable;
threadLayout.getPresenter().bindLoadable(loadable);
threadLayout.getPresenter().requestData();
navigationItem.title = loadable.title;
navigationItem.updateTitle();
setPinIconState(threadLayout.getPresenter().isPinned());
}
}
@Override
public void onShowPosts() {
super.onShowPosts();
if (!navigationItem.title.equals(loadable.title)) {
navigationItem.title = loadable.title;
navigationItem.updateTitle();
}
}
@Override
public void onMenuItemClicked(ToolbarMenuItem item) {
switch (item.getId()) {

@ -91,6 +91,7 @@ public class ThreadLayout extends LoadView implements ThreadPresenter.ThreadPres
public void showPosts(ChanThread thread) {
threadListLayout.showPosts(thread, !visible);
switchVisible(true);
callback.onShowPosts();
}
@Override
@ -188,5 +189,7 @@ public class ThreadLayout extends LoadView implements ThreadPresenter.ThreadPres
void openThread(Loadable threadLoadable);
void showImages(List<PostImage> images, int index, Loadable loadable, ThumbnailView thumbnail);
void onShowPosts();
}
}

@ -20,7 +20,9 @@ package org.floens.chan.ui.toolbar;
import android.content.Context;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.floens.chan.R;
import org.floens.chan.ui.view.FloatingMenu;
import org.floens.chan.ui.view.FloatingMenuItem;
@ -30,15 +32,25 @@ public class NavigationItem {
public String title = "";
public ToolbarMenu menu;
public boolean hasBack = true;
public LinearLayout view;
public FloatingMenu middleMenu;
public View rightView;
public boolean hasDrawer = false;
LinearLayout view;
public ToolbarMenuItem createOverflow(Context context, ToolbarMenuItem.ToolbarMenuItemCallback callback, List<FloatingMenuItem> items) {
ToolbarMenuItem overflow = menu.createOverflow(callback);
FloatingMenu overflowMenu = new FloatingMenu(context, overflow.getView(), items);
overflow.setSubMenu(overflowMenu);
return overflow;
}
public void updateTitle() {
if (view != null) {
TextView titleView = (TextView) view.findViewById(R.id.title);
if (titleView != null) {
titleView.setText(title);
}
}
}
}

@ -89,15 +89,6 @@ public class Toolbar extends LinearLayout implements View.OnClickListener {
}
}
public void updateTitle(NavigationItem item) {
if (item.view != null) {
TextView title = (TextView) item.view.findViewById(R.id.title);
if (title != null) {
title.setText(item.title);
}
}
}
public void setCallback(ToolbarCallback callback) {
this.callback = callback;
}
@ -132,10 +123,8 @@ public class Toolbar extends LinearLayout implements View.OnClickListener {
arrowMenuView.setImageDrawable(arrowMenuDrawable);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//noinspection deprecation
arrowMenuView.setBackgroundDrawable(getAttrDrawable(android.R.attr.selectableItemBackgroundBorderless));
arrowMenuView.setBackground(getAttrDrawable(getContext(), android.R.attr.selectableItemBackgroundBorderless));
} else {
//noinspection deprecation
arrowMenuView.setBackgroundResource(R.drawable.gray_background_selector);
}

@ -58,10 +58,8 @@ public class ToolbarMenuItem implements View.OnClickListener, FloatingMenu.Float
imageView.setImageDrawable(drawable);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//noinspection deprecation
imageView.setBackgroundDrawable(getAttrDrawable(android.R.attr.selectableItemBackgroundBorderless));
imageView.setBackground(getAttrDrawable(context, android.R.attr.selectableItemBackgroundBorderless));
} else {
//noinspection deprecation
imageView.setBackgroundResource(R.drawable.gray_background_selector);
}
}

@ -100,13 +100,6 @@ public class AndroidUtils {
}
}
public static int getAttrPixel(int attr) {
TypedArray typedArray = ChanApplication.con.getTheme().obtainStyledAttributes(new int[]{attr});
int pixels = typedArray.getDimensionPixelSize(0, 0);
typedArray.recycle();
return pixels;
}
public static int getAttrColor(Context context, int attr) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(new int[]{attr});
int color = typedArray.getColor(0, 0);
@ -114,8 +107,8 @@ public class AndroidUtils {
return color;
}
public static Drawable getAttrDrawable(int attr) {
TypedArray typedArray = ChanApplication.con.getTheme().obtainStyledAttributes(new int[]{attr});
public static Drawable getAttrDrawable(Context context, int attr) {
TypedArray typedArray = context.obtainStyledAttributes(new int[]{attr});
Drawable drawable = typedArray.getDrawable(0);
typedArray.recycle();
return drawable;
@ -148,7 +141,7 @@ public class AndroidUtils {
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService( Context.INPUT_METHOD_SERVICE);
InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(view, 0);
}
});

@ -7,7 +7,7 @@
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_height="match_parent"
android:ellipsize="end"
android:gravity="center_vertical"
android:paddingLeft="16dp"

@ -3,7 +3,7 @@
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="?attr/selectableItemBackground">
android:background="?android:attr/selectableItemBackground">
<org.floens.chan.ui.view.ThumbnailView
android:id="@+id/thumb"
@ -17,7 +17,7 @@
<TextView
android:id="@+id/text"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:ellipsize="end"
android:gravity="center_vertical"
@ -27,4 +27,15 @@
android:textColor="#ff212121"
android:textSize="14sp" />
<TextView
android:id="@+id/watch_count"
android:layout_width="48dp"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackground"
android:singleLine="true"
android:gravity="center"
android:paddingRight="16dp"
android:textColor="#ff000000"
android:textSize="14sp" />
</LinearLayout>

Loading…
Cancel
Save