From f70e45701ffd8c0966e3a4ef2130cf37bf4836b4 Mon Sep 17 00:00:00 2001 From: jens Date: Sat, 22 May 2021 02:51:31 +0200 Subject: [PATCH 1/3] Wifirouter --- app/build.gradle | 3 + .../wifi_router/HotSpotIntentService.java | 255 ++++++++++++++++++ .../actions/wifi_router/MagicActivity.java | 58 ++++ .../MyOnStartTetheringCallback.java | 15 ++ .../wifi_router/MyOreoWifiManager.java | 205 ++++++++++++++ 5 files changed, 536 insertions(+) create mode 100644 app/src/main/java/com/jens/automation2/actions/wifi_router/HotSpotIntentService.java create mode 100644 app/src/main/java/com/jens/automation2/actions/wifi_router/MagicActivity.java create mode 100644 app/src/main/java/com/jens/automation2/actions/wifi_router/MyOnStartTetheringCallback.java create mode 100644 app/src/main/java/com/jens/automation2/actions/wifi_router/MyOreoWifiManager.java diff --git a/app/build.gradle b/app/build.gradle index 220075cc..0da3bc48 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -70,6 +70,9 @@ dependencies { apkFlavorImplementation 'com.google.firebase:firebase-appindexing:19.2.0' apkFlavorImplementation 'com.google.android.gms:play-services-location:17.1.0' + + implementation 'com.linkedin.dexmaker:dexmaker:2.25.0' + implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'com.google.android.material:material:1.3.0' testImplementation 'junit:junit:4.+' diff --git a/app/src/main/java/com/jens/automation2/actions/wifi_router/HotSpotIntentService.java b/app/src/main/java/com/jens/automation2/actions/wifi_router/HotSpotIntentService.java new file mode 100644 index 00000000..8dff8126 --- /dev/null +++ b/app/src/main/java/com/jens/automation2/actions/wifi_router/HotSpotIntentService.java @@ -0,0 +1,255 @@ +package com.jens.automation2.actions.wifi_router; + +import android.Manifest; +import android.app.IntentService; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.wifi.WifiManager; +import android.os.Build; +import android.util.Log; + +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.core.app.NotificationCompat; +import androidx.core.content.ContextCompat; + +import java.lang.reflect.Method; + +import static android.content.ContentValues.TAG; + +/** + * An {@link IntentService} subclass for handling asynchronous task requests in + * a service on a separate handler thread. + */ +public class HotSpotIntentService extends IntentService +{ + /** + Id for running service in foreground + */ + private static int FOREGROUND_ID=1338; + private static final String CHANNEL_ID = "control_app"; + + // Action names...assigned in manifest. + private String ACTION_TURNON; + private String ACTION_TURNOFF; + private String DATAURI_TURNON; + private String DATAURI_TURNOFF; + private Intent mStartIntent; + + @RequiresApi(api = Build.VERSION_CODES.O) + MyOreoWifiManager mMyOreoWifiManager; + + + /** + * Creates an IntentService. Invoked by your subclass's constructor. + * + + */ + public HotSpotIntentService() + { + super("HotSpotIntentService"); + } + + /** + * Helper method to start this intent from {@link HotSpotIntentReceiver} + * @param context + * @param intent + */ + public static void start(Context context,Intent intent) + { + Intent i = new Intent(context, HotSpotIntentService.class); + i.setAction(intent.getAction()); + i.setData(intent.getData()); + context.startService(i); + } + + @Override + protected void onHandleIntent(@Nullable Intent intent) + { + ACTION_TURNON = getString(R.string.intent_action_turnon); + ACTION_TURNOFF = getString(R.string.intent_action_turnoff); + + DATAURI_TURNON = getString(R.string.intent_data_host_turnon); + DATAURI_TURNOFF = getString(R.string.intent_data_host_turnoff); + + // Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); + Log.i(TAG,"Received start intent"); + + mStartIntent = intent; + + if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) + { + + } + else + { + carryOn(); + } + + } + + + private void carryOn() + { + boolean turnOn = true; + if (mStartIntent != null) + { + final String action = mStartIntent.getAction(); + final String data = mStartIntent.getDataString(); + if (ACTION_TURNON.equals(action) || (data!=null && data.contains(DATAURI_TURNON))) + { + turnOn = true; + Log.i(TAG,"Action/data to turn on hotspot"); + } + else if (ACTION_TURNOFF.equals(action)|| (data!=null && data.contains(DATAURI_TURNOFF))) + { + turnOn = false; + Log.i(TAG,"Action/data to turn off hotspot"); + } + + if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.O) + { + hotspotOreo(turnOn); + } + else + { + turnOnHotspotPreOreo(turnOn); + } + } + } + + private boolean turnOnHotspotPreOreo(boolean turnOn) + { + { + WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); + + Method[] methods = wifiManager.getClass().getDeclaredMethods(); + for (Method method : methods) + { + if (method.getName().equals("setWifiApEnabled")) + { + try + { + if (turnOn) + { + wifiManager.setWifiEnabled(false); //Turning off wifi because tethering requires wifi to be off + method.invoke(wifiManager, null, true); //Activating tethering + return true; + } + else + { + method.invoke(wifiManager, null, false); //Deactivating tethering + wifiManager.setWifiEnabled(true); //Turning on wifi ...should probably be done from a saved setting + return true; + } + } + catch (Exception e) + { + return false; + } + } + } + + //Error setWifiApEnabled not found + return false; + } + } + + /** + * + */ + @RequiresApi(api = Build.VERSION_CODES.O) + private void hotspotOreo(boolean turnOn){ + + if (mMyOreoWifiManager ==null) + { + mMyOreoWifiManager = new MyOreoWifiManager(this); + } + + if (turnOn) + { + //this dont work + MyOnStartTetheringCallback callback = new MyOnStartTetheringCallback() + { + @Override + public void onTetheringStarted() + { + startForeground(FOREGROUND_ID, buildForegroundNotification()); + } + + @Override + public void onTetheringFailed() + { + + } + }; + + mMyOreoWifiManager.startTethering(callback); + } + else + { + mMyOreoWifiManager.stopTethering(); + stopForeground(true); + stopSelf(); + } + } + + //**************************************************************************************** + + + /** + * Build low priority notification for running this service as a foreground service. + * @return + */ + private Notification buildForegroundNotification() + { + registerNotifChnnl(this); + + Intent stopIntent = new Intent(this, HotSpotIntentService.class); + stopIntent.setAction(getString(R.string.intent_action_turnoff)); + + PendingIntent pendingIntent = PendingIntent.getService(this,0, stopIntent, 0); + + NotificationCompat.Builder b=new NotificationCompat.Builder(this,CHANNEL_ID); + b.setOngoing(true) + .setContentTitle("WifiHotSpot is On") + .addAction(new NotificationCompat.Action( + R.drawable.turn_off, + "TURN OFF HOTSPOT", + pendingIntent + )) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setCategory(Notification.CATEGORY_SERVICE) + .setSmallIcon(R.drawable.notif_hotspot_black_24dp); + + + return(b.build()); + } + + private static void registerNotifChnnl(Context context) + { + if (Build.VERSION.SDK_INT >= 26) + { + NotificationManager mngr = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE); + if (mngr.getNotificationChannel(CHANNEL_ID) != null) + { + return; + } + // + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, + context.getString(R.string.notification_chnnl), + NotificationManager.IMPORTANCE_LOW); + // Configure the notification channel. + channel.setDescription(context.getString(R.string.notification_chnnl_location_descr)); + channel.enableLights(false); + channel.enableVibration(false); + mngr.createNotificationChannel(channel); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/jens/automation2/actions/wifi_router/MagicActivity.java b/app/src/main/java/com/jens/automation2/actions/wifi_router/MagicActivity.java new file mode 100644 index 00000000..5ee99c31 --- /dev/null +++ b/app/src/main/java/com/jens/automation2/actions/wifi_router/MagicActivity.java @@ -0,0 +1,58 @@ +package com.jens.automation2.actions.wifi_router; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; +import android.widget.Toast; + +public class MagicActivity extends Activity +{ + public static void useMagicActivityToTurnOn(Context c) + { + Uri uri = new Uri.Builder().scheme(c.getString(R.string.intent_data_scheme)).authority(c.getString(R.string.intent_data_host_turnon)).build(); + Toast.makeText(c,"Turn on. Uri: "+uri.toString(),Toast.LENGTH_LONG).show(); + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(uri); + i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + c.startActivity(i); + } + + public static void useMagicActivityToTurnOff(Context c) + { + Uri uri = new Uri.Builder().scheme(c.getString(R.string.intent_data_scheme)).authority(c.getString(R.string.intent_data_host_turnoff)).build(); + Toast.makeText(c,"Turn off. Uri: "+uri.toString(),Toast.LENGTH_LONG).show(); + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(uri); + i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + c.startActivity(i); + } + + private static final String TAG = MagicActivity.class.getSimpleName(); + + @Override + protected void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + Log.e(TAG, "onCreate"); + } + + @Override + void onPermissionsOkay() + { + carryOnWithHotSpotting(); + } + + /** + * The whole purpose of this activity - to start {@link HotSpotIntentService} + * This may be called straright away in {@code onCreate} or after permissions granted. + */ + private void carryOnWithHotSpotting() + { + Intent intent = getIntent(); + HotSpotIntentService.start(this, intent); + finish(); + } +} diff --git a/app/src/main/java/com/jens/automation2/actions/wifi_router/MyOnStartTetheringCallback.java b/app/src/main/java/com/jens/automation2/actions/wifi_router/MyOnStartTetheringCallback.java new file mode 100644 index 00000000..0d26ebd5 --- /dev/null +++ b/app/src/main/java/com/jens/automation2/actions/wifi_router/MyOnStartTetheringCallback.java @@ -0,0 +1,15 @@ +package com.jens.automation2.actions.wifi_router; + +public abstract class MyOnStartTetheringCallback +{ + /** + * Called when tethering has been successfully started. + */ + public abstract void onTetheringStarted(); + + /** + * Called when starting tethering failed. + */ + public abstract void onTetheringFailed(); + +} \ No newline at end of file diff --git a/app/src/main/java/com/jens/automation2/actions/wifi_router/MyOreoWifiManager.java b/app/src/main/java/com/jens/automation2/actions/wifi_router/MyOreoWifiManager.java new file mode 100644 index 00000000..a00b4c2a --- /dev/null +++ b/app/src/main/java/com/jens/automation2/actions/wifi_router/MyOreoWifiManager.java @@ -0,0 +1,205 @@ +package com.jens.automation2.actions.wifi_router; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; +import android.os.Build; +import android.os.Handler; +import android.util.Log; +import androidx.annotation.RequiresApi; + +import com.android.dx.stock.ProxyBuilder; + +import java.io.File; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.util.Arrays; + +/** + * Created by jonro on 19/03/2018. + */ + +@RequiresApi(api = Build.VERSION_CODES.O) +public class MyOreoWifiManager +{ + private static final String TAG = MyOreoWifiManager.class.getSimpleName(); + + private Context mContext; + private WifiManager mWifiManager; + private ConnectivityManager mConnectivityManager; + + public MyOreoWifiManager(Context c) + { + mContext = c; + mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); + mConnectivityManager = (ConnectivityManager) mContext.getSystemService(ConnectivityManager.class); + } + + /** + * This sets the Wifi SSID and password + * Call this before {@code startTethering} if app is a system/privileged app + * Requires: android.permission.TETHER_PRIVILEGED which is only granted to system apps + */ + public void configureHotspot(String name, String password) + { + WifiConfiguration apConfig = new WifiConfiguration(); + apConfig.SSID = name; + apConfig.preSharedKey = password; + apConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + try + { + Method setConfigMethod = mWifiManager.getClass().getMethod("setWifiApConfiguration", WifiConfiguration.class); + boolean status = (boolean) setConfigMethod.invoke(mWifiManager, apConfig); + Log.d(TAG, "setWifiApConfiguration - success? " + status); + } + catch (Exception e) + { + Log.e(TAG, "Error in configureHotspot"); + e.printStackTrace(); + } + } + + /** + * Checks where tethering is on. + * This is determined by the getTetheredIfaces() method, + * that will return an empty array if not devices are tethered + * + * @return true if a tethered device is found, false if not found + */ + public boolean isTetherActive() + { + try + { + Method method = mConnectivityManager.getClass().getDeclaredMethod("getTetheredIfaces"); + if (method == null) + { + Log.e(TAG, "getTetheredIfaces is null"); + } + else + { + String res[] = (String []) method.invoke(mConnectivityManager, null); + Log.d(TAG, "getTetheredIfaces invoked"); + Log.d(TAG, Arrays.toString(res)); + if (res.length > 0) + { + return true; + } + } + } + catch (Exception e) + { + Log.e(TAG, "Error in getTetheredIfaces"); + e.printStackTrace(); + } + return false; + } + + /** + * This enables tethering using the ssid/password defined in Settings App>Hotspot & tethering + * Does not require app to have system/privileged access + * Credit: Vishal Sharma - https://stackoverflow.com/a/52219887 + */ + public boolean startTethering(final MyOnStartTetheringCallback callback) + { + + // On Pie if we try to start tethering while it is already on, it will + // be disabled. This is needed when startTethering() is called programmatically. + if (isTetherActive()) + { + Log.d(TAG, "Tether already active, returning"); + return false; + } + + File outputDir = mContext.getCodeCacheDir(); + Object proxy; + try + { + proxy = ProxyBuilder.forClass(OnStartTetheringCallbackClass()) + .dexCache(outputDir).handler(new InvocationHandler() + { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + switch (method.getName()) + { + case "onTetheringStarted": + callback.onTetheringStarted(); + break; + case "onTetheringFailed": + callback.onTetheringFailed(); + break; + default: + ProxyBuilder.callSuper(proxy, method, args); + } + return null; + } + + }).build(); + } + catch (Exception e) + { + Log.e(TAG, "Error in enableTethering ProxyBuilder"); + e.printStackTrace(); + return false; + } + + Method method = null; + try + { + method = mConnectivityManager.getClass().getDeclaredMethod("startTethering", int.class, boolean.class, OnStartTetheringCallbackClass(), Handler.class); + if (method == null) + { + Log.e(TAG, "startTetheringMethod is null"); + } + else + { + method.invoke(mConnectivityManager, ConnectivityManager.TYPE_MOBILE, false, proxy, null); + Log.d(TAG, "startTethering invoked"); + } + return true; + } + catch (Exception e) + { + Log.e(TAG, "Error in enableTethering"); + e.printStackTrace(); + } + return false; + } + + public void stopTethering() + { + try + { + Method method = mConnectivityManager.getClass().getDeclaredMethod("stopTethering", int.class); + if (method == null) + { + Log.e(TAG, "stopTetheringMethod is null"); + } + else + { + method.invoke(mConnectivityManager, ConnectivityManager.TYPE_MOBILE); + Log.d(TAG, "stopTethering invoked"); + } + } + catch (Exception e) + { + Log.e(TAG, "stopTethering error: " + e.toString()); + e.printStackTrace(); + } + } + + private Class OnStartTetheringCallbackClass() + { + try + { + return Class.forName("android.net.ConnectivityManager$OnStartTetheringCallback"); + } + catch (ClassNotFoundException e) + { + Log.e(TAG, "OnStartTetheringCallbackClass error: " + e.toString()); + e.printStackTrace(); + } + return null; + } +} From 7fd8d1cfd0dd4a6a760835508197b2589e1c76d7 Mon Sep 17 00:00:00 2001 From: jens Date: Wed, 26 May 2021 19:55:21 +0200 Subject: [PATCH 2/3] Translation --- app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-es/strings.xml | 7 +++++++ app/src/main/res/values/strings.xml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index e3ca0a82..a5df1ce3 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -115,7 +115,7 @@ Regeln Alle Auslöser sind UND-verknüpft. D.h. die Regel wird nur zutreffen, wenn alle Bedingungen erfüllt sind. Wenn Sie eine ODER-Verknüpfung möchten, müssen Sie eine weitere Regel erstellen. Zeiträume - Wenn Sie eine Regel mit einem oder mehreren Zeiträumen erstellen, haben Sie zwei Möglichkeiten. Sie können wählen, ob der Auslöser besagt, daß der Zeitraum entweder verlassen ODER betreten wird. In jedem Fall wird die Regel nur einmal ausgelöst.\nWenn eine Regel z.B. besagt \"betrete timeframe xyz\" und das Klingeltonprofil in Vibration ändert, bedeutet das NICHT, daß das Gerät hinterher automatisch wieder zum normalen Klingelprofil zurückschaltet. Wenn das erwünscht ist, muß eine weitere Regel mit einem Folgezeitraum erstellen werden. + Wenn Sie eine Regel mit einem Zeitraum erstellen, haben Sie zwei Möglichkeiten. Sie können wählen, ob der Auslöser besagt, daß der Zeitraum entweder verlassen ODER betreten wird. In jedem Fall wird die Regel nur einmal ausgelöst. Wenn eine Regel z.B. besagt \"betrete timeframe xyz\" und das Klingeltonprofil in Vibration ändert, bedeutet das NICHT, daß das Gerät hinterher automatisch wieder zum normalen Klingelprofil zurückschaltet. Wenn das erwünscht ist, muß eine weitere Regel mit einem Folgezeitraum erstellen werden. Auf dem Hauptbildschirm können Sie die Funktion Tonänderunugen sperren benutzen, um vorrübergehend regelbasierte Tonänderungen zu deaktivieren. Z.B. könnten Sie in einer Situation oder an einem Ort sein, wo Klingeltöne normalerweise in Ordnung sind, aber dieses eine Mal würde es stören. Die Funktion wird automatisch wieder deaktiviert nachdem die eingestellte Zeit abgelaufen ist. Klicken Sie den + Knopf, um die angezeigte Zeit zur Frist hinzuzufügen. Sobald es aktiv ist, können Sie es mit dem Schalter rechts wieder abschalten (und so regelbasierte Tonänderungen wieder ermöglichen). Umkehrbare Regeln Regeln haben eine Einstellung namens \"Umschaltbar\". Das bedeutet: Wurde eine Regel ausgeführt und anschließend treten dieselben Auslöser wieder ein, wird die Regel nochmal in umgekehrter Weise ausgeführt, wo es möglich ist. Gegenwärtig funktioniert das nur im Zusammenspiel mit NFC Tags. Wenn Sie einen Tag zwei Mal berühren und es eine umschaltbare Regel zu diesem Tag gibt, wird das Programm das Gegenteil von gegenwärtigen Zustand tun, z.B. WLAN ausschalten, wenn es gegenwärtig eingeschaltet ist. diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index b462482d..fe29d9d9 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -373,4 +373,11 @@ Incluya las paréntecis en su texto.\n\n[uniqueid] - el número único de su dis Controlar conexiones de la app wireguard Enviar configuración y procotolo al developer (via email). Necesita permiso root para este functión. Después encenda la función \"ejecutar norma manualmente\" para presentar el permiso superuser dialogo. Es necesita elegir \"siempre permitir root para esta app\". En caso contrario la norma no puede funcionar al fondo. + Todas las condiciones estan y-conectadas. La norma solo se aplica si las condiciones estan. Si quiere \"o\" cree otra norma. + Si crea una norma con un periodo, tiene dos posibilidades. Puede eligir entre entrar y salir del periodo. En cualquier caso la acción solo será ejecutada una vez. Si crea una norma que tiene la condición \"entrar periodo xyz\" y la acción \"cambiar perfil de sonido a vibración\" no significa va a regresar automaticamente a un perfil alto cuando el periodo este terminado. Si quiere eso tiene que crear otra norma con otro periodo. + En la pantalla principal puede usar \"bloquear cambios de sonido\" para temporalmente evitar, basado en la norma, cambios de sonido. Por ejemplo podria estar en una situación o en un sitio donde tono de llamada estan bien, pero esta vez no fuese oportuno. La función se desactiva automaticamente despues del tiempo configurado. Cliquee el + botton para adicionar mas tiempo. Se puede desactivar la función antes tambien usando el boton interuptor (en consecuencia reactivar cambios de sonido, basados en normas). + Normas reversibles + Normas tienen una configuracion que se llama \"reversible\". Significa si una norma es ejecutada y despues las mismas condiciones estan, la norma es ejecutada de forma reversible donde sea posible. Actualmente solo funciona con la condición NFC. Si toca el tag dos veces, y hay una norma reversible con este tag, la applicación va a hacer lo contrario de la situación actual, por ejemplo desactivar wifi cuando esta activo. + Si crea una norma con vigilancia del proceso la aplicación va a revisar periodicamente si el proceso esta activo. Puede ajustar la frecuencia en la configuración. Puede relentizar la reacción, pero vigilancia permanente gasta mucha bateria. No hay una broadcast del sistema operativo para este evento. + Muchos fabricantes intentan conservar energia en limitando la actividades de segundo plano de otras apps. Desafortunadamente el resultado es que en esas aplicaciones no funcionan por completo. Automation esta entre ellas. Vease esta pagina para determinar como excluir Automation de tales medidas. \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index df38178e..f79297a4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -127,7 +127,7 @@ Rules All triggers in a rule are AND-connected. The rule will only apply if all triggers are met. If you want OR create another rule. TimeFrames - If you specify a rule with a timeframe you have two choices. You can choose between entering and leaving a timeframe. Either way an action is triggered only once.\nSo if you create a rule that has \"entering timeframe xyz\" as trigger and let it change your sound profile to vibrate that does not mean that the phone will automatically go to ring if the timeframe is over. If you want that you need to specify another rule with another timeframe. + If you specify a rule with a timeframe you have two choices. You can choose between entering and leaving a timeframe. Either way an action is triggered only once. So if you create a rule that has \"entering timeframe xyz\" as trigger and let it change your sound profile to vibrate that does not mean that the phone will automatically go to ring if the timeframe is over. If you want that you need to specify another rule with another timeframe. On the main screen you can use lock sound changes to temporarily avoid rule based sound changes. E.g. you may be in a situation or place where usually ringtones are ok, but this one time it would be disturbing. The feature will automatically deactivate once the configured time has elapsed. Click the + button to add the given amount of time. Once it is active you may deactivate it again using the toggle button (and that way enable rule based sound changes again). Toggable rules Rules have a flag called \"Toggable\". This means that if a rule is executed and afterwards the same triggers apply again the rule will be executed in an opposite mode where applicable. Currently this will only happen in conjunction with NFC tags. If you tap them twice and there\'s a toggable rule associated with it it will do the opposite of the current situation, e.g. deactivate wifi if it\'s currently activated. From 7182698b8a700335db8707c45944f86b2397501a Mon Sep 17 00:00:00 2001 From: jens Date: Sun, 30 May 2021 12:27:27 +0200 Subject: [PATCH 3/3] Wifi tethering without root above Oreo works again. --- .../java/com/jens/automation2/Actions.java | 89 +++--- .../wifi_router/HotSpotIntentService.java | 255 ------------------ .../actions/wifi_router/MagicActivity.java | 58 ---- .../MyOnStartTetheringCallback.java | 6 +- .../wifi_router/MyOreoWifiManager.java | 46 ++-- 5 files changed, 87 insertions(+), 367 deletions(-) delete mode 100644 app/src/main/java/com/jens/automation2/actions/wifi_router/HotSpotIntentService.java delete mode 100644 app/src/main/java/com/jens/automation2/actions/wifi_router/MagicActivity.java diff --git a/app/src/main/java/com/jens/automation2/Actions.java b/app/src/main/java/com/jens/automation2/Actions.java index 09a11457..53e8b34e 100644 --- a/app/src/main/java/com/jens/automation2/Actions.java +++ b/app/src/main/java/com/jens/automation2/Actions.java @@ -20,9 +20,12 @@ import android.telephony.SmsManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Log; +import android.view.View; import android.view.WindowManager; import android.widget.Toast; +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; @@ -190,44 +193,70 @@ public class Actions if(((state && !desiredState) || (!state && desiredState))) { - WifiManager wifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE); - Method[] methods = wifiManager.getClass().getDeclaredMethods(); - for(Method method : methods) + if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { - Miscellaneous.logEvent("i", "WifiAp", "Trying to find appropriate method... " + method.getName(), 5); - if(method.getName().equals("setWifiApEnabled")) + WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + Method[] methods = wifiManager.getClass().getDeclaredMethods(); + for (Method method : methods) { - try + Miscellaneous.logEvent("i", "WifiAp", "Trying to find appropriate method... " + method.getName(), 5); + if (method.getName().equals("setWifiApEnabled")) { - String desiredString = ""; - if(desiredState) - desiredString = "activate"; - else - desiredString = "deactivate"; - - if(!toggleActionIfPossible) + try { - Miscellaneous.logEvent("i", "WifiAp", "Trying to " + desiredString + " wifi ap...", 2); - if(!method.isAccessible()) - method.setAccessible(true); - method.invoke(wifiManager, null, desiredState); - } - else - { - Miscellaneous.logEvent("i", "WifiAp", "Trying to " + context.getResources().getString(R.string.toggle) + " wifi ap...", 2); - if(!method.isAccessible()) - method.setAccessible(true); - method.invoke(wifiManager, null, !state); - } + String desiredString = ""; + if (desiredState) + desiredString = "activate"; + else + desiredString = "deactivate"; - Miscellaneous.logEvent("i", "WifiAp", "Wifi ap " + desiredString + "d.", 2); - } - catch(Exception e) - { - Miscellaneous.logEvent("i", "WifiAp", context.getResources().getString(R.string.errorActivatingWifiAp) + ". " + e.getMessage(), 2); + if (!toggleActionIfPossible) + { + Miscellaneous.logEvent("i", "WifiAp", "Trying to " + desiredString + " wifi ap...", 2); + if (!method.isAccessible()) + method.setAccessible(true); + method.invoke(wifiManager, null, desiredState); + } + else + { + Miscellaneous.logEvent("i", "WifiAp", "Trying to " + context.getResources().getString(R.string.toggle) + " wifi ap...", 2); + if (!method.isAccessible()) + method.setAccessible(true); + method.invoke(wifiManager, null, !state); + } + + Miscellaneous.logEvent("i", "WifiAp", "Wifi ap " + desiredString + "d.", 2); + } + catch (Exception e) + { + Miscellaneous.logEvent("i", "WifiAp", context.getResources().getString(R.string.errorActivatingWifiAp) + ". " + e.getMessage(), 2); + } } } } + else + { + MyOnStartTetheringCallback cb = new MyOnStartTetheringCallback() + { + @Override + public void onTetheringStarted() + { + Log.i("Tether", "Läuft"); + } + + @Override + public void onTetheringFailed() + { + Log.i("Tether", "Doof"); + } + }; + + MyOreoWifiManager mowm = new MyOreoWifiManager(context); + if(desiredState) + mowm.startTethering(cb); + else + mowm.stopTethering(); + } } return true; } diff --git a/app/src/main/java/com/jens/automation2/actions/wifi_router/HotSpotIntentService.java b/app/src/main/java/com/jens/automation2/actions/wifi_router/HotSpotIntentService.java deleted file mode 100644 index 8dff8126..00000000 --- a/app/src/main/java/com/jens/automation2/actions/wifi_router/HotSpotIntentService.java +++ /dev/null @@ -1,255 +0,0 @@ -package com.jens.automation2.actions.wifi_router; - -import android.Manifest; -import android.app.IntentService; -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.net.wifi.WifiManager; -import android.os.Build; -import android.util.Log; - -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.core.app.NotificationCompat; -import androidx.core.content.ContextCompat; - -import java.lang.reflect.Method; - -import static android.content.ContentValues.TAG; - -/** - * An {@link IntentService} subclass for handling asynchronous task requests in - * a service on a separate handler thread. - */ -public class HotSpotIntentService extends IntentService -{ - /** - Id for running service in foreground - */ - private static int FOREGROUND_ID=1338; - private static final String CHANNEL_ID = "control_app"; - - // Action names...assigned in manifest. - private String ACTION_TURNON; - private String ACTION_TURNOFF; - private String DATAURI_TURNON; - private String DATAURI_TURNOFF; - private Intent mStartIntent; - - @RequiresApi(api = Build.VERSION_CODES.O) - MyOreoWifiManager mMyOreoWifiManager; - - - /** - * Creates an IntentService. Invoked by your subclass's constructor. - * - - */ - public HotSpotIntentService() - { - super("HotSpotIntentService"); - } - - /** - * Helper method to start this intent from {@link HotSpotIntentReceiver} - * @param context - * @param intent - */ - public static void start(Context context,Intent intent) - { - Intent i = new Intent(context, HotSpotIntentService.class); - i.setAction(intent.getAction()); - i.setData(intent.getData()); - context.startService(i); - } - - @Override - protected void onHandleIntent(@Nullable Intent intent) - { - ACTION_TURNON = getString(R.string.intent_action_turnon); - ACTION_TURNOFF = getString(R.string.intent_action_turnoff); - - DATAURI_TURNON = getString(R.string.intent_data_host_turnon); - DATAURI_TURNOFF = getString(R.string.intent_data_host_turnoff); - - // Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); - Log.i(TAG,"Received start intent"); - - mStartIntent = intent; - - if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) - { - - } - else - { - carryOn(); - } - - } - - - private void carryOn() - { - boolean turnOn = true; - if (mStartIntent != null) - { - final String action = mStartIntent.getAction(); - final String data = mStartIntent.getDataString(); - if (ACTION_TURNON.equals(action) || (data!=null && data.contains(DATAURI_TURNON))) - { - turnOn = true; - Log.i(TAG,"Action/data to turn on hotspot"); - } - else if (ACTION_TURNOFF.equals(action)|| (data!=null && data.contains(DATAURI_TURNOFF))) - { - turnOn = false; - Log.i(TAG,"Action/data to turn off hotspot"); - } - - if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.O) - { - hotspotOreo(turnOn); - } - else - { - turnOnHotspotPreOreo(turnOn); - } - } - } - - private boolean turnOnHotspotPreOreo(boolean turnOn) - { - { - WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); - - Method[] methods = wifiManager.getClass().getDeclaredMethods(); - for (Method method : methods) - { - if (method.getName().equals("setWifiApEnabled")) - { - try - { - if (turnOn) - { - wifiManager.setWifiEnabled(false); //Turning off wifi because tethering requires wifi to be off - method.invoke(wifiManager, null, true); //Activating tethering - return true; - } - else - { - method.invoke(wifiManager, null, false); //Deactivating tethering - wifiManager.setWifiEnabled(true); //Turning on wifi ...should probably be done from a saved setting - return true; - } - } - catch (Exception e) - { - return false; - } - } - } - - //Error setWifiApEnabled not found - return false; - } - } - - /** - * - */ - @RequiresApi(api = Build.VERSION_CODES.O) - private void hotspotOreo(boolean turnOn){ - - if (mMyOreoWifiManager ==null) - { - mMyOreoWifiManager = new MyOreoWifiManager(this); - } - - if (turnOn) - { - //this dont work - MyOnStartTetheringCallback callback = new MyOnStartTetheringCallback() - { - @Override - public void onTetheringStarted() - { - startForeground(FOREGROUND_ID, buildForegroundNotification()); - } - - @Override - public void onTetheringFailed() - { - - } - }; - - mMyOreoWifiManager.startTethering(callback); - } - else - { - mMyOreoWifiManager.stopTethering(); - stopForeground(true); - stopSelf(); - } - } - - //**************************************************************************************** - - - /** - * Build low priority notification for running this service as a foreground service. - * @return - */ - private Notification buildForegroundNotification() - { - registerNotifChnnl(this); - - Intent stopIntent = new Intent(this, HotSpotIntentService.class); - stopIntent.setAction(getString(R.string.intent_action_turnoff)); - - PendingIntent pendingIntent = PendingIntent.getService(this,0, stopIntent, 0); - - NotificationCompat.Builder b=new NotificationCompat.Builder(this,CHANNEL_ID); - b.setOngoing(true) - .setContentTitle("WifiHotSpot is On") - .addAction(new NotificationCompat.Action( - R.drawable.turn_off, - "TURN OFF HOTSPOT", - pendingIntent - )) - .setPriority(NotificationCompat.PRIORITY_LOW) - .setCategory(Notification.CATEGORY_SERVICE) - .setSmallIcon(R.drawable.notif_hotspot_black_24dp); - - - return(b.build()); - } - - private static void registerNotifChnnl(Context context) - { - if (Build.VERSION.SDK_INT >= 26) - { - NotificationManager mngr = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE); - if (mngr.getNotificationChannel(CHANNEL_ID) != null) - { - return; - } - // - NotificationChannel channel = new NotificationChannel( - CHANNEL_ID, - context.getString(R.string.notification_chnnl), - NotificationManager.IMPORTANCE_LOW); - // Configure the notification channel. - channel.setDescription(context.getString(R.string.notification_chnnl_location_descr)); - channel.enableLights(false); - channel.enableVibration(false); - mngr.createNotificationChannel(channel); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/jens/automation2/actions/wifi_router/MagicActivity.java b/app/src/main/java/com/jens/automation2/actions/wifi_router/MagicActivity.java deleted file mode 100644 index 5ee99c31..00000000 --- a/app/src/main/java/com/jens/automation2/actions/wifi_router/MagicActivity.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.jens.automation2.actions.wifi_router; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.util.Log; -import android.widget.Toast; - -public class MagicActivity extends Activity -{ - public static void useMagicActivityToTurnOn(Context c) - { - Uri uri = new Uri.Builder().scheme(c.getString(R.string.intent_data_scheme)).authority(c.getString(R.string.intent_data_host_turnon)).build(); - Toast.makeText(c,"Turn on. Uri: "+uri.toString(),Toast.LENGTH_LONG).show(); - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(uri); - i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - c.startActivity(i); - } - - public static void useMagicActivityToTurnOff(Context c) - { - Uri uri = new Uri.Builder().scheme(c.getString(R.string.intent_data_scheme)).authority(c.getString(R.string.intent_data_host_turnoff)).build(); - Toast.makeText(c,"Turn off. Uri: "+uri.toString(),Toast.LENGTH_LONG).show(); - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(uri); - i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - c.startActivity(i); - } - - private static final String TAG = MagicActivity.class.getSimpleName(); - - @Override - protected void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - Log.e(TAG, "onCreate"); - } - - @Override - void onPermissionsOkay() - { - carryOnWithHotSpotting(); - } - - /** - * The whole purpose of this activity - to start {@link HotSpotIntentService} - * This may be called straright away in {@code onCreate} or after permissions granted. - */ - private void carryOnWithHotSpotting() - { - Intent intent = getIntent(); - HotSpotIntentService.start(this, intent); - finish(); - } -} diff --git a/app/src/main/java/com/jens/automation2/actions/wifi_router/MyOnStartTetheringCallback.java b/app/src/main/java/com/jens/automation2/actions/wifi_router/MyOnStartTetheringCallback.java index 0d26ebd5..52db2e92 100644 --- a/app/src/main/java/com/jens/automation2/actions/wifi_router/MyOnStartTetheringCallback.java +++ b/app/src/main/java/com/jens/automation2/actions/wifi_router/MyOnStartTetheringCallback.java @@ -1,5 +1,10 @@ package com.jens.automation2.actions.wifi_router; +/* + Class taken from here: + https://github.com/aegis1980/WifiHotSpot + */ + public abstract class MyOnStartTetheringCallback { /** @@ -11,5 +16,4 @@ public abstract class MyOnStartTetheringCallback * Called when starting tethering failed. */ public abstract void onTetheringFailed(); - } \ No newline at end of file diff --git a/app/src/main/java/com/jens/automation2/actions/wifi_router/MyOreoWifiManager.java b/app/src/main/java/com/jens/automation2/actions/wifi_router/MyOreoWifiManager.java index a00b4c2a..36186d09 100644 --- a/app/src/main/java/com/jens/automation2/actions/wifi_router/MyOreoWifiManager.java +++ b/app/src/main/java/com/jens/automation2/actions/wifi_router/MyOreoWifiManager.java @@ -1,5 +1,10 @@ package com.jens.automation2.actions.wifi_router; +/* + Class taken from here: + https://github.com/aegis1980/WifiHotSpot + */ + import android.content.Context; import android.net.ConnectivityManager; import android.net.wifi.WifiConfiguration; @@ -10,6 +15,7 @@ import android.util.Log; import androidx.annotation.RequiresApi; import com.android.dx.stock.ProxyBuilder; +import com.jens.automation2.Miscellaneous; import java.io.File; import java.lang.reflect.InvocationHandler; @@ -51,12 +57,11 @@ public class MyOreoWifiManager { Method setConfigMethod = mWifiManager.getClass().getMethod("setWifiApConfiguration", WifiConfiguration.class); boolean status = (boolean) setConfigMethod.invoke(mWifiManager, apConfig); - Log.d(TAG, "setWifiApConfiguration - success? " + status); + Miscellaneous.logEvent("i", "configureHotspot()", "setWifiApConfiguration - success? " + status, 2); } catch (Exception e) { - Log.e(TAG, "Error in configureHotspot"); - e.printStackTrace(); + Miscellaneous.logEvent("e", "configureHotspot()", "Error in configureHotspot: " + Log.getStackTraceString(e), 2); } } @@ -74,13 +79,14 @@ public class MyOreoWifiManager Method method = mConnectivityManager.getClass().getDeclaredMethod("getTetheredIfaces"); if (method == null) { - Log.e(TAG, "getTetheredIfaces is null"); + Miscellaneous.logEvent("i", "getTetheredIfaces()", "getTetheredIfaces is null", 2); } else { String res[] = (String []) method.invoke(mConnectivityManager, null); - Log.d(TAG, "getTetheredIfaces invoked"); - Log.d(TAG, Arrays.toString(res)); + Miscellaneous.logEvent("i", "isTetherActive()", "getTetheredIfaces invoked", 5); + Miscellaneous.logEvent("i", "isTetherActive()", Arrays.toString(res), 4); + if (res.length > 0) { return true; @@ -89,8 +95,7 @@ public class MyOreoWifiManager } catch (Exception e) { - Log.e(TAG, "Error in getTetheredIfaces"); - e.printStackTrace(); + Miscellaneous.logEvent("e", "isTetherActive()", "Error in getTetheredIfaces: " + Log.getStackTraceString(e), 2); } return false; } @@ -102,12 +107,11 @@ public class MyOreoWifiManager */ public boolean startTethering(final MyOnStartTetheringCallback callback) { - // On Pie if we try to start tethering while it is already on, it will // be disabled. This is needed when startTethering() is called programmatically. if (isTetherActive()) { - Log.d(TAG, "Tether already active, returning"); + Miscellaneous.logEvent("i", "startTethering()", "Tether already active, returning", 2); return false; } @@ -139,8 +143,7 @@ public class MyOreoWifiManager } catch (Exception e) { - Log.e(TAG, "Error in enableTethering ProxyBuilder"); - e.printStackTrace(); + Miscellaneous.logEvent("e", "startTethering()", "Error in enableTethering ProxyBuilder", 2); return false; } @@ -150,19 +153,18 @@ public class MyOreoWifiManager method = mConnectivityManager.getClass().getDeclaredMethod("startTethering", int.class, boolean.class, OnStartTetheringCallbackClass(), Handler.class); if (method == null) { - Log.e(TAG, "startTetheringMethod is null"); + Miscellaneous.logEvent("w", "startTethering()", "startTetheringMethod is null", 2); } else { method.invoke(mConnectivityManager, ConnectivityManager.TYPE_MOBILE, false, proxy, null); - Log.d(TAG, "startTethering invoked"); + Miscellaneous.logEvent("i", "startTethering()", "startTethering invoked", 5); } return true; } catch (Exception e) { - Log.e(TAG, "Error in enableTethering"); - e.printStackTrace(); + Miscellaneous.logEvent("w", "startTethering()", "Error in enableTethering: " + Log.getStackTraceString(e), 2); } return false; } @@ -174,18 +176,17 @@ public class MyOreoWifiManager Method method = mConnectivityManager.getClass().getDeclaredMethod("stopTethering", int.class); if (method == null) { - Log.e(TAG, "stopTetheringMethod is null"); + Miscellaneous.logEvent("w", "stopTethering", "stopTetheringMethod is null", 2); } else { method.invoke(mConnectivityManager, ConnectivityManager.TYPE_MOBILE); - Log.d(TAG, "stopTethering invoked"); + Miscellaneous.logEvent("i", "stopTethering", "stopTethering invoked", 5); } } catch (Exception e) { - Log.e(TAG, "stopTethering error: " + e.toString()); - e.printStackTrace(); + Miscellaneous.logEvent("e", "stopTethering", "stopTethering error: " + Log.getStackTraceString(e), 1); } } @@ -197,9 +198,8 @@ public class MyOreoWifiManager } catch (ClassNotFoundException e) { - Log.e(TAG, "OnStartTetheringCallbackClass error: " + e.toString()); - e.printStackTrace(); + Miscellaneous.logEvent("e", "OnStartTetheringCallbackClass()", "OnStartTetheringCallbackClass error: " + Log.getStackTraceString(e), 1); } return null; } -} +} \ No newline at end of file