From 01e12dfbf9375646733141b607ed76525f14661d Mon Sep 17 00:00:00 2001 From: Florens Douwes Date: Thu, 10 Apr 2014 23:34:06 +0200 Subject: [PATCH] Added a badge on the ActionBar icon for new posts. Colored gray and red with red as new replies. Shows the total count of new posts of watching pins. --- Chan/src/org/floens/chan/core/model/Pin.java | 10 +- .../floens/chan/core/watch/PinWatcher.java | 11 +- .../floens/chan/core/watch/WatchNotifier.java | 2 +- .../src/org/floens/chan/ui/BadgeDrawable.java | 65 ++++++++++ .../floens/chan/ui/activity/BaseActivity.java | 113 +++++++++++------- .../floens/chan/ui/adapter/PinnedAdapter.java | 2 +- 6 files changed, 142 insertions(+), 61 deletions(-) create mode 100644 Chan/src/org/floens/chan/ui/BadgeDrawable.java diff --git a/Chan/src/org/floens/chan/core/model/Pin.java b/Chan/src/org/floens/chan/core/model/Pin.java index ad088854..69329740 100644 --- a/Chan/src/org/floens/chan/core/model/Pin.java +++ b/Chan/src/org/floens/chan/core/model/Pin.java @@ -39,6 +39,8 @@ public class Pin { @DatabaseField public int quoteNewCount; + + public boolean isError = false; public PinWatcher getPinWatcher() { return pinWatcher; @@ -82,14 +84,6 @@ public class Pin { pinWatcher = null; } } - - public boolean isError() { - if (pinWatcher != null) { - return pinWatcher.isError(); - } else { - return false; - } - } } diff --git a/Chan/src/org/floens/chan/core/watch/PinWatcher.java b/Chan/src/org/floens/chan/core/watch/PinWatcher.java index ad8935a3..1b6e4cf1 100644 --- a/Chan/src/org/floens/chan/core/watch/PinWatcher.java +++ b/Chan/src/org/floens/chan/core/watch/PinWatcher.java @@ -17,7 +17,6 @@ public class PinWatcher implements Loader.LoaderListener { private final Pin pin; private Loader loader; - private boolean isError = false; private final List posts = new ArrayList(); private boolean wereNewQuotes = false; @@ -36,7 +35,7 @@ public class PinWatcher implements Loader.LoaderListener { } public void update() { - if (!isError) { + if (!pin.isError) { loader.loadMoreIfTime(); } } @@ -80,21 +79,17 @@ public class PinWatcher implements Loader.LoaderListener { } } - public boolean isError() { - return isError; - } - @Override public void onError(VolleyError error) { Logger.e(TAG, "PinWatcher onError: ", error); - isError = true; + pin.isError = true; WatchService.onPinWatcherResult(); } @Override public void onData(List result, boolean append) { - isError = false; + pin.isError = false; posts.clear(); posts.addAll(result); diff --git a/Chan/src/org/floens/chan/core/watch/WatchNotifier.java b/Chan/src/org/floens/chan/core/watch/WatchNotifier.java index 18b9d5dc..0f065b91 100644 --- a/Chan/src/org/floens/chan/core/watch/WatchNotifier.java +++ b/Chan/src/org/floens/chan/core/watch/WatchNotifier.java @@ -75,7 +75,7 @@ public class WatchNotifier { for (Pin pin : watchingPins) { PinWatcher watcher = pin.getPinWatcher(); - if (watcher == null || watcher.isError()) + if (watcher == null || pin.isError) continue; boolean add = false; diff --git a/Chan/src/org/floens/chan/ui/BadgeDrawable.java b/Chan/src/org/floens/chan/ui/BadgeDrawable.java new file mode 100644 index 00000000..d6877127 --- /dev/null +++ b/Chan/src/org/floens/chan/ui/BadgeDrawable.java @@ -0,0 +1,65 @@ +package org.floens.chan.ui; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; + +public class BadgeDrawable { + public static Drawable get(Resources resources, int id, int count, boolean red) { + BitmapFactory.Options opt = new BitmapFactory.Options(); + opt.inMutable = true; + Bitmap bitmap = BitmapFactory.decodeResource(resources, id, opt); + int w = bitmap.getWidth(); + int h = bitmap.getHeight(); + + Paint paint = new Paint(); + paint.setAntiAlias(true); + + Canvas canvas = new Canvas(bitmap); + + float badgeX = w * 0.3f; + float badgeY = h * 0.3f; + float badgeW = w * 0.6f; + float badgeH = h * 0.6f; + + RectF rect = new RectF(badgeX, badgeY, badgeX + badgeW, badgeY + badgeH); + if (red) { + paint.setColor(0xffff4444); + } else { + paint.setColor(0xaa000000); + } + canvas.drawRoundRect(rect, w * 0.1f, h * 0.1f, paint); + + String text = Integer.toString(count); + if (count > 999) { + text = "1k+"; + } + + paint.setColor(0xffffffff); + + float textHeight; + float bottomOffset; + if (text.length() <= 2) { + textHeight = badgeH * 0.8f; + bottomOffset = badgeH * 0.2f; + } else { + textHeight = badgeH * 0.5f; + bottomOffset = badgeH * 0.3f; + } + + paint.setTextSize(textHeight); + + Rect bounds = new Rect(); + paint.getTextBounds(text, 0, text.length(), bounds); + + canvas.drawText(text, badgeX + badgeW / 2f - bounds.right / 2f, badgeY + badgeH - bottomOffset, paint); + + return new BitmapDrawable(resources, bitmap); + } +} diff --git a/Chan/src/org/floens/chan/ui/activity/BaseActivity.java b/Chan/src/org/floens/chan/ui/activity/BaseActivity.java index c5d22d3f..71db5180 100644 --- a/Chan/src/org/floens/chan/ui/activity/BaseActivity.java +++ b/Chan/src/org/floens/chan/ui/activity/BaseActivity.java @@ -1,10 +1,13 @@ package org.floens.chan.ui.activity; +import java.util.List; + import org.floens.chan.ChanApplication; import org.floens.chan.R; import org.floens.chan.core.manager.PinnedManager; import org.floens.chan.core.model.Pin; import org.floens.chan.core.model.Post; +import org.floens.chan.ui.BadgeDrawable; import org.floens.chan.ui.SwipeDismissListViewTouchListener; import org.floens.chan.ui.SwipeDismissListViewTouchListener.DismissCallbacks; import org.floens.chan.ui.adapter.PinnedAdapter; @@ -15,6 +18,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnShowListener; import android.content.Intent; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.nfc.NdefMessage; import android.nfc.NdefRecord; @@ -51,12 +55,14 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene /** * Called when a post has been clicked in the pinned drawer + * * @param post */ abstract public void openPin(Pin post); /** * Called when a post has been clicked in the listview + * * @param post */ abstract public void onOPClicked(Post post); @@ -74,6 +80,8 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene initPane(); ChanApplication.getPinnedManager().addPinListener(this); + + updateIcon(); } @Override @@ -87,7 +95,7 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene protected void onNewIntent(Intent intent) { super.onNewIntent(intent); -// pinDrawer.openDrawer(pinDrawerView); + // pinDrawer.openDrawer(pinDrawerView); } private void initPane() { @@ -106,7 +114,7 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene pinDrawer.setDrawerListener(pinDrawerListener); 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.reload(); @@ -116,7 +124,8 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene @Override public void onItemClick(AdapterView parent, View view, int position, long id) { Pin pin = pinnedAdapter.getItem(position); - if (pin == null || pin.type == Pin.Type.HEADER) return; + if (pin == null || pin.type == Pin.Type.HEADER) + return; openPin(pin); } }); @@ -125,7 +134,8 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene @Override public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { Pin post = pinnedAdapter.getItem(position); - if (post == null || post.type == Pin.Type.HEADER) return false; + if (post == null || post.type == Pin.Type.HEADER) + return false; changePinTitle(post); @@ -134,19 +144,19 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene }); SwipeDismissListViewTouchListener touchListener = new SwipeDismissListViewTouchListener(pinDrawerView, - new DismissCallbacks() { - @Override - public void onDismiss(ListView listView, int[] reverseSortedPositions) { - for (int position : reverseSortedPositions) { - removePin(pinnedAdapter.getItem(position)); + new DismissCallbacks() { + @Override + public void onDismiss(ListView listView, int[] reverseSortedPositions) { + for (int position : reverseSortedPositions) { + removePin(pinnedAdapter.getItem(position)); + } } - } - @Override - public boolean canDismiss(int position) { - return pinnedAdapter.getItem(position).type != Pin.Type.HEADER; - } - }); + @Override + public boolean canDismiss(int position) { + return pinnedAdapter.getItem(position).type != Pin.Type.HEADER; + } + }); pinDrawerView.setOnTouchListener(touchListener); pinDrawerView.setOnScrollListener(touchListener.makeScrollListener()); @@ -156,6 +166,30 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene public void onPinsChanged() { pinnedAdapter.reload(); pinDrawerView.invalidate(); + updateIcon(); + } + + private void updateIcon() { + List list = ChanApplication.getPinnedManager().getWatchingPins(); + if (list.size() > 0) { + int count = 0; + boolean color = false; + for (Pin p : list) { + count += p.getNewPostsCount(); + if (p.getNewQuoteCount() > 0) { + color = true; + } + } + + if (count > 0) { + Drawable icon = BadgeDrawable.get(getResources(), R.drawable.ic_launcher, count, color); + getActionBar().setIcon(icon); + } else { + getActionBar().setIcon(R.drawable.ic_launcher); + } + } else { + getActionBar().setIcon(R.drawable.ic_launcher); + } } public void addPin(Pin pin) { @@ -177,25 +211,21 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene text.setSelectAllOnFocus(true); AlertDialog dialog = new AlertDialog.Builder(this) - .setPositiveButton(R.string.change, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface d, int which) { - String value = text.getText().toString(); - - if (!TextUtils.isEmpty(value)) { - pin.loadable.title = value; - updatePin(pin); + .setPositiveButton(R.string.change, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface d, int which) { + String value = text.getText().toString(); + + if (!TextUtils.isEmpty(value)) { + pin.loadable.title = value; + updatePin(pin); + } } - } - }) - .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface d, int which) { - } - }) - .setTitle(R.string.drawer_pinned_change_title) - .setView(text) - .create(); + }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface d, int which) { + } + }).setTitle(R.string.drawer_pinned_change_title).setView(text).create(); text.requestFocus(); @@ -212,7 +242,7 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene @Override public boolean onOptionsItemSelected(MenuItem item) { - switch(item.getItemId()) { + switch (item.getItemId()) { case R.id.action_settings: startActivity(new Intent(this, SettingsActivity.class)); return true; @@ -243,6 +273,7 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene /** * Set the url that Android Beam and the share action will send. + * * @param url */ public void setShareUrl(String url) { @@ -252,12 +283,12 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene NdefRecord record = null; try { record = NdefRecord.createUri(url); - } catch(IllegalArgumentException e) { + } catch (IllegalArgumentException e) { e.printStackTrace(); return; } - NdefMessage message = new NdefMessage(new NdefRecord[] {record}); + NdefMessage message = new NdefMessage(new NdefRecord[] { record }); adapter.setNdefPushMessage(message, this); } @@ -270,8 +301,9 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene } /** - * 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. + * 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. + * * @param url */ public void showUrlOpenPicker(String url) { @@ -296,8 +328,3 @@ public abstract class BaseActivity extends Activity implements PanelSlideListene } } } - - - - - diff --git a/Chan/src/org/floens/chan/ui/adapter/PinnedAdapter.java b/Chan/src/org/floens/chan/ui/adapter/PinnedAdapter.java index 43dfd438..0dc223cc 100644 --- a/Chan/src/org/floens/chan/ui/adapter/PinnedAdapter.java +++ b/Chan/src/org/floens/chan/ui/adapter/PinnedAdapter.java @@ -50,7 +50,7 @@ public class PinnedAdapter extends ArrayAdapter { TextView itemCount = (TextView) view.findViewById(R.id.drawer_item_count); - if (item.isError()) { + if (item.isError) { itemCount.setText("Err"); } else { int count = item.getNewPostsCount();