8 Commits

14 changed files with 151 additions and 46 deletions

View File

@@ -67,21 +67,21 @@ android {
} }
dependencies { dependencies {
implementation 'org.jetbrains:annotations:15.0' implementation 'org.jetbrains:annotations:26.1.0'
googlePlayFlavorImplementation 'com.google.firebase:firebase-appindexing:20.0.0' googlePlayFlavorImplementation 'com.google.firebase:firebase-appindexing:20.0.0'
googlePlayFlavorImplementation 'com.google.android.gms:play-services-location:18.0.0' googlePlayFlavorImplementation 'com.google.android.gms:play-services-location:20.0.0'
apkFlavorImplementation 'com.google.firebase:firebase-appindexing:20.0.0' apkFlavorImplementation 'com.google.firebase:firebase-appindexing:20.0.0'
apkFlavorImplementation 'com.google.android.gms:play-services-location:18.0.0' apkFlavorImplementation 'com.google.android.gms:play-services-location:20.0.0'
implementation 'com.linkedin.dexmaker:dexmaker:2.28.1' implementation 'com.linkedin.dexmaker:dexmaker:2.28.6'
implementation 'org.apache.commons:commons-lang3:3.0' implementation 'org.apache.commons:commons-lang3:3.20.0'
//implementation "androidx.security:security-crypto:1.0.0" //implementation "androidx.security:security-crypto:1.0.0"
//implementation "androidx.security:security-identity-credential:1.0.0-alpha02" //implementation "androidx.security:security-identity-credential:1.0.0-alpha02"
implementation 'androidx.appcompat:appcompat:1.4.2' implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.material:material:1.6.1' implementation 'com.google.android.material:material:1.6.1'
testImplementation 'junit:junit:4' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
} }

View File

@@ -8,6 +8,7 @@ import android.widget.Toast;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.net.URLEncoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
@@ -689,8 +690,8 @@ public class Action
try try
{ {
url = Miscellaneous.replaceVariablesInText(url, context); url = Miscellaneous.replaceVariablesInText(url, context);
if(!StringUtils.isEmpty(params)) // if(!StringUtils.isEmpty(params))
params = Miscellaneous.replaceVariablesInText(params, context); // params = URLEncoder.encode(Miscellaneous.replaceVariablesInText(params, context), "UTF-8");
Actions myAction = new Actions(); Actions myAction = new Actions();
@@ -738,14 +739,24 @@ public class Action
{ {
// has params // has params
String[] paramPairs = parameters[4].split(Action.actionParameters2SeparatorOuter); String[] paramPairs = parameters[4].split(Action.actionParameters2SeparatorOuter);
String value = "";
for(String pair : paramPairs) for(String pair : paramPairs)
{ {
String[] pieces = pair.split(Action.actionParameters2SeparatorInner); String[] pieces = pair.split(Action.actionParameters2SeparatorInner);
if(pieces.length == 2)
httpParams.put(pieces[0], pieces[1]); try
{
value = Miscellaneous.replaceVariablesInText(pieces[1], Miscellaneous.getAnyContext());
} }
catch (Exception e)
{
value = "error";
} }
if(pieces.length == 2)
httpParams.put(pieces[0], value);
}
}
} }
String response = httpErrorDefaultText; String response = httpErrorDefaultText;
@@ -758,10 +769,10 @@ public class Action
Miscellaneous.logEvent("i", "HTTP Request", "Attempt " + String.valueOf(attempts++) + " of " + String.valueOf(Settings.httpAttempts), 3); Miscellaneous.logEvent("i", "HTTP Request", "Attempt " + String.valueOf(attempts++) + " of " + String.valueOf(Settings.httpAttempts), 3);
// Either thorough checking or no encryption // Either thorough checking or no encryption
if(!Settings.httpAcceptAllCertificates || !urlString.toLowerCase(Locale.getDefault()).contains("https")) // if(!Settings.httpAcceptAllCertificates || !urlString.toLowerCase(Locale.getDefault()).contains("https"))
response = Miscellaneous.downloadURL(urlString, urlUsername, urlPassword, method, httpParams); response = Miscellaneous.downloadURL(urlString, urlUsername, urlPassword, method, httpParams);
else // else
response = Miscellaneous.downloadUrlWithoutCertificateChecking(urlString, urlUsername, urlPassword, method, httpParams); // response = Miscellaneous.downloadUrlWithoutCertificateChecking(urlString, urlUsername, urlPassword, method, httpParams);
try try
{ {

View File

@@ -5,6 +5,7 @@ import android.app.TabActivity;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.nfc.NfcAdapter;
import android.os.Bundle; import android.os.Bundle;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.widget.TabHost; import android.widget.TabHost;
@@ -61,22 +62,24 @@ public class ActivityMainTabLayout extends TabActivity
tabHost.setCurrentTab(Settings.startScreen); tabHost.setCurrentTab(Settings.startScreen);
} }
@Override @Override
protected void onResume() protected void onResume()
{ {
super.onResume(); super.onResume();
Miscellaneous.setDisplayLanguage(this); Miscellaneous.setDisplayLanguage(this);
// Miscellaneous.logEvent("i", "NFC", "ActivityMainTabLayout.onResume().", 5); // Miscellaneous.logEvent("i", "NFC", "ActivityMainTabLayout.onResume().", 5);
if(!(getIntent().getAction().isEmpty()) && getIntent().getAction().equals(NfcAdapter.ACTION_NDEF_DISCOVERED))
{
NfcReceiver.checkIntentForNFC(this, getIntent()); NfcReceiver.checkIntentForNFC(this, getIntent());
// NfcReceiver.checkIntentForNFC(this, new Intent(this.getApplicationContext(), this.getClass())); moveTaskToBack(true);
}
} }
@Override @Override
protected void onNewIntent(Intent intent) protected void onNewIntent(Intent intent)
{ {
// Miscellaneous.logEvent("i", "NFC", "ActivityMainTabLayout.onNewIntent().", 5); // Miscellaneous.logEvent("i", "NFC", "ActivityMainTabLayout.onNewIntent().", 5);
// setIntent(intent);
NfcReceiver.checkIntentForNFC(this, intent); NfcReceiver.checkIntentForNFC(this, intent);
} }
} }

View File

@@ -19,6 +19,7 @@ import android.widget.EditText;
import android.widget.ListView; import android.widget.ListView;
import android.widget.RadioButton; import android.widget.RadioButton;
import android.widget.TableLayout; import android.widget.TableLayout;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -38,6 +39,7 @@ public class ActivityManageActionTriggerUrl extends Activity
CheckBox chkTriggerUrlUseAuthentication; CheckBox chkTriggerUrlUseAuthentication;
RadioButton rbTriggerUrlMethodGet, rbTriggerUrlMethodPost; RadioButton rbTriggerUrlMethodGet, rbTriggerUrlMethodPost;
TableLayout tlTriggerUrlAuthentication; TableLayout tlTriggerUrlAuthentication;
TextView tvHttpParameterExplanation;
ArrayAdapter<String> httpParametersAdapter; ArrayAdapter<String> httpParametersAdapter;
private ArrayList<String> httpParamsList = new ArrayList<>(); private ArrayList<String> httpParamsList = new ArrayList<>();
@@ -67,6 +69,7 @@ public class ActivityManageActionTriggerUrl extends Activity
etParameterName = (EditText) findViewById(R.id.etParameterName); etParameterName = (EditText) findViewById(R.id.etParameterName);
etParameterValue = (EditText)findViewById(R.id.etParameterValue); etParameterValue = (EditText)findViewById(R.id.etParameterValue);
bAddHttpParam = (Button)findViewById(R.id.bAddHttpParam); bAddHttpParam = (Button)findViewById(R.id.bAddHttpParam);
tvHttpParameterExplanation = (TextView)findViewById(R.id.tvHttpParameterExplanation);
etParameterName.setEnabled(false); etParameterName.setEnabled(false);
etParameterValue.setEnabled(false); etParameterValue.setEnabled(false);
@@ -85,6 +88,8 @@ public class ActivityManageActionTriggerUrl extends Activity
} }
}); });
tvHttpParameterExplanation.setText(String.format(getResources().getString(R.string.httpParametersExplanation), Miscellaneous.httpMainData, Miscellaneous.doNoEncodingString));
httpParametersAdapter = new ArrayAdapter<String>(this, R.layout.text_view_for_poi_listview_mediumtextsize, httpParamsList); httpParametersAdapter = new ArrayAdapter<String>(this, R.layout.text_view_for_poi_listview_mediumtextsize, httpParamsList);
bSaveTriggerUrl.setOnClickListener(new OnClickListener() bSaveTriggerUrl.setOnClickListener(new OnClickListener()

View File

@@ -92,6 +92,7 @@ import java.security.KeyManagementException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.text.DateFormat; import java.text.DateFormat;
@@ -128,10 +129,34 @@ public class Miscellaneous extends Service
protected static String writeableFolderStringCache = null; protected static String writeableFolderStringCache = null;
protected final static String http_error_string = "HTTP_ERROR"; protected final static String http_error_string = "HTTP_ERROR";
protected final static String last_trigger_url_result_string = "last_trigger_url_result"; protected final static String last_trigger_url_result_string = "last_trigger_url_result";
protected final static String httpMainData = "httpMainData";
protected final static String doNoEncodingString = "NoEncoding";
protected final static String httpEncoding = "UTF-8";
public static Context startupContext; public static Context startupContext;
public static final String lineSeparator = System.getProperty("line.separator"); public static final String lineSeparator = System.getProperty("line.separator");
public static class TrustAllCertificates implements X509TrustManager
{
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
{
// Do nothing (trust all clients)
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
{
// Do nothing (trust all servers)
}
@Override
public X509Certificate[] getAcceptedIssuers()
{
return new X509Certificate[0]; // No accepted issuers
}
}
public static String downloadURL(String url, String username, String password, String method, Map<String, String> httpParams) public static String downloadURL(String url, String username, String password, String method, Map<String, String> httpParams)
{ {
HttpClient httpclient = new DefaultHttpClient(); HttpClient httpclient = new DefaultHttpClient();
@@ -148,6 +173,18 @@ public class Miscellaneous extends Service
if(url.toLowerCase().contains("https")) if(url.toLowerCase().contains("https"))
{ {
connection = (HttpsURLConnection) urlObject.openConnection(); connection = (HttpsURLConnection) urlObject.openConnection();
if(Settings.httpAcceptAllCertificates)
{
SSLContext sslContext = SSLContext.getInstance("TLS"); // Use "TLS" (not "SSL" which is outdated)
sslContext.init(
null, // No KeyManager (client authentication not needed)
new TrustManager[]{new TrustAllCertificates()}, // Use our trust manager
new SecureRandom() // Secure random number generator
);
((HttpsURLConnection)connection).setSSLSocketFactory(sslContext.getSocketFactory());
((HttpsURLConnection)connection).setHostnameVerifier((hostname, session) -> true); // Trust all hostnames
}
} }
else else
connection = (HttpURLConnection) urlObject.openConnection(); connection = (HttpURLConnection) urlObject.openConnection();
@@ -166,6 +203,7 @@ public class Miscellaneous extends Service
if(httpParams.size() > 0) if(httpParams.size() > 0)
{ {
connection.setRequestMethod("POST"); connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setDoInput(true); connection.setDoInput(true);
connection.setDoOutput(true); connection.setDoOutput(true);
@@ -175,8 +213,8 @@ public class Miscellaneous extends Service
paramPairs.add(new BasicNameValuePair(key, httpParams.get(key))); paramPairs.add(new BasicNameValuePair(key, httpParams.get(key)));
OutputStream os = connection.getOutputStream(); OutputStream os = connection.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8")); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, httpEncoding));
writer.write(getQuery(paramPairs)); writer.write(getQuery(paramPairs, method));
writer.flush(); writer.flush();
writer.close(); writer.close();
} }
@@ -206,21 +244,52 @@ public class Miscellaneous extends Service
} }
} }
private static String getQuery(List<NameValuePair> params) throws UnsupportedEncodingException private static String getQuery(List<NameValuePair> params, String method) throws UnsupportedEncodingException
{ {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
boolean first = true; boolean first = true;
for (NameValuePair pair : params) for (NameValuePair pair : params)
{
if (method.equals(ActivityManageActionTriggerUrl.methodGet))
{ {
if (first) if (first)
first = false; first = false;
else else
result.append("&"); result.append("&");
}
else if (pair.getName().startsWith(httpMainData))
continue; // if post and main data skip lookup run
result.append(pair.getName());
result.append(URLEncoder.encode(pair.getName(), "UTF-8"));
result.append("="); result.append("=");
result.append(URLEncoder.encode(pair.getValue(), "UTF-8"));
if (pair.getName().endsWith(doNoEncodingString))
result.append(URLEncoder.encode(pair.getValue(), httpEncoding));
else
result.append(pair.getValue());
}
/*
If method is POST and there's a httpMainData field, we transfer
this one at the end without parameter name and equals sign.
*/
if (method.equals(ActivityManageActionTriggerUrl.methodPost))
{
for (NameValuePair pair : params)
{
if (pair.getName().startsWith(httpMainData))
{
if (pair.getName().endsWith(doNoEncodingString))
result.append(pair.getValue());
else
result.append(URLEncoder.encode(pair.getValue(), httpEncoding));
return result.toString();
}
}
} }
return result.toString(); return result.toString();
@@ -819,7 +888,7 @@ public class Miscellaneous extends Service
String notificationTitle = NotificationListener.getLastNotification().getTitle(); String notificationTitle = NotificationListener.getLastNotification().getTitle();
if (notificationTitle != null && notificationTitle.length() > 0) if (notificationTitle != null && notificationTitle.length() > 0)
source = source.replace("[notificationTitle]", escapeStringForUrl(notificationTitle)); source = source.replace("[notificationTitle]", notificationTitle);
else else
{ {
source = source.replace("[notificationTitle]", "notificationTitle unknown"); source = source.replace("[notificationTitle]", "notificationTitle unknown");

View File

@@ -711,7 +711,7 @@ public class XmlFileInterface
private static Rule readRule(XmlPullParser parser) throws XmlPullParserException, IOException private static Rule readRule(XmlPullParser parser) throws XmlPullParserException, IOException
{ {
/* FILE EXAMPE: /* FILE EXAMPLE:
* ***************** * *****************
* <Automation> * <Automation>
* <PointOfInterestCollection> * <PointOfInterestCollection>
@@ -1095,7 +1095,7 @@ public class XmlFileInterface
private static Action readAction(XmlPullParser parser) throws IOException, XmlPullParserException private static Action readAction(XmlPullParser parser) throws IOException, XmlPullParserException
{ {
/* FILE EXAMPE: /* FILE EXAMPLE:
* ***************** * *****************
* <Automation> * <Automation>
* <PointOfInterestCollection> * <PointOfInterestCollection>

View File

@@ -140,6 +140,17 @@
</TableRow> </TableRow>
<TableRow>
<TextView
android:id="@+id/tvHttpParameterExplanation"
android:layout_span="2"
android:layout_marginBottom="@dimen/default_margin"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</TableRow>
<TableRow <TableRow
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">

View File

@@ -977,4 +977,5 @@
<string name="enterAvalue">Enter a value.</string> <string name="enterAvalue">Enter a value.</string>
<string name="setSystemSettingCapital">Set system setting</string> <string name="setSystemSettingCapital">Set system setting</string>
<string name="examplesWriteSecureSettings">I do not keep a list of possible settings. Please find those settings on your own. See <a href="https://developer.android.com/reference/android/provider/Settings.Secure">here</a> for some (incomplete) examples.</string> <string name="examplesWriteSecureSettings">I do not keep a list of possible settings. Please find those settings on your own. See <a href="https://developer.android.com/reference/android/provider/Settings.Secure">here</a> for some (incomplete) examples.</string>
<string name="httpParametersExplanation">If any parameter name starts with %1$s while method is POST, it will not be transmitted like the other parameters (with key=value), but transferred as main data.\nIf the parameter ends with %2$s, no encoding will be performed.</string>
</resources> </resources>

View File

@@ -2,10 +2,10 @@
buildscript { buildscript {
repositories { repositories {
google() google()
jcenter() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.2.2' classpath 'com.android.tools.build:gradle:8.13.2'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
@@ -15,7 +15,7 @@ buildscript {
allprojects { allprojects {
repositories { repositories {
google() google()
jcenter() mavenCentral()
} }
} }

View File

@@ -0,0 +1,2 @@
* Added: Added further options to the trigger url action.
* Added: Gradle and libraries updated.

View File

@@ -64,8 +64,8 @@ Supported actions:
* Change location setting * Change location setting
* Send text message * Send text message
It's quite hard to keep this app working across the many different hardwares as well as the many changes Android undergoes over the versions. I can test it in the emulator, but that cannot show all bugs. It's quite hard to keep this app working across the many different devices as well as the many changes Android undergoes over the versions. I can test it in the emulator, but that cannot show all bugs.
So if a certain feature is not working on your device - let me know. Over the years I have fixed almost all bugs that have been reasonably reported to me. But for that I'm dependend on your input. So if a certain feature is not working on your device - let me know. Over the years I have fixed almost all bugs that have been reasonably reported to me. But for that I'm dependent on your input.
If you have a problem and think about contacting me please If you have a problem and think about contacting me please
- update to the latest version first and see if your problem persists there, too. - update to the latest version first and see if your problem persists there, too.

View File

@@ -17,3 +17,6 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
android.useAndroidX=true android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX # Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true android.enableJetifier=true
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip