Resolves #471 Actionable notification
This commit is contained in:
@ -56,6 +56,12 @@
|
|||||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
<receiver android:name=".NotificationActionReceiver" android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||||
|
<action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name="io.flutter.plugins.androidalarmmanager.AlarmService"
|
android:name="io.flutter.plugins.androidalarmmanager.AlarmService"
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package com.keyboardcrumbs.hassclient;
|
package com.keyboardcrumbs.hassclient;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
@ -16,8 +20,9 @@ import android.util.Log;
|
|||||||
import com.google.firebase.messaging.FirebaseMessagingService;
|
import com.google.firebase.messaging.FirebaseMessagingService;
|
||||||
import com.google.firebase.messaging.RemoteMessage;
|
import com.google.firebase.messaging.RemoteMessage;
|
||||||
|
|
||||||
import androidx.work.OneTimeWorkRequest;
|
import android.graphics.Bitmap;
|
||||||
import androidx.work.WorkManager;
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.webkit.URLUtil;
|
||||||
|
|
||||||
|
|
||||||
public class MessagingService extends FirebaseMessagingService {
|
public class MessagingService extends FirebaseMessagingService {
|
||||||
@ -31,14 +36,7 @@ public class MessagingService extends FirebaseMessagingService {
|
|||||||
if (data.size() > 0) {
|
if (data.size() > 0) {
|
||||||
Log.d(TAG, "Message data payload: " + data);
|
Log.d(TAG, "Message data payload: " + data);
|
||||||
if (data.containsKey("body") || data.containsKey("title")) {
|
if (data.containsKey("body") || data.containsKey("title")) {
|
||||||
sendNotification(
|
sendNotification(data);
|
||||||
data.get("body"),
|
|
||||||
data.get("title"),
|
|
||||||
data.get("channelId"),
|
|
||||||
data.get("action1"),
|
|
||||||
data.get("action2"),
|
|
||||||
data.get("action3")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,22 +47,31 @@ public class MessagingService extends FirebaseMessagingService {
|
|||||||
//TODO update token
|
//TODO update token
|
||||||
}
|
}
|
||||||
|
|
||||||
private void executeAction() {
|
private void sendNotification(Map<String, String> data) {
|
||||||
OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(NotificationActionWorker.class)
|
String channelId, messageBody, messageTitle, imageUrl;
|
||||||
.build();
|
String nTag;
|
||||||
WorkManager.getInstance().beginWith(work).enqueue();
|
if (!data.containsKey("channelId")) {
|
||||||
}
|
|
||||||
|
|
||||||
private void sendNotification(String messageBody, String messageTitle, String channelId, String action1, String action2, String action3) {
|
|
||||||
if (channelId == null) {
|
|
||||||
channelId = "ha_notify";
|
channelId = "ha_notify";
|
||||||
|
} else {
|
||||||
|
channelId = data.get("channelId");
|
||||||
}
|
}
|
||||||
if (messageBody == null) {
|
if (!data.containsKey("body")) {
|
||||||
messageBody = "";
|
messageBody = "";
|
||||||
|
} else {
|
||||||
|
messageBody = data.get("body");
|
||||||
}
|
}
|
||||||
if (messageTitle == null) {
|
if (!data.containsKey("title")) {
|
||||||
messageTitle = "HA Client";
|
messageTitle = "HA Client";
|
||||||
|
} else {
|
||||||
|
messageTitle = data.get("title");
|
||||||
}
|
}
|
||||||
|
if (!data.containsKey("tag")) {
|
||||||
|
nTag = String.valueOf(System.currentTimeMillis());
|
||||||
|
} else {
|
||||||
|
nTag = data.get("tag");
|
||||||
|
}
|
||||||
|
Log.d(TAG, "Notification tag: " + nTag);
|
||||||
|
imageUrl = data.get("image");
|
||||||
Intent intent = new Intent(this, MainActivity.class);
|
Intent intent = new Intent(this, MainActivity.class);
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
|
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
|
||||||
@ -75,10 +82,26 @@ public class MessagingService extends FirebaseMessagingService {
|
|||||||
.setSmallIcon(R.drawable.mini_icon)
|
.setSmallIcon(R.drawable.mini_icon)
|
||||||
.setContentTitle(messageTitle)
|
.setContentTitle(messageTitle)
|
||||||
.setContentText(messageBody)
|
.setContentText(messageBody)
|
||||||
.setAutoCancel(false)
|
.setAutoCancel(true)
|
||||||
.setSound(defaultSoundUri)
|
.setSound(defaultSoundUri)
|
||||||
.setContentIntent(pendingIntent);
|
.setContentIntent(pendingIntent);
|
||||||
|
if (URLUtil.isValidUrl(imageUrl)) {
|
||||||
|
Bitmap image = getBitmapFromURL(imageUrl);
|
||||||
|
if (image != null) {
|
||||||
|
notificationBuilder.setStyle(new NotificationCompat.BigPictureStyle().bigPicture(image).bigLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.blank_icon)));
|
||||||
|
notificationBuilder.setLargeIcon(image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 1; i <= 3; i++) {
|
||||||
|
if (data.containsKey("action" + i)) {
|
||||||
|
Intent broadcastIntent = new Intent(this, NotificationActionReceiver.class);
|
||||||
|
Log.d(TAG, "Putting a tag to the action: " + nTag);
|
||||||
|
broadcastIntent.putExtra("tag", nTag);
|
||||||
|
broadcastIntent.putExtra("actionData", data.get("action" + i + "_data"));
|
||||||
|
PendingIntent actionIntent = PendingIntent.getBroadcast(this, i, broadcastIntent, 0);
|
||||||
|
notificationBuilder.addAction(R.drawable.mini_icon, data.get("action" + i), actionIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
NotificationManager notificationManager =
|
NotificationManager notificationManager =
|
||||||
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
|
||||||
@ -90,6 +113,19 @@ public class MessagingService extends FirebaseMessagingService {
|
|||||||
notificationManager.createNotificationChannel(channel);
|
notificationManager.createNotificationChannel(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
|
notificationManager.notify(nTag, 0 /* ID of notification */, notificationBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bitmap getBitmapFromURL(String imageUrl) {
|
||||||
|
try {
|
||||||
|
URL url = new URL(imageUrl);
|
||||||
|
URLConnection connection = url.openConnection();
|
||||||
|
connection.setDoInput(true);
|
||||||
|
connection.connect();
|
||||||
|
InputStream input = connection.getInputStream();
|
||||||
|
return BitmapFactory.decodeStream(input);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
package com.keyboardcrumbs.hassclient;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
|
||||||
|
import android.webkit.URLUtil;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
public class NotificationActionReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
private static final String TAG = "NotificationActionReceiver";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
String rawActionData = intent.getStringExtra("actionData");
|
||||||
|
String notificationTag = intent.getStringExtra("tag");
|
||||||
|
Log.d(TAG, "Has 'tag': " + intent.hasExtra("tag"));
|
||||||
|
Log.d(TAG, "Canceling notification by tag: " + notificationTag);
|
||||||
|
NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
notificationManager.cancel(notificationTag, 0);
|
||||||
|
SharedPreferences prefs = context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE);
|
||||||
|
String webhookId = prefs.getString("flutter.app-webhook-id", null);
|
||||||
|
if (webhookId != null) {
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "Got webhook id");
|
||||||
|
String requestUrl = prefs.getString("flutter.hassio-res-protocol", "") +
|
||||||
|
"://" +
|
||||||
|
prefs.getString("flutter.hassio-domain", "") +
|
||||||
|
":" +
|
||||||
|
prefs.getString("flutter.hassio-port", "") + "/api/webhook/" + webhookId;
|
||||||
|
JSONObject actionData = new JSONObject(rawActionData);
|
||||||
|
Log.d(TAG, "request url: " + requestUrl);
|
||||||
|
if (URLUtil.isValidUrl(requestUrl)) {
|
||||||
|
JSONObject dataToSend = new JSONObject();
|
||||||
|
JSONObject requestData = new JSONObject();
|
||||||
|
if (actionData.getString("action").equals("call-service")) {
|
||||||
|
dataToSend.put("type", "call_service");
|
||||||
|
requestData.put("domain", actionData.getString("service").split("\\.")[0]);
|
||||||
|
requestData.put("service", actionData.getString("service").split("\\.")[1]);
|
||||||
|
if (actionData.has("service_data")) {
|
||||||
|
requestData.put("service_data", actionData.get("service_data"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dataToSend.put("type", "fire_event");
|
||||||
|
requestData.put("event_type", "ha_client_event");
|
||||||
|
}
|
||||||
|
dataToSend.put("data", requestData);
|
||||||
|
String stringRequest = dataToSend.toString();
|
||||||
|
Log.d(TAG, "Data to send home: " + stringRequest);
|
||||||
|
SendTask sendTask = new SendTask();
|
||||||
|
sendTask.execute(requestUrl, stringRequest);
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Invalid url");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error handling notification action", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Webhook id not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +0,0 @@
|
|||||||
package com.keyboardcrumbs.hassclient;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.work.Worker;
|
|
||||||
import androidx.work.WorkerParameters;
|
|
||||||
|
|
||||||
public class NotificationActionWorker extends Worker {
|
|
||||||
|
|
||||||
private static final String TAG = "NotificationActionWorker";
|
|
||||||
|
|
||||||
public NotificationActionWorker(@NonNull Context appContext, @NonNull WorkerParameters workerParams) {
|
|
||||||
super(appContext, workerParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Result doWork() {
|
|
||||||
Log.d(TAG, "Performing long running task in scheduled job");
|
|
||||||
// TODO(developer): add long running task here.
|
|
||||||
return Result.success();
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,48 @@
|
|||||||
|
package com.keyboardcrumbs.hassclient;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
public class SendTask extends AsyncTask<String, String, String> {
|
||||||
|
|
||||||
|
private static final String TAG = "SendTask";
|
||||||
|
|
||||||
|
public SendTask(){
|
||||||
|
//set context variables if required
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
super.onPreExecute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String doInBackground(String... params) {
|
||||||
|
String urlString = params[0];
|
||||||
|
String data = params[1];
|
||||||
|
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "Connecting and sending...");
|
||||||
|
URL url = new URL(urlString);
|
||||||
|
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
|
||||||
|
urlConnection.setRequestMethod("POST");
|
||||||
|
urlConnection.setRequestProperty("Content-Type", "application/json");
|
||||||
|
urlConnection.setDoOutput(true);
|
||||||
|
byte[] outputBytes = data.getBytes("UTF-8");
|
||||||
|
OutputStream os = urlConnection.getOutputStream();
|
||||||
|
os.write(outputBytes);
|
||||||
|
|
||||||
|
int responseCode = urlConnection.getResponseCode();
|
||||||
|
|
||||||
|
Log.d(TAG, "responseCode: " + responseCode);
|
||||||
|
urlConnection.disconnect();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error sending data", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
BIN
android/app/src/main/res/drawable/blank_icon.png
Normal file
BIN
android/app/src/main/res/drawable/blank_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 461 B |
Reference in New Issue
Block a user