diff --git a/app/src/apkFlavor/AndroidManifest.xml b/app/src/apkFlavor/AndroidManifest.xml index dda2ec1..5f733da 100644 --- a/app/src/apkFlavor/AndroidManifest.xml +++ b/app/src/apkFlavor/AndroidManifest.xml @@ -147,7 +147,7 @@ - diff --git a/app/src/fdroidFlavor/AndroidManifest.xml b/app/src/fdroidFlavor/AndroidManifest.xml index 949361a..9ee8098 100644 --- a/app/src/fdroidFlavor/AndroidManifest.xml +++ b/app/src/fdroidFlavor/AndroidManifest.xml @@ -145,6 +145,7 @@ + diff --git a/app/src/googlePlayFlavor/AndroidManifest.xml b/app/src/googlePlayFlavor/AndroidManifest.xml index dba361f..9ce871d 100644 --- a/app/src/googlePlayFlavor/AndroidManifest.xml +++ b/app/src/googlePlayFlavor/AndroidManifest.xml @@ -139,6 +139,7 @@ + diff --git a/app/src/main/java/com/jens/automation2/Action.java b/app/src/main/java/com/jens/automation2/Action.java index 1a5da67..bccbf4a 100644 --- a/app/src/main/java/com/jens/automation2/Action.java +++ b/app/src/main/java/com/jens/automation2/Action.java @@ -112,6 +112,8 @@ public class Action return context.getResources().getString(R.string.setScreenBrightness); case createNotification: return context.getResources().getString(R.string.createNotification); + case closeNotification: + return context.getResources().getString(R.string.closeNotifications); default: return "Unknown"; } diff --git a/app/src/main/java/com/jens/automation2/Actions.java b/app/src/main/java/com/jens/automation2/Actions.java index 03ded08..caf6985 100644 --- a/app/src/main/java/com/jens/automation2/Actions.java +++ b/app/src/main/java/com/jens/automation2/Actions.java @@ -1,5 +1,8 @@ package com.jens.automation2; +import static com.jens.automation2.receivers.NotificationListener.EXTRA_TEXT; +import static com.jens.automation2.receivers.NotificationListener.EXTRA_TITLE; + import android.Manifest; import android.annotation.SuppressLint; import android.annotation.TargetApi; @@ -19,6 +22,7 @@ import android.net.ConnectivityManager; import android.net.Uri; import android.net.wifi.WifiManager; import android.os.Build; +import android.os.Bundle; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.VibrationEffect; @@ -37,7 +41,9 @@ import com.jens.automation2.actions.wifi_router.MyOnStartTetheringCallback; import com.jens.automation2.actions.wifi_router.MyOreoWifiManager; import com.jens.automation2.location.WifiBroadcastReceiver; import com.jens.automation2.receivers.ConnectivityReceiver; +import com.jens.automation2.receivers.NotificationListener; +import org.apache.commons.lang3.StringUtils; import org.apache.http.client.HttpClient; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.Scheme; @@ -106,7 +112,85 @@ public class Actions NotificationManager nm = (NotificationManager) Miscellaneous.getAnyContext().getSystemService(Context.NOTIFICATION_SERVICE); for(StatusBarNotification n : nm.getActiveNotifications()) { - n. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) + { + String[] params = action.getParameter2().split(Action.actionParameter2Split); + + String myApp = params[0]; + String myTitleDir = params[1]; + String requiredTitle = params[2]; + String myTextDir = params[3]; + String requiredText; + if (params.length >= 5) + requiredText = params[4]; + else + requiredText = ""; + + for (StatusBarNotification sbn : NotificationListener.getInstance().getActiveNotifications()) + { + String notificationApp = sbn.getPackageName(); + String notificationTitle = null; + String notificationText = null; + + Miscellaneous.logEvent("i", "NotificationCloseCheck", "Checking if this notification matches our rule " + action.getParentRule().getName() + ". App: " + notificationApp + ", title: " + notificationTitle + ", text: " + notificationText, 5); + + if (!myApp.equals("-1")) + { + if (!notificationApp.equalsIgnoreCase(myApp)) + { + Miscellaneous.logEvent("i", "NotificationCloseCheck", "Notification app name does not match rule.", 5); + continue; + } + } + else + { + if(myApp.equals(BuildConfig.APPLICATION_ID)) + { + continue; + } + } + + /* + If there are multiple notifications ("stacked") title or text might be null: + https://stackoverflow.com/questions/28047767/notificationlistenerservice-not-reading-text-of-stacked-notifications + */ + + Bundle extras = sbn.getNotification().extras; + + // T I T L E + if (extras.containsKey(EXTRA_TITLE)) + notificationTitle = sbn.getNotification().extras.getString(EXTRA_TITLE); + + if (!StringUtils.isEmpty(requiredTitle)) + { + if (!Miscellaneous.compare(myTitleDir, requiredTitle, notificationTitle)) + { + Miscellaneous.logEvent("i", "NotificationCloseCheck", "Notification title does not match rule.", 5); + continue; + } + } + + // T E X T + + if (extras.containsKey(EXTRA_TEXT)) + notificationText = sbn.getNotification().extras.getString(EXTRA_TEXT); + + if (!StringUtils.isEmpty(requiredText)) + { + if (!Miscellaneous.compare(myTextDir, requiredText, notificationText)) + { + Miscellaneous.logEvent("i", "NotificationCloseCheck", "Notification text does not match rule.", 5); + continue; + } + } + + Miscellaneous.logEvent("i", "NotificationCloseCheck", "All criteria matches. Closing notification: " + sbn.getNotification().toString(), 3); + if(NotificationListener.getInstance() != null) + NotificationListener.getInstance().dismissNotification(sbn); + else + Miscellaneous.logEvent("i", "NotificationCloseCheck", "NotificationListener instance is null. Can\'t close notification.", 3); + } + } } } diff --git a/app/src/main/java/com/jens/automation2/ActivityManageActionCloseNotification.java b/app/src/main/java/com/jens/automation2/ActivityManageActionCloseNotification.java index a2691ec..b8c36a2 100644 --- a/app/src/main/java/com/jens/automation2/ActivityManageActionCloseNotification.java +++ b/app/src/main/java/com/jens/automation2/ActivityManageActionCloseNotification.java @@ -24,6 +24,8 @@ import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; +import org.apache.commons.lang3.StringUtils; + import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -38,15 +40,13 @@ public class ActivityManageActionCloseNotification extends Activity public static final String intentNameNotificationText = "text"; public static final String intentNameNotificationDirection = "direction"; - public static Trigger editedNotificationTrigger; boolean edit = false; ProgressDialog progressDialog = null; EditText etNotificationTitle, etNotificationText; - Button bSelectApp, bSaveTriggerNotification; + Button bSelectApp, bSaveActionCloseNotification; Spinner spinnerTitleDirection, spinnerTextDirection; TextView tvSelectedApplication; - CheckBox chkNotificationDirection; private static List pInfos = null; public static Trigger resultingTrigger; @@ -258,16 +258,15 @@ public class ActivityManageActionCloseNotification extends Activity protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_manage_trigger_notification); + setContentView(R.layout.activity_manage_action_close_notification); etNotificationTitle = (EditText)findViewById(R.id.etNotificationTitle); etNotificationText = (EditText)findViewById(R.id.etNotificationText); bSelectApp = (Button)findViewById(R.id.bSelectApp); - bSaveTriggerNotification = (Button)findViewById(R.id.bSaveTriggerNotification); + bSaveActionCloseNotification = (Button)findViewById(R.id.bSaveActionCloseNotification); spinnerTitleDirection = (Spinner)findViewById(R.id.spinnerTitleDirection); spinnerTextDirection = (Spinner)findViewById(R.id.spinnerTextDirection); tvSelectedApplication = (TextView)findViewById(R.id.etActivityOrActionPath); - chkNotificationDirection = (CheckBox)findViewById(R.id.chkNotificationDirection); directions = new String[] { getResources().getString(R.string.directionStringEquals), @@ -293,19 +292,7 @@ public class ActivityManageActionCloseNotification extends Activity } }); - chkNotificationDirection.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() - { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) - { - if(isChecked) - chkNotificationDirection.setText(getResources().getString(R.string.notificationAppears)); - else - chkNotificationDirection.setText(getResources().getString(R.string.notificationDisappears)); - } - }); - - bSaveTriggerNotification.setOnClickListener(new OnClickListener() + bSaveActionCloseNotification.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) @@ -321,22 +308,29 @@ public class ActivityManageActionCloseNotification extends Activity String textDir = Trigger.getMatchCode(spinnerTextDirection.getSelectedItem().toString()); String text = etNotificationText.getText().toString(); + Intent responseData = new Intent(); if(edit) { - editedNotificationTrigger.setTriggerParameter(chkNotificationDirection.isChecked()); - editedNotificationTrigger.setTriggerParameter2(app + triggerParameter2Split + titleDir + triggerParameter2Split + title + triggerParameter2Split + textDir + triggerParameter2Split + text); - ActivityManageActionCloseNotification.this.setResult(RESULT_OK); +// editedNotificationAction.setTriggerParameter(chkNotificationDirection.isChecked()); + responseData.putExtra(ActivityManageRule.intentNameActionParameter2, app + Action.actionParameter2Split + titleDir + Action.actionParameter2Split + title + Action.actionParameter2Split + textDir + Action.actionParameter2Split + text); + ActivityManageActionCloseNotification.this.setResult(RESULT_OK, responseData); } else { - Intent data = new Intent(); - data.putExtra(intentNameNotificationDirection, chkNotificationDirection.isChecked()); - data.putExtra(intentNameNotificationApp, app); - data.putExtra(intentNameNotificationTitleDir, titleDir); - data.putExtra(intentNameNotificationTitle, title); - data.putExtra(intentNameNotificationTextDir, textDir); - data.putExtra(intentNameNotificationText, text); - ActivityManageActionCloseNotification.this.setResult(RESULT_OK, data); +// data.putExtra(intentNameNotificationDirection, chkNotificationDirection.isChecked()); + responseData.putExtra(ActivityManageRule.intentNameActionParameter2, + app + Action.actionParameter2Split + + titleDir + Action.actionParameter2Split + + title + Action.actionParameter2Split + + textDir + Action.actionParameter2Split + + text + ); +// data.putExtra(intentNameNotificationApp, app); +// data.putExtra(intentNameNotificationTitleDir, titleDir); +// data.putExtra(intentNameNotificationTitle, title); +// data.putExtra(intentNameNotificationTextDir, textDir); +// data.putExtra(intentNameNotificationText, text); + ActivityManageActionCloseNotification.this.setResult(RESULT_OK, responseData); } finish(); @@ -344,18 +338,16 @@ public class ActivityManageActionCloseNotification extends Activity }); Intent i = getIntent(); - if(i.getBooleanExtra("edit", false) == true) + if(!StringUtils.isBlank(i.getStringExtra(ActivityManageRule.intentNameActionParameter2))) { edit = true; - loadValuesIntoGui(); + loadValuesIntoGui(i.getStringExtra(ActivityManageRule.intentNameActionParameter2)); } } - private void loadValuesIntoGui() + private void loadValuesIntoGui(String param) { - chkNotificationDirection.setChecked(editedNotificationTrigger.getTriggerParameter()); - - String[] params = editedNotificationTrigger.getTriggerParameter2().split(triggerParameter2Split); + String[] params = param.split(Action.actionParameter2Split); String app = params[0]; String titleDir = params[1]; diff --git a/app/src/main/java/com/jens/automation2/ActivityManageRule.java b/app/src/main/java/com/jens/automation2/ActivityManageRule.java index dbbee8b..1875480 100644 --- a/app/src/main/java/com/jens/automation2/ActivityManageRule.java +++ b/app/src/main/java/com/jens/automation2/ActivityManageRule.java @@ -1589,6 +1589,8 @@ public class ActivityManageRule extends Activity items.add(new Item(typesLong[i].toString(), R.drawable.vibrate)); else if(types[i].toString().equals(Action_Enum.createNotification.toString())) items.add(new Item(typesLong[i].toString(), R.drawable.notification)); + else if(types[i].toString().equals(Action_Enum.closeNotification.toString())) + items.add(new Item(typesLong[i].toString(), R.drawable.notification)); else if(types[i].toString().equals(Action_Enum.sendTextMessage.toString())) { // if(ActivityPermissions.isPermissionDeclaratedInManifest(ActivityManageSpecificRule.this, "android.permission.SEND_SMS") && !Miscellaneous.isGooglePlayInstalled(ActivityManageSpecificRule.this)) diff --git a/app/src/main/java/com/jens/automation2/ActivityManageTriggerNotification.java b/app/src/main/java/com/jens/automation2/ActivityManageTriggerNotification.java index 5ade113..167f5d0 100644 --- a/app/src/main/java/com/jens/automation2/ActivityManageTriggerNotification.java +++ b/app/src/main/java/com/jens/automation2/ActivityManageTriggerNotification.java @@ -263,7 +263,7 @@ public class ActivityManageTriggerNotification extends Activity etNotificationTitle = (EditText)findViewById(R.id.etNotificationTitle); etNotificationText = (EditText)findViewById(R.id.etNotificationText); bSelectApp = (Button)findViewById(R.id.bSelectApp); - bSaveTriggerNotification = (Button)findViewById(R.id.bSaveTriggerNotification); + bSaveTriggerNotification = (Button)findViewById(R.id.bSaveActionCloseNotification); spinnerTitleDirection = (Spinner)findViewById(R.id.spinnerTitleDirection); spinnerTextDirection = (Spinner)findViewById(R.id.spinnerTextDirection); tvSelectedApplication = (TextView)findViewById(R.id.etActivityOrActionPath); diff --git a/app/src/main/java/com/jens/automation2/ActivityPermissions.java b/app/src/main/java/com/jens/automation2/ActivityPermissions.java index d2db3b9..b1fed68 100644 --- a/app/src/main/java/com/jens/automation2/ActivityPermissions.java +++ b/app/src/main/java/com/jens/automation2/ActivityPermissions.java @@ -649,6 +649,9 @@ public class ActivityPermissions extends Activity case playSound: addToArrayListUnique(Manifest.permission.READ_EXTERNAL_STORAGE, requiredPermissions); break; + case closeNotification: + addToArrayListUnique(Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE, requiredPermissions); + break; case turnScreenOnOrOff: if(action.getParameter1()) addToArrayListUnique(Manifest.permission.WAKE_LOCK, requiredPermissions); diff --git a/app/src/main/java/com/jens/automation2/Miscellaneous.java b/app/src/main/java/com/jens/automation2/Miscellaneous.java index 38b9f5b..4f3335e 100644 --- a/app/src/main/java/com/jens/automation2/Miscellaneous.java +++ b/app/src/main/java/com/jens/automation2/Miscellaneous.java @@ -629,27 +629,43 @@ public class Miscellaneous extends Service if(source.contains("[notificationTitle]")) { - String notificationTitle = NotificationListener.getLastNotification().getTitle(); + if(NotificationListener.getLastNotification() != null) + { + String notificationTitle = NotificationListener.getLastNotification().getTitle(); - if(notificationTitle != null && notificationTitle.length() > 0) - source = source.replace("[notificationTitle]", notificationTitle); + if (notificationTitle != null && notificationTitle.length() > 0) + source = source.replace("[notificationTitle]", notificationTitle); + else + { + source = source.replace("[notificationTitle]", "notificationTitle unknown"); + Miscellaneous.logEvent("w", "Variable replacement", "notificationTitle was empty.", 3); + } + } else { - source = source.replace("notificationTitle unknown", notificationTitle); - Miscellaneous.logEvent("w", "Variable replacement", "notificationTitle was empty.", 3); + source = source.replace("[notificationTitle]", "notificationTitle unknown"); + Miscellaneous.logEvent("w", "Variable replacement", "lastNotification was empty.", 3); } } if(source.contains("[notificationText]")) { - String notificationText = NotificationListener.getLastNotification().getText(); + if(NotificationListener.getLastNotification() != null) + { + String notificationText = NotificationListener.getLastNotification().getText(); - if(notificationText != null && notificationText.length() > 0) - source = source.replace("[notificationText]", notificationText); + if (notificationText != null && notificationText.length() > 0) + source = source.replace("[notificationText]", notificationText); + else + { + source = source.replace("[notificationText]", "notificationText unknown"); + Miscellaneous.logEvent("w", "Variable replacement", "notificationText was empty.", 3); + } + } else { - source = source.replace("notificationText unknown", notificationText); - Miscellaneous.logEvent("w", "Variable replacement", "notificationText was empty.", 3); + source = source.replace("[notificationText]", "notificationText unknown"); + Miscellaneous.logEvent("w", "Variable replacement", "lastNotification was empty.", 3); } } diff --git a/app/src/main/java/com/jens/automation2/ReceiverCoordinator.java b/app/src/main/java/com/jens/automation2/ReceiverCoordinator.java index de8b1e5..d59fd1b 100644 --- a/app/src/main/java/com/jens/automation2/ReceiverCoordinator.java +++ b/app/src/main/java/com/jens/automation2/ReceiverCoordinator.java @@ -51,6 +51,7 @@ public class ReceiverCoordinator HeadphoneJackListener.class, //NfcReceiver.class, NoiseListener.class, + //NotificationListener.class, PhoneStatusListener.class, ProcessListener.class, TimeZoneListener.class @@ -132,12 +133,6 @@ public class ReceiverCoordinator Miscellaneous.logEvent("w", "Error in new model", Log.getStackTraceString(e), 3); } -// if(Settings.useAccelerometerForPositioning && !Miscellaneous.isAndroidEmulator()) -// { -// accelerometerHandler = new AccelerometerHandler(); -// mySensorActivity = new SensorActivity(this); -// } - // startPhoneStateListener PhoneStatusListener.startPhoneStatusListener(AutomationService.getInstance()); // also used to mute anouncements during calls @@ -163,7 +158,7 @@ public class ReceiverCoordinator if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.noiseLevel)) NoiseListener.startNoiseListener(AutomationService.getInstance()); - // startNoiseListener + // startProcessListener if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.process_started_stopped)) ProcessListener.startProcessListener(AutomationService.getInstance()); @@ -241,7 +236,7 @@ public class ReceiverCoordinator // timeFrame -> too inexpensive to shutdown - if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.charging) | Rule.isAnyRuleUsing(Trigger.Trigger_Enum.usb_host_connection) | Rule.isAnyRuleUsing(Trigger.Trigger_Enum.batteryLevel)) + if(Rule.isAnyRuleUsing(Trigger.Trigger_Enum.charging) || Rule.isAnyRuleUsing(Trigger.Trigger_Enum.usb_host_connection) || Rule.isAnyRuleUsing(Trigger.Trigger_Enum.batteryLevel)) { if(BatteryReceiver.haveAllPermission()) BatteryReceiver.startBatteryReceiver(AutomationService.getInstance()); diff --git a/app/src/main/java/com/jens/automation2/receivers/NotificationListener.java b/app/src/main/java/com/jens/automation2/receivers/NotificationListener.java index 28ef525..85c180f 100644 --- a/app/src/main/java/com/jens/automation2/receivers/NotificationListener.java +++ b/app/src/main/java/com/jens/automation2/receivers/NotificationListener.java @@ -1,9 +1,12 @@ package com.jens.automation2.receivers; import android.annotation.SuppressLint; +import android.bluetooth.BluetoothDevice; +import android.content.IntentFilter; import android.os.Build; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; +import android.util.Log; import androidx.annotation.RequiresApi; @@ -19,9 +22,10 @@ import java.util.Calendar; @SuppressLint("OverrideAbstract") @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) -public class NotificationListener extends NotificationListenerService +public class NotificationListener extends NotificationListenerService// implements AutomationListenerInterface { static Calendar lastResponseToNotification = null; + static boolean listenerRunning = false; static NotificationListener instance; static SimpleNotification lastNotification = null; @@ -37,6 +41,8 @@ public class NotificationListener extends NotificationListenerService // a bitmap to be used instead of the small icon when showing the notification payload public static final String EXTRA_LARGE_ICON = "android.largeIcon"; + protected static IntentFilter notificationReceiverIntentFilter = null; + public static SimpleNotification getLastNotification() { return lastNotification; @@ -78,16 +84,7 @@ public class NotificationListener extends NotificationListenerService { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { - String app = sbn.getPackageName(); - String title = sbn.getNotification().extras.getString(EXTRA_TITLE); - String text = sbn.getNotification().extras.getString(EXTRA_TEXT); - - lastNotification = new SimpleNotification(); - lastNotification.publishTime = Miscellaneous.calendarFromLong(sbn.getPostTime()); - lastNotification.created = created; - lastNotification.app = app; - lastNotification.title = title; - lastNotification.text = text; + lastNotification = convertNotificationToSimpleNotification(created, sbn); ArrayList ruleCandidates = Rule.findRuleCandidates(Trigger.Trigger_Enum.notification); for (int i = 0; i < ruleCandidates.size(); i++) @@ -100,6 +97,68 @@ public class NotificationListener extends NotificationListenerService return false; } + @RequiresApi(api = Build.VERSION_CODES.KITKAT) + public static SimpleNotification convertNotificationToSimpleNotification(boolean created, StatusBarNotification input) + { + String app = input.getPackageName(); + String title = input.getNotification().extras.getString(EXTRA_TITLE); + String text = input.getNotification().extras.getString(EXTRA_TEXT); + + SimpleNotification returnNotification = new SimpleNotification(); + returnNotification.publishTime = Miscellaneous.calendarFromLong(input.getPostTime()); + returnNotification.created = created; + returnNotification.app = app; + returnNotification.title = title; + returnNotification.text = text; + + return returnNotification; + } + + /*@Override + public void startListener(AutomationService automationService) + { + if(instance == null) + instance = new NotificationListener(); + + if(notificationReceiverIntentFilter == null) + { + notificationReceiverIntentFilter = new IntentFilter(); + notificationReceiverIntentFilter.addAction("android.service.notification.NotificationListenerService"); + } + + try + { + if(!listenerRunning) + { + Miscellaneous.logEvent("i", "NotificationListener", "Starting NotificationListener", 4); + listenerRunning = true; + AutomationService.getInstance().registerReceiver(instance, notificationReceiverIntentFilter); + } + } + catch(Exception ex) + { + Miscellaneous.logEvent("e", "BluetoothReceiver", "Error starting BluetoothReceiver: " + Log.getStackTraceString(ex), 3); + } + } + + @Override + public void stopListener(AutomationService automationService) + { + + } + + @Override + public boolean isListenerRunning() + { + return false; + } + + @Override + public Trigger.Trigger_Enum[] getMonitoredTrigger() + { + return new Trigger.Trigger_Enum[0]; + }*/ + public static class SimpleNotification { boolean created; @@ -168,4 +227,13 @@ public class NotificationListener extends NotificationListenerService { super.onListenerDisconnected(); } + + public void dismissNotification(StatusBarNotification sbn) + { + if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + cancelNotification(sbn.getPackageName(), sbn.getTag(), sbn.getId()); + else + cancelNotification(sbn.getKey()); + + } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_manage_action_close_notification.xml b/app/src/main/res/layout/activity_manage_action_close_notification.xml index 8737c4e..9b2a7ab 100644 --- a/app/src/main/res/layout/activity_manage_action_close_notification.xml +++ b/app/src/main/res/layout/activity_manage_action_close_notification.xml @@ -68,6 +68,14 @@ android:layout_marginVertical="@dimen/default_margin" android:background="#aa000000" /> + + @@ -136,7 +144,7 @@