mirror of
https://github.com/TrianguloY/UrlChecker.git
synced 2024-09-19 20:02:16 +02:00
commit
c45b48c8f4
@ -29,6 +29,7 @@
|
||||
android:name=".dialogs.MainDialog"
|
||||
android:exported="true"
|
||||
android:noHistory="true"
|
||||
android:windowSoftInputMode="adjustResize|stateHidden"
|
||||
android:theme="@android:style/Theme.DeviceDefault.Dialog">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
@ -3,6 +3,7 @@ package com.trianguloy.urlchecker.dialogs;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
@ -57,6 +58,8 @@ public class JsonEditor {
|
||||
.setCancelable(false)
|
||||
.show();
|
||||
|
||||
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||
|
||||
// prepare more dialog
|
||||
// these are configured here to disable automatic auto-closing when they are pressed
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||
|
@ -4,8 +4,8 @@ import android.content.Context;
|
||||
import android.os.Build;
|
||||
|
||||
import com.trianguloy.urlchecker.R;
|
||||
import com.trianguloy.urlchecker.utilities.Enums;
|
||||
import com.trianguloy.urlchecker.utilities.GenericPref;
|
||||
import com.trianguloy.urlchecker.utilities.TranslatableEnum;
|
||||
|
||||
/**
|
||||
* Static elements related to CTabs
|
||||
@ -35,7 +35,7 @@ public class CTabs {
|
||||
/**
|
||||
* CTabs configuration
|
||||
*/
|
||||
public enum Config implements TranslatableEnum {
|
||||
public enum Config implements Enums.IdEnum, Enums.StringEnum {
|
||||
AUTO(0, R.string.auto),
|
||||
ON(1, R.string.mOpen_ctabsOn),
|
||||
OFF(2, R.string.mOpen_ctabsOff),
|
||||
|
@ -5,6 +5,7 @@ import android.app.AlertDialog;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
@ -223,6 +224,8 @@ public class ClearUrlCatalog {
|
||||
.setCancelable(true)
|
||||
.show();
|
||||
|
||||
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||
|
||||
// prepare more dialog
|
||||
// these are configured here to allow auto-closing the dialog when they are pressed
|
||||
Button updateNow = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
|
@ -0,0 +1,269 @@
|
||||
package com.trianguloy.urlchecker.modules.companions;
|
||||
|
||||
import static com.trianguloy.urlchecker.utilities.JavaUtils.valueOrDefault;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.trianguloy.urlchecker.modules.AModuleDialog;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Represents intent flags
|
||||
* Stores all the flags and it states, can be retrieved as list or as int
|
||||
* Non-compatible flags are ignored
|
||||
*/
|
||||
public class Flags {
|
||||
|
||||
// ------------------- static -------------------
|
||||
|
||||
// https://github.com/MuntashirAkon/AppManager/blob/19782da4c8556c817ba5795554a1cc21f38af13a/app/src/main/java/io/github/muntashirakon/AppManager/intercept/ActivityInterceptor.java#L92
|
||||
private static final Set<String> ALL_FLAGS = Set.of(
|
||||
// all have a 'FLAG_' prefix
|
||||
"GRANT_READ_URI_PERMISSION",
|
||||
"GRANT_WRITE_URI_PERMISSION",
|
||||
"FROM_BACKGROUND",
|
||||
"DEBUG_LOG_RESOLUTION",
|
||||
"EXCLUDE_STOPPED_PACKAGES",
|
||||
"INCLUDE_STOPPED_PACKAGES",
|
||||
"GRANT_PERSISTABLE_URI_PERMISSION",
|
||||
"GRANT_PREFIX_URI_PERMISSION",
|
||||
"DIRECT_BOOT_AUTO",
|
||||
"IGNORE_EPHEMERAL",
|
||||
"ACTIVITY_NO_HISTORY",
|
||||
"ACTIVITY_SINGLE_TOP",
|
||||
"ACTIVITY_NEW_TASK",
|
||||
"ACTIVITY_MULTIPLE_TASK",
|
||||
"ACTIVITY_CLEAR_TOP",
|
||||
"ACTIVITY_FORWARD_RESULT",
|
||||
"ACTIVITY_PREVIOUS_IS_TOP",
|
||||
"ACTIVITY_EXCLUDE_FROM_RECENTS",
|
||||
"ACTIVITY_BROUGHT_TO_FRONT",
|
||||
"ACTIVITY_RESET_TASK_IF_NEEDED",
|
||||
"ACTIVITY_LAUNCHED_FROM_HISTORY",
|
||||
"ACTIVITY_NEW_DOCUMENT",
|
||||
"ACTIVITY_NO_USER_ACTION",
|
||||
"ACTIVITY_REORDER_TO_FRONT",
|
||||
"ACTIVITY_NO_ANIMATION",
|
||||
"ACTIVITY_CLEAR_TASK",
|
||||
"ACTIVITY_TASK_ON_HOME",
|
||||
"ACTIVITY_RETAIN_IN_RECENTS",
|
||||
"ACTIVITY_LAUNCH_ADJACENT",
|
||||
"ACTIVITY_MATCH_EXTERNAL",
|
||||
"ACTIVITY_REQUIRE_NON_BROWSER",
|
||||
"ACTIVITY_REQUIRE_DEFAULT"
|
||||
);
|
||||
|
||||
private static final Map<String, Integer> compatibleFlags = new TreeMap<>(); // TreeMap to have the entries sorted by key
|
||||
|
||||
static {
|
||||
// Only get flags that are present in the current Android version
|
||||
for (var flag : ALL_FLAGS) {
|
||||
try {
|
||||
compatibleFlags.put(flag, (Integer) Intent.class.getField("FLAG_" + flag).get(null));
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, Integer> getCompatibleFlags() {
|
||||
return new TreeMap<>(compatibleFlags);
|
||||
}
|
||||
|
||||
// ------------------- CRUD -------------------
|
||||
|
||||
private Set<String> flags;
|
||||
|
||||
// TODO store non-compatible flags? as String "0x001000", int 0x001000
|
||||
|
||||
public Flags() {
|
||||
this(0x00000000);
|
||||
}
|
||||
|
||||
public Flags(int hex) {
|
||||
setFlags(hex);
|
||||
}
|
||||
|
||||
public boolean isSet(String flag) {
|
||||
return flags.contains(flag);
|
||||
}
|
||||
|
||||
public int getFlagsAsInt() {
|
||||
return flagsSetToHex(flags);
|
||||
}
|
||||
|
||||
public Set<String> getFlagsAsSet() {
|
||||
return new TreeSet<>(flags); // TreeSet to have the entries sorted
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the stored flags with the received hex
|
||||
*/
|
||||
public void setFlags(int hex) {
|
||||
this.flags = hexFlagsToSet(hex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the stored flags with the received list
|
||||
*/
|
||||
public void setFlags(Set<String> names) {
|
||||
Set<String> res = new HashSet<>(names);
|
||||
res.retainAll(compatibleFlags.keySet());
|
||||
this.flags = res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the flag to the received boolean
|
||||
*/
|
||||
public boolean setFlag(String flag, boolean bool) {
|
||||
if (compatibleFlags.containsKey(flag)) {
|
||||
if (bool) {
|
||||
return flags.add(flag);
|
||||
} else {
|
||||
return flags.remove(flag);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add flags by applying a mask
|
||||
*/
|
||||
public void addFlags(int hex) {
|
||||
flags.addAll(hexFlagsToSet(hex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a list of flags based on its name
|
||||
*/
|
||||
public void addFlags(Set<String> flags) {
|
||||
this.flags.addAll(flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a flag based on its name
|
||||
*/
|
||||
public boolean addFlag(String name) {
|
||||
if (compatibleFlags.containsKey(name)) {
|
||||
return flags.add(name);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove flags by applying a mask
|
||||
*/
|
||||
public boolean removeFlags(int hex) {
|
||||
return flags.removeAll(hexFlagsToSet(hex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a list of flags based on its name
|
||||
*/
|
||||
public boolean removeFlags(Set<String> flags) {
|
||||
return this.flags.removeAll(flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a flag based on its name
|
||||
*/
|
||||
public boolean removeFlag(String name) {
|
||||
return flags.remove(name);
|
||||
}
|
||||
|
||||
// ------------------- utils -------------------
|
||||
|
||||
/**
|
||||
* Decode an int as set
|
||||
*/
|
||||
private static Set<String> hexFlagsToSet(int hex) {
|
||||
var foundFlags = new HashSet<String>();
|
||||
for (var flag : compatibleFlags.entrySet()) {
|
||||
// check if flag is present
|
||||
if ((hex & flag.getValue()) != 0) {
|
||||
foundFlags.add(flag.getKey());
|
||||
}
|
||||
}
|
||||
return foundFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a set as hex
|
||||
*/
|
||||
private static int flagsSetToHex(Set<String> set) {
|
||||
int hex = 0x00000000;
|
||||
for (var flag : set) {
|
||||
if (compatibleFlags.containsKey(flag)) {
|
||||
hex = hex | compatibleFlags.get(flag);
|
||||
}
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
|
||||
// ------------------- store/load flags -------------------
|
||||
// this handles the store and load of the flags, if something wants to get the flags
|
||||
// it should always use these methods.
|
||||
|
||||
private static final String DATA_FLAGS = "flagsEditor.flags";
|
||||
private static final String REGEX = "(0x)?[a-fA-F\\d]{1,8}";
|
||||
private static final int BASE = 16;
|
||||
|
||||
/**
|
||||
* parses a text as an hexadecimal flags string.
|
||||
* Returns null if invalid
|
||||
*/
|
||||
public static Integer toInteger(String text) {
|
||||
if (text != null && text.matches(REGEX)) {
|
||||
return Integer.parseInt(text.replaceAll("^0x", ""), BASE);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an int flags to string
|
||||
*/
|
||||
public static String toHexString(int flags) {
|
||||
return "0x" + Integer.toString(flags, BASE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the flags from GlobalData, if it is not defined it will return null
|
||||
*/
|
||||
public static Integer getGlobalFlagsNullable(AModuleDialog instance) {
|
||||
return toInteger(instance.getData(DATA_FLAGS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the flags from GlobalData, if none were found it gets the flags from the intent that
|
||||
* started this activity
|
||||
*/
|
||||
public static int getGlobalFlagsNonNull(AModuleDialog instance, Activity cntx) {
|
||||
return getGlobalFlagsOrDefault(instance, cntx.getIntent().getFlags());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the flags from GlobalData, if none were found it gets the flags from default
|
||||
* Can be used by other modules
|
||||
*/
|
||||
public static int getGlobalFlagsOrDefault(AModuleDialog instance, int defaultFlags) {
|
||||
return valueOrDefault(toInteger(instance.getData(DATA_FLAGS)), defaultFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the flags in GlobalData
|
||||
*/
|
||||
public static void setGlobalFlags(Flags flags, AModuleDialog instance) {
|
||||
instance.putData(DATA_FLAGS, flags == null ? null : toHexString(flags.getFlagsAsInt()));
|
||||
}
|
||||
}
|
@ -1,14 +1,16 @@
|
||||
package com.trianguloy.urlchecker.modules.list;
|
||||
|
||||
import static com.trianguloy.urlchecker.utilities.JavaUtils.valueOrDefault;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.text.Editable;
|
||||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.Button;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
@ -18,15 +20,27 @@ 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.companions.Flags;
|
||||
import com.trianguloy.urlchecker.url.UrlData;
|
||||
import com.trianguloy.urlchecker.utilities.AndroidUtils;
|
||||
import com.trianguloy.urlchecker.utilities.DefaultTextWatcher;
|
||||
import com.trianguloy.urlchecker.utilities.Enums;
|
||||
import com.trianguloy.urlchecker.utilities.GenericPref;
|
||||
import com.trianguloy.urlchecker.utilities.Inflater;
|
||||
import com.trianguloy.urlchecker.utilities.InternalFile;
|
||||
import com.trianguloy.urlchecker.utilities.JavaUtils;
|
||||
import com.trianguloy.urlchecker.views.CycleImageButton;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* This module allows flag edition
|
||||
@ -37,6 +51,8 @@ public class FlagsModule extends AModuleData {
|
||||
return new GenericPref.Str("flagsEditor_defaultFlags", null, cntx);
|
||||
}
|
||||
|
||||
public static final String DEFAULT_GROUP = "default";
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "flagsEditor";
|
||||
@ -65,279 +81,398 @@ public class FlagsModule extends AModuleData {
|
||||
|
||||
class FlagsDialog extends AModuleDialog {
|
||||
|
||||
public static final String DATA_FLAGS = "flagsEditor.flags";
|
||||
private final Flags defaultFlags;
|
||||
private final Flags currentFlags;
|
||||
|
||||
// https://github.com/MuntashirAkon/AppManager/blob/19782da4c8556c817ba5795554a1cc21f38af13a/app/src/main/java/io/github/muntashirakon/AppManager/intercept/ActivityInterceptor.java#L92
|
||||
private static final List<String> ALL_FLAGS = List.of(
|
||||
"FLAG_GRANT_READ_URI_PERMISSION",
|
||||
"FLAG_GRANT_WRITE_URI_PERMISSION",
|
||||
"FLAG_FROM_BACKGROUND",
|
||||
"FLAG_DEBUG_LOG_RESOLUTION",
|
||||
"FLAG_EXCLUDE_STOPPED_PACKAGES",
|
||||
"FLAG_INCLUDE_STOPPED_PACKAGES",
|
||||
"FLAG_GRANT_PERSISTABLE_URI_PERMISSION",
|
||||
"FLAG_GRANT_PREFIX_URI_PERMISSION",
|
||||
"FLAG_DIRECT_BOOT_AUTO",
|
||||
"FLAG_IGNORE_EPHEMERAL",
|
||||
"FLAG_ACTIVITY_NO_HISTORY",
|
||||
"FLAG_ACTIVITY_SINGLE_TOP",
|
||||
"FLAG_ACTIVITY_NEW_TASK",
|
||||
"FLAG_ACTIVITY_MULTIPLE_TASK",
|
||||
"FLAG_ACTIVITY_CLEAR_TOP",
|
||||
"FLAG_ACTIVITY_FORWARD_RESULT",
|
||||
"FLAG_ACTIVITY_PREVIOUS_IS_TOP",
|
||||
"FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS",
|
||||
"FLAG_ACTIVITY_BROUGHT_TO_FRONT",
|
||||
"FLAG_ACTIVITY_RESET_TASK_IF_NEEDED",
|
||||
"FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY",
|
||||
"FLAG_ACTIVITY_NEW_DOCUMENT",
|
||||
"FLAG_ACTIVITY_NO_USER_ACTION",
|
||||
"FLAG_ACTIVITY_REORDER_TO_FRONT",
|
||||
"FLAG_ACTIVITY_NO_ANIMATION",
|
||||
"FLAG_ACTIVITY_CLEAR_TASK",
|
||||
"FLAG_ACTIVITY_TASK_ON_HOME",
|
||||
"FLAG_ACTIVITY_RETAIN_IN_RECENTS",
|
||||
"FLAG_ACTIVITY_LAUNCH_ADJACENT",
|
||||
"FLAG_ACTIVITY_MATCH_EXTERNAL",
|
||||
"FLAG_ACTIVITY_REQUIRE_NON_BROWSER",
|
||||
"FLAG_ACTIVITY_REQUIRE_DEFAULT"
|
||||
);
|
||||
private final Map<String, Integer> flagMap = new TreeMap<>(); // TreeMap to have the entries sorted by key
|
||||
private Map<String, FlagsConfig.FlagState> flagsStatePref;
|
||||
|
||||
private final GenericPref.Str defaultFlagsPref;
|
||||
private ViewGroup shownFlagsVG;
|
||||
private EditText searchInput;
|
||||
private ViewGroup hiddenFlagsVG;
|
||||
private ImageView overflowButton;
|
||||
|
||||
private EditText flagsHexText;
|
||||
private AutoCompleteTextView flagNameText;
|
||||
private ImageButton more;
|
||||
private LinearLayout box;
|
||||
private JSONObject groups;
|
||||
|
||||
public FlagsDialog(MainDialog dialog) {
|
||||
super(dialog);
|
||||
|
||||
defaultFlagsPref = FlagsModule.DEFAULTFLAGS_PREF(dialog);
|
||||
|
||||
try {
|
||||
// Only get flags that are present in the current Android version
|
||||
for (var field : Intent.class.getFields()) {
|
||||
if (ALL_FLAGS.contains(field.getName())) {
|
||||
flagMap.put(field.getName(), (Integer) field.get(null));
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
defaultFlags = new Flags(getActivity().getIntent().getFlags());
|
||||
currentFlags = new Flags();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLayoutId() {
|
||||
return R.layout.dialog_editflags;
|
||||
return R.layout.dialog_flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitialize(View views) {
|
||||
box = views.findViewById(R.id.box);
|
||||
flagsHexText = views.findViewById(R.id.flagsHexText);
|
||||
initGroups();
|
||||
|
||||
// set the flags to the adapter of the input text
|
||||
flagNameText = views.findViewById(R.id.flagText);
|
||||
flagNameText.setAdapter(new ArrayAdapter<>(getActivity(), android.R.layout.simple_spinner_dropdown_item, new ArrayList<>(
|
||||
flagMap.keySet()
|
||||
)));
|
||||
// so the dropdown gets the maximum width possible
|
||||
flagNameText.setDropDownAnchor(R.id.addFlagLayout);
|
||||
// FIXME better search, currently it is an autofill, not a search
|
||||
// FIXME sometimes its hidden behind keyboard
|
||||
shownFlagsVG = views.findViewById(R.id.shownFlags);
|
||||
searchInput = views.findViewById(R.id.search);
|
||||
hiddenFlagsVG = views.findViewById(R.id.hiddenFlags);
|
||||
|
||||
// get initial flags
|
||||
var defaultFlagsStr = defaultFlagsPref.get();
|
||||
if (defaultFlagsStr != null) {
|
||||
setFlags(toInteger(defaultFlagsStr));
|
||||
}
|
||||
// Button to open the `box` with the hidden flags (more indicator)
|
||||
overflowButton = views.findViewById(R.id.overflowButton);
|
||||
|
||||
// press add to add a flag
|
||||
views.<Button>findViewById(R.id.add).setOnClickListener(v -> {
|
||||
var flag = flagMap.get(flagNameText.getText().toString());
|
||||
if (flag != null) {
|
||||
setFlags(getFlagsNonNull() | flag);
|
||||
} else {
|
||||
Toast.makeText(getActivity(), R.string.mFlags_invalid, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
// Update views
|
||||
updateLayout();
|
||||
});
|
||||
|
||||
// press 'more' to expand/collapse the box
|
||||
more = views.findViewById(R.id.more);
|
||||
more.setOnClickListener(v -> {
|
||||
box.setVisibility(box.getVisibility() == View.GONE ? View.VISIBLE : View.GONE);
|
||||
// Update views
|
||||
updateLayout();
|
||||
});
|
||||
box.setVisibility(View.GONE); // start collapsed
|
||||
|
||||
// press edit to start editing, press again to save
|
||||
var edit = views.<Button>findViewById(R.id.edit);
|
||||
edit.setOnClickListener(v -> {
|
||||
if (flagsHexText.isEnabled()) {
|
||||
// requested to save
|
||||
var flags = toInteger(flagsHexText.getText().toString());
|
||||
if (flags != null) {
|
||||
// Extract flags
|
||||
setFlags(flags);
|
||||
}
|
||||
}
|
||||
flagsHexText.setEnabled(!flagsHexText.isEnabled());
|
||||
// Update views
|
||||
updateLayout();
|
||||
});
|
||||
// long press to reset
|
||||
edit.setOnLongClickListener(v -> {
|
||||
// Resets the flags
|
||||
setFlags(null);
|
||||
// Update views
|
||||
updateLayout();
|
||||
return true;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayUrl(UrlData urlData) {
|
||||
updateLayout();
|
||||
}
|
||||
|
||||
private void updateLayout() {
|
||||
|
||||
// set text
|
||||
var flags = getFlagsNonNull();
|
||||
flagsHexText.setText(toHexString(flags));
|
||||
|
||||
// set current flags list
|
||||
var decodedFlags = decodeFlags(flags);
|
||||
box.removeAllViews();
|
||||
if (decodedFlags.size() == 0) {
|
||||
// no flags, disable
|
||||
AndroidUtils.setEnabled(more, false);
|
||||
more.setImageResource(R.drawable.arrow_right);
|
||||
} else {
|
||||
// flags, enable
|
||||
AndroidUtils.setEnabled(more, true);
|
||||
more.setImageResource(box.getVisibility() == View.VISIBLE ? R.drawable.arrow_down : R.drawable.arrow_right);
|
||||
// For each flag, create a button+text
|
||||
for (var flag : decodedFlags) {
|
||||
var button_text = Inflater.inflate(R.layout.button_text, box, getActivity());
|
||||
|
||||
// Button that removes the flag
|
||||
var button = button_text.<Button>findViewById(R.id.button);
|
||||
button.setText(R.string.remove);
|
||||
// Apply mask to remove flag
|
||||
button.setOnClickListener(v -> {
|
||||
setFlags(getFlagsNonNull() & ~flagMap.get(flag));
|
||||
// Update views
|
||||
updateLayout();
|
||||
// Hide hidden flags
|
||||
hiddenFlagsVG.setVisibility(View.GONE);
|
||||
AndroidUtils.toggleableListener(overflowButton,
|
||||
v -> hiddenFlagsVG.setVisibility(hiddenFlagsVG.getVisibility() == View.GONE ? View.VISIBLE : View.GONE),
|
||||
v -> {
|
||||
searchInput.setVisibility(hiddenFlagsVG.getVisibility());
|
||||
updateMoreIndicator();
|
||||
});
|
||||
|
||||
var text = button_text.<TextView>findViewById(R.id.text);
|
||||
text.setText(flag);
|
||||
// SEARCH
|
||||
// Set up search text
|
||||
searchInput.addTextChangedListener(new DefaultTextWatcher() {
|
||||
@Override
|
||||
public void afterTextChanged(Editable text) {
|
||||
for (int i = 0; i < hiddenFlagsVG.getChildCount(); i++) {
|
||||
var checkbox_text = hiddenFlagsVG.getChildAt(i);
|
||||
String flag = ((TextView) checkbox_text.findViewById(R.id.text)).getText().toString();
|
||||
String search = text.toString();
|
||||
// Set visibility based on search text
|
||||
checkbox_text.setVisibility(JavaUtils.containsWords(flag, search) ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// TODO spinner with groups
|
||||
loadGroup(FlagsModule.DEFAULT_GROUP);
|
||||
}
|
||||
|
||||
private void initGroups() {
|
||||
String fileString = new InternalFile(FlagsConfig.CONF_FILE, getActivity()).get();
|
||||
groups = new JSONObject();
|
||||
if (fileString != null) {
|
||||
try {
|
||||
groups = new JSONObject(fileString).getJSONObject("groups");
|
||||
} catch (JSONException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------- utils -------------------
|
||||
|
||||
/**
|
||||
* Decode an int as flags
|
||||
*/
|
||||
private List<String> decodeFlags(int hex) {
|
||||
var foundFlags = new ArrayList<String>();
|
||||
for (var flag : flagMap.entrySet()) {
|
||||
// check if flag is present
|
||||
if ((hex & flag.getValue()) != 0) {
|
||||
foundFlags.add(flag.getKey());
|
||||
// To get all the groups names
|
||||
private List<String> getGroups() {
|
||||
List<String> res = new ArrayList<>();
|
||||
// Always add FlagsModule.DEFAULT_GROUP first, even if it doesn't exist
|
||||
res.add(FlagsModule.DEFAULT_GROUP);
|
||||
for (Iterator<String> it = groups.keys(); it.hasNext(); ) {
|
||||
String group = it.next();
|
||||
if (!group.equals(FlagsModule.DEFAULT_GROUP)) {
|
||||
res.add(group);
|
||||
}
|
||||
}
|
||||
return foundFlags;
|
||||
return res;
|
||||
}
|
||||
|
||||
// ------------------- store/load flags -------------------
|
||||
// this handles the store and load of the flags, if something wants to get the flags
|
||||
// it should always use these methods.
|
||||
void loadGroup(String group) {
|
||||
currentFlags.setFlags(0);
|
||||
|
||||
private static final int BASE = 16;
|
||||
protected static final String REGEX = "(0x)?[a-fA-F\\d]{1,8}";
|
||||
// Load json
|
||||
JSONObject groupPref = null;
|
||||
try {
|
||||
groupPref = groups.getJSONObject(group);
|
||||
} catch (JSONException ignore) {
|
||||
}
|
||||
|
||||
/**
|
||||
* parses a text as an hexadecimal flags string.
|
||||
* Returns null if invalid
|
||||
*/
|
||||
public static Integer toInteger(String text) {
|
||||
if (text != null && text.matches(REGEX)) {
|
||||
return Integer.parseInt(text.replaceAll("^0x", ""), BASE);
|
||||
|
||||
// Put shown flags
|
||||
Set<String> shownFlagsSet = new TreeSet<>();
|
||||
// Get state preference of flag from json and then store it in a map
|
||||
flagsStatePref = new HashMap<>();
|
||||
if (groupPref != null) {
|
||||
for (Iterator<String> it = groupPref.keys(); it.hasNext(); ) {
|
||||
String flag = it.next();
|
||||
try {
|
||||
flagsStatePref.put(flag, Enums.toEnum(FlagsConfig.FlagState.class,
|
||||
groupPref.getJSONObject(flag).getInt("state")));
|
||||
if (groupPref.getJSONObject(flag).getBoolean("show")) {
|
||||
shownFlagsSet.add(flag);
|
||||
}
|
||||
} catch (JSONException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If it is not in shownFlags it must be in hiddenFlags
|
||||
Set<String> hiddenFlagsSet = new TreeSet<>(Flags.getCompatibleFlags().keySet());
|
||||
hiddenFlagsSet.removeAll(shownFlagsSet);
|
||||
|
||||
// Fill boxes with flags, load flags into currentFlags too
|
||||
fillWithFlags(shownFlagsSet, shownFlagsVG);
|
||||
fillWithFlags(hiddenFlagsSet, hiddenFlagsVG);
|
||||
|
||||
// Update global
|
||||
Flags.setGlobalFlags(currentFlags, this);
|
||||
|
||||
updateMoreIndicator();
|
||||
}
|
||||
|
||||
void updateMoreIndicator() {
|
||||
if (hiddenFlagsVG.getChildCount() == 0) {
|
||||
overflowButton.setImageDrawable(null);
|
||||
} else {
|
||||
return null;
|
||||
overflowButton.setImageResource(hiddenFlagsVG.getVisibility() == View.VISIBLE ? R.drawable.arrow_down : R.drawable.arrow_right);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an int flags to string
|
||||
* Sets up a ViewGroup with the flags received. The state of the flag is read from flagsStatePref.
|
||||
* The default state of the flag is read from defaultFlags. Also sets currentFlags.
|
||||
*
|
||||
* @param flags flags to add
|
||||
* @param vg ViewGroup to fill with flags
|
||||
*/
|
||||
public static String toHexString(int flags) {
|
||||
return "0x" + Integer.toString(flags, BASE);
|
||||
private void fillWithFlags(Set<String> flags, ViewGroup vg) {
|
||||
vg.removeAllViews();
|
||||
|
||||
for (String flag : flags) {
|
||||
var checkbox_text = Inflater.inflate(R.layout.dialog_flags_entry, vg, getActivity());
|
||||
|
||||
// Checkbox
|
||||
var checkBox = checkbox_text.<ImageView>findViewById(R.id.state);
|
||||
boolean bool;
|
||||
switch (valueOrDefault(flagsStatePref.get(flag), FlagsConfig.FlagState.AUTO)) {
|
||||
case ON:
|
||||
bool = true;
|
||||
break;
|
||||
case OFF:
|
||||
bool = false;
|
||||
break;
|
||||
case AUTO:
|
||||
default:
|
||||
bool = defaultFlags.isSet(flag);
|
||||
}
|
||||
currentFlags.setFlag(flag, bool);
|
||||
|
||||
checkBox.setTag(R.id.text, flag);
|
||||
AndroidUtils.toggleableListener(checkBox,
|
||||
v -> {
|
||||
currentFlags.setFlag(flag, !currentFlags.isSet(flag));
|
||||
|
||||
// Update global
|
||||
Flags.setGlobalFlags(currentFlags, this);
|
||||
|
||||
// To update debug module view of GlobalData
|
||||
setUrl(new UrlData(getUrl()).dontTriggerOwn().asMinorUpdate());
|
||||
},
|
||||
v -> checkBox.setImageResource(currentFlags.isSet(flag) ? R.drawable.flag_on : R.drawable.flag_off)
|
||||
);
|
||||
|
||||
// Text
|
||||
((TextView) checkbox_text.findViewById(R.id.text)).setText(flag);
|
||||
|
||||
// Color indicators
|
||||
var defaultIndicator = checkbox_text.findViewById(R.id.defaultIndicator);
|
||||
var preferenceIndicator = checkbox_text.findViewById(R.id.preferenceIndicator);
|
||||
|
||||
checkBox.setTag(R.id.defaultIndicator, defaultIndicator);
|
||||
checkBox.setTag(R.id.preferenceIndicator, preferenceIndicator);
|
||||
|
||||
setColors(flag, defaultIndicator, preferenceIndicator);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the flags from GlobalData, if it is not defined it will return null
|
||||
* Intended for use in other modules
|
||||
*/
|
||||
public static Integer getFlagsNullable(AModuleDialog instance) {
|
||||
return toInteger(instance.getData(DATA_FLAGS));
|
||||
}
|
||||
void setColors(String flag, View defaultIndicator, View preferenceIndicator) {
|
||||
AndroidUtils.setRoundedColor(defaultFlags.isSet(flag) ? R.color.good : R.color.bad, defaultIndicator);
|
||||
|
||||
/**
|
||||
* Loads the flags from GlobalData, if none were found it gets the flags from the intent that
|
||||
* started this activity
|
||||
*/
|
||||
private int getFlagsNonNull() {
|
||||
return getFlagsOrDefault(this, getActivity().getIntent().getFlags());
|
||||
}
|
||||
int color;
|
||||
switch (valueOrDefault(flagsStatePref.get(flag), FlagsConfig.FlagState.AUTO)) {
|
||||
case ON:
|
||||
color = R.color.good;
|
||||
break;
|
||||
case OFF:
|
||||
color = R.color.bad;
|
||||
break;
|
||||
case AUTO:
|
||||
default:
|
||||
color = R.color.grey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the flags from GlobalData, if none were found it gets the flags from default
|
||||
* Can be used by other modules
|
||||
*/
|
||||
public static int getFlagsOrDefault(AModuleDialog instance, int defaultFlags) {
|
||||
var flags = toInteger(instance.getData(DATA_FLAGS));
|
||||
return flags == null ? defaultFlags : flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the flags in GlobalData
|
||||
*/
|
||||
private void setFlags(Integer flags) {
|
||||
putData(DATA_FLAGS, flags == null ? null : toHexString(flags));
|
||||
AndroidUtils.setRoundedColor(color, preferenceIndicator);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FlagsConfig extends AModuleConfig {
|
||||
private final GenericPref.Str defaultFlagsPref;
|
||||
|
||||
protected static final String CONF_FILE = "flags_editor_settings";
|
||||
|
||||
public FlagsConfig(ModulesActivity activity) {
|
||||
super(activity);
|
||||
defaultFlagsPref = FlagsModule.DEFAULTFLAGS_PREF(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLayoutId() {
|
||||
return R.layout.config_flagseditor;
|
||||
return R.layout.config_flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitialize(View views) {
|
||||
defaultFlagsPref.attachToEditText(
|
||||
views.findViewById(R.id.flags),
|
||||
str -> str,
|
||||
str -> str.matches(FlagsDialog.REGEX) ? str : defaultFlagsPref.defaultValue
|
||||
);
|
||||
views.findViewById(R.id.button).setOnClickListener(showDialog -> {
|
||||
View flagsDialogLayout = getActivity().getLayoutInflater().inflate(R.layout.flags_editor, null);
|
||||
ViewGroup box = flagsDialogLayout.findViewById(R.id.box);
|
||||
InternalFile file = new InternalFile(CONF_FILE, getActivity());
|
||||
|
||||
// Get all flags
|
||||
fillBoxViewGroup(box, file, FlagsModule.DEFAULT_GROUP);
|
||||
|
||||
AlertDialog alertDialog = new AlertDialog.Builder(getActivity())
|
||||
.setView(flagsDialogLayout)
|
||||
.setPositiveButton(views.getContext().getText(R.string.save), (dialog, which) -> {
|
||||
// Save the settings
|
||||
storePreferences(box, file, FlagsModule.DEFAULT_GROUP);
|
||||
})
|
||||
.setNegativeButton(views.getContext().getText(android.R.string.cancel), null)
|
||||
.setNeutralButton(views.getContext().getText(R.string.reset), null)
|
||||
.show();
|
||||
|
||||
alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||
|
||||
alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(listener -> {
|
||||
// Reset current group flags (does not save)
|
||||
resetFlags(box);
|
||||
});
|
||||
|
||||
// Search
|
||||
((EditText) flagsDialogLayout.findViewById(R.id.search)).addTextChangedListener(new DefaultTextWatcher() {
|
||||
@Override
|
||||
public void afterTextChanged(Editable text) {
|
||||
for (int i = 0; i < box.getChildCount(); i++) {
|
||||
var entryView = box.getChildAt(i);
|
||||
String flag = ((TextView) entryView.findViewById(R.id.text)).getText().toString();
|
||||
String search = text.toString();
|
||||
// Set visibility based on search text
|
||||
entryView.setVisibility(JavaUtils.containsWords(flag, search) ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// TODO add dialog button to set all to on/off/auto
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void fillBoxViewGroup(ViewGroup vg, InternalFile file, String group) {
|
||||
|
||||
String prefString = file.get();
|
||||
JSONObject oldPref = null; // Null if there is no file or fails to parse
|
||||
try {
|
||||
oldPref = prefString == null ? null : new JSONObject(file.get()).getJSONObject("groups").getJSONObject(group);
|
||||
} catch (JSONException ignored) {
|
||||
}
|
||||
|
||||
// Fill the box
|
||||
for (String flag : Flags.getCompatibleFlags().keySet()) {
|
||||
var entryView = Inflater.inflate(R.layout.flags_editor_entry, vg, getActivity());
|
||||
|
||||
TextView textView = entryView.findViewById(R.id.text);
|
||||
textView.setText(flag);
|
||||
|
||||
var flagState = entryView.<CycleImageButton<FlagState>>findViewById(R.id.state);
|
||||
flagState.setStates(List.of(FlagState.values()));
|
||||
|
||||
// Load preferences from settings
|
||||
Boolean showValue = null;
|
||||
Integer stateValue = null;
|
||||
// Get current preferences
|
||||
if (oldPref != null) {
|
||||
JSONObject flagPref;
|
||||
try {
|
||||
flagPref = oldPref.getJSONObject(flag);
|
||||
showValue = flagPref.getBoolean("show");
|
||||
stateValue = flagPref.getInt("state");
|
||||
} catch (JSONException ignored) {
|
||||
}
|
||||
}
|
||||
flagState.setCurrentState(Enums.toEnum(FlagState.class, valueOrDefault(stateValue, FlagState.AUTO.id)));
|
||||
var show = entryView.<ImageButton>findViewById(R.id.show);
|
||||
show.setTag(valueOrDefault(showValue, false));
|
||||
AndroidUtils.toggleableListener(show,
|
||||
v -> v.setTag(v.getTag() == Boolean.FALSE),
|
||||
v -> v.setImageResource(v.getTag() == Boolean.TRUE ? R.drawable.show : R.drawable.hide)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void storePreferences(ViewGroup vg, InternalFile file, String group) {
|
||||
// Retrieve previous config, to keep other groups
|
||||
JSONObject oldSettings = null;
|
||||
String content = file.get();
|
||||
// It's ok if there is no file yet
|
||||
if (content != null) {
|
||||
try {
|
||||
oldSettings = new JSONObject(content);
|
||||
} catch (JSONException ignore) {
|
||||
// If the json fails to parse then we will create a new file
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Collect all the settings of the vg
|
||||
JSONObject newSettings = new JSONObject();
|
||||
for (int i = 0; i < vg.getChildCount(); i++) {
|
||||
View v = vg.getChildAt(i);
|
||||
|
||||
FlagState state = v.<CycleImageButton<FlagState>>findViewById(R.id.state).getCurrentState();
|
||||
boolean show = v.findViewById(R.id.show).getTag() == Boolean.TRUE;
|
||||
newSettings.put(((TextView) v.findViewById(R.id.text)).getText().toString(),
|
||||
new JSONObject()
|
||||
.put("state", state.getId())
|
||||
.put("show", show));
|
||||
}
|
||||
// If there are no old settings, create a new one
|
||||
// Replace the old settings from group with the new ones
|
||||
newSettings = oldSettings == null ?
|
||||
new JSONObject().put("groups", new JSONObject().put(group, newSettings)) :
|
||||
oldSettings.put("groups", oldSettings.getJSONObject("groups").put(group, newSettings));
|
||||
// TODO should groups be sorted?
|
||||
file.set(newSettings.toString());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(getActivity(), R.string.toast_invalid, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void resetFlags(ViewGroup vg) {
|
||||
// Set everything to default values
|
||||
for (int i = 0; i < vg.getChildCount(); i++) {
|
||||
View v = vg.getChildAt(i);
|
||||
v.<CycleImageButton<FlagState>>findViewById(R.id.state).setCurrentState(FlagState.AUTO);
|
||||
var visible = v.<ImageButton>findViewById(R.id.show);
|
||||
visible.setImageResource(R.drawable.hide);
|
||||
visible.setTag(Boolean.FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
public enum FlagState implements Enums.IdEnum, Enums.ImageEnum {
|
||||
AUTO(0, R.drawable.flag_auto),
|
||||
ON(1, R.drawable.flag_on),
|
||||
OFF(2, R.drawable.flag_off),
|
||||
;
|
||||
|
||||
// -----
|
||||
|
||||
private final int id;
|
||||
private final int imageResource;
|
||||
|
||||
FlagState(int id, int imageResource) {
|
||||
this.id = id;
|
||||
this.imageResource = imageResource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getImageResource() {
|
||||
return imageResource;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.text.util.Linkify;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
@ -138,6 +139,8 @@ class LogConfig extends AModuleConfig {
|
||||
// show
|
||||
AlertDialog dialog = builder.show();
|
||||
|
||||
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||
|
||||
// prepare more dialog
|
||||
// these are configured here to disable automatic auto-closing when they are pressed
|
||||
if (editable) {
|
||||
|
@ -17,6 +17,7 @@ import com.trianguloy.urlchecker.modules.AModuleConfig;
|
||||
import com.trianguloy.urlchecker.modules.AModuleData;
|
||||
import com.trianguloy.urlchecker.modules.AModuleDialog;
|
||||
import com.trianguloy.urlchecker.modules.companions.CTabs;
|
||||
import com.trianguloy.urlchecker.modules.companions.Flags;
|
||||
import com.trianguloy.urlchecker.modules.companions.LastOpened;
|
||||
import com.trianguloy.urlchecker.url.UrlData;
|
||||
import com.trianguloy.urlchecker.utilities.AndroidUtils;
|
||||
@ -251,7 +252,7 @@ class OpenDialog extends AModuleDialog {
|
||||
}
|
||||
|
||||
// Get flags from global data (probably set by flags module, if active)
|
||||
Integer flags = FlagsDialog.getFlagsNullable(this);
|
||||
Integer flags = Flags.getGlobalFlagsNullable(this);
|
||||
if (flags != null) {
|
||||
intent.setFlags(flags);
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.trianguloy.urlchecker.modules.list;
|
||||
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
|
||||
@ -13,6 +12,7 @@ import com.trianguloy.urlchecker.modules.AModuleData;
|
||||
import com.trianguloy.urlchecker.modules.AModuleDialog;
|
||||
import com.trianguloy.urlchecker.modules.DescriptionConfig;
|
||||
import com.trianguloy.urlchecker.url.UrlData;
|
||||
import com.trianguloy.urlchecker.utilities.DefaultTextWatcher;
|
||||
import com.trianguloy.urlchecker.utilities.DoubleEvent;
|
||||
|
||||
/**
|
||||
@ -41,9 +41,10 @@ public class TextInputModule extends AModuleData {
|
||||
}
|
||||
}
|
||||
|
||||
class TextInputDialog extends AModuleDialog implements TextWatcher {
|
||||
class TextInputDialog extends AModuleDialog {
|
||||
|
||||
private final DoubleEvent doubleEdit = new DoubleEvent(1000); // if two updates happens in less than this milliseconds, they are considered as the same
|
||||
private boolean skipUpdate = false;
|
||||
|
||||
private EditText edtxt_url;
|
||||
|
||||
@ -59,39 +60,31 @@ class TextInputDialog extends AModuleDialog implements TextWatcher {
|
||||
@Override
|
||||
public void onInitialize(View views) {
|
||||
edtxt_url = views.findViewById(R.id.url);
|
||||
edtxt_url.addTextChangedListener(this);
|
||||
edtxt_url.addTextChangedListener(new DefaultTextWatcher() {
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (skipUpdate) return;
|
||||
|
||||
// new url by the user
|
||||
var newUrlData = new UrlData(s.toString())
|
||||
.dontTriggerOwn()
|
||||
.disableUpdates();
|
||||
|
||||
// mark as minor if too quick
|
||||
if (doubleEdit.checkAndTrigger()) newUrlData.asMinorUpdate();
|
||||
|
||||
// set
|
||||
setUrl(newUrlData);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayUrl(UrlData urlData) {
|
||||
// setText fires the afterTextChanged listener, so we need to remove it
|
||||
edtxt_url.removeTextChangedListener(this);
|
||||
// setText fires the afterTextChanged listener, so we need to skip it
|
||||
skipUpdate = true;
|
||||
edtxt_url.setText(urlData.url);
|
||||
edtxt_url.addTextChangedListener(this);
|
||||
skipUpdate = false;
|
||||
doubleEdit.reset(); // next user update, even if immediately after, will be considered new
|
||||
}
|
||||
|
||||
// ------------------- TextWatcher -------------------
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
// new url by the user
|
||||
var newUrlData = new UrlData(s.toString())
|
||||
.dontTriggerOwn()
|
||||
.disableUpdates();
|
||||
|
||||
// mark as minor if too quick
|
||||
if (doubleEdit.checkAndTrigger()) newUrlData.asMinorUpdate();
|
||||
|
||||
// set
|
||||
setUrl(newUrlData);
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,8 @@ import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.trianguloy.urlchecker.R;
|
||||
@ -19,6 +17,7 @@ import com.trianguloy.urlchecker.modules.AModuleDialog;
|
||||
import com.trianguloy.urlchecker.modules.companions.VirusTotalUtility;
|
||||
import com.trianguloy.urlchecker.url.UrlData;
|
||||
import com.trianguloy.urlchecker.utilities.AndroidUtils;
|
||||
import com.trianguloy.urlchecker.utilities.DefaultTextWatcher;
|
||||
import com.trianguloy.urlchecker.utilities.GenericPref;
|
||||
import com.trianguloy.urlchecker.utilities.UrlUtils;
|
||||
|
||||
@ -57,7 +56,7 @@ public class VirusTotalModule extends AModuleData {
|
||||
}
|
||||
}
|
||||
|
||||
class VirusTotalConfig extends AModuleConfig implements TextWatcher {
|
||||
class VirusTotalConfig extends AModuleConfig {
|
||||
|
||||
final GenericPref.Str api_key;
|
||||
|
||||
@ -79,23 +78,15 @@ class VirusTotalConfig extends AModuleConfig implements TextWatcher {
|
||||
|
||||
@Override
|
||||
public void onInitialize(View views) {
|
||||
final EditText edit_key = (EditText) views.findViewById(R.id.api_key);
|
||||
var edit_key = views.<TextView>findViewById(R.id.api_key);
|
||||
edit_key.setText(api_key.get());
|
||||
edit_key.addTextChangedListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
api_key.set(s.toString());
|
||||
if (!canBeEnabled()) disable();
|
||||
edit_key.addTextChangedListener(new DefaultTextWatcher() {
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
api_key.set(s.toString());
|
||||
if (!canBeEnabled()) disable();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,7 +128,7 @@ public interface AndroidSettings {
|
||||
/**
|
||||
* The theme setting
|
||||
*/
|
||||
enum Theme implements TranslatableEnum {
|
||||
enum Theme implements Enums.IdEnum, Enums.StringEnum {
|
||||
DEFAULT(0, R.string.deviceDefault),
|
||||
DARK(1, R.string.spin_darkTheme),
|
||||
LIGHT(2, R.string.spin_lightTheme),
|
||||
|
@ -0,0 +1,21 @@
|
||||
package com.trianguloy.urlchecker.utilities;
|
||||
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
|
||||
/***
|
||||
* TextWatcher empty implementation, so you can override only what you need
|
||||
*/
|
||||
public class DefaultTextWatcher implements TextWatcher {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package com.trianguloy.urlchecker.utilities;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public interface Enums {
|
||||
|
||||
interface StringEnum {
|
||||
/**
|
||||
* This must return the string resourced associated with this enum value
|
||||
*/
|
||||
int getStringResource();
|
||||
}
|
||||
|
||||
interface IdEnum {
|
||||
/**
|
||||
* The id of the saved preference. Must never change
|
||||
*/
|
||||
int getId();
|
||||
}
|
||||
|
||||
interface ImageEnum {
|
||||
int getImageResource();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an enum from an id
|
||||
*/
|
||||
static <TE extends IdEnum> TE toEnum(Class<TE> te, int id) {
|
||||
TE[] enumConstants = te.getEnumConstants();
|
||||
for (TE constant : enumConstants) {
|
||||
if (constant.getId() == id) {
|
||||
return constant;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a map of id and enum
|
||||
*/
|
||||
static <TE extends IdEnum> Map<Integer, TE> toEnumMap(Class<TE> te) {
|
||||
Map<Integer, TE> res = new HashMap<>();
|
||||
TE[] enumConstants = te.getEnumConstants();
|
||||
for (TE constant : enumConstants) {
|
||||
res.put(constant.getId(), constant);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ package com.trianguloy.urlchecker.utilities;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
@ -198,15 +197,7 @@ public abstract class GenericPref<T> {
|
||||
*/
|
||||
public void attachToEditText(EditText editText, JavaUtils.UnaryOperator<String> loadMod, JavaUtils.UnaryOperator<String> storeMod) {
|
||||
editText.setText(loadMod.apply(get()));
|
||||
editText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
|
||||
editText.addTextChangedListener(new DefaultTextWatcher() {
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
set(storeMod.apply(s.toString()));
|
||||
@ -257,7 +248,7 @@ public abstract class GenericPref<T> {
|
||||
/**
|
||||
* A list of options (enumeration) preference
|
||||
*/
|
||||
static public class Enumeration<T extends Enum<T> & TranslatableEnum> extends GenericPref<T> {
|
||||
static public class Enumeration<T extends Enum<T> & Enums.IdEnum & Enums.StringEnum> extends GenericPref<T> {
|
||||
private final Class<T> type;
|
||||
|
||||
public Enumeration(String prefName, T defaultValue, Class<T> type, Context cntx) {
|
||||
|
@ -45,6 +45,33 @@ public interface JavaUtils {
|
||||
: clamp(max, value, min);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a filter to both strings to check if all words of keywords are in body.
|
||||
* The order does not matter.
|
||||
*/
|
||||
static boolean containsWords(String body, String keywords){
|
||||
JavaUtils.Function<String, String> filter = s -> s.toUpperCase().replaceAll("[\\s-_]+", " ");
|
||||
// Match all words
|
||||
String[] words = filter.apply(keywords).split(" ");
|
||||
body = filter.apply(body);
|
||||
boolean match = true;
|
||||
for (String str : words) {
|
||||
if (!body.contains(str)){
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object, or default if null
|
||||
* java.util.Optional requires api 24
|
||||
*/
|
||||
static <T> T valueOrDefault(T value, T defaultValue){
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* java.util.function.Consumer requires api 24
|
||||
*/
|
||||
|
@ -1,17 +0,0 @@
|
||||
package com.trianguloy.urlchecker.utilities;
|
||||
|
||||
/**
|
||||
* On java, an enum can't be extended :(
|
||||
* This is the best I can do...unless I discover how to create annotations!
|
||||
*/
|
||||
public interface TranslatableEnum {
|
||||
/**
|
||||
* The id of the saved preference. Must never change
|
||||
*/
|
||||
int getId();
|
||||
|
||||
/**
|
||||
* This must return the string resourced associated with this enum value
|
||||
*/
|
||||
int getStringResource();
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package com.trianguloy.urlchecker.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import com.trianguloy.urlchecker.utilities.Enums;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CycleImageButton<T extends Enums.ImageEnum> extends ImageButton {
|
||||
|
||||
private List<T> states;
|
||||
private int currentState;
|
||||
|
||||
public CycleImageButton(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public CycleImageButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public CycleImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public void setStates(List<T> states) {
|
||||
this.states = states;
|
||||
updateImageResource(0);
|
||||
}
|
||||
|
||||
public void setCurrentState(T currentState) {
|
||||
updateImageResource(states.indexOf(currentState));
|
||||
}
|
||||
|
||||
public T getCurrentState() {
|
||||
return states == null || states.isEmpty() ? null : states.get(currentState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performClick() {
|
||||
updateImageResource(currentState + 1);
|
||||
return super.performClick();
|
||||
}
|
||||
|
||||
private void updateImageResource(int newState) {
|
||||
if (states == null || states.isEmpty()) {
|
||||
setImageDrawable(null);
|
||||
} else {
|
||||
currentState = newState >= 0 ? newState % states.size() : 0;
|
||||
setImageResource(states.get(currentState).getImageResource());
|
||||
}
|
||||
}
|
||||
}
|
13
app/src/main/res/drawable/flag_auto.xml
Normal file
13
app/src/main/res/drawable/flag_auto.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<!-- <path-->
|
||||
<!-- android:fillColor="@color/app"-->
|
||||
<!-- android:pathData="M5 4 5 6 7 6 18 15 18 17 20 17 20 6 14 6 13 4M5 7 5 9 7 9 7 7M5 13 5 15 7 15 7 13M5 16 5 18 7 18 7 16M5 19 5 21 7 21 7 19M8 13 8 15 10 15 10 13M11 13 11 15 12 15 13 17 14 17 14 15 13 13M15 15 15 17 17 17 17 15M5 10 5 12 7 12 7 10" />-->
|
||||
<path
|
||||
android:fillColor="@color/app"
|
||||
android:pathData="M5 4 5 15 7 15 7 13 18 8 20 8 20 6 14 6 13 4M5 16 5 18 7 18 7 16M5 19 5 21 7 21 7 19M8 13 8 15 10 15 10 13M11 13 11 15 12 15 13 17 14 17 14 15 13 13M15 15 15 17 17 17 17 15M18 15 18 17 20 17 20 15M18 12 18 14 20 14 20 12M18 9 18 11 20 11 20 9" />
|
||||
</vector>
|
10
app/src/main/res/drawable/flag_off.xml
Normal file
10
app/src/main/res/drawable/flag_off.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/app"
|
||||
android:pathData="M5 4 5 6 7 6 7 4M5 7 5 9 7 9 7 7M5 13 5 15 7 15 7 13M5 16 5 18 7 18 7 16M5 19 5 21 7 21 7 19M8 13 8 15 10 15 10 13M11 13 11 15 12 15 13 17 14 17 14 15 13 13M15 15 15 17 17 17 17 15M18 15 18 17 20 17 20 15M18 12 18 14 20 14 20 12M18 6 18 8 20 8 20 6M15 6 15 8 17 8 17 6M11 4 11 6 12 6 13 8 14 8 14 6 13 4M8 4 8 6 10 6 10 4M5 10 5 12 7 12 7 10M18 9 18 11 20 11 20 9" />
|
||||
</vector>
|
10
app/src/main/res/drawable/flag_on.xml
Normal file
10
app/src/main/res/drawable/flag_on.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/app"
|
||||
android:pathData="M14 6 13 4H5v17h2v-7h5l1 2h7V6z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/hide.xml
Normal file
10
app/src/main/res/drawable/hide.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/app"
|
||||
android:pathData="M12 17C6 17 1 8 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c0-4-5 5-11 5z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/show.xml
Normal file
10
app/src/main/res/drawable/show.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@color/app"
|
||||
android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z" />
|
||||
</vector>
|
@ -10,11 +10,11 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/mFlag_desc" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/flags"
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:hint="0xFFFFFFFF"
|
||||
android:inputType="textPersonName" />
|
||||
android:text="@string/mFlag_editFlags" />
|
||||
|
||||
</LinearLayout>
|
@ -1,76 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/flagHexLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/edit"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:text="@string/edit" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/flagsHexText"
|
||||
style="@android:style/Widget.TextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:enabled="false"
|
||||
android:inputType="textPersonName"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/addFlagLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/add"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:text="@string/add" />
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/flagText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ems="10"
|
||||
android:hint="@string/mFlag_flagHint" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/more"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:src="@drawable/arrow_down"
|
||||
android:tint="?attr/colorAccent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/box"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
44
app/src/main/res/layout/dialog_flags.xml
Normal file
44
app/src/main/res/layout/dialog_flags.xml
Normal file
@ -0,0 +1,44 @@
|
||||
<?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:layout_gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/shownFlags"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right|end|center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/search"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="@android:string/search_go" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/overflowButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/smallPadding"
|
||||
android:src="@drawable/arrow_down"
|
||||
android:tint="?attr/colorAccent" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/hiddenFlags"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
|
||||
</LinearLayout>
|
36
app/src/main/res/layout/dialog_flags_entry.xml
Normal file
36
app/src/main/res/layout/dialog_flags_entry.xml
Normal file
@ -0,0 +1,36 @@
|
||||
<?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">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/state"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="@dimen/square_button"
|
||||
android:layout_height="@dimen/square_button"
|
||||
android:layout_weight="0"
|
||||
android:src="@drawable/flag_auto" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/defaultIndicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="3dp"
|
||||
android:layout_weight="0" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/preferenceIndicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="3dp"
|
||||
android:layout_weight="0" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical" />
|
||||
|
||||
</LinearLayout>
|
25
app/src/main/res/layout/flags_editor.xml
Normal file
25
app/src/main/res/layout/flags_editor.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/search"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:hint="@android:string/search_go"
|
||||
android:inputType="textPersonName" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/box"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"></LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
33
app/src/main/res/layout/flags_editor_entry.xml
Normal file
33
app/src/main/res/layout/flags_editor_entry.xml
Normal file
@ -0,0 +1,33 @@
|
||||
<?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">
|
||||
|
||||
<view
|
||||
android:id="@+id/state"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
class="com.trianguloy.urlchecker.views.CycleImageButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="0dp"
|
||||
android:minHeight="0dp"
|
||||
android:src="@drawable/flag_auto" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="left|center_vertical" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/show"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="0dp"
|
||||
android:minHeight="0dp"
|
||||
android:src="@drawable/show" />
|
||||
|
||||
</LinearLayout>
|
@ -2,4 +2,5 @@
|
||||
<resources>
|
||||
<dimen name="padding">25dp</dimen>
|
||||
<dimen name="smallPadding">10dp</dimen>
|
||||
<dimen name="square_button">35dp</dimen>
|
||||
</resources>
|
@ -100,6 +100,9 @@ Hope you find the app useful! And don't hesitate to suggest features, report bug
|
||||
<string name="remove">Remove</string>
|
||||
<string name="edit">Edit</string>
|
||||
<string name="add">Add</string>
|
||||
<string name="on">Enabled</string>
|
||||
<string name="off">Disabled</string>
|
||||
|
||||
<!--
|
||||
json
|
||||
-->
|
||||
@ -304,8 +307,10 @@ Their api is rate limited to 10 requests per hour for new checks. The module res
|
||||
<string name="mFlags_name">Flags editor</string>
|
||||
<string name="mFlags_invalid">Invalid flag</string>
|
||||
<string name="mFlag_desc">"[Beta feature] This is an advanced module, it allows you to edit the intent flags when opening another app.
|
||||
In the field below you can put flags that will overwrite the default ones, those are taken from the intent that was used to open this app.
|
||||
While using the module you can hold the edit button to set the flags to the default flags. You can add flags by writing their name in the flags field"</string>
|
||||
\nThis module will show a list of flags, you can change if the flag is set or not by taping the icon. There is also an overflow menu to hide flags in there, it is just visual, flags will still use the assigned state.
|
||||
\nThe flags will have two color indicators next to the checkbox. The left one shows the value received from the intent, the right one indicates the state set by the module itself. Red means the flag is not set and green means the flag is set. Additionally the preference indicator can be grey, this means that the state was set to auto.
|
||||
\nWith the button below you can edit the default state and the visibility of the flags."</string>
|
||||
<string name="mFlag_flagHint">Flag name</string>
|
||||
<string name="mFlag_editFlags">Edit default flag values</string>
|
||||
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user