mirror of
https://github.com/TrianguloY/UrlChecker.git
synced 2024-09-19 20:02:16 +02:00
Flags module update
This commit is contained in:
parent
027351bdf5
commit
020051846d
@ -0,0 +1,206 @@
|
||||
package com.trianguloy.urlchecker.modules.companions;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
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(
|
||||
"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 static final Map<String, Integer> compatibleFlags = new TreeMap<>(); // TreeMap to have the entries sorted by key
|
||||
|
||||
static {
|
||||
try {
|
||||
// Only get flags that are present in the current Android version
|
||||
for (var field : Intent.class.getFields()) {
|
||||
if (ALL_FLAGS.contains(field.getName())) {
|
||||
compatibleFlags.put(field.getName(), (Integer) field.get(null));
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
@ -1,14 +1,19 @@
|
||||
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.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
@ -18,15 +23,25 @@ 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.GenericPref;
|
||||
import com.trianguloy.urlchecker.utilities.Inflater;
|
||||
import com.trianguloy.urlchecker.utilities.InternalFile;
|
||||
import com.trianguloy.urlchecker.utilities.JavaUtils;
|
||||
import com.trianguloy.urlchecker.utilities.TranslatableEnum;
|
||||
|
||||
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
|
||||
@ -67,196 +82,240 @@ class FlagsDialog extends AModuleDialog {
|
||||
|
||||
public static final String DATA_FLAGS = "flagsEditor.flags";
|
||||
|
||||
// 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 final Flags defaultFlags;
|
||||
private final Flags currentFlags;
|
||||
|
||||
private final GenericPref.Str defaultFlagsPref;
|
||||
private Map<String, FlagsConfig.FlagState> flagsStatePref;
|
||||
|
||||
private EditText flagsHexText;
|
||||
private AutoCompleteTextView flagNameText;
|
||||
private ImageButton more;
|
||||
private LinearLayout box;
|
||||
private ViewGroup shownFlagsVG;
|
||||
private ViewGroup hiddenFlagsAndSearchVG;
|
||||
|
||||
private ViewGroup hiddenFlagsVG;
|
||||
|
||||
private ImageView overflowButton;
|
||||
|
||||
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_flagseditor;
|
||||
}
|
||||
|
||||
@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);
|
||||
hiddenFlagsAndSearchVG = views.findViewById(R.id.hiddenFlagsAndSearch);
|
||||
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);
|
||||
overflowButton.setOnClickListener(v -> {
|
||||
hiddenFlagsAndSearchVG.setVisibility(
|
||||
hiddenFlagsAndSearchVG.getVisibility() == View.GONE ? View.VISIBLE : View.GONE);
|
||||
updateMoreIndicator();
|
||||
});
|
||||
|
||||
// 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();
|
||||
// Hide hidden flags
|
||||
hiddenFlagsAndSearchVG.setVisibility(View.GONE);
|
||||
|
||||
// SEARCH
|
||||
// Set up search text
|
||||
((EditText) views.findViewById(R.id.search)).addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
// 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
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
|
||||
// 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);
|
||||
@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);
|
||||
}
|
||||
}
|
||||
flagsHexText.setEnabled(!flagsHexText.isEnabled());
|
||||
// Update views
|
||||
updateLayout();
|
||||
});
|
||||
// long press to reset
|
||||
edit.setOnLongClickListener(v -> {
|
||||
// Resets the flags
|
||||
setFlags(null);
|
||||
// Update views
|
||||
updateLayout();
|
||||
return true;
|
||||
});
|
||||
|
||||
// TODO spinner with groups
|
||||
loadGroup("default");
|
||||
}
|
||||
|
||||
@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();
|
||||
});
|
||||
|
||||
var text = button_text.<TextView>findViewById(R.id.text);
|
||||
text.setText(flag);
|
||||
private void initGroups(){
|
||||
String fileString = new InternalFile(FlagsConfig.CONF_FILE, getActivity()).get();
|
||||
groups = null;
|
||||
if (fileString != null){
|
||||
try {
|
||||
groups = new JSONObject(fileString).getJSONObject("groups");
|
||||
} catch (JSONException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------- utils -------------------
|
||||
// To get all the groups names
|
||||
private List<String> getGroups(){
|
||||
List<String> res = new ArrayList<>();
|
||||
// Always add "default" first, even if it doesn't exist
|
||||
res.add("default");
|
||||
for (Iterator<String> it = groups.keys(); it.hasNext(); ) {
|
||||
String group = it.next();
|
||||
if (!group.equals("default")){
|
||||
res.add(group);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void loadGroup(String group){
|
||||
currentFlags.setFlags(0);
|
||||
|
||||
// Load json
|
||||
JSONObject groupPref = null;
|
||||
try {
|
||||
groupPref = groups.getJSONObject(group);
|
||||
} catch (JSONException ignore) {
|
||||
}
|
||||
|
||||
|
||||
// STATE
|
||||
// Get state preference of flag from json and then store it in a map
|
||||
flagsStatePref = new HashMap<>();
|
||||
if (groupPref != null) {
|
||||
try {
|
||||
Map<Integer, FlagsConfig.FlagState> flagsStateMap = TranslatableEnum.toEnumMap(FlagsConfig.FlagState.class);
|
||||
for (Iterator<String> it = groupPref.keys(); it.hasNext(); ) {
|
||||
String flag = it.next();
|
||||
flagsStatePref.put(flag, flagsStateMap.get(groupPref.getJSONObject(flag).getInt("state")));
|
||||
}
|
||||
} catch (JSONException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// SHOW
|
||||
// Put shown flags
|
||||
Set<String> shownFlagsSet = new TreeSet<>();
|
||||
if (groupPref != null) {
|
||||
try {
|
||||
for (Iterator<String> it = groupPref.keys(); it.hasNext(); ) {
|
||||
String flag = it.next();
|
||||
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
|
||||
setGlobalFlags(currentFlags);
|
||||
|
||||
updateMoreIndicator();
|
||||
}
|
||||
|
||||
void updateMoreIndicator(){
|
||||
overflowButton.setImageResource(hiddenFlagsVG.getChildCount() == 0 ? 0
|
||||
: hiddenFlagsAndSearchVG.getVisibility() == View.VISIBLE ? R.drawable.arrow_down
|
||||
: R.drawable.arrow_right);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an int as flags
|
||||
* 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
|
||||
*/
|
||||
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());
|
||||
private void fillWithFlags(Set<String> flags, ViewGroup vg){
|
||||
vg.removeAllViews();
|
||||
|
||||
// Checkbox listener
|
||||
CompoundButton.OnCheckedChangeListener l = (v, isChecked) -> {
|
||||
// Store flag
|
||||
String flag = (String) v.getTag(R.id.text);
|
||||
currentFlags.setFlag(flag, isChecked);
|
||||
// Update global
|
||||
setGlobalFlags(currentFlags);
|
||||
|
||||
// To update debug module view of GlobalData
|
||||
setUrl(new UrlData(getUrl()).dontTriggerOwn().asMinorUpdate());
|
||||
};
|
||||
|
||||
for (String flag : flags) {
|
||||
var checkbox_text = Inflater.inflate(R.layout.checkbox_text, vg, getActivity());
|
||||
|
||||
// Checkbox
|
||||
CheckBox checkBox = checkbox_text.findViewById(R.id.checkbox);
|
||||
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);
|
||||
}
|
||||
checkBox.setChecked(bool);
|
||||
currentFlags.setFlag(flag ,bool);
|
||||
|
||||
checkBox.setTag(R.id.text, flag);
|
||||
checkBox.setOnCheckedChangeListener(l);
|
||||
|
||||
// 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);
|
||||
}
|
||||
return foundFlags;
|
||||
|
||||
}
|
||||
|
||||
void setColors(String flag, View defaultIndicator, View preferenceIndicator){
|
||||
AndroidUtils.setRoundedColor(defaultFlags.isSet(flag) ? R.color.good : R.color.bad , defaultIndicator);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
AndroidUtils.setRoundedColor(color, preferenceIndicator);
|
||||
}
|
||||
|
||||
// ------------------- store/load flags -------------------
|
||||
@ -289,7 +348,7 @@ class FlagsDialog extends AModuleDialog {
|
||||
* 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) {
|
||||
public static Integer getGlobalFlagsNullable(AModuleDialog instance) {
|
||||
return toInteger(instance.getData(DATA_FLAGS));
|
||||
}
|
||||
|
||||
@ -297,34 +356,34 @@ class FlagsDialog extends AModuleDialog {
|
||||
* 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());
|
||||
private int getGlobalFlagsNonNull() {
|
||||
return getGlobalFlagsOrDefault(this, getActivity().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 getFlagsOrDefault(AModuleDialog instance, int defaultFlags) {
|
||||
var flags = toInteger(instance.getData(DATA_FLAGS));
|
||||
return flags == null ? defaultFlags : flags;
|
||||
public static int getGlobalFlagsOrDefault(AModuleDialog instance, int defaultFlags) {
|
||||
return valueOrDefault(toInteger(instance.getData(DATA_FLAGS)), defaultFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the flags in GlobalData
|
||||
*/
|
||||
private void setFlags(Integer flags) {
|
||||
putData(DATA_FLAGS, flags == null ? null : toHexString(flags));
|
||||
private void setGlobalFlags(Flags flags) {
|
||||
putData(DATA_FLAGS, flags == null ? null : toHexString(flags.getFlagsAsInt()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class FlagsConfig extends AModuleConfig {
|
||||
private final GenericPref.Str defaultFlagsPref;
|
||||
|
||||
protected static final String CONF_FILE = "flags_editor_settings";
|
||||
private Map<Integer, Integer> stateToIndex;
|
||||
|
||||
public FlagsConfig(ModulesActivity activity) {
|
||||
super(activity);
|
||||
defaultFlagsPref = FlagsModule.DEFAULTFLAGS_PREF(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -334,10 +393,200 @@ class FlagsConfig extends AModuleConfig {
|
||||
|
||||
@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.flagseditor_groupeditor, null);
|
||||
ViewGroup box = flagsDialogLayout.findViewById(R.id.box);
|
||||
InternalFile file = new InternalFile(CONF_FILE, flagsDialogLayout.getContext());
|
||||
|
||||
// Get all flags
|
||||
fillBoxViewGroup(box, file, "default");
|
||||
|
||||
AlertDialog alertDialog = new AlertDialog.Builder(getActivity())
|
||||
.setView(flagsDialogLayout)
|
||||
.setPositiveButton(views.getContext().getText(R.string.save), null)
|
||||
.setNegativeButton(views.getContext().getText(android.R.string.cancel), null)
|
||||
.setNeutralButton(views.getContext().getText(R.string.reset), null)
|
||||
.show();
|
||||
|
||||
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(listener -> {
|
||||
// Save the settings
|
||||
storePreferences(box, file, "default");
|
||||
});
|
||||
|
||||
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 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 text) {
|
||||
for (int i = 0; i < box.getChildCount(); i++) {
|
||||
var text_spinner_checkbox = box.getChildAt(i);
|
||||
String flag = ((TextView) text_spinner_checkbox.findViewById(R.id.text)).getText().toString();
|
||||
String search = text.toString();
|
||||
// Set visibility based on search text
|
||||
text_spinner_checkbox.setVisibility(
|
||||
JavaUtils.containsWords(flag, search) ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// TODO add dialog button to set all to on/off/auto
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// FIXME spinner gfx bug
|
||||
private void fillBoxViewGroup(ViewGroup vg, InternalFile file, String group){
|
||||
// Set spinner items
|
||||
FlagState[] spinnerItems = FlagState.class.getEnumConstants();
|
||||
List<String> spinnerItemsList = new ArrayList<>(spinnerItems.length);
|
||||
stateToIndex = new HashMap<>();
|
||||
for (int i = 0; i < spinnerItems.length; i++) {
|
||||
spinnerItemsList.add(vg.getContext().getString(spinnerItems[i].getStringResource()));
|
||||
// Map state to index
|
||||
stateToIndex.put(spinnerItems[i].getId(), i);
|
||||
}
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(
|
||||
vg.getContext(),
|
||||
android.R.layout.simple_spinner_item,
|
||||
spinnerItemsList
|
||||
);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
// Store order info in vg
|
||||
vg.setTag(spinnerItems);
|
||||
|
||||
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 text_spinner_checkbox = Inflater.inflate(R.layout.text_spinner_checkbox, vg, getActivity());
|
||||
TextView textView = text_spinner_checkbox.findViewById(R.id.text);
|
||||
textView.setText(flag);
|
||||
|
||||
Spinner spinner = text_spinner_checkbox.findViewById(R.id.spinner);
|
||||
spinner.setAdapter(adapter);
|
||||
spinner.setTag(spinnerItems);
|
||||
|
||||
// Load preferences from settings
|
||||
if (oldPref != null) {
|
||||
JSONObject flagPref;
|
||||
try {
|
||||
flagPref = oldPref.getJSONObject(flag);
|
||||
|
||||
// select current option
|
||||
spinner.setSelection(valueOrDefault(stateToIndex.get(flagPref.getInt("state")),
|
||||
FlagState.AUTO.getId()));
|
||||
|
||||
((CheckBox) text_spinner_checkbox.findViewById(R.id.checkbox)).setChecked(flagPref.getBoolean("show"));
|
||||
} catch (JSONException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
// Retrieve order of spinner
|
||||
FlagState[] spinnerItems = (FlagState[]) vg.getTag();
|
||||
|
||||
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 = spinnerItems[((Spinner) v.findViewById(R.id.spinner)).getSelectedItemPosition()];
|
||||
boolean show = ((CheckBox) v.findViewById(R.id.checkbox)).isChecked();
|
||||
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){
|
||||
// Retrieve order of spinner
|
||||
FlagState[] spinnerItems = (FlagState[]) vg.getTag();
|
||||
|
||||
// Index of default
|
||||
int def;
|
||||
for (def = 0; def < spinnerItems.length; def++){
|
||||
if (spinnerItems[def] == FlagState.AUTO){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set everything to default values
|
||||
for (int i = 0; i < vg.getChildCount(); i++) {
|
||||
View v = vg.getChildAt(i);
|
||||
((Spinner) v.findViewById(R.id.spinner)).setSelection(def);
|
||||
((CheckBox) v.findViewById(R.id.checkbox)).setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
public enum FlagState implements TranslatableEnum {
|
||||
AUTO(0, R.string.auto),
|
||||
ON(1, R.string.on),
|
||||
OFF(2, R.string.off),
|
||||
;
|
||||
|
||||
// -----
|
||||
|
||||
private final int id;
|
||||
private final int stringResource;
|
||||
|
||||
FlagState(int id, int stringResource) {
|
||||
this.id = id;
|
||||
this.stringResource = stringResource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStringResource() {
|
||||
return stringResource;
|
||||
}
|
||||
}
|
||||
}
|
@ -251,7 +251,7 @@ class OpenDialog extends AModuleDialog {
|
||||
}
|
||||
|
||||
// Get flags from global data (probably set by flags module, if active)
|
||||
Integer flags = FlagsDialog.getFlagsNullable(this);
|
||||
Integer flags = FlagsDialog.getGlobalFlagsNullable(this);
|
||||
if (flags != null) {
|
||||
intent.setFlags(flags);
|
||||
}
|
||||
|
@ -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,5 +1,8 @@
|
||||
package com.trianguloy.urlchecker.utilities;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* On java, an enum can't be extended :(
|
||||
* This is the best I can do...unless I discover how to create annotations!
|
||||
@ -14,4 +17,29 @@ public interface TranslatableEnum {
|
||||
* This must return the string resourced associated with this enum value
|
||||
*/
|
||||
int getStringResource();
|
||||
|
||||
/**
|
||||
* Get an enum from an id
|
||||
*/
|
||||
static <TE extends TranslatableEnum> 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 TranslatableEnum> 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;
|
||||
}
|
||||
}
|
||||
|
34
app/src/main/res/layout/checkbox_text.xml
Normal file
34
app/src/main/res/layout/checkbox_text.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<?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">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0" />
|
||||
|
||||
<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>
|
@ -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>
|
52
app/src/main/res/layout/dialog_flagseditor.xml
Normal file
52
app/src/main/res/layout/dialog_flagseditor.xml
Normal file
@ -0,0 +1,52 @@
|
||||
<?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="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|center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/overflowButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/smallPadding"
|
||||
android:layout_weight="0"
|
||||
android:src="@drawable/arrow_down"
|
||||
android:tint="?attr/colorAccent" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/hiddenFlagsAndSearch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/search"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@android:string/search_go" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/hiddenFlags"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
25
app/src/main/res/layout/flagseditor_groupeditor.xml
Normal file
25
app/src/main/res/layout/flagseditor_groupeditor.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>
|
27
app/src/main/res/layout/text_spinner_checkbox.xml
Normal file
27
app/src/main/res/layout/text_spinner_checkbox.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?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">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="left|center_vertical" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:gravity="right|center_vertical" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0"
|
||||
android:gravity="center" />
|
||||
</LinearLayout>
|
@ -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
|
||||
-->
|
||||
@ -303,8 +306,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 checkbox. There is also an overflow menu to hide flags in there, to avoid cluttering.
|
||||
\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 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