Resolves #471 Actionable notification

This commit is contained in:
Yegor Vialov 2020-05-24 23:33:20 +00:00
parent 92a1230267
commit 55868d1dfe
6 changed files with 182 additions and 48 deletions

View File

@ -56,6 +56,12 @@
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</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
android:name="io.flutter.plugins.androidalarmmanager.AlarmService"

View File

@ -1,6 +1,10 @@
package com.keyboardcrumbs.hassclient;
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.NotificationManager;
@ -16,8 +20,9 @@ import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.webkit.URLUtil;
public class MessagingService extends FirebaseMessagingService {
@ -31,14 +36,7 @@ public class MessagingService extends FirebaseMessagingService {
if (data.size() > 0) {
Log.d(TAG, "Message data payload: " + data);
if (data.containsKey("body") || data.containsKey("title")) {
sendNotification(
data.get("body"),
data.get("title"),
data.get("channelId"),
data.get("action1"),
data.get("action2"),
data.get("action3")
);
sendNotification(data);
}
}
}
@ -49,22 +47,31 @@ public class MessagingService extends FirebaseMessagingService {
//TODO update token
}
private void executeAction() {
OneTimeWorkRequest work = new OneTimeWorkRequest.Builder(NotificationActionWorker.class)
.build();
WorkManager.getInstance().beginWith(work).enqueue();
}
private void sendNotification(String messageBody, String messageTitle, String channelId, String action1, String action2, String action3) {
if (channelId == null) {
private void sendNotification(Map<String, String> data) {
String channelId, messageBody, messageTitle, imageUrl;
String nTag;
if (!data.containsKey("channelId")) {
channelId = "ha_notify";
} else {
channelId = data.get("channelId");
}
if (messageBody == null) {
if (!data.containsKey("body")) {
messageBody = "";
} else {
messageBody = data.get("body");
}
if (messageTitle == null) {
if (!data.containsKey("title")) {
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.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
@ -75,10 +82,26 @@ public class MessagingService extends FirebaseMessagingService {
.setSmallIcon(R.drawable.mini_icon)
.setContentTitle(messageTitle)
.setContentText(messageBody)
.setAutoCancel(false)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.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) getSystemService(Context.NOTIFICATION_SERVICE);
@ -90,6 +113,19 @@ public class MessagingService extends FirebaseMessagingService {
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;
}
}
}

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B