Merge branch 'WifiRouter' into development
# Conflicts: # app/src/main/res/values-es/strings.xml
This commit is contained in:
commit
f22e4854ee
@ -70,6 +70,9 @@ dependencies {
|
|||||||
apkFlavorImplementation 'com.google.firebase:firebase-appindexing:19.2.0'
|
apkFlavorImplementation 'com.google.firebase:firebase-appindexing:19.2.0'
|
||||||
apkFlavorImplementation 'com.google.android.gms:play-services-location:17.1.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 'androidx.appcompat:appcompat:1.2.0'
|
||||||
implementation 'com.google.android.material:material:1.3.0'
|
implementation 'com.google.android.material:material:1.3.0'
|
||||||
testImplementation 'junit:junit:4.+'
|
testImplementation 'junit:junit:4.+'
|
||||||
|
@ -20,9 +20,12 @@ import android.telephony.SmsManager;
|
|||||||
import android.telephony.SubscriptionManager;
|
import android.telephony.SubscriptionManager;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.Toast;
|
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.location.WifiBroadcastReceiver;
|
||||||
import com.jens.automation2.receivers.ConnectivityReceiver;
|
import com.jens.automation2.receivers.ConnectivityReceiver;
|
||||||
|
|
||||||
@ -190,45 +193,71 @@ public class Actions
|
|||||||
|
|
||||||
if(((state && !desiredState) || (!state && desiredState)))
|
if(((state && !desiredState) || (!state && desiredState)))
|
||||||
{
|
{
|
||||||
WifiManager wifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
|
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
||||||
|
{
|
||||||
|
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
|
||||||
Method[] methods = wifiManager.getClass().getDeclaredMethods();
|
Method[] methods = wifiManager.getClass().getDeclaredMethods();
|
||||||
for(Method method : methods)
|
for (Method method : methods)
|
||||||
{
|
{
|
||||||
Miscellaneous.logEvent("i", "WifiAp", "Trying to find appropriate method... " + method.getName(), 5);
|
Miscellaneous.logEvent("i", "WifiAp", "Trying to find appropriate method... " + method.getName(), 5);
|
||||||
if(method.getName().equals("setWifiApEnabled"))
|
if (method.getName().equals("setWifiApEnabled"))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
String desiredString = "";
|
String desiredString = "";
|
||||||
if(desiredState)
|
if (desiredState)
|
||||||
desiredString = "activate";
|
desiredString = "activate";
|
||||||
else
|
else
|
||||||
desiredString = "deactivate";
|
desiredString = "deactivate";
|
||||||
|
|
||||||
if(!toggleActionIfPossible)
|
if (!toggleActionIfPossible)
|
||||||
{
|
{
|
||||||
Miscellaneous.logEvent("i", "WifiAp", "Trying to " + desiredString + " wifi ap...", 2);
|
Miscellaneous.logEvent("i", "WifiAp", "Trying to " + desiredString + " wifi ap...", 2);
|
||||||
if(!method.isAccessible())
|
if (!method.isAccessible())
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
method.invoke(wifiManager, null, desiredState);
|
method.invoke(wifiManager, null, desiredState);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Miscellaneous.logEvent("i", "WifiAp", "Trying to " + context.getResources().getString(R.string.toggle) + " wifi ap...", 2);
|
Miscellaneous.logEvent("i", "WifiAp", "Trying to " + context.getResources().getString(R.string.toggle) + " wifi ap...", 2);
|
||||||
if(!method.isAccessible())
|
if (!method.isAccessible())
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
method.invoke(wifiManager, null, !state);
|
method.invoke(wifiManager, null, !state);
|
||||||
}
|
}
|
||||||
|
|
||||||
Miscellaneous.logEvent("i", "WifiAp", "Wifi ap " + desiredString + "d.", 2);
|
Miscellaneous.logEvent("i", "WifiAp", "Wifi ap " + desiredString + "d.", 2);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Miscellaneous.logEvent("i", "WifiAp", context.getResources().getString(R.string.errorActivatingWifiAp) + ". " + e.getMessage(), 2);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.jens.automation2.actions.wifi_router;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Class taken from here:
|
||||||
|
https://github.com/aegis1980/WifiHotSpot
|
||||||
|
*/
|
||||||
|
|
||||||
|
public abstract class MyOnStartTetheringCallback
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Called when tethering has been successfully started.
|
||||||
|
*/
|
||||||
|
public abstract void onTetheringStarted();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when starting tethering failed.
|
||||||
|
*/
|
||||||
|
public abstract void onTetheringFailed();
|
||||||
|
}
|
@ -0,0 +1,205 @@
|
|||||||
|
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;
|
||||||
|
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 com.jens.automation2.Miscellaneous;
|
||||||
|
|
||||||
|
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);
|
||||||
|
Miscellaneous.logEvent("i", "configureHotspot()", "setWifiApConfiguration - success? " + status, 2);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Miscellaneous.logEvent("e", "configureHotspot()", "Error in configureHotspot: " + Log.getStackTraceString(e), 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
Miscellaneous.logEvent("i", "getTetheredIfaces()", "getTetheredIfaces is null", 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
String res[] = (String []) method.invoke(mConnectivityManager, null);
|
||||||
|
Miscellaneous.logEvent("i", "isTetherActive()", "getTetheredIfaces invoked", 5);
|
||||||
|
Miscellaneous.logEvent("i", "isTetherActive()", Arrays.toString(res), 4);
|
||||||
|
|
||||||
|
if (res.length > 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Miscellaneous.logEvent("e", "isTetherActive()", "Error in getTetheredIfaces: " + Log.getStackTraceString(e), 2);
|
||||||
|
}
|
||||||
|
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())
|
||||||
|
{
|
||||||
|
Miscellaneous.logEvent("i", "startTethering()", "Tether already active, returning", 2);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
Miscellaneous.logEvent("e", "startTethering()", "Error in enableTethering ProxyBuilder", 2);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Method method = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
method = mConnectivityManager.getClass().getDeclaredMethod("startTethering", int.class, boolean.class, OnStartTetheringCallbackClass(), Handler.class);
|
||||||
|
if (method == null)
|
||||||
|
{
|
||||||
|
Miscellaneous.logEvent("w", "startTethering()", "startTetheringMethod is null", 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
method.invoke(mConnectivityManager, ConnectivityManager.TYPE_MOBILE, false, proxy, null);
|
||||||
|
Miscellaneous.logEvent("i", "startTethering()", "startTethering invoked", 5);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Miscellaneous.logEvent("w", "startTethering()", "Error in enableTethering: " + Log.getStackTraceString(e), 2);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopTethering()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Method method = mConnectivityManager.getClass().getDeclaredMethod("stopTethering", int.class);
|
||||||
|
if (method == null)
|
||||||
|
{
|
||||||
|
Miscellaneous.logEvent("w", "stopTethering", "stopTetheringMethod is null", 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
method.invoke(mConnectivityManager, ConnectivityManager.TYPE_MOBILE);
|
||||||
|
Miscellaneous.logEvent("i", "stopTethering", "stopTethering invoked", 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Miscellaneous.logEvent("e", "stopTethering", "stopTethering error: " + Log.getStackTraceString(e), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class OnStartTetheringCallbackClass()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Class.forName("android.net.ConnectivityManager$OnStartTetheringCallback");
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException e)
|
||||||
|
{
|
||||||
|
Miscellaneous.logEvent("e", "OnStartTetheringCallbackClass()", "OnStartTetheringCallbackClass error: " + Log.getStackTraceString(e), 1);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -115,7 +115,7 @@
|
|||||||
<string name="rules">Regeln</string>
|
<string name="rules">Regeln</string>
|
||||||
<string name="helpTextRules">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.</string>
|
<string name="helpTextRules">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.</string>
|
||||||
<string name="timeframes">Zeiträume</string>
|
<string name="timeframes">Zeiträume</string>
|
||||||
<string name="helpTextTimeFrame">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.</string>
|
<string name="helpTextTimeFrame">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.</string>
|
||||||
<string name="helpTextSound">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).</string>
|
<string name="helpTextSound">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).</string>
|
||||||
<string name="toggableRules">Umkehrbare Regeln</string>
|
<string name="toggableRules">Umkehrbare Regeln</string>
|
||||||
<string name="helpTextToggable">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.</string>
|
<string name="helpTextToggable">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.</string>
|
||||||
|
@ -127,7 +127,7 @@
|
|||||||
<string name="rules">Rules</string>
|
<string name="rules">Rules</string>
|
||||||
<string name="helpTextRules">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.</string>
|
<string name="helpTextRules">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.</string>
|
||||||
<string name="timeframes">TimeFrames</string>
|
<string name="timeframes">TimeFrames</string>
|
||||||
<string name="helpTextTimeFrame">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.</string>
|
<string name="helpTextTimeFrame">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.</string>
|
||||||
<string name="helpTextSound">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).</string>
|
<string name="helpTextSound">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).</string>
|
||||||
<string name="toggableRules">Toggable rules</string>
|
<string name="toggableRules">Toggable rules</string>
|
||||||
<string name="helpTextToggable">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.</string>
|
<string name="helpTextToggable">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.</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user