diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index cee67568bb6..406a64d24a6 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -107,6 +107,9 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/notifications/AndroidAutoHeardReceiver.java b/src/org/thoughtcrime/securesms/notifications/AndroidAutoHeardReceiver.java
new file mode 100644
index 00000000000..fad0d04473f
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/notifications/AndroidAutoHeardReceiver.java
@@ -0,0 +1,79 @@
+/**
+ * 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.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.support.annotation.Nullable;
+
+import org.thoughtcrime.securesms.ApplicationContext;
+import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.database.DatabaseFactory;
+import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
+import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
+import org.thoughtcrime.securesms.jobs.MultiDeviceReadUpdateJob;
+import org.whispersystems.libsignal.logging.Log;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Marks an Android Auto as read after the driver have listened to it
+ */
+public class AndroidAutoHeardReceiver extends MasterSecretBroadcastReceiver {
+
+ public static final String TAG = AndroidAutoHeardReceiver.class.getSimpleName();
+ public static final String HEARD_ACTION = "org.thoughtcrime.securesms.notifications.ANDROID_AUTO_HEARD";
+ public static final String THREAD_IDS_EXTRA = "car_heard_thread_ids";
+
+ @Override
+ protected void onReceive(final Context context, Intent intent,
+ @Nullable final MasterSecret masterSecret)
+ {
+ if (!HEARD_ACTION.equals(intent.getAction()))
+ return;
+
+ final long[] threadIds = intent.getLongArrayExtra(THREAD_IDS_EXTRA);
+
+ if (threadIds != null) {
+ ((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE))
+ .cancel(MessageNotifier.NOTIFICATION_ID);
+
+ new AsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ List messageIdsCollection = new LinkedList<>();
+
+ for (long threadId : threadIds) {
+ Log.i(TAG, "Marking meassage as read: " + threadId);
+ List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(threadId);
+
+ messageIdsCollection.addAll(messageIds);
+ }
+
+ MessageNotifier.updateNotification(context, masterSecret);
+ MarkReadReceiver.process(context, messageIdsCollection);
+
+ return null;
+ }
+ }.execute();
+ }
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java b/src/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java
new file mode 100644
index 00000000000..5169b857ed7
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java
@@ -0,0 +1,111 @@
+/**
+ * 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.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.RemoteInput;
+
+import org.thoughtcrime.securesms.attachments.Attachment;
+import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.database.DatabaseFactory;
+import org.thoughtcrime.securesms.database.MessagingDatabase;
+import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
+import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
+import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
+import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
+import org.thoughtcrime.securesms.recipients.RecipientFactory;
+import org.thoughtcrime.securesms.recipients.Recipients;
+import org.thoughtcrime.securesms.sms.MessageSender;
+import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
+import org.whispersystems.libsignal.logging.Log;
+import org.whispersystems.libsignal.util.guava.Optional;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Get the response text from the Android Auto and sends an message as a reply
+ */
+public class AndroidAutoReplyReceiver extends MasterSecretBroadcastReceiver {
+
+ public static final String TAG = AndroidAutoReplyReceiver.class.getSimpleName();
+ public static final String REPLY_ACTION = "org.thoughtcrime.securesms.notifications.ANDROID_AUTO_REPLY";
+ public static final String RECIPIENT_IDS_EXTRA = "car_recipient_ids";
+ public static final String VOICE_REPLY_KEY = "car_voice_reply_key";
+ public static final String THREAD_ID_EXTRA = "car_reply_thread_id";
+
+ @Override
+ protected void onReceive(final Context context, Intent intent,
+ final @Nullable MasterSecret masterSecret)
+ {
+ if (!REPLY_ACTION.equals(intent.getAction())) return;
+
+ Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
+
+ if (remoteInput == null) return;
+
+ final long[] recipientIds = intent.getLongArrayExtra(RECIPIENT_IDS_EXTRA);
+ final long threadId = intent.getLongExtra(THREAD_ID_EXTRA, -1);
+ final CharSequence responseText = getMessageText(intent);
+ final Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientIds, false);
+
+ if (responseText != null) {
+ new AsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+
+ long replyThreadId;
+
+ Optional preferences = DatabaseFactory.getRecipientPreferenceDatabase(context).getRecipientsPreferences(recipientIds);
+ int subscriptionId = preferences.isPresent() ? preferences.get().getDefaultSubscriptionId().or(-1) : -1;
+ long expiresIn = preferences.isPresent() ? preferences.get().getExpireMessages() * 1000 : 0;
+
+ if (recipients.isGroupRecipient()) {
+ Log.i("AndroidAutoReplyReceiver", "GroupRecipient, Sending media message");
+ OutgoingMediaMessage reply = new OutgoingMediaMessage(recipients, responseText.toString(), new LinkedList(), System.currentTimeMillis(), subscriptionId, expiresIn, 0);
+ replyThreadId = MessageSender.send(context, masterSecret, reply, threadId, false);
+ } else {
+ Log.i("AndroidAutoReplyReceiver", "Sending regular message ");
+ OutgoingTextMessage reply = new OutgoingTextMessage(recipients, responseText.toString(), expiresIn, subscriptionId);
+ replyThreadId = MessageSender.send(context, masterSecret, reply, threadId, false);
+ }
+
+ List messageIds = DatabaseFactory.getThreadDatabase(context).setRead(replyThreadId);
+ MessageNotifier.updateNotification(context, masterSecret);
+ MarkReadReceiver.process(context, messageIds);
+
+ return null;
+ }
+ }.execute();
+ }
+ }
+
+ private CharSequence getMessageText(Intent intent) {
+ Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
+ if (remoteInput != null) {
+ return remoteInput.getCharSequence(VOICE_REPLY_KEY);
+ }
+ return null;
+ }
+
+}
diff --git a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java
index 3af64a69b29..0695001fadd 100644
--- a/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java
+++ b/src/org/thoughtcrime/securesms/notifications/MessageNotifier.java
@@ -258,6 +258,8 @@ private static void sendSingleThreadNotification(@NonNull Context context,
notificationState.getMarkAsReadIntent(context),
notificationState.getQuickReplyIntent(context, notifications.get(0).getRecipients()),
notificationState.getWearableReplyIntent(context, notifications.get(0).getRecipients()));
+ builder.addAndroidAutoAction(notificationState.getAndroidAutoReplyIntent(context, notifications.get(0).getRecipients()),
+ notificationState.getAndroidAutoHeardIntent(context, notifications.get(0).getRecipients()), notifications.get(0).getTimestamp());
ListIterator iterator = notifications.listIterator(notifications.size());
diff --git a/src/org/thoughtcrime/securesms/notifications/NotificationState.java b/src/org/thoughtcrime/securesms/notifications/NotificationState.java
index 51391b8d17d..d0f42edea8d 100644
--- a/src/org/thoughtcrime/securesms/notifications/NotificationState.java
+++ b/src/org/thoughtcrime/securesms/notifications/NotificationState.java
@@ -102,6 +102,35 @@ public PendingIntent getWearableReplyIntent(Context context, Recipients recipien
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
+ public PendingIntent getAndroidAutoReplyIntent(Context context, Recipients recipients) {
+ if (threads.size() != 1) throw new AssertionError("We only support replies to single thread notifications!");
+
+ Intent intent = new Intent(AndroidAutoReplyReceiver.REPLY_ACTION);
+ intent.putExtra(AndroidAutoReplyReceiver.RECIPIENT_IDS_EXTRA, recipients.getIds());
+ intent.putExtra(AndroidAutoReplyReceiver.THREAD_ID_EXTRA, (long)threads.toArray()[0]);
+ intent.setPackage(context.getPackageName());
+
+ return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ public PendingIntent getAndroidAutoHeardIntent(Context context, Recipients recipients) {
+ long[] threadArray = new long[threads.size()];
+ int index = 0;
+ for (long thread : threads) {
+ Log.w("NotificationState", "getAndroidAutoHeardIntent Added thread: " + thread);
+ threadArray[index++] = thread;
+ }
+
+ Intent intent = new Intent(AndroidAutoHeardReceiver.HEARD_ACTION);
+ intent.putExtra(AndroidAutoHeardReceiver.THREAD_IDS_EXTRA, threadArray);
+ intent.setPackage(context.getPackageName());
+
+ Log.w("NotificationState", "getAndroidAutoHeardIntent - Pending array off intent length: " +
+ intent.getLongArrayExtra(AndroidAutoHeardReceiver.THREAD_IDS_EXTRA).length);
+
+ return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
public PendingIntent getQuickReplyIntent(Context context, Recipients recipients) {
if (threads.size() != 1) throw new AssertionError("We only support replies to single thread notifications!");
diff --git a/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java b/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java
index 3987c8dd145..4a2bc529e1b 100644
--- a/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java
+++ b/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java
@@ -101,6 +101,27 @@ public void setPrimaryMessageBody(@NonNull Recipients threadRecipients,
}
}
+ public void addAndroidAutoAction(@NonNull PendingIntent androidAutoReplyIntent,
+ @NonNull PendingIntent androidAutoHeardIntent, long timestamp)
+ {
+
+ if (mContentTitle == null || mContentText == null)
+ return;
+
+ RemoteInput remoteInput = new RemoteInput.Builder(AndroidAutoReplyReceiver.VOICE_REPLY_KEY)
+ .setLabel(context.getString(R.string.MessageNotifier_reply))
+ .build();
+
+ NotificationCompat.CarExtender.UnreadConversation.Builder unreadConversationBuilder =
+ new NotificationCompat.CarExtender.UnreadConversation.Builder(mContentTitle.toString())
+ .addMessage(mContentText.toString())
+ .setLatestTimestamp(timestamp)
+ .setReadPendingIntent(androidAutoHeardIntent)
+ .setReplyAction(androidAutoReplyIntent, remoteInput);
+
+ extend(new NotificationCompat.CarExtender().setUnreadConversation(unreadConversationBuilder.build()));
+ }
+
public void addActions(@Nullable MasterSecret masterSecret,
@NonNull PendingIntent markReadIntent,
@NonNull PendingIntent quickReplyIntent,