) {
Nameable> ni = (Nameable>) item;
switch (ni.getNameRes()) {
@@ -237,7 +245,6 @@ public class MessageListActivity extends AppCompatActivity
getSupportFragmentManager().beginTransaction()
.replace(R.id.message_detail_container, fragment)
.commit();
-
} else {
// In single-pane mode, simply start the detail activity
// for the selected item ID.
diff --git a/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java b/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java
index 2dca634..abbd60d 100644
--- a/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java
+++ b/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java
@@ -82,6 +82,11 @@ public class MessageListFragment extends ListFragment {
super.onCreate(savedInstanceState);
bmc = Singleton.getBitmessageContext(getActivity());
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
updateList(((MessageListActivity) getActivity()).getSelectedLabel());
}
diff --git a/app/src/main/java/ch/dissem/apps/abit/MessageListener.java b/app/src/main/java/ch/dissem/apps/abit/MessageListener.java
index 6b533f8..c9adc4f 100644
--- a/app/src/main/java/ch/dissem/apps/abit/MessageListener.java
+++ b/app/src/main/java/ch/dissem/apps/abit/MessageListener.java
@@ -16,54 +16,134 @@
package ch.dissem.apps.abit;
+import android.annotation.TargetApi;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Typeface;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.ContactsContract;
import android.support.v7.app.NotificationCompat;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.style.StyleSpan;
import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.entity.Plaintext;
+import java.util.LinkedList;
+
/**
- * Created by chris on 22.08.15.
+ * Listens for decrypted Bitmessage messages. Does show a notification.
+ *
+ * Should show a notification when the app isn't running, but update the message list when it is. Also,
+ * notifications should be combined.
+ *
*/
public class MessageListener implements BitmessageContext.Listener {
+ private static final StyleSpan SPAN_EMPHASIS = new StyleSpan(Typeface.BOLD);
private final Context ctx;
private final NotificationManager manager;
+ private final LinkedList unacknowledged = new LinkedList<>();
+ private final int pictureSize;
+ private int numberOfUnacknowledgedMessages = 0;
public MessageListener(Context ctx) {
this.ctx = ctx.getApplicationContext();
this.manager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ this.pictureSize = getMaxContactPhotoSize(ctx);
+ }
+
+ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ public static int getMaxContactPhotoSize(final Context context) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ // Note that this URI is safe to call on the UI thread.
+ final Uri uri = ContactsContract.DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI;
+ final String[] projection = new String[]{ContactsContract.DisplayPhoto.DISPLAY_MAX_DIM};
+ final Cursor c = context.getContentResolver().query(uri, projection, null, null, null);
+ try {
+ c.moveToFirst();
+ return c.getInt(0);
+ } finally {
+ c.close();
+ }
+ }
+ // fallback: 96x96 is the max contact photo size for pre-ICS versions
+ return 96;
}
@Override
public void receive(final Plaintext plaintext) {
- // TODO
-// ctx.runOnUiThread(new Runnable() {
-// @Override
-// public void run() {
+ synchronized (unacknowledged) {
+ unacknowledged.addFirst(plaintext);
+ numberOfUnacknowledgedMessages++;
+ if (unacknowledged.size() > 5) {
+ unacknowledged.removeLast();
+ }
+ }
+
NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx);
- builder.setSmallIcon(R.drawable.ic_notification_new_message)
- .setContentTitle(plaintext.getFrom().toString())
- .setContentText(plaintext.getSubject());
+ if (numberOfUnacknowledgedMessages == 1) {
+ Spannable bigText = new SpannableString(plaintext.getSubject() + "\n" + plaintext.getText());
+ bigText.setSpan(SPAN_EMPHASIS, 0, plaintext.getSubject().length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ builder.setSmallIcon(R.drawable.ic_notification_new_message)
+ .setLargeIcon(toBitmap(new Identicon(plaintext.getFrom())))
+ .setContentTitle(plaintext.getFrom().toString())
+ .setContentText(plaintext.getSubject())
+ .setStyle(new NotificationCompat.BigTextStyle().bigText(bigText))
+ .setContentInfo("Info");
- NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
- inboxStyle.setBigContentTitle(plaintext.getFrom().toString());
- inboxStyle.setSummaryText(plaintext.getSubject());
- String text = plaintext.getText();
- if (text.length() > 100)
- inboxStyle.addLine(text.substring(0, 100) + "…");
- else
- inboxStyle.addLine(text);
- builder.setStyle(inboxStyle);
+ Intent showMessageIntent = new Intent(ctx, MessageListActivity.class);
+ showMessageIntent.putExtra(MessageListActivity.EXTRA_SHOW_MESSAGE, plaintext);
+ PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 0, showMessageIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ builder.setContentIntent(pendingIntent);
- Intent intent = new Intent(ctx, MessageListActivity.class);
- intent.putExtra(MessageListActivity.EXTRA_SHOW_MESSAGE, plaintext);
- PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 0, intent, 0);
- builder.setContentIntent(pendingIntent);
+ builder.addAction(R.drawable.ic_action_reply, ctx.getString(R.string.reply), pendingIntent);
+ builder.addAction(R.drawable.ic_action_delete, ctx.getString(R.string.delete), pendingIntent);
+ } else {
+ builder.setSmallIcon(R.drawable.ic_notification_new_message)
+ .setContentTitle(ctx.getString(R.string.n_new_messages, this.unacknowledged.size()))
+ .setContentText(ctx.getString(R.string.app_name));
+
+ NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
+ synchronized (unacknowledged) {
+ inboxStyle.setBigContentTitle(ctx.getString(R.string.n_new_messages, numberOfUnacknowledgedMessages));
+ for (Plaintext msg : unacknowledged) {
+ Spannable sb = new SpannableString(msg.getFrom() + " " + msg.getSubject());
+ sb.setSpan(SPAN_EMPHASIS, 0, String.valueOf(msg.getFrom()).length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+ inboxStyle.addLine(sb);
+ }
+ }
+ builder.setStyle(inboxStyle);
+
+ Intent intent = new Intent(ctx, MessageListActivity.class);
+ intent.setAction(MessageListActivity.ACTION_SHOW_INBOX);
+ PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 1, intent, 0);
+ builder.setContentIntent(pendingIntent);
+ }
manager.notify(0, builder.build());
-// }
-// });
+ }
+
+ private Bitmap toBitmap(Identicon identicon) {
+ Bitmap bitmap = Bitmap.createBitmap(pictureSize, pictureSize, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ identicon.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ identicon.draw(canvas);
+ return bitmap;
+ }
+
+ public void resetNotification() {
+ manager.cancel(0);
+ synchronized (unacknowledged) {
+ unacknowledged.clear();
+ numberOfUnacknowledgedMessages = 0;
+ }
}
}
diff --git a/app/src/main/java/ch/dissem/apps/abit/repositories/AndroidMessageRepository.java b/app/src/main/java/ch/dissem/apps/abit/repositories/AndroidMessageRepository.java
index 8fa1bd6..b290834 100644
--- a/app/src/main/java/ch/dissem/apps/abit/repositories/AndroidMessageRepository.java
+++ b/app/src/main/java/ch/dissem/apps/abit/repositories/AndroidMessageRepository.java
@@ -19,6 +19,7 @@ package ch.dissem.apps.abit.repositories;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
+import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase;
import ch.dissem.apps.abit.R;
import ch.dissem.bitmessage.InternalContext;
@@ -151,6 +152,24 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont
return label;
}
+ @Override
+ public int countUnread(Label label) {
+ String where;
+ if (label != null) {
+ where = "id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label.getId() + ") AND ";
+ } else {
+ where = "";
+ }
+ SQLiteDatabase db = sql.getReadableDatabase();
+ Cursor c = db.query(
+ TABLE_NAME, new String[]{COLUMN_ID},
+ where + "id IN (SELECT message_id FROM Message_Label WHERE label_id IN (" +
+ "SELECT id FROM Label WHERE type = '" + Label.Type.UNREAD.name() + "'))",
+ null, null, null, null
+ );
+ return c.getColumnCount();
+ }
+
@Override
public List findMessages(Label label) {
if (label != null) {
@@ -258,6 +277,8 @@ public class AndroidMessageRepository implements MessageRepository, InternalCont
db.insertOrThrow(JOIN_TABLE_NAME, null, values);
}
db.setTransactionSuccessful();
+ } catch (SQLiteConstraintException e) {
+ LOG.trace(e.getMessage(), e);
} catch (IOException e) {
LOG.error(e.getMessage(), e);
} finally {
diff --git a/app/src/main/res/drawable-hdpi/ic_action_delete.png b/app/src/main/res/drawable-hdpi/ic_action_delete.png
new file mode 100644
index 0000000..4bb5259
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_delete.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_reply.png b/app/src/main/res/drawable-hdpi/ic_action_reply.png
new file mode 100644
index 0000000..be372f5
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_reply.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_delete.png b/app/src/main/res/drawable-mdpi/ic_action_delete.png
new file mode 100644
index 0000000..95258c3
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_delete.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_reply.png b/app/src/main/res/drawable-mdpi/ic_action_reply.png
new file mode 100644
index 0000000..f93102a
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_reply.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_delete.png b/app/src/main/res/drawable-xhdpi/ic_action_delete.png
new file mode 100644
index 0000000..35c97c1
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_delete.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_reply.png b/app/src/main/res/drawable-xhdpi/ic_action_reply.png
new file mode 100644
index 0000000..ea7c4ea
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_reply.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_delete.png b/app/src/main/res/drawable-xxhdpi/ic_action_delete.png
new file mode 100644
index 0000000..cfb99e1
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_delete.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_reply.png b/app/src/main/res/drawable-xxhdpi/ic_action_reply.png
new file mode 100644
index 0000000..9b6d9ef
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_reply.png differ
diff --git a/app/src/main/res/layout/fragment_message_list.xml b/app/src/main/res/layout/fragment_message_list.xml
index 6e7982b..cd73e5b 100644
--- a/app/src/main/res/layout/fragment_message_list.xml
+++ b/app/src/main/res/layout/fragment_message_list.xml
@@ -2,18 +2,21 @@
+ android:layout_height="match_parent">
+ android:layout_alignParentBottom="true"/>
Import
Cancel
Broadcast
+ %d new messages
+ Reply
+ Delete