diff --git a/app/src/apkFlavor/AndroidManifest.xml b/app/src/apkFlavor/AndroidManifest.xml index 7fe8c4c7..f5404b05 100644 --- a/app/src/apkFlavor/AndroidManifest.xml +++ b/app/src/apkFlavor/AndroidManifest.xml @@ -202,17 +202,19 @@ - + - + + + + - - + diff --git a/app/src/fdroidFlavor/AndroidManifest.xml b/app/src/fdroidFlavor/AndroidManifest.xml index 9c567b89..9cb7f341 100644 --- a/app/src/fdroidFlavor/AndroidManifest.xml +++ b/app/src/fdroidFlavor/AndroidManifest.xml @@ -180,8 +180,18 @@ - + + + + + + + + diff --git a/app/src/googlePlayFlavor/AndroidManifest.xml b/app/src/googlePlayFlavor/AndroidManifest.xml index 71dc8f79..5d9f79ad 100644 --- a/app/src/googlePlayFlavor/AndroidManifest.xml +++ b/app/src/googlePlayFlavor/AndroidManifest.xml @@ -175,6 +175,18 @@ + + + + + + + + + + { + @Override + public int compareTo(CustomPackageInfo another) + { + String name1 = ""; + String name2 = ""; + + ApplicationInfo aInfo1 = this.applicationInfo; + if (aInfo1 != null) + { + name1 = (String) ActivityManageNotificationTrigger.this.getPackageManager().getApplicationLabel(aInfo1); + } + ApplicationInfo aInfo2 = another.applicationInfo; + if (aInfo2 != null) + { + name2 = (String) ActivityManageNotificationTrigger.this.getPackageManager().getApplicationLabel(aInfo2); + } + + return name1.compareTo(name2); + } + + } + + private static List pInfos = null; + public static Action resultingAction; + + private static final String[] supportedIntentTypes = { "boolean", "byte", "char", "double", "float", "int", "long", "short", "String" }; + private ArrayList intentPairList = new ArrayList(); + + ArrayAdapter intentTypeSpinnerAdapter, intentPairAdapter; + + public static void getActivityList(final Context context) + { + if(pInfos == null) + { + pInfos = context.getPackageManager().getInstalledPackages(PackageManager.GET_ACTIVITIES); + Collections.sort(pInfos, new Comparator() + { + public int compare(PackageInfo obj1, PackageInfo obj2) + { + String name1 = ""; + String name2 = ""; + + ApplicationInfo aInfo1 = obj1.applicationInfo; + if (aInfo1 != null) + { + name1 = (String) context.getPackageManager().getApplicationLabel(aInfo1); + } + ApplicationInfo aInfo2 = obj2.applicationInfo; + if (aInfo2 != null) + { + name2 = (String) context.getPackageManager().getApplicationLabel(aInfo2); + } + + return name1.compareTo(name2); + } + }); + } + } + + public static String[] getApplicationNameListString(Context myContext) + { + // Generate the actual list + getActivityList(myContext); + + ArrayList returnList = new ArrayList(); + + for (PackageInfo pInfo : pInfos) + { + ApplicationInfo aInfo = pInfo.applicationInfo; + if (aInfo != null) + { + String aLabel; + + aLabel = (String) myContext.getPackageManager().getApplicationLabel(aInfo); + + ActivityInfo[] aInfos = pInfo.activities; + if (aInfos != null && aInfos.length > 0) // Only put Applications into the list that have packages. + { + if(!returnList.contains(aLabel)) + returnList.add(aLabel); + } + } + } + + return returnList.toArray(new String[returnList.size()]); + } + + public static String[] getPackageListString(Context myContext, String applicationLabel) + { + // Generate the actual list + getActivityList(myContext); + + ArrayList returnList = new ArrayList(); + + for (PackageInfo pInfo : pInfos) + { + if(myContext.getPackageManager().getApplicationLabel(pInfo.applicationInfo).equals(applicationLabel)) + { + ActivityInfo[] aInfos = pInfo.activities; + if (aInfos != null && aInfos.length > 0) + { + returnList.add(pInfo.packageName); + } + } + } + + return returnList.toArray(new String[returnList.size()]); + } + + public static String[] getPackageListString(Context myContext) + { + // Generate the actual list + getActivityList(myContext); + + ArrayList returnList = new ArrayList(); + + for (PackageInfo pInfo : pInfos) + { + ActivityInfo[] aInfos = pInfo.activities; + if (aInfos != null && aInfos.length > 0) + { + returnList.add(pInfo.packageName); + } + else + Miscellaneous.logEvent("w", "Empty Application", "Application " + myContext.getPackageManager().getApplicationLabel(pInfo.applicationInfo) + " doesn\'t have packages.", 5); + } + + return returnList.toArray(new String[returnList.size()]); + } + + public static String[] getActivityListForPackageName(String packageName) + { + ArrayList returnList = new ArrayList(); + + for (PackageInfo pInfo : pInfos) + { + if(pInfo.packageName.equals(packageName)) + { + ActivityInfo[] aInfos = pInfo.activities; + if (aInfos != null) + { + for (ActivityInfo activityInfo : aInfos) + { + returnList.add(activityInfo.name); + } + } + } + } + + return returnList.toArray(new String[returnList.size()]); + } + + public static ActivityInfo getActivityInfoForPackageNameAndActivityName(String packageName, String activityName) + { + for (PackageInfo pInfo : pInfos) + { + if(pInfo.packageName.equals(packageName)) + { + ActivityInfo[] aInfos = pInfo.activities; + if (aInfos != null) + { + for (ActivityInfo activityInfo : aInfos) + { + if(activityInfo.name.equals(activityName)) + return activityInfo; + } + } + } + } + + return null; + } + + private AlertDialog getActionStartActivityDialog1() + { + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); + alertDialogBuilder.setTitle(getResources().getString(R.string.selectApplication)); + final String[] applicationArray = ActivityManageNotificationTrigger.getApplicationNameListString(this); + alertDialogBuilder.setItems(applicationArray, new DialogInterface.OnClickListener() + { + @Override + public void onClick(DialogInterface dialog, int which) + { + dialog.dismiss(); + getActionStartActivityDialog2(applicationArray[which]).show(); + } + }); + AlertDialog alertDialog = alertDialogBuilder.create(); + + return alertDialog; + } + private AlertDialog getActionStartActivityDialog2(String applicationName) + { + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); + alertDialogBuilder.setTitle(getResources().getString(R.string.selectPackageOfApplication)); + final String[] packageArray = ActivityManageNotificationTrigger.getPackageListString(this, applicationName); + alertDialogBuilder.setItems(packageArray, new DialogInterface.OnClickListener() + { + @Override + public void onClick(DialogInterface dialog, int which) + { + getActionStartActivityDialog3(packageArray[which]).show(); + Miscellaneous.messageBox(getResources().getString(R.string.hint), getResources().getString(R.string.chooseActivityHint), ActivityManageNotificationTrigger.this).show(); + + } + }); + AlertDialog alertDialog = alertDialogBuilder.create(); + + return alertDialog; + } + private AlertDialog getActionStartActivityDialog3(final String packageName) + { + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); + alertDialogBuilder.setTitle(getResources().getString(R.string.selectActivityToBeStarted)); + final String activityArray[] = ActivityManageNotificationTrigger.getActivityListForPackageName(packageName); + alertDialogBuilder.setItems(activityArray, new DialogInterface.OnClickListener() + { + @Override + public void onClick(DialogInterface dialog, int which) + { + ActivityInfo ai = ActivityManageNotificationTrigger.getActivityInfoForPackageNameAndActivityName(packageName, activityArray[which]); + tvSelectedActivity.setText(ai.packageName + ";" + ai.name); + } + }); + AlertDialog alertDialog = alertDialogBuilder.create(); + + return alertDialog; + } + + @Override + protected void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + setContentView(R.layout.action_start_activity); + + lvIntentPairs = (ListView)findViewById(R.id.lvIntentPairs); + etParameterName = (EditText)findViewById(R.id.etParameterName); + etParameterValue = (EditText)findViewById(R.id.etParameterValue); + bSelectApp = (Button)findViewById(R.id.bSelectApp); + bAddIntentPair = (Button)findViewById(R.id.bAddIntentPair); + bSaveActionStartOtherActivity = (Button)findViewById(R.id.bSaveActionStartOtherActivity); + spinnerParameterType = (Spinner)findViewById(R.id.spinnerParameterType); + tvSelectedActivity = (TextView)findViewById(R.id.tvSelectedActivity); + + intentTypeSpinnerAdapter = new ArrayAdapter(this, R.layout.text_view_for_poi_listview_mediumtextsize, ActivityManageNotificationTrigger.supportedIntentTypes); + spinnerParameterType.setAdapter(intentTypeSpinnerAdapter); + intentTypeSpinnerAdapter.notifyDataSetChanged(); + + intentPairAdapter = new ArrayAdapter(this, R.layout.text_view_for_poi_listview_smalltextsize, intentPairList); + + bSelectApp.setOnClickListener(new OnClickListener() + { + @Override + public void onClick(View v) + { + GetActivityListTask getActivityListTask = new GetActivityListTask(); + getActivityListTask.execute(); + progressDialog = ProgressDialog.show(ActivityManageNotificationTrigger.this, "", ActivityManageNotificationTrigger.this.getResources().getString(R.string.gettingListOfInstalledApplications)); + } + }); + + bAddIntentPair.setOnClickListener(new OnClickListener() + { + @Override + public void onClick(View v) + { + // type;name;value + if(spinnerParameterType.getSelectedItem().toString().length() == 0) + { + Toast.makeText(ActivityManageNotificationTrigger.this, getResources().getString(R.string.selectTypeOfIntentPair), Toast.LENGTH_LONG).show(); + return; + } + + if(etParameterName.getText().toString().length() == 0) + { + Toast.makeText(ActivityManageNotificationTrigger.this, getResources().getString(R.string.enterNameForIntentPair), Toast.LENGTH_LONG).show(); + return; + } + + if(etParameterValue.getText().toString().length() == 0) + { + Toast.makeText(ActivityManageNotificationTrigger.this, getResources().getString(R.string.enterValueForIntentPair), Toast.LENGTH_LONG).show(); + return; + } + + String param = supportedIntentTypes[spinnerParameterType.getSelectedItemPosition()] + "/" + etParameterName.getText().toString() + "/" + etParameterValue.getText().toString(); + intentPairList.add(param); + + spinnerParameterType.setSelection(0); + etParameterName.setText(""); + etParameterValue.setText(""); + + updateIntentPairList(); + } + }); + + lvIntentPairs.setOnItemLongClickListener(new OnItemLongClickListener() + { + @Override + public boolean onItemLongClick(AdapterView arg0, View arg1, int arg2, long arg3) + { + getIntentPairDialog(arg2).show(); + return false; + } + }); + + bSaveActionStartOtherActivity.setOnClickListener(new OnClickListener() + { + @Override + public void onClick(View v) + { + if(saveAction()) + { + ActivityManageNotificationTrigger.this.setResult(RESULT_OK); + finish(); + } + } + }); + + lvIntentPairs.setOnTouchListener(new OnTouchListener() + { + @Override + public boolean onTouch(View v, MotionEvent event) + { + v.getParent().requestDisallowInterceptTouchEvent(true); + return false; + } + }); + + spinnerParameterType.setOnItemSelectedListener(new OnItemSelectedListener() + { + @Override + public void onItemSelected(AdapterView arg0, View arg1, int arg2, long arg3) + { + if(supportedIntentTypes[arg2].equals("double") | supportedIntentTypes[arg2].equals("float") | supportedIntentTypes[arg2].equals("int") | supportedIntentTypes[arg2].equals("long") | supportedIntentTypes[arg2].equals("short")) + ActivityManageNotificationTrigger.this.etParameterValue.setInputType(InputType.TYPE_CLASS_NUMBER); + else + ActivityManageNotificationTrigger.this.etParameterValue.setInputType(InputType.TYPE_CLASS_TEXT); + } + + @Override + public void onNothingSelected(AdapterView arg0) + { + // TODO Auto-generated method stub + + } + }); + + Intent i = getIntent(); + if(i.getBooleanExtra("edit", false) == true) + { + edit = true; + loadValuesIntoGui(); + } + } + + private void loadValuesIntoGui() + { + String[] params = resultingAction.getParameter2().split(";"); + if(params.length >= 2) + { + tvSelectedActivity.setText(params[0] + ";" + params[1]); + + if(params.length > 2) + { + intentPairList.clear(); + + for(int i=2; i + { + @Override + protected Void doInBackground(Void... params) + { + getActivityList(ActivityManageNotificationTrigger.this); + return null; + } + + @Override + protected void onPostExecute(Void result) + { + progressDialog.dismiss(); + getActionStartActivityDialog1().show(); + } + + + } +} diff --git a/app/src/main/java/com/jens/automation2/ActivityManageRule.java b/app/src/main/java/com/jens/automation2/ActivityManageRule.java index 2a208094..1bcd161d 100644 --- a/app/src/main/java/com/jens/automation2/ActivityManageRule.java +++ b/app/src/main/java/com/jens/automation2/ActivityManageRule.java @@ -95,6 +95,8 @@ public class ActivityManageRule extends Activity final static int requestCodeActionScreenBrightnessAdd = 401; final static int requestCodeActionScreenBrightnessEdit = 402; final static int requestCodeActionSendTextMessage = 7001; + final static int requestCodeTriggerNotificationAdd = 8000; + final static int requestCodeTriggerNfcNotificationEdit = 8001; public static ActivityManageRule getInstance() { @@ -466,6 +468,8 @@ public class ActivityManageRule extends Activity items.add(new Item(typesLong[i].toString(), R.drawable.bluetooth)); else if(types[i].toString().equals(Trigger_Enum.headsetPlugged.toString())) items.add(new Item(typesLong[i].toString(), R.drawable.headphone)); + else if(types[i].toString().equals(Trigger_Enum.notification.toString())) + items.add(new Item(typesLong[i].toString(), R.drawable.notification)); else items.add(new Item(typesLong[i].toString(), R.drawable.placeholder)); } @@ -536,6 +540,13 @@ public class ActivityManageRule extends Activity booleanChoices = new String[]{getResources().getString(R.string.connected), getResources().getString(R.string.disconnected)}; else if(triggerType == Trigger_Enum.process_started_stopped) booleanChoices = new String[]{getResources().getString(R.string.started), getResources().getString(R.string.stopped)}; + else if(triggerType == Trigger_Enum.notification) + { + newTrigger.setTriggerType(Trigger_Enum.notification); + Intent nfcEditor = new Intent(myContext, ActivityManageNotificationTrigger.class); + startActivityForResult(nfcEditor, requestCodeTriggerNotificationAdd); + return; + } else if(triggerType == Trigger_Enum.airplaneMode) booleanChoices = new String[]{getResources().getString(R.string.activated), getResources().getString(R.string.deactivated)}; else if(triggerType == Trigger_Enum.roaming) @@ -1139,6 +1150,18 @@ public class ActivityManageRule extends Activity else Miscellaneous.logEvent("w", "ActivityManageNfc", "No nfc id returned. Assuming abort.", 5); } + else if(requestCode == requestCodeTriggerNotificationAdd) + { + //add notification + if(resultCode == RESULT_OK) + { + //newTrigger.setNfcTagId(ActivityManageNfc.generatedId); + ruleToEdit.getTriggerSet().add(newTrigger); + this.refreshTriggerList(); + } + else + Miscellaneous.logEvent("w", "ActivityManageNfc", "No nfc id returned. Assuming abort.", 5); + } else if(requestCode == requestCodeActionSpeakTextAdd) { if(resultCode == RESULT_OK) diff --git a/app/src/main/java/com/jens/automation2/AutomationService.java b/app/src/main/java/com/jens/automation2/AutomationService.java index 487598d3..072eec03 100644 --- a/app/src/main/java/com/jens/automation2/AutomationService.java +++ b/app/src/main/java/com/jens/automation2/AutomationService.java @@ -287,6 +287,8 @@ public class AutomationService extends Service implements OnInitListener myLocationProvider.applySettingsAndRules(); ReceiverCoordinator.applySettingsAndRules(); + + Miscellaneous.createDismissableNotification("test", 4711, null); } @Override diff --git a/app/src/main/java/com/jens/automation2/Miscellaneous.java b/app/src/main/java/com/jens/automation2/Miscellaneous.java index 6b6441e9..f9581db4 100644 --- a/app/src/main/java/com/jens/automation2/Miscellaneous.java +++ b/app/src/main/java/com/jens/automation2/Miscellaneous.java @@ -848,7 +848,7 @@ public class Miscellaneous extends Service mNotificationManager.notify(0, dismissableNotification);*/ } - public static void createDismissableNotificationSdk26(String textToDisplay, int notificationId, PendingIntent pendingIntent) + static void createDismissableNotificationSdk26(String textToDisplay, int notificationId, PendingIntent pendingIntent) { NotificationManager mNotificationManager = (NotificationManager) AutomationService.getInstance().getSystemService(Context.NOTIFICATION_SERVICE); diff --git a/app/src/main/java/com/jens/automation2/Trigger.java b/app/src/main/java/com/jens/automation2/Trigger.java index a9f92ba5..344a27ad 100644 --- a/app/src/main/java/com/jens/automation2/Trigger.java +++ b/app/src/main/java/com/jens/automation2/Trigger.java @@ -21,7 +21,7 @@ public class Trigger */ public enum Trigger_Enum { - pointOfInterest, timeFrame, charging, batteryLevel, usb_host_connection, speed, noiseLevel, wifiConnection, process_started_stopped, airplaneMode, roaming, nfcTag, activityDetection, bluetoothConnection, headsetPlugged, phoneCall; //phoneCall always needs to be at the very end because of Google's shitty so called privacy + pointOfInterest, timeFrame, charging, batteryLevel, usb_host_connection, speed, noiseLevel, wifiConnection, process_started_stopped, airplaneMode, roaming, nfcTag, activityDetection, bluetoothConnection, headsetPlugged, notification, phoneCall; //phoneCall always needs to be at the very end because of Google's shitty so called privacy public String getFullName(Context context) { @@ -59,6 +59,8 @@ public class Trigger return context.getResources().getString(R.string.bluetoothConnection); case headsetPlugged: return context.getResources().getString(R.string.triggerHeadsetPlugged); + case notification: + return context.getResources().getString(R.string.notification); default: return "Unknown"; } 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 1725f2a8..ea3af70a 100644 --- a/app/src/main/java/com/jens/automation2/receivers/NotificationListener.java +++ b/app/src/main/java/com/jens/automation2/receivers/NotificationListener.java @@ -1,48 +1,55 @@ package com.jens.automation2.receivers; import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; import android.os.Build; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import androidx.annotation.RequiresApi; +// See here for reference: http://gmariotti.blogspot.com/2013/11/notificationlistenerservice-and-kitkat.html + @SuppressLint("OverrideAbstract") @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public class NotificationListener extends NotificationListenerService { - static NotificationListener instance; + // the title of the notification, + public static final String EXTRA_TITLE = "android.title"; + // the main text payload + public static final String EXTRA_TEXT = "android.text"; - public static boolean startNotificationListenerService() - { - if(instance == null) - instance = new NotificationListener(); + // a third line of text, as supplied to + public static final String EXTRA_SUB_TEXT = "android.subText"; - instance.c - } + // a bitmap to be used instead of the small icon when showing the notification payload + public static final String EXTRA_LARGE_ICON = "android.largeIcon"; @Override public void onCreate() { super.onCreate(); - nlservicereciver = new NLServiceReceiver(); - IntentFilter filter = new IntentFilter(); - filter.addAction("com.kpbird.nlsexample.NOTIFICATION_LISTENER_SERVICE_EXAMPLE"); - registerReceiver(nlservicereciver,filter); } + @RequiresApi(api = Build.VERSION_CODES.KITKAT) @Override public void onNotificationPosted(StatusBarNotification sbn) { super.onNotificationPosted(sbn); + String app = sbn.getPackageName(); + String title = sbn.getNotification().extras.getString(EXTRA_TITLE); + String text = sbn.getNotification().extras.getString(EXTRA_TEXT); } - @Override - public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) - { - super.onNotificationPosted(sbn, rankingMap); - } +// @Override +// public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) +// { +// super.onNotificationPosted(sbn, rankingMap); +// sbn.getNotification().extras.getString(EXTRA_TITLE); +// sbn.getNotification().extras.getString(EXTRA_TEXT; +// } @Override public void onListenerConnected() @@ -56,5 +63,9 @@ public class NotificationListener extends NotificationListenerService super.onListenerDisconnected(); } - -} + public static void openNotificationAccessWindow(Context context) + { + Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"); + context.startActivity(intent); + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/notification.png b/app/src/main/res/drawable-hdpi/notification.png new file mode 100644 index 00000000..62b8d94d Binary files /dev/null and b/app/src/main/res/drawable-hdpi/notification.png differ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ebd97e50..9823399d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -611,4 +611,5 @@ Location cannot be determined anymore. Click here to find out why. Unfortunately your location cannot be determined anymore. A debt of gratitude is owed to Google for its infinite wisdom and amiableness.\\n\\nLet me explain this further. Starting with Android 10 a new permission was introduced that is needed to determine your location in the background (which of course is required for an app like this). Whilst I consider that a good idea in general the chicanery it involves for developers are not.\\n\\nWhen developing an app you can try to qualify for this permission by abiding to a catalog of requirements. Unfortunately new versions of my app have been rejected over a period of three months. I fulfilled all these requirements, Google\'s shitty development support claimed I would not. After giving them proof that I did after all - I got a response like \"I cannot help you anymore\". Eventually I gave up. \\n\\nAs a consequence the Google Play version can NOT use your location as a trigger anymore. My only alternative option would have been to have this application removed from the store entirely.\\n\\nI\'m very sorry about that, but I\'ve tried my best arguing with a \"support\" that repeatedly failed to pass the Turing test.\\n\\nThe good news: You can still have it all!\\n\\nAutomation is now open source and can be found in F-Droid. That is an app store that really cares about your privacy - rather than just acting like that. Simply backup your config file, uninstall this app, install it again from F-Droid, restore your config file - done.\\n\\nClick here to find out more: Config and log files are stored in folder %1$s + Notification \ No newline at end of file