0
0
mirror of https://github.com/TrianguloY/UrlChecker.git synced 2024-09-19 20:02:16 +02:00

Merge branch 'TrianguloY:master' into master

This commit is contained in:
Ilithy 2022-07-24 16:14:27 +02:00 committed by GitHub
commit 9865236599
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 971 additions and 561 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,91 @@
package com.trianguloy.urlchecker.dialogs;
import android.app.Activity;
import android.app.AlertDialog;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.trianguloy.urlchecker.R;
import org.json.JSONException;
import org.json.JSONObject;
/**
* @see this#show
*/
public class JsonEditor {
public interface Listener {
/**
* Current data that the user wants to save.
* Return true if it is ok and the dialog should be dismissed, false otherwise.
*/
boolean onSave(JSONObject content);
}
/**
* Displays a generic editor for json content.
*/
public static void show(JSONObject content, JSONObject reset, int description, Activity cntx, Listener onSave) {
// prepare dialog content
View views = cntx.getLayoutInflater().inflate(R.layout.json_editor, null);
views.<TextView>findViewById(R.id.description).setText(description);
// init rules
EditText data = views.findViewById(R.id.data);
data.setText(noFailToString(content));
// formatter
views.findViewById(R.id.format).setOnClickListener(v -> {
try {
data.setText(new JSONObject(data.getText().toString()).toString(2));
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(cntx, R.string.toast_invalid, Toast.LENGTH_SHORT).show();
}
});
// prepare dialog
AlertDialog dialog = new AlertDialog.Builder(cntx)
.setView(views)
.setPositiveButton(R.string.save, null) // set below
.setNegativeButton(android.R.string.cancel, null)
.setNeutralButton(R.string.reset, null) // set below
.setCancelable(false)
.show();
// prepare more dialog
// these are configured here to allow auto-closing the dialog when they are pressed
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
try {
if (onSave.onSave(new JSONObject(data.getText().toString()))) {
dialog.dismiss();
}
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(cntx, R.string.toast_invalid, Toast.LENGTH_SHORT).show();
}
});
dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
// clear catalog and reload internal
data.setText(noFailToString(reset));
});
}
/**
* Formats a json into a valid string that doesn't throws a JSON exception
* (as a function because I needed a final string)
*/
private static String noFailToString(JSONObject content) {
try {
return content.toString(2);
} catch (JSONException e) {
// panic, don't format then
return content.toString();
}
}
}

View File

@ -40,14 +40,11 @@ public class MainDialog extends Activity {
private int updating = 0;
/**
* A module changed the url
*
* @param urlData the new url and its data
* @param providerModule which module changed it (null if first change)
* A module (null if first change) want to set a new url. Return true if set, false if not.
*/
public void onNewUrl(UrlData urlData, AModuleDialog providerModule) {
public boolean onNewUrl(UrlData urlData, AModuleDialog providerModule) {
// test and mark recursion
if (updating > MAX_UPDATES) return;
if (updating > MAX_UPDATES) return false;
if (urlData.disableUpdates) updating = MAX_UPDATES;
updating++;
int updating_current = updating;
@ -66,9 +63,10 @@ public class MainDialog extends Activity {
e.printStackTrace();
AndroidUtils.assertError("Exception in onNewUrl for module " + (providerModule == null ? "-none-" : providerModule.getClass().getName()));
}
if (updating_current != updating) return;
if (updating_current != updating) return true;
}
updating = 0;
return true;
}
/**

View File

@ -51,8 +51,8 @@ public abstract class AModuleDialog implements Fragment {
*
* @param url new url
*/
protected final void setUrl(String url) {
setUrl(new UrlData(url));
protected final boolean setUrl(String url) {
return setUrl(new UrlData(url));
}
/**
@ -60,8 +60,8 @@ public abstract class AModuleDialog implements Fragment {
*
* @param urlData new url and data
*/
protected final void setUrl(UrlData urlData) {
dialog.onNewUrl(urlData, this);
protected final boolean setUrl(UrlData urlData) {
return dialog.onNewUrl(urlData, this);
}
}

View File

@ -2,23 +2,28 @@ package com.trianguloy.urlchecker.modules.companions;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.util.Log;
import android.util.Pair;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.trianguloy.urlchecker.R;
import com.trianguloy.urlchecker.dialogs.JsonEditor;
import com.trianguloy.urlchecker.utilities.AndroidUtils;
import com.trianguloy.urlchecker.utilities.AssetFile;
import com.trianguloy.urlchecker.utilities.GenericPref;
import com.trianguloy.urlchecker.utilities.InternalFile;
import com.trianguloy.urlchecker.utilities.JavaUtilities;
import com.trianguloy.urlchecker.utilities.StreamUtils;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Manages the local catalog with the rules
@ -27,7 +32,8 @@ public class ClearUrlCatalog {
/* ------------------- constants ------------------- */
private static final String fileName = "data.minify.json";
private final InternalFile custom = new InternalFile("clearUrlCatalog");
private final AssetFile builtIn = new AssetFile("data.minify.json");
private static final int AUTOUPDATE_PERIOD = /* 1 week (in milliseconds) */
7/*days*/ * 24/*hours*/ * 60/*minutes*/ * 60/*seconds*/ * 1000/*milliseconds*/;
@ -48,6 +54,8 @@ public class ClearUrlCatalog {
hashURL.init(cntx);
autoUpdate.init(cntx);
lastUpdate.init(cntx);
custom.init(cntx);
builtIn.init(cntx);
updateIfNecessary();
}
@ -57,18 +65,22 @@ public class ClearUrlCatalog {
/**
* Returns the catalog content as text
*/
public String getRaw() {
public String getCatalog() {
// get the updated file first
try {
return StreamUtils.inputStream2String(cntx.openFileInput(fileName));
} catch (IOException ignored) {
}
String internal = custom.get();
if (internal != null) return internal;
// no updated file or can't read, use built-in one
try {
return StreamUtils.inputStream2String(cntx.getAssets().open(fileName));
} catch (IOException ignored) {
}
return getBuiltIn();
}
/**
* Returns the built-in catalog
*/
public String getBuiltIn() {
// read internal file
String builtIn = this.builtIn.get();
if (builtIn != null) return builtIn;
// can't read either? panic! return empty
return "{\"providers\":{}}";
@ -76,61 +88,68 @@ public class ClearUrlCatalog {
/**
* Parses and returns the providers from the catalog
* Returns a list of pairs: [(rule,data),...]
*/
public static JSONObject getProviders(Activity cntx) {
public static List<Pair<String, JSONObject>> getRules(Activity cntx) {
try {
return new JSONObject(new ClearUrlCatalog(cntx).getRaw())
.getJSONObject("providers");
// prepare
List<Pair<String, JSONObject>> rules = new ArrayList<>();
ClearUrlCatalog clearUrlCatalog = new ClearUrlCatalog(cntx);
JSONObject json = JavaUtilities.toJson(clearUrlCatalog.getCatalog());
// extract and merge each provider
for (String provider : JavaUtilities.toList(json.keys())) {
JSONObject providerData = json.getJSONObject(provider);
for (String rule : JavaUtilities.toList(providerData.keys())) {
rules.add(Pair.create(rule, providerData.getJSONObject(rule)));
}
}
return rules;
} catch (JSONException e) {
// invalid catalog, return empty
AndroidUtils.assertError(e.getMessage());
return new JSONObject();
return Collections.emptyList();
}
}
/**
* Returns the rules formatted
* Saves a new local catalog. Returns true if it was saved correctly, false on error.
* When merge is true, only the top-objects with the same key are replaced.
*/
public String getFormattedRules() {
try {
return new JSONObject(getRaw()).toString(2);
} catch (JSONException e) {
e.printStackTrace();
return "";
public boolean setRules(JSONObject rules, boolean merge) {
// merge rules if required
if (merge) {
try {
// replace only the top objects
JSONObject merged = JavaUtilities.toJson(getCatalog());
for (String key : JavaUtilities.toList(rules.keys())) {
merged.put(key, rules.getJSONObject(key));
}
rules = merged;
} catch (JSONException e) {
e.printStackTrace();
// can't be parsed
return false;
}
}
}
// compact
String content = rules.toString();
/**
* Saves a new local catalog. Returns true if it was saved correctly, false on error
*/
public boolean setRules(String content) {
// the same, already saved
if (content.equals(getRaw())) return true;
// first test if it can be parsed
try {
new JSONObject(content).getJSONObject("providers");
} catch (JSONException e) {
e.printStackTrace();
// can't be parsed
return false;
}
// store json
try (FileOutputStream fos = cntx.openFileOutput(fileName, Context.MODE_PRIVATE)) {
fos.write(content.getBytes(StreamUtils.UTF_8));
// same as builtin (maybe a reset?), clear custom
if (content.equals(getBuiltIn())) {
clear();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
// store
return custom.set(content);
}
/**
* Deletes the custom catalog, built-in one will be returned afterwards
*/
public void clear() {
cntx.deleteFile(fileName);
custom.delete();
lastUpdate.clear();
}
@ -140,48 +159,16 @@ public class ClearUrlCatalog {
* Show the rules editor dialog
*/
public void showEditor() {
// prepare dialog content
View views = cntx.getLayoutInflater().inflate(R.layout.config_clearurls_editor, null);
// init rules
EditText rules = views.findViewById(R.id.rules);
rules.setText(getFormattedRules());
// formatter
views.findViewById(R.id.format).setOnClickListener(v -> {
try {
rules.setText(new JSONObject(rules.getText().toString()).toString(2));
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(cntx, R.string.toast_invalid, Toast.LENGTH_SHORT).show();
}
});
// prepare dialog
AlertDialog dialog = new AlertDialog.Builder(cntx)
.setView(views)
.setPositiveButton(R.string.save, null) // set below
.setNegativeButton(android.R.string.cancel, null)
.setNeutralButton(R.string.reset, null) // set below
.setCancelable(false)
.show();
// prepare more dialog
// these are configured here to allow auto-closing the dialog when they are pressed
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
if (setRules(rules.getText().toString())) {
JsonEditor.show(JavaUtilities.toJson(getCatalog()), JavaUtilities.toJson(getBuiltIn()), R.string.mClear_editor, cntx, content -> {
if (setRules(content, false)) {
// saved data, close dialog
dialog.dismiss();
return true;
} else {
// invalid data, keep dialog and show why
Toast.makeText(cntx, R.string.toast_invalid, Toast.LENGTH_LONG).show();
return false;
}
});
dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
// clear catalog and reload internal
clear();
rules.setText(getFormattedRules());
});
}
/**
@ -232,6 +219,9 @@ public class ClearUrlCatalog {
// ------------------- internal -------------------
/**
* If the catalog is old, updates it in background. Otherwise does nothing.
*/
private void updateIfNecessary() {
if (autoUpdate.get() && lastUpdate.get() + AUTOUPDATE_PERIOD < System.currentTimeMillis()) {
new Thread(() -> {
@ -277,8 +267,17 @@ public class ClearUrlCatalog {
}
}
// parse json
JSONObject json;
try {
json = new JSONObject(rawRules);
} catch (JSONException e) {
e.printStackTrace();
return R.string.toast_invalid;
}
// valid, save and update
if (setRules(rawRules)) {
if (setRules(json, true)) {
lastUpdate.set(System.currentTimeMillis());
return R.string.mClear_updated;
} else {

View File

@ -0,0 +1,92 @@
package com.trianguloy.urlchecker.modules.companions;
import android.app.Activity;
import com.trianguloy.urlchecker.R;
import com.trianguloy.urlchecker.dialogs.JsonEditor;
import com.trianguloy.urlchecker.utilities.InternalFile;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Represents the catalog of the Pattern module
*/
public class PatternCatalog {
private final InternalFile custom = new InternalFile("patterns");
private final Activity cntx;
public PatternCatalog(Activity cntx) {
this.cntx = cntx;
custom.init(cntx);
}
/**
* Returns the current catalog
*/
public JSONObject getCatalog() {
// get the updated file first
try {
String content = custom.get();
if (content != null) return new JSONObject(content);
} catch (JSONException ignored) {
}
// no updated file or can't read, use built-in one
return getBuiltIn();
}
/**
* Gets the builtin patterns
*/
public JSONObject getBuiltIn() {
try {
// build from the translated strings
return new JSONObject()
.put(cntx.getString(R.string.mPttrn_ascii), new JSONObject()
.put("regex", ".*[^\\p{ASCII}].*")
)
.put(cntx.getString(R.string.mPttrn_http), new JSONObject()
.put("regex", "^http://(.*)")
.put("replacement", "https://$1")
)
.put(cntx.getString(R.string.mPttrn_noSchemeHttp), new JSONObject()
.put("regex", "^(?!.*:).*")
.put("replacement", "http://$0")
)
.put(cntx.getString(R.string.mPttrn_noSchemeHttps), new JSONObject()
.put("regex", "^(?!.*:).*")
.put("replacement", "https://$0")
)
;
} catch (JSONException e) {
e.printStackTrace();
return new JSONObject();
}
}
/**
* Saves a json as new catalog
*/
public boolean save(JSONObject content) {
// same as builtin (maybe a reset?), delete custom
if (content.equals(getBuiltIn())) {
custom.delete();
return true;
}
// store
return custom.set(content.toString());
}
/**
* Shows a dialog to manually edit the catalog
*/
public void showEditor() {
JsonEditor.show(getCatalog(), getBuiltIn(), R.string.mPttrn_editor, cntx, this::save);
}
}

View File

@ -14,7 +14,7 @@ import java.net.URLEncoder;
/**
* Class that manages the virusTotal connection
* // TODO: replace with generic POST class and move logic to VirusTotalModule
* TODO: replace with generic POST class and move logic to VirusTotalModule
*/
public class VirusTotalUtility {

View File

@ -1,5 +1,6 @@
package com.trianguloy.urlchecker.modules.list;
import android.util.Pair;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
@ -21,7 +22,7 @@ import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -109,7 +110,7 @@ class ClearUrlDialog extends AModuleDialog implements View.OnClickListener {
private final GenericPref.Bool verbose = ClearUrlModule.VERBOSE_PREF();
private final GenericPref.Bool auto = ClearUrlModule.AUTO_PREF();
private final JSONObject data;
private final List<Pair<String, JSONObject>> data;
private TextView info;
private Button fix;
@ -121,18 +122,19 @@ class ClearUrlDialog extends AModuleDialog implements View.OnClickListener {
verbose.init(dialog);
auto.init(dialog);
data = ClearUrlCatalog.getProviders(getActivity());
data = ClearUrlCatalog.getRules(getActivity());
}
@Override
public int getLayoutId() {
return R.layout.dialog_clearurl;
return R.layout.button_text;
}
@Override
public void onInitialize(View views) {
info = views.findViewById(R.id.text);
fix = views.findViewById(R.id.fix);
fix = views.findViewById(R.id.button);
fix.setText(R.string.mClear_clear);
fix.setOnClickListener(this);
}
@ -150,14 +152,12 @@ class ClearUrlDialog extends AModuleDialog implements View.OnClickListener {
}
fix.setEnabled(false);
try {
Iterator<String> providers = data.keys();
whileProvider:
while (providers.hasNext()) {
// evaluate each provider
String provider = providers.next();
JSONObject providerData = data.getJSONObject(provider);
whileProvider:
for (Pair<String, JSONObject> pair : data) {
// evaluate each provider
String provider = pair.first;
JSONObject providerData = pair.second;
try {
if (!matcher(providerData.getString("urlPattern"), cleared).find()) {
continue;
}
@ -278,11 +278,13 @@ class ClearUrlDialog extends AModuleDialog implements View.OnClickListener {
cleared = "http://" + cleared;
}
}
} catch (JSONException | UnsupportedEncodingException e) {
e.printStackTrace();
if (verbose.get()) {
append(R.string.mClear_error);
details(provider);
}
}
} catch (JSONException | UnsupportedEncodingException e) {
e.printStackTrace();
append(R.string.mClear_error);
}
// url changed, enable button
@ -351,7 +353,8 @@ class ClearUrlDialog extends AModuleDialog implements View.OnClickListener {
* Utility to set the info background color. Manages color importance
*/
private void setColor(int color) {
if (info.getTag() != null && info.getTag().equals(R.color.bad) && color == R.color.warning) return; // keep bad instead of replacing with warning
if (info.getTag() != null && info.getTag().equals(R.color.bad) && color == R.color.warning)
return; // keep bad instead of replacing with warning
info.setTag(color);
AndroidUtils.setRoundedColor(color, info, getActivity());
}

View File

@ -163,6 +163,9 @@ class OpenDialog extends AModuleDialog implements View.OnClickListener, PopupMen
// ------------------- Spinner -------------------
/**
* Populates the spinner with the apps that android says that can open it
*/
private void updateSpinner() {
packages = PackageUtilities.getOtherPackages(UrlUtilities.getViewIntent(getUrl(), null), getActivity());

View File

@ -1,6 +1,10 @@
package com.trianguloy.urlchecker.modules.list;
import android.app.Activity;
import android.util.Pair;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.trianguloy.urlchecker.R;
@ -9,10 +13,13 @@ import com.trianguloy.urlchecker.dialogs.MainDialog;
import com.trianguloy.urlchecker.modules.AModuleConfig;
import com.trianguloy.urlchecker.modules.AModuleData;
import com.trianguloy.urlchecker.modules.AModuleDialog;
import com.trianguloy.urlchecker.modules.DescriptionConfig;
import com.trianguloy.urlchecker.modules.companions.PatternCatalog;
import com.trianguloy.urlchecker.url.UrlData;
import com.trianguloy.urlchecker.utilities.AndroidUtils;
import com.trianguloy.urlchecker.utilities.ClickableLinks;
import com.trianguloy.urlchecker.utilities.Inflater;
import com.trianguloy.urlchecker.utilities.JavaUtilities;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
@ -39,16 +46,49 @@ public class PatternModule extends AModuleData {
@Override
public AModuleConfig getConfig(ConfigActivity cntx) {
return new DescriptionConfig(R.string.mPttrn_desc);
return new PatternConfig(cntx);
}
}
class PatternDialog extends AModuleDialog implements ClickableLinks.OnUrlListener {
class PatternConfig extends AModuleConfig implements View.OnClickListener {
private final PatternCatalog catalog;
public PatternConfig(Activity cntx) {
catalog = new PatternCatalog(cntx);
}
@Override
public boolean canBeEnabled() {
return true;
}
@Override
public int getLayoutId() {
return R.layout.config_patterns;
}
@Override
public void onInitialize(View views) {
views.findViewById(R.id.button).setOnClickListener(this);
}
@Override
public void onClick(View view) {
catalog.showEditor();
}
}
class PatternDialog extends AModuleDialog implements View.OnClickListener {
private TextView txt_pattern;
private LinearLayout box;
private final PatternCatalog catalog;
public PatternDialog(MainDialog dialog) {
super(dialog);
catalog = new PatternCatalog(dialog);
}
@Override
@ -59,64 +99,69 @@ class PatternDialog extends AModuleDialog implements ClickableLinks.OnUrlListene
@Override
public void onInitialize(View views) {
txt_pattern = views.findViewById(R.id.pattern);
box = views.findViewById(R.id.box);
}
@Override
public void onNewUrl(UrlData urlData) {
List<String> messages = new ArrayList<>();
List<Pair<String, String>> messages = new ArrayList<>();
String url = urlData.url;
// check for non-ascii characters
String strange = url.replaceAll("\\p{ASCII}", "");
if (!strange.isEmpty()) {
messages.add(getActivity().getString(R.string.mPttrn_ascii, strange));
// for each pattern
JSONObject patterns = catalog.getCatalog();
for (String pattern : JavaUtilities.toList(patterns.keys())) {
try {
JSONObject data = patterns.optJSONObject(pattern);
if (data == null) continue;
if (!data.optBoolean("enabled", true)) continue;
String regex = data.optString("regex", "(?!)");
if (url.matches(regex)) {
String replacement = data.has("replacement") ? data.optString("replacement") : null;
if (replacement != null) {
replacement = url.replaceAll(regex, replacement);
if (data.optBoolean("automatic")
&& setUrl(replacement)) {
return;
}
messages.add(Pair.create(pattern, replacement));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
// check for http
if (url.startsWith("http://")) {
messages.add(getActivity().getString(R.string.mPttrn_http));
}
// check for missing protocol
if (!url.matches("^https?://.*")) {
messages.add(getActivity().getString(R.string.mPttrn_noProtocol));
}
// TODO: other checks?
box.removeAllViews();
if (messages.isEmpty()) {
// no messages, all good
txt_pattern.setText(R.string.mPttrn_ok);
AndroidUtils.clearRoundedColor(txt_pattern);
txt_pattern.setVisibility(View.VISIBLE);
} else {
// messages to show, concatenate them
txt_pattern.setText("");
boolean newline = false;
for (String message : messages) {
if (newline) txt_pattern.append("\n");
newline = true;
txt_pattern.append(message);
// messages to show, set them
txt_pattern.setVisibility(View.GONE);
for (Pair<String, String> pair : messages) {
String label = pair.first;
String newUrl = pair.second;
View row = Inflater.inflate(R.layout.button_text, box, getActivity());
// text
TextView text = row.findViewById(R.id.text);
text.setText(label);
AndroidUtils.setRoundedColor(R.color.warning, text, getActivity());
// button
Button fix = row.findViewById(R.id.button);
fix.setText(R.string.mPttrn_fix);
fix.setEnabled(newUrl != null);
fix.setTag(newUrl); // will set this when clicked
fix.setOnClickListener(this);
}
AndroidUtils.setRoundedColor(R.color.warning, txt_pattern, getActivity());
}
ClickableLinks.linkify(txt_pattern, this);
}
@Override
public void onLinkClick(String tag) {
switch (tag) {
case "http":
// replace http with https
setUrl(getUrl().replaceFirst("^http://", "https://"));
break;
case "+http":
// prepend http
setUrl("http://" + getUrl());
break;
case "+https":
// prepend https
setUrl("https://" + getUrl());
break;
}
public void onClick(View view) {
Object tag = view.getTag();
if (tag != null) setUrl(tag.toString());
}
}

View File

@ -67,7 +67,8 @@ class RemoveQueriesDialog extends AModuleDialog implements View.OnClickListener
@Override
public void onInitialize(View views) {
info = views.findViewById(R.id.text);
remove = views.findViewById(R.id.fix);
remove = views.findViewById(R.id.button);
remove.setText(R.string.mRemove_all);
box = views.findViewById(R.id.box);
// expand queries
@ -111,7 +112,8 @@ class RemoveQueriesDialog extends AModuleDialog implements View.OnClickListener
// for each query, create a button
for (int i = 0; i < parts.getQueries(); i++) {
Button queryRemover = Inflater.inflate(R.layout.extra_remove_button, box, getActivity());
Button queryRemover = Inflater.inflate(R.layout.button_text, box, getActivity())
.findViewById(R.id.button);
queryRemover.setTag(i); // will remove this i query when clicked
queryRemover.setOnClickListener(this);
String queryName = parts.getQueryName(i);
@ -186,7 +188,8 @@ class RemoveQueriesDialog extends AModuleDialog implements View.OnClickListener
public String getUrl() {
StringBuilder sb = new StringBuilder(preQuery);
// first query after '?', the rest after '&'
for (int i = 0; i < queries.size(); ++i) sb.append(i == 0 ? "?" : "&").append(queries.get(i));
for (int i = 0; i < queries.size(); ++i)
sb.append(i == 0 ? "?" : "&").append(queries.get(i));
sb.append(postQuery);
return sb.toString();
}

View File

@ -60,14 +60,15 @@ class StatusDialog extends AModuleDialog implements View.OnClickListener, Clicka
@Override
public int getLayoutId() {
return R.layout.dialog_status;
return R.layout.button_text;
}
@Override
public void onInitialize(View views) {
check = views.findViewById(R.id.check);
check = views.findViewById(R.id.button);
check.setText(R.string.mStatus_check);
check.setOnClickListener(this);
info = views.findViewById(R.id.info);
info = views.findViewById(R.id.text);
}
@Override

View File

@ -117,15 +117,16 @@ class VirusTotalDialog extends AModuleDialog implements View.OnClickListener, Vi
@Override
public int getLayoutId() {
return R.layout.dialog_virustotal;
return R.layout.button_text;
}
@Override
public void onInitialize(View views) {
btn_scan = views.findViewById(R.id.scan);
txt_result = views.findViewById(R.id.result);
btn_scan = views.findViewById(R.id.button);
btn_scan.setText(R.string.mVT_scan);
btn_scan.setOnClickListener(this);
txt_result = views.findViewById(R.id.text);
txt_result.setOnClickListener(this);
txt_result.setOnLongClickListener(this);
}
@ -140,10 +141,10 @@ class VirusTotalDialog extends AModuleDialog implements View.OnClickListener, Vi
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.scan:
case R.id.button:
scanOrCancel();
break;
case R.id.result:
case R.id.text:
showInfo(false);
break;
}
@ -151,7 +152,7 @@ class VirusTotalDialog extends AModuleDialog implements View.OnClickListener, Vi
@Override
public boolean onLongClick(View v) {
if (v.getId() == R.id.result) {
if (v.getId() == R.id.text) {
showInfo(true);
return true;
}

View File

@ -0,0 +1,34 @@
package com.trianguloy.urlchecker.utilities;
import android.content.Context;
import java.io.IOException;
/**
* Represents a file from assets (read-only)
*/
public class AssetFile {
private final String fileName;
private Context cntx;
public AssetFile(String fileName) {
this.fileName = fileName;
}
public void init(Context cntx) {
this.cntx = cntx;
}
/**
* Returns the content of the file, or null if can't be read)
*/
public String get() {
// get the updated file first
try {
return StreamUtils.inputStream2String(cntx.getAssets().open(fileName));
} catch (IOException ignored) {
return null;
}
}
}

View File

@ -0,0 +1,61 @@
package com.trianguloy.urlchecker.utilities;
import android.content.Context;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* Represents an internal file, can be modified
*/
public class InternalFile {
private final String fileName;
private Context cntx;
public InternalFile(String fileName) {
this.fileName = fileName;
}
public void init(Context cntx) {
this.cntx = cntx;
}
/**
* Returns the content, null if the file doesn't exists or can't be read
*/
public String get() {
try {
return StreamUtils.inputStream2String(cntx.openFileInput(fileName));
} catch (IOException ignored) {
return null;
}
}
/**
* Sets a new file content
*/
public boolean set(String content) {
// the same, already saved
if (content.equals(get())) {
return true;
}
// store
try (FileOutputStream fos = cntx.openFileOutput(fileName, Context.MODE_PRIVATE)) {
fos.write(content.getBytes(StreamUtils.UTF_8));
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
/**
* Deletes the file
*/
public void delete() {
cntx.deleteFile(fileName);
}
}

View File

@ -0,0 +1,38 @@
package com.trianguloy.urlchecker.utilities;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Generic Java utils.
* I prefer smaller and more available apps, even if they require an older API and not using Kotlin
*/
public class JavaUtilities {
/**
* Converts an iterator to a list
*/
public static <T> List<T> toList(Iterator<T> iterator) {
List<T> list = new ArrayList<>();
while (iterator.hasNext()) {
list.add(iterator.next());
}
return list;
}
/**
* Converts a string into a json object, returns empty on failure
*/
public static JSONObject toJson(String content) {
try {
return new JSONObject(content);
} catch (JSONException e) {
// invalid catalog, return empty
return new JSONObject();
}
}
}

View File

@ -5,12 +5,12 @@
android:gravity="center_vertical">
<Button
android:id="@+id/fix"
android:id="@+id/button"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:text="@string/mClear_clear" />
android:minHeight="0dp" />
<TextView
android:id="@+id/text"

View File

@ -44,12 +44,15 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/mClear_updater" /><Button
android:id="@+id/edit"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/mClear_edit" /></LinearLayout>
android:text="@string/mClear_updater" />
<Button
android:id="@+id/edit"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/json_edit" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/mPttrn_desc" />
<Button
android:id="@+id/button"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/json_edit" />
</LinearLayout>

View File

@ -9,6 +9,7 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:minHeight="0dp"
android:src="@android:drawable/ic_media_rew" />
<ImageButton
@ -17,6 +18,7 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:minHeight="0dp"
android:src="@android:drawable/ic_media_previous" />
<ImageButton
@ -25,6 +27,7 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:minHeight="0dp"
android:src="@android:drawable/ic_menu_recent_history" />
<ImageButton
@ -33,6 +36,7 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:minHeight="0dp"
android:src="@android:drawable/ic_media_next" />
<ImageButton
@ -41,6 +45,7 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:minHeight="0dp"
android:src="@android:drawable/ic_media_ff" />
</LinearLayout>

View File

@ -10,5 +10,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:paddingRight="5dp" />
android:paddingRight="5dp"
android:text="@string/mPttrn_ok" />
<LinearLayout
android:id="@+id/box"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"></LinearLayout>
</LinearLayout>

View File

@ -5,28 +5,10 @@
android:gravity="center_vertical"
android:orientation="vertical">
<LinearLayout
<include
layout="@layout/button_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<Button
android:id="@+id/fix"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:text="@string/mRemove_all" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingRight="5dp"
android:paddingEnd="5dp" />
</LinearLayout>
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/box"

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<Button
android:id="@+id/check"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:text="@string/mStatus_check" />
<TextView
android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:paddingRight="5dp" />
</LinearLayout>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<Button
android:id="@+id/scan"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:text="@string/mVT_scan" />
<TextView
android:id="@+id/result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:paddingRight="5dp" />
</LinearLayout>

View File

@ -5,19 +5,33 @@
android:orientation="vertical"
android:padding="@dimen/smallPadding">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:text="@string/mClear_editor" /><FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:text="@string/json_desc" />
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="2dp"
android:background="?android:attr/listDivider" />
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"><EditText
android:id="@+id/rules"
android:id="@+id/data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"

View File

@ -27,16 +27,28 @@ Translation help from: Tiago Carmo, Ilithy, Idris."</string>
<string name="save">Save</string>
<string name="reset">Reset</string>
<string name="json_desc">[Beta feature] This is an advanced editor, the content must be formatted into valid JSON. You can press the top-right button to format and validate it.</string>
<string name="json_edit">Advanced editor</string>
<!-- modules -->
<string name="mPttrn_name">Patterns checker</string>
<string name="mPttrn_desc">"Checks for common issues in the url:
<string name="mPttrn_desc">"Checks for common issues in the url. Built-in rules:
* Non-ascii characters like greek letters. This can be used for phishing: googĺe.com vs google.com
* http links, you should use https instead"</string>
<string name="mPttrn_ascii">Warning! Non ascii characters found "%s"</string>
<string name="mPttrn_http">Http link, consider using https: [[http|Fix]]</string>
<string name="mPttrn_noProtocol">Missing protocol. [[+http|Add http]] or [[+https|add https]]</string>
<string name="mPttrn_ok">No issues found</string>
* http links, you should use https instead
* Missing http(s) scheme"</string>
<string name="mPttrn_fix">Fix</string>
<string name="mPttrn_ascii">Warning! Non ascii characters found</string>
<string name="mPttrn_http">Http link, consider using https</string>
<string name="mPttrn_noSchemeHttp">Missing http scheme.</string>
<string name="mPttrn_noSchemeHttps">Missing https scheme.</string>
<string name="mPttrn_ok">No patterns match</string>
<string name="mPttrn_editor">"Here you can edit or add new pattern rules. Format:
- 'regex': valid java regex that will be matched against the url.
- 'replacement': if provided, the 'fix' button will run 'url=url.replaceAll(regex,replacement)'.
- 'automatic': set to true to automatically apply the rule.
- 'enabled': set to false to disable the rule.
Note: if you edit the rules, app updates will not modify built-in ones (you can restore them with the reset option)."</string>
<string name="mOpen_name"><![CDATA[Open & Share]]></string>
<string name="mOpen_desc">"Contains the following buttons (left to right):
@ -101,7 +113,7 @@ This module can\'t be disabled."</string>
<string name="mClear_name">Clear URL</string>
<string name="mClear_desc">This module removes tracking, referrer and other useless parameters from the url. It also allows for common offline url redirections.</string>
<string name="mClear_tm">Uses the ClearURLs catalog from https://docs.clearurls.xyz/latest/specs/rules/</string>
<string name="mClear_tm">Built-in catalog copied from ClearURLs: https://docs.clearurls.xyz/latest/specs/rules/</string>
<string name="mClear_clear">Apply</string>
<string name="mClear_matches">Matches %s</string>
<string name="mClear_blocked">- BLOCKED!</string>
@ -116,9 +128,8 @@ This module can\'t be disabled."</string>
<string name="mClear_auto">Apply automatically</string>
<string name="mClear_toggleReferral">Enable referral marketing (referral parameters won\'t be cleared)</string>
<string name="mClear_toggleVerbose">Enable verbose info for the matching process</string>
<string name="mClear_error">An error occurred while checking rules</string>
<string name="mClear_error">An error occurred while checking rule</string>
<string name="mClear_updater">Updater</string>
<string name="mClear_edit">Edit rules</string>
<string name="mClear_sourceDesc">URL of the JSON file catalog with the rules:</string>
<string name="mClear_urlHint">Catalog url</string>
<string name="mClear_hashDesc">URL of the catalog hash. If omitted, the download will not be checked to see if it was tampered/modified (not recommended)</string>
@ -130,7 +141,7 @@ This module can\'t be disabled."</string>
<string name="mClear_hashError">Unable to fetch the hash, make sure the url is valid and you have an active connection</string>
<string name="mClear_hashMismatch">Hashes do not match, if you want to skip the check remove the hash url</string>
<string name="mClear_restore">Restore ClearUrl paths</string>
<string name="mClear_editor">This is an advance feature. Here you can directly edit the catalog rules. They must follow the format from https://docs.clearurls.xyz/latest/specs/rules/.\nNote that any modification here will be overwritten if the catalog is updated manually or automatically from the updater dialog.</string>
<string name="mClear_editor">Here you can directly edit the catalog rules. They must follow the format from https://docs.clearurls.xyz/latest/specs/rules/.\nNote that any modification to the parent object \'providers\' will be overwritten if the catalog is updated manually or automatically from the updater dialog. To have personal rules set them in a different parent object (any name).</string>
<string name="mRemove_name">Remove Queries</string>
<string name="mRemove_desc">With this module you can remove queries from the URL.\nPress the button to remove all queries or press the arrow to remove queries one at a time.\nThanks to PabloOQ for the idea and original implementation!</string>
@ -140,4 +151,5 @@ This module can\'t be disabled."</string>
<string name="mRemove_all">Remove all</string>
<string name="mRemove_one">Remove %s</string>
<string name="mRemove_empty">Remove empty</string>
</resources>

View File

@ -7,7 +7,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath 'com.android.tools.build:gradle:7.2.1'
// NOTE: Do not place your application dependencies here; they belong

View File

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