Notifications could still use some fine tuning, but should work fine for now

This commit is contained in:
Christian Basler 2015-09-04 08:19:07 +02:00
parent e5b00c7453
commit 496fffe6ee
16 changed files with 158 additions and 35 deletions

View File

@ -6,6 +6,8 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<application
android:allowBackup="true"

View File

@ -29,6 +29,9 @@ public class Identicon extends Drawable {
private static final int CENTER_COLUMN = 5;
private final Paint paint;
private float width;
private float height;
private float cellWidth;
private float cellHeight;
private byte[] hash;
@ -54,15 +57,11 @@ public class Identicon extends Drawable {
}
}
protected byte getByte(int index) {
return hash[index % hash.length];
}
@Override
public void draw(Canvas canvas) {
float x, y;
paint.setColor(background);
canvas.drawPaint(paint);
canvas.drawCircle(width/2, height/2, width/2, paint);
paint.setColor(color);
for (int row = 0; row < SIZE; row++) {
for (int column = 0; column < SIZE; column++) {
@ -88,13 +87,16 @@ public class Identicon extends Drawable {
@Override
public int getOpacity() {
return PixelFormat.OPAQUE;
return PixelFormat.TRANSPARENT;
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
width = bounds.width();
height = bounds.height();
cellWidth = bounds.width() / (float) SIZE;
cellHeight = bounds.height() / (float) SIZE;
}

View File

@ -52,7 +52,8 @@ import java.util.ArrayList;
*/
public class MessageListActivity extends AppCompatActivity
implements MessageListFragment.Callbacks {
public static final String EXTRA_SHOW_MESSAGE = "show_message";
public static final String EXTRA_SHOW_MESSAGE = "ch.dissem.abit.ShowMessage";
public static final String ACTION_SHOW_INBOX = "ch.dissem.abit.ShowInbox";
private static final Logger LOG = LoggerFactory.getLogger(MessageListActivity.class);
private static final int ADD_IDENTITY = 1;
@ -95,7 +96,12 @@ public class MessageListActivity extends AppCompatActivity
createDrawer(toolbar);
// TODO: If exposing deep links into your app, handle intents here.
Singleton.getMessageListener(this).resetNotification();
// handle intents
if (getIntent().hasExtra(EXTRA_SHOW_MESSAGE)) {
onItemSelected((Plaintext) getIntent().getSerializableExtra(EXTRA_SHOW_MESSAGE));
}
}
private void createDrawer(Toolbar toolbar) {
@ -103,6 +109,7 @@ public class MessageListActivity extends AppCompatActivity
for (BitmessageAddress identity : bmc.addresses().getIdentities()) {
LOG.info("Adding identity " + identity.getAddress());
profiles.add(new ProfileDrawerItem()
.withIcon(new Identicon(identity))
.withName(identity.toString())
.withEmail(identity.getAddress())
.withTag(identity)
@ -173,6 +180,7 @@ public class MessageListActivity extends AppCompatActivity
selectedLabel = (Label) item.getTag();
((MessageListFragment) getSupportFragmentManager()
.findFragmentById(R.id.message_list)).updateList(selectedLabel);
return true;
} else if (item instanceof Nameable<?>) {
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.

View File

@ -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());
}

View File

@ -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.
* <p>
* Should show a notification when the app isn't running, but update the message list when it is. Also,
* notifications should be combined.
* </p>
*/
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<Plaintext> 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);
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());
.setContentText(plaintext.getSubject())
.setStyle(new NotificationCompat.BigTextStyle().bigText(bigText))
.setContentInfo("Info");
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);
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();
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);
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.putExtra(MessageListActivity.EXTRA_SHOW_MESSAGE, plaintext);
PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 0, intent, 0);
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;
}
}
}

View File

@ -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<Plaintext> 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 {

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 775 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 B

View File

@ -2,18 +2,21 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:fab="http://schemas.android.com/tools">
android:layout_height="match_parent">
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@id/android:list"
android:paddingBottom="88dp"
android:clipToPadding="false"
android:scrollbarStyle="outsideOverlay"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true" />
android:layout_alignParentBottom="true"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_compose_message"

View File

@ -24,4 +24,7 @@
<string name="do_import">Import</string>
<string name="cancel">Cancel</string>
<string name="broadcast">Broadcast</string>
<string name="n_new_messages">%d new messages</string>
<string name="reply">Reply</string>
<string name="delete">Delete</string>
</resources>