diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 3e9bb545ea..71e173e18f 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -316,6 +316,14 @@ + + + + + + diff --git a/res/drawable-hdpi/ic_reply.png b/res/drawable-hdpi/ic_reply.png new file mode 100644 index 0000000000..b3bae92895 Binary files /dev/null and b/res/drawable-hdpi/ic_reply.png differ diff --git a/res/drawable-mdpi/ic_reply.png b/res/drawable-mdpi/ic_reply.png new file mode 100644 index 0000000000..ce00dbc4b4 Binary files /dev/null and b/res/drawable-mdpi/ic_reply.png differ diff --git a/res/drawable-xhdpi/ic_reply.png b/res/drawable-xhdpi/ic_reply.png new file mode 100644 index 0000000000..31df111267 Binary files /dev/null and b/res/drawable-xhdpi/ic_reply.png differ diff --git a/res/drawable-xxhdpi/ic_reply.png b/res/drawable-xxhdpi/ic_reply.png new file mode 100644 index 0000000000..119006014d Binary files /dev/null and b/res/drawable-xxhdpi/ic_reply.png differ diff --git a/res/values/strings.xml b/res/values/strings.xml index 4363536aa1..6704027544 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -982,6 +982,14 @@ Transport icon + + Reply + + Yes + No + OK + Thanks + diff --git a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java index ef3cf8cae8..ef82154ce0 100644 --- a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java +++ b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java @@ -36,6 +36,7 @@ import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat.Action; import android.support.v4.app.NotificationCompat.BigTextStyle; import android.support.v4.app.NotificationCompat.InboxStyle; +import android.support.v4.app.RemoteInput; import android.text.Spannable; import android.text.SpannableString; import android.text.SpannableStringBuilder; @@ -86,6 +87,8 @@ public class MessageNotifier { private volatile static long visibleThread = -1; + public static final String EXTRA_VOICE_REPLY = "extra_voice_reply"; + public static void setVisibleThread(long threadId) { visibleThread = threadId; } @@ -222,9 +225,22 @@ public class MessageNotifier { Action markAsReadAction = new Action(R.drawable.check, context.getString(R.string.MessageNotifier_mark_read), notificationState.getMarkAsReadIntent(context, masterSecret)); + + Action replyAction = new Action(R.drawable.ic_reply_white_36dp, + context.getString(R.string.MessageNotifier_reply), + notifications.get(0).getReplyIntent(context)); + + Action wearableReplyAction = new Action.Builder(R.drawable.ic_reply, + context.getString(R.string.wear_reply_label), + notificationState.getReplyIntent(context, masterSecret, recipient.getRecipientId())) + .addRemoteInput(new RemoteInput.Builder(EXTRA_VOICE_REPLY).setLabel(context.getString(R.string.wear_reply_label)).build()) + .build(); + builder.addAction(markAsReadAction); - builder.addAction(new Action(R.drawable.ic_reply_white_36dp, context.getString(R.string.MessageNotifier_reply), notifications.get(0).getReplyIntent(context))); - builder.extend(new NotificationCompat.WearableExtender().addAction(markAsReadAction)); + builder.addAction(replyAction); + + builder.extend(new NotificationCompat.WearableExtender().addAction(markAsReadAction) + .addAction(wearableReplyAction)); } SpannableStringBuilder content = new SpannableStringBuilder(); diff --git a/src/org/thoughtcrime/securesms/notifications/NotificationState.java b/src/org/thoughtcrime/securesms/notifications/NotificationState.java index 3fc3bc15d9..52ac5563bb 100644 --- a/src/org/thoughtcrime/securesms/notifications/NotificationState.java +++ b/src/org/thoughtcrime/securesms/notifications/NotificationState.java @@ -4,11 +4,11 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.net.Uri; +import android.os.Bundle; import android.support.annotation.Nullable; import android.util.Log; import org.thoughtcrime.securesms.crypto.MasterSecret; -import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase; import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.VibrateState; import org.thoughtcrime.securesms.recipients.Recipients; @@ -71,6 +71,19 @@ public class NotificationState { } public PendingIntent getMarkAsReadIntent(Context context, MasterSecret masterSecret) { + Bundle extras = new Bundle(); + extras.putParcelable("master_secret", masterSecret); + return craftIntent(context, MarkReadReceiver.CLEAR_ACTION, extras); + } + + public PendingIntent getReplyIntent(Context context, MasterSecret masterSecret, long recipientId) { + Bundle extras = new Bundle(); + extras.putParcelable("master_secret", masterSecret); + extras.putLong("recipient_id", recipientId); + return craftIntent(context, WearReplyReceiver.REPLY_ACTION, extras); + } + + private PendingIntent craftIntent(Context context, String intentAction, Bundle extras) { long[] threadArray = new long[threads.size()]; int index = 0; @@ -79,16 +92,16 @@ public class NotificationState { threadArray[index++] = thread; } - Intent intent = new Intent(MarkReadReceiver.CLEAR_ACTION); + Intent intent = new Intent(intentAction); intent.putExtra("thread_ids", threadArray); - intent.putExtra("master_secret", masterSecret); + intent.putExtras(extras); intent.setPackage(context.getPackageName()); // XXX : This is an Android bug. If we don't pull off the extra // once before handing off the PendingIntent, the array will be // truncated to one element when the PendingIntent fires. Thanks guys! Log.w("NotificationState", "Pending array off intent length: " + - intent.getLongArrayExtra("thread_ids").length); + intent.getLongArrayExtra("thread_ids").length); return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } diff --git a/src/org/thoughtcrime/securesms/notifications/WearReplyReceiver.java b/src/org/thoughtcrime/securesms/notifications/WearReplyReceiver.java new file mode 100644 index 0000000000..6929c81f11 --- /dev/null +++ b/src/org/thoughtcrime/securesms/notifications/WearReplyReceiver.java @@ -0,0 +1,86 @@ +/** + * Copyright (C) 2011 Whisper Systems + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.thoughtcrime.securesms.notifications; + +import android.app.NotificationManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.app.RemoteInput; +import android.util.Log; + +import org.thoughtcrime.securesms.crypto.MasterSecret; +import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientFactory; +import org.thoughtcrime.securesms.recipients.RecipientProvider; +import org.thoughtcrime.securesms.recipients.Recipients; +import org.thoughtcrime.securesms.sms.MessageSender; +import org.thoughtcrime.securesms.sms.OutgoingTextMessage; + +/** + * Get the response text from the Wearable Device and sends an message as a reply + * + * @author Alix Ducros (Ported to TextSecure-Codebase by Christoph Haefner) + */ +public class WearReplyReceiver extends BroadcastReceiver { + + public static final String TAG = WearReplyReceiver.class.getSimpleName(); + public static final String REPLY_ACTION = "org.thoughtcrime.securesms.notifications.WEAR_REPLY"; + + @Override + public void onReceive(final Context context, Intent intent) { + if (!intent.getAction().equals(REPLY_ACTION)) + return; + + Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); + if (remoteInput == null) + return; + + final long[] threadIds = intent.getLongArrayExtra("thread_ids"); + final MasterSecret masterSecret = intent.getParcelableExtra("master_secret"); + final long recipientId = intent.getLongExtra("recipient_id", -1); + final CharSequence responseText = remoteInput.getCharSequence(MessageNotifier.EXTRA_VOICE_REPLY); + + final Recipients recipients = RecipientFactory.getRecipientsForIds(context, new long[]{recipientId}, false); + + if (threadIds != null && masterSecret != null) { + + ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)) + .cancel(MessageNotifier.NOTIFICATION_ID); + + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + for (long threadId : threadIds) { + Log.w(TAG, "Marking as read: " + threadId); + DatabaseFactory.getThreadDatabase(context).setRead(threadId); + } + + OutgoingTextMessage reply = new OutgoingTextMessage(recipients, responseText.toString()); + MessageSender.send(context, masterSecret, reply, threadIds[0], false); + + MessageNotifier.updateNotification(context, masterSecret); + return null; + } + }.execute(); + } + } +}