diff --git a/app/src/apkFlavor/AndroidManifest.xml b/app/src/apkFlavor/AndroidManifest.xml index e4464193..7d22baea 100644 --- a/app/src/apkFlavor/AndroidManifest.xml +++ b/app/src/apkFlavor/AndroidManifest.xml @@ -167,6 +167,7 @@ + diff --git a/app/src/fdroidFlavor/AndroidManifest.xml b/app/src/fdroidFlavor/AndroidManifest.xml index 8236850a..d219b249 100644 --- a/app/src/fdroidFlavor/AndroidManifest.xml +++ b/app/src/fdroidFlavor/AndroidManifest.xml @@ -165,6 +165,7 @@ + diff --git a/app/src/googlePlayFlavor/AndroidManifest.xml b/app/src/googlePlayFlavor/AndroidManifest.xml index aa4c5766..dd348758 100644 --- a/app/src/googlePlayFlavor/AndroidManifest.xml +++ b/app/src/googlePlayFlavor/AndroidManifest.xml @@ -152,6 +152,7 @@ + diff --git a/app/src/main/java/com/jens/automation2/Action.java b/app/src/main/java/com/jens/automation2/Action.java index c75796f9..351b80ba 100644 --- a/app/src/main/java/com/jens/automation2/Action.java +++ b/app/src/main/java/com/jens/automation2/Action.java @@ -330,7 +330,15 @@ public class Action if (parts.length > 4 && !StringUtils.isBlank(parts[4])) returnString.append(", " + Miscellaneous.getAnyContext().getResources().getString(R.string.ifString) + " " + Miscellaneous.getAnyContext().getResources().getString(R.string.text) + " " + Trigger.getMatchString(parts[3]) + " " + parts[4]); - + } + else if(this.getAction().equals(Action_Enum.setWifi)) + { + if(!StringUtils.isEmpty(this.parameter2)) + { + boolean useRoot = Boolean.parseBoolean(this.parameter2); + if(useRoot) + returnString.append(" " + Miscellaneous.getAnyContext().getResources().getString(R.string.usingRoot)); + } } else if(this.getAction().equals(Action_Enum.controlMediaPlayback)) { @@ -552,7 +560,11 @@ public class Action Actions.sendBroadcast(context, this.getParameter2()); break; case runExecutable: - Actions.runExecutable(context, this.getParameter1(), this.getParameter2()); + String[] execParts = this.getParameter2().split(Action.actionParameter2Split); + if(execParts.length == 1) + Actions.runExecutable(context, this.getParameter1(), execParts[0], null); + else if(execParts.length == 2) + Actions.runExecutable(context, this.getParameter1(), execParts[0], execParts[1]); break; default: Miscellaneous.logEvent("w", "Action", context.getResources().getString(R.string.unknownActionSpecified), 3); diff --git a/app/src/main/java/com/jens/automation2/Actions.java b/app/src/main/java/com/jens/automation2/Actions.java index d113fd71..c34657aa 100644 --- a/app/src/main/java/com/jens/automation2/Actions.java +++ b/app/src/main/java/com/jens/automation2/Actions.java @@ -52,7 +52,12 @@ import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.conn.util.InetAddressUtils; import org.apache.http.impl.client.DefaultHttpClient; +import java.io.BufferedReader; import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -63,7 +68,9 @@ import java.security.KeyStore; import java.util.Calendar; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; @@ -1908,8 +1915,156 @@ public class Actions } } - public static boolean runExecutable(Context context, boolean runAsRoot, String pathAndParams) + public static boolean runExecutable(Context context, boolean runAsRoot, String path, String parameters) { - return false; + if(runAsRoot) + { + if(!StringUtils.isEmpty(parameters)) + return executeCommandViaSu(new String[] { path + " " + parameters }); + else + return executeCommandViaSu(new String[] { path }); + } + else + { + Object[] result; + + File executable = new File(path); + File workingDir = new File(executable.getParent()); + + if(!StringUtils.isEmpty(parameters)) + result = runExternalApplication(path, 0, workingDir, parameters); + else + result = runExternalApplication(path, 0, workingDir, null); + + boolean execResult = (boolean) result[0]; + + return execResult; + } + } + + /** + * + * @param commandToExecute + * @param timeout + * @param params + * @return Returns an array: 0=exit code, 1=cmdline output + */ + public static Object[] runExternalApplication(String commandToExecute, long timeout, File workingDirectory, String params) + { + /* + * Classes stolen from https://github.com/stleary/JSON-java + */ + + String fullCommand; + + if(!StringUtils.isEmpty(params)) + fullCommand = commandToExecute + " " + params; + else + fullCommand = commandToExecute; + + Miscellaneous.logEvent("i", "Running executable", "Running external application " + fullCommand, 4); + + Object[] returnObject = new Object[2]; + + StringBuilder output = new StringBuilder(); + String line = null; + OutputStream stdin = null; + InputStream stderr = null; + InputStream stdout = null; + + try + { + Process process = null; + + if(workingDirectory != null) + process = Runtime.getRuntime().exec(fullCommand, null, workingDirectory); + else + process = Runtime.getRuntime().exec(fullCommand); + stdin = process.getOutputStream (); + stderr = process.getErrorStream (); + stdout = process.getInputStream (); + + // "write" the parms into stdin + /*line = "param1" + "\n"; + stdin.write(line.getBytes() ); + stdin.flush(); + + line = "param2" + "\n"; + stdin.write(line.getBytes() ); + stdin.flush(); + + line = "param3" + "\n"; + stdin.write(line.getBytes() ); + stdin.flush();*/ + + stdin.close(); + + // clean up if any output in stdout + BufferedReader brCleanUp = new BufferedReader (new InputStreamReader (stdout)); + while ((line = brCleanUp.readLine ()) != null) + { + Miscellaneous.logEvent ("i", "Running executable", "[Stdout] " + line, 4); + output.append(line); + } + brCleanUp.close(); + + // clean up if any output in stderr + brCleanUp = new BufferedReader (new InputStreamReader(stderr)); + while ((line = brCleanUp.readLine ()) != null) + { + Miscellaneous.logEvent ("i", "Running executable", "[Stderr] " + line, 4); + output.append(line); + } + brCleanUp.close(); + + try + { + // Wait for the process to exit, we want the return code + if(timeout > 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + { + try + { + if(!process.waitFor(timeout, TimeUnit.MILLISECONDS)) + { + Miscellaneous.logEvent("i", "Running executable", "Timeout of " + String.valueOf(timeout) + " ms reached. Killing check attempt.", 3); + process.destroyForcibly(); + } + } + catch(NoSuchMethodError e) + { + process.waitFor(); + } + } + else + process.waitFor(); + } + catch (InterruptedException e) + { + Miscellaneous.logEvent("i", "Running executable", "Waiting for process failed: " + Log.getStackTraceString(e), 4); + Miscellaneous.logEvent("i", "Running executable", Log.getStackTraceString(e), 1); + } + + if(process.exitValue() == 0) + Miscellaneous.logEvent("i", "Running executable", "ReturnCode: " + String.valueOf(process.exitValue()), 4); + else + Miscellaneous.logEvent("i", "Running executable", "External execution (RC=" + String.valueOf(process.exitValue()) + ") returned error: " + output.toString(), 3); + + returnObject[0] = process.exitValue(); + returnObject[1] = output.toString(); + + return returnObject; + } + catch (IOException e) + { + Miscellaneous.logEvent("e", "Running executable", Log.getStackTraceString(e), 1); + } + + Miscellaneous.logEvent("i", "Running executable", "Error running external application.", 1); + +// if(slotMap != null) +// for(String key : slotMap.keySet()) +// System.clearProperty(key); + + return null; } } \ No newline at end of file diff --git a/app/src/main/java/com/jens/automation2/ActivityMainScreen.java b/app/src/main/java/com/jens/automation2/ActivityMainScreen.java index 909bec9d..d88bc842 100644 --- a/app/src/main/java/com/jens/automation2/ActivityMainScreen.java +++ b/app/src/main/java/com/jens/automation2/ActivityMainScreen.java @@ -80,7 +80,7 @@ public class ActivityMainScreen extends ActivityGeneric bDonate = (Button)findViewById(R.id.bDonate); - if(!BuildConfig.FLAVOR.equalsIgnoreCase("googlePlayFlavor")) + if(!BuildConfig.FLAVOR.equalsIgnoreCase(AutomationService.flavor_name_googleplay)) bDonate.setVisibility(View.VISIBLE); toggleService.setChecked(AutomationService.isMyServiceRunning(this)); diff --git a/app/src/main/java/com/jens/automation2/ActivityManageActionRunExecutable.java b/app/src/main/java/com/jens/automation2/ActivityManageActionRunExecutable.java index 3486691d..e332ff38 100644 --- a/app/src/main/java/com/jens/automation2/ActivityManageActionRunExecutable.java +++ b/app/src/main/java/com/jens/automation2/ActivityManageActionRunExecutable.java @@ -76,7 +76,7 @@ public class ActivityManageActionRunExecutable extends Activity } else { - if(!chkRunExecAsRoot.isChecked() && !executableFile.canExecute()) + if(false)//!chkRunExecAsRoot.isChecked() && !executableFile.canExecute()) { Toast.makeText(ActivityManageActionRunExecutable.this, getResources().getString(R.string.fileNotExecutable), Toast.LENGTH_LONG).show(); return; @@ -85,12 +85,12 @@ public class ActivityManageActionRunExecutable extends Activity } Intent returnData = new Intent(); - returnData.putExtra("actionParameter1", chkRunExecAsRoot.isChecked()); + returnData.putExtra(ActivityManageRule.intentNameActionParameter1, chkRunExecAsRoot.isChecked()); if(etRunExecutableParameters.getText() != null && !StringUtils.isEmpty(etRunExecutableParameters.getText().toString())) - returnData.putExtra("actionParameter2", etRunExecutablePath.getText().toString() + Action.actionParameter2Split + etRunExecutableParameters.getText().toString()); + returnData.putExtra(ActivityManageRule.intentNameActionParameter2, etRunExecutablePath.getText().toString() + Action.actionParameter2Split + etRunExecutableParameters.getText().toString()); else - returnData.putExtra("actionParameter2", etRunExecutablePath.getText().toString()); + returnData.putExtra(ActivityManageRule.intentNameActionParameter2, etRunExecutablePath.getText().toString()); setResult(RESULT_OK, returnData); finish(); diff --git a/app/src/main/java/com/jens/automation2/ActivityManageActionStartActivity.java b/app/src/main/java/com/jens/automation2/ActivityManageActionStartActivity.java index cc0dd2eb..58787aa0 100644 --- a/app/src/main/java/com/jens/automation2/ActivityManageActionStartActivity.java +++ b/app/src/main/java/com/jens/automation2/ActivityManageActionStartActivity.java @@ -380,7 +380,7 @@ public class ActivityManageActionStartActivity extends Activity int targetSdkVersion = getApplicationContext().getApplicationInfo().targetSdkVersion; if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && targetSdkVersion >= 30 && !ActivityPermissions.havePermission(Manifest.permission.QUERY_ALL_PACKAGES, ActivityManageActionStartActivity.this))// && shouldShowRequestPermissionRationale(Manifest.permission.QUERY_ALL_PACKAGES)) { - if(BuildConfig.FLAVOR.equals("googlePlayFlavor")) + if(BuildConfig.FLAVOR.equals(AutomationService.flavor_name_googleplay)) { // This ain't possible anymore. Miscellaneous.messageBox(getResources().getString(R.string.info), getResources().getString(R.string.featureNotInGooglePlayVersion) + Miscellaneous.lineSeparator + Miscellaneous.lineSeparator + getResources().getString(R.string.startActivityInsertManually), ActivityManageActionStartActivity.this).show(); diff --git a/app/src/main/java/com/jens/automation2/ActivityManageActionWifi.java b/app/src/main/java/com/jens/automation2/ActivityManageActionWifi.java new file mode 100644 index 00000000..63364aa4 --- /dev/null +++ b/app/src/main/java/com/jens/automation2/ActivityManageActionWifi.java @@ -0,0 +1,63 @@ +package com.jens.automation2; + +import android.app.Activity; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.RadioButton; +import android.widget.TextView; + +import androidx.annotation.Nullable; + +public class ActivityManageActionWifi extends Activity +{ + CheckBox chkWifiRunAsRoot; + RadioButton rbActionWifiOn, rbActionWifiOff; + Button bActionWifiSave; + TextView tvWifiExplanation1, tvWifiExplanation2; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_manage_action_wifi); + + chkWifiRunAsRoot = (CheckBox)findViewById(R.id.chkWifiRunAsRoot); + rbActionWifiOn = (RadioButton) findViewById(R.id.rbActionWifiOn); + rbActionWifiOff = (RadioButton)findViewById(R.id.rbActionWifiOff); + bActionWifiSave = (Button) findViewById(R.id.bActionWifiSave); + tvWifiExplanation1 = (TextView)findViewById(R.id.tvWifiExplanation1); + tvWifiExplanation2 = (TextView)findViewById(R.id.tvWifiExplanation2); + + Intent input = getIntent(); + if(input.hasExtra(ActivityManageRule.intentNameActionParameter1)) + rbActionWifiOn.setChecked(input.getBooleanExtra(ActivityManageRule.intentNameActionParameter1, true)); + + if(input.hasExtra(ActivityManageRule.intentNameActionParameter2)) + chkWifiRunAsRoot.setChecked(Boolean.parseBoolean(input.getStringExtra(ActivityManageRule.intentNameActionParameter2))); + +// if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) +// Miscellaneous.messageBox(getResources().getString(R.string.app_name), getResources().getString(R.string.android10WifiToggleNotice), ActivityManageActionWifi.this).show(); + + if(BuildConfig.FLAVOR.equals(AutomationService.flavor_name_googleplay)) + tvWifiExplanation1.setVisibility(View.VISIBLE); + else + tvWifiExplanation1.setVisibility(View.GONE); + + bActionWifiSave.setOnClickListener(new View.OnClickListener() + { + @Override + public void onClick(View view) + { + Intent response = new Intent(); + response.putExtra(ActivityManageRule.intentNameActionParameter1, rbActionWifiOn.isChecked()); + response.putExtra(ActivityManageRule.intentNameActionParameter2, String.valueOf(chkWifiRunAsRoot.isChecked())); + setResult(RESULT_OK, response); + finish(); + } + }); + } +} diff --git a/app/src/main/java/com/jens/automation2/ActivityManageRule.java b/app/src/main/java/com/jens/automation2/ActivityManageRule.java index e8f55704..21a96c6d 100644 --- a/app/src/main/java/com/jens/automation2/ActivityManageRule.java +++ b/app/src/main/java/com/jens/automation2/ActivityManageRule.java @@ -125,6 +125,8 @@ public class ActivityManageRule extends Activity final static int requestCodeActionSendBroadcastEdit = 812; final static int requestCodeActionRunExecutableAdd = 813; final static int requestCodeActionRunExecutableEdit = 814; + final static int requestCodeActionSetWifiAdd = 815; + final static int requestCodeActionSetWifiEdit = 816; public static ActivityManageRule getInstance() { @@ -390,6 +392,12 @@ public class ActivityManageRule extends Activity activityEditRunExecutableIntent.putExtra(intentNameActionParameter2, a.getParameter2()); startActivityForResult(activityEditRunExecutableIntent, requestCodeActionRunExecutableEdit); break; + case setWifi: + Intent activityEditSetWifiIntent = new Intent(ActivityManageRule.this, ActivityManageActionWifi.class); + activityEditSetWifiIntent.putExtra(intentNameActionParameter1, a.getParameter1()); + activityEditSetWifiIntent.putExtra(intentNameActionParameter2, a.getParameter2()); + startActivityForResult(activityEditSetWifiIntent, requestCodeActionSetWifiEdit); + break; case controlMediaPlayback: Intent activityEditControlMediaIntent = new Intent(ActivityManageRule.this, ActivityManageActionControlMedia.class); activityEditControlMediaIntent.putExtra(ActivityManageRule.intentNameActionParameter2, a.getParameter2()); @@ -1518,6 +1526,17 @@ public class ActivityManageRule extends Activity this.refreshActionList(); } } + else if(requestCode == requestCodeActionSetWifiAdd) + { + if(resultCode == RESULT_OK) + { + newAction.setParentRule(ruleToEdit); + newAction.setParameter1(data.getBooleanExtra(ActivityManageRule.intentNameActionParameter1, true)); + newAction.setParameter2(data.getStringExtra(ActivityManageRule.intentNameActionParameter2)); + ruleToEdit.getActionSet().add(newAction); + this.refreshActionList(); + } + } else if(requestCode == requestCodeActionCreateNotificationAdd) { if(resultCode == RESULT_OK) @@ -1581,6 +1600,21 @@ public class ActivityManageRule extends Activity this.refreshActionList(); } } + else if(requestCode == requestCodeActionSetWifiEdit) + { + if(resultCode == RESULT_OK) + { + ruleToEdit.getActionSet().get(editIndex).setParentRule(ruleToEdit); + + if(data.hasExtra(intentNameActionParameter1) && data.hasExtra(intentNameActionParameter2)) + { + ruleToEdit.getActionSet().get(editIndex).setParameter1(data.getBooleanExtra(intentNameActionParameter1, false)); + ruleToEdit.getActionSet().get(editIndex).setParameter2(data.getStringExtra(intentNameActionParameter2)); + } + + this.refreshActionList(); + } + } else if(requestCode == requestCodeActionControlMediaEdit) { if(resultCode == RESULT_OK) @@ -1844,10 +1878,8 @@ public class ActivityManageRule extends Activity else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setWifi.toString())) { newAction.setAction(Action_Enum.setWifi); - getActionParameter1Dialog(ActivityManageRule.this).show(); - - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) - Miscellaneous.messageBox(context.getResources().getString(R.string.app_name), context.getResources().getString(R.string.android10WifiToggleNotice), context).show(); + Intent editSetWifiIntent = new Intent(context, ActivityManageActionWifi.class); + startActivityForResult(editSetWifiIntent, requestCodeActionSetWifiAdd); } else if(Action.getActionTypesAsArray()[which].toString().equals(Action_Enum.setBluetooth.toString())) { diff --git a/app/src/main/java/com/jens/automation2/ActivityPermissions.java b/app/src/main/java/com/jens/automation2/ActivityPermissions.java index 62a47c56..34d04caf 100644 --- a/app/src/main/java/com/jens/automation2/ActivityPermissions.java +++ b/app/src/main/java/com/jens/automation2/ActivityPermissions.java @@ -250,7 +250,7 @@ public class ActivityPermissions extends Activity } else if(s.equalsIgnoreCase(Manifest.permission.ACTIVITY_RECOGNITION) || s.equalsIgnoreCase(permissionNameGoogleActivityDetection)) { - if(!BuildConfig.FLAVOR.equalsIgnoreCase("fdroidFlavor")) + if(!BuildConfig.FLAVOR.equalsIgnoreCase(AutomationService.flavor_name_fdroid)) if (!havePermission(s, context)) return true; } @@ -381,7 +381,7 @@ public class ActivityPermissions extends Activity } else if (singlePermission.equalsIgnoreCase(Manifest.permission.ACTIVITY_RECOGNITION) || singlePermission.equalsIgnoreCase(permissionNameGoogleActivityDetection)) { - if (!BuildConfig.FLAVOR.equalsIgnoreCase("fdroidFlavor")) + if (!BuildConfig.FLAVOR.equalsIgnoreCase(AutomationService.flavor_name_fdroid)) addToArrayListUnique(singlePermission, requiredPermissions); } else diff --git a/app/src/main/java/com/jens/automation2/ActivitySettings.java b/app/src/main/java/com/jens/automation2/ActivitySettings.java index a8e81a3c..6c69ce73 100644 --- a/app/src/main/java/com/jens/automation2/ActivitySettings.java +++ b/app/src/main/java/com/jens/automation2/ActivitySettings.java @@ -18,7 +18,7 @@ public class ActivitySettings extends PreferenceActivity super.onCreate(savedInstanceState); addPreferencesFromResource(layout.activity_settings); - if(BuildConfig.FLAVOR.equals("apkFlavor")) + if(BuildConfig.FLAVOR.equals(AutomationService.flavor_name_apk)) { chkPrefUpdateCheck = (CheckBoxPreference) findPreference("automaticUpdateCheck"); chkPrefUpdateCheck.setEnabled(true); diff --git a/app/src/main/java/com/jens/automation2/AutomationService.java b/app/src/main/java/com/jens/automation2/AutomationService.java index 98f31063..2d5fc7fd 100644 --- a/app/src/main/java/com/jens/automation2/AutomationService.java +++ b/app/src/main/java/com/jens/automation2/AutomationService.java @@ -43,6 +43,10 @@ public class AutomationService extends Service implements OnInitListener protected final static int notificationIdRestrictions = 1005; protected final static int notificationIdLocationRestriction = 1006; + public static final String flavor_name_apk = "apkFlavor"; + public static final String flavor_name_fdroid = "fdroidFlavor"; + public static final String flavor_name_googleplay = "googlePlayFlavor"; + final static String NOTIFICATION_CHANNEL_ID_SERVICE = "com.jens.automation2_service"; final static String NOTIFICATION_CHANNEL_NAME_SERVICE = "Service notification"; diff --git a/app/src/main/java/com/jens/automation2/Miscellaneous.java b/app/src/main/java/com/jens/automation2/Miscellaneous.java index c55297b8..e21bd74b 100644 --- a/app/src/main/java/com/jens/automation2/Miscellaneous.java +++ b/app/src/main/java/com/jens/automation2/Miscellaneous.java @@ -1620,7 +1620,7 @@ public class Miscellaneous extends Service { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - if (BuildConfig.FLAVOR.equalsIgnoreCase("googlePlayFlavor")) + if (BuildConfig.FLAVOR.equalsIgnoreCase(AutomationService.flavor_name_googleplay)) { if(checkExistingRules) { diff --git a/app/src/main/java/com/jens/automation2/ReceiverCoordinator.java b/app/src/main/java/com/jens/automation2/ReceiverCoordinator.java index 4d759539..6ba9f9a7 100644 --- a/app/src/main/java/com/jens/automation2/ReceiverCoordinator.java +++ b/app/src/main/java/com/jens/automation2/ReceiverCoordinator.java @@ -318,7 +318,7 @@ public class ReceiverCoordinator MediaPlayerListener.getInstance().stopListener(AutomationService.getInstance()); } - if(!BuildConfig.FLAVOR.equalsIgnoreCase("fdroidFlavor")) + if(!BuildConfig.FLAVOR.equalsIgnoreCase(AutomationService.flavor_name_fdroid)) { if (Rule.isAnyRuleUsing(Trigger.Trigger_Enum.activityDetection)) { diff --git a/app/src/main/res/layout/activity_manage_action_wifi.xml b/app/src/main/res/layout/activity_manage_action_wifi.xml new file mode 100644 index 00000000..a9db50fc --- /dev/null +++ b/app/src/main/res/layout/activity_manage_action_wifi.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +