Show more items in attribute value completion

Auto-fill already set value in input fields in value editor
This commit is contained in:
Akash Yadav
2022-03-16 19:19:01 +05:30
parent 9d8124c2e8
commit d71e736ecf
17 changed files with 776 additions and 494 deletions
+1
View File
@@ -61,6 +61,7 @@
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_divider.xml" value="0.25" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_editor_bottom_sheet.xml" value="0.19572649572649573" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_editor_text_actions.xml" value="0.25" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_reference_attr_editor.xml" value="0.25" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_resource_input_field.xml" value="0.25" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_string_attr_editor.xml" value="0.25" />
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_symbol_item.xml" value="0.20512820512820512" />
Binary file not shown.
@@ -0,0 +1,106 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.fragments.attr;
import static com.itsaky.androidide.utils.Logger.instance;
import android.text.Editable;
import android.text.TextUtils;
import android.widget.AutoCompleteTextView;
import androidx.annotation.NonNull;
import com.blankj.utilcode.util.ThreadUtils;
import com.google.android.material.textfield.MaterialAutoCompleteTextView;
import com.itsaky.androidide.adapters.AttrValueCompletionAdapter;
import com.itsaky.androidide.utils.Logger;
import com.itsaky.androidide.utils.TextWatcherAdapter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
/**
* @author Akash Yadav
*/
public abstract class AbstractReferenceEditor extends BaseValueEditorFragment {
private static final Logger LOG = instance ("ReferenceEditor");
public TextWatcherAdapter resInputWatcher;
@Override
protected void notifyValueChanged (@NonNull String newValue) {
try {
super.notifyValueChanged (newValue);
} catch (Throwable e) {
LOG.error ("Unable to update resource value to '" + newValue + "'", e);
}
}
protected void setupReferenceInput (MaterialAutoCompleteTextView referenceInput) {
Objects.requireNonNull (referenceInput);
referenceInput.setText (attribute.getValue ());
if (resInputWatcher != null) {
referenceInput.removeTextChangedListener (resInputWatcher);
}
resInputWatcher =
new TextWatcherAdapter () {
@Override
public void afterTextChanged (@NonNull Editable s) {
final var text = s.toString ().trim ();
if (TextUtils.isEmpty (text)) {
return;
}
notifyValueChanged (text);
}
};
referenceInput.addTextChangedListener (resInputWatcher);
CompletableFuture.supplyAsync (this::computeReferenceItems)
.whenComplete (
(list, error) -> handleAutoCompleteResult (list, error, referenceInput));
}
protected void handleAutoCompleteResult (
List<String> list, Throwable error, AutoCompleteTextView referenceInput) {
if (list == null || list.isEmpty ()) {
LOG.error ("No completion items found");
LOG.error ("Error was:", error);
return;
}
LOG.debug ("Found", list.size (), "resource items for completion");
ThreadUtils.runOnUiThread (
() -> {
final var adapter = new AttrValueCompletionAdapter (requireContext (), list);
referenceInput.setThreshold (1);
referenceInput.setAdapter (adapter);
adapter.notifyDataSetChanged ();
});
}
protected List<String> computeReferenceItems () {
return new ArrayList<> ();
}
}
@@ -35,51 +35,61 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/** @author Akash Yadav */
public class BooleanEditor extends ReferenceEditor {
/**
* @author Akash Yadav
*/
public class BooleanEditor extends AbstractReferenceEditor {
private LayoutBooleanAttrEditorBinding binding;
@Nullable
@Override
public View onCreateView(
public View onCreateView (
@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
this.binding = LayoutBooleanAttrEditorBinding.inflate(inflater, container, false);
return this.binding.getRoot();
this.binding = LayoutBooleanAttrEditorBinding.inflate (inflater, container, false);
return this.binding.getRoot ();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
this.binding.choiceGroup.setOnCheckedChangeListener(
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated (view, savedInstanceState);
this.binding.choiceGroup.setOnCheckedChangeListener (
(group, checkedId) -> {
final var checked = (RadioButton) group.findViewById(checkedId);
notifyValueChanged(checked.getText().toString().trim());
final var checked = (RadioButton) group.findViewById (checkedId);
notifyValueChanged (checked.getText ().toString ().trim ());
});
setupReferenceInput(
(MaterialAutoCompleteTextView) this.binding.booleanResInput.getEditText());
switch (attribute.getValue ()) {
case "true":
this.binding.boolTrue.setChecked (true);
break;
case "false":
this.binding.boolFalse.setChecked (true);
}
setupReferenceInput (
(MaterialAutoCompleteTextView) this.binding.booleanResInput.getEditText ());
}
@Override
protected List<String> computeReferenceItems() {
final var list = new ArrayList<String>();
final var tables = ValuesTableFactory.getAllTables();
for (var entry : tables.entrySet()) {
final var dimens = entry.getValue().getTable("bool");
protected List<String> computeReferenceItems () {
final var list = new ArrayList<String> ();
final var tables = ValuesTableFactory.getAllTables ();
for (var entry : tables.entrySet ()) {
final var dimens = entry.getValue ().getTable ("bool");
if (dimens != null) {
list.addAll(
dimens.keySet().stream().map("@bool/"::concat).collect(Collectors.toSet()));
list.addAll (
dimens.keySet ().stream ().map ("@bool/"::concat).collect (Collectors.toSet ()));
}
}
list.addAll(
FrameworkValues.listBools().stream()
.map("@android:bool/"::concat)
.collect(Collectors.toList()));
list.addAll (
FrameworkValues.listBools ().stream ()
.map ("@android:bool/"::concat)
.collect (Collectors.toList ()));
return list;
}
}
@@ -18,6 +18,7 @@
package com.itsaky.androidide.fragments.attr;
import android.app.Dialog;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -27,6 +28,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.textfield.MaterialAutoCompleteTextView;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.colorpicker.ColorPickerView;
import com.itsaky.androidide.databinding.LayoutColorAttrEditorBinding;
import com.itsaky.androidide.utils.DialogUtils;
@@ -40,7 +42,7 @@ import java.util.stream.Collectors;
/**
* @author Akash Yadav
*/
public class ColorEditor extends ReferenceEditor {
public class ColorEditor extends AbstractReferenceEditor {
private LayoutColorAttrEditorBinding binding;
private Dialog colorPickerDialog;
@@ -58,6 +60,13 @@ public class ColorEditor extends ReferenceEditor {
this.binding.pickColor.setOnClickListener (v -> getColorPickerDialog ().show ());
setupReferenceInput ((MaterialAutoCompleteTextView) this.binding.colorResInput.getEditText ());
try {
final var col = Color.parseColor (attribute.getValue ());
this.binding.colorPreview.setCardBackgroundColor (col);
} catch (Throwable th) {
// ignored
}
}
@Override
@@ -78,6 +87,10 @@ public class ColorEditor extends ReferenceEditor {
FrameworkValues.listColors ().stream ()
.map ("@android:color/"::concat)
.collect (Collectors.toList ()));
final var resTable = StudioApp.getInstance ().getResourceTable ();
list.addAll (resTable.listResourceNames ("color"));
return list;
}
@@ -19,9 +19,11 @@ package com.itsaky.androidide.fragments.attr;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -35,88 +37,105 @@ import com.itsaky.inflater.values.FrameworkValues;
import com.itsaky.inflater.values.ValuesTableFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/** @author Akash Yadav */
public class DimensionEditor extends ReferenceEditor {
private static final Logger LOG = Logger.instance("DimensionEditor");
/**
* @author Akash Yadav
*/
public class DimensionEditor extends AbstractReferenceEditor {
private static final Logger LOG = Logger.instance ("DimensionEditor");
public TextWatcherAdapter dimensionInputWatcher;
private LayoutDimensionAttrEditorBinding binding;
private String[] dimensionUnits;
@Nullable
@Override
public View onCreateView(
public View onCreateView (
@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
this.binding = LayoutDimensionAttrEditorBinding.inflate(inflater, container, false);
return this.binding.getRoot();
this.binding = LayoutDimensionAttrEditorBinding.inflate (inflater, container, false);
return this.binding.getRoot ();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated (view, savedInstanceState);
final var dimensionInput =
Objects.requireNonNull(this.binding.dimensionInput.getEditText());
Objects.requireNonNull (this.binding.dimensionInput.getEditText ());
setDimensionValue (dimensionInput);
if (dimensionInputWatcher != null) {
dimensionInput.removeTextChangedListener(dimensionInputWatcher);
dimensionInput.removeTextChangedListener (dimensionInputWatcher);
}
dimensionInputWatcher =
new TextWatcherAdapter() {
new TextWatcherAdapter () {
@Override
public void afterTextChanged(@NonNull Editable s) {
final var text = s.toString().trim();
public void afterTextChanged (@NonNull Editable s) {
final var text = s.toString ().trim ();
final var unit =
getDimensionUnits()[binding.unitSelector.getSelectedItemPosition()];
getDimensionUnits ()[binding.unitSelector.getSelectedItemPosition ()];
// This will call CommonParseUtils#parseDimension
notifyValueChanged(text.concat(unit));
notifyValueChanged (text.concat (unit));
}
};
dimensionInput.addTextChangedListener(dimensionInputWatcher);
dimensionInput.addTextChangedListener (dimensionInputWatcher);
final var dimensionResInput =
(MaterialAutoCompleteTextView)
Objects.requireNonNull(binding.dimensionResInput.getEditText());
setupReferenceInput(dimensionResInput);
Objects.requireNonNull (binding.dimensionResInput.getEditText ());
setupReferenceInput (dimensionResInput);
}
@NonNull
private String[] getDimensionUnits() {
if (dimensionUnits == null) {
dimensionUnits =
requireContext().getResources().getStringArray(R.array.dimension_units);
}
return dimensionUnits;
}
@Override
protected List<String> computeReferenceItems() {
final var list = new ArrayList<String>();
final var tables = ValuesTableFactory.getAllTables();
for (var entry : tables.entrySet()) {
final var dimens = entry.getValue().getTable("dimen");
if (dimens != null) {
list.addAll(
dimens.keySet().stream()
.map("@dimen/"::concat)
.collect(Collectors.toSet()));
private void setDimensionValue (EditText dimensionInput) {
final var val = attribute.getValue ();
var dimension = val.substring (0, val.length () - 2);
if (TextUtils.isDigitsOnly (dimension)) {
dimensionInput.setText (dimension);
final var arr = requireContext ().getResources ().getStringArray (R.array.dimension_units);
final var index = Arrays.asList (arr).indexOf (val.substring (dimension.length ()));
if (index >= 0) {
this.binding.unitSelector.setSelection (index);
}
}
list.addAll(
FrameworkValues.listDimens().stream()
.map("@android:dimen/"::concat)
.collect(Collectors.toList()));
}
@NonNull
private String[] getDimensionUnits () {
if (dimensionUnits == null) {
dimensionUnits =
requireContext ().getResources ().getStringArray (R.array.dimension_units);
}
return dimensionUnits;
}
@Override
protected List<String> computeReferenceItems () {
final var list = new ArrayList<String> ();
final var tables = ValuesTableFactory.getAllTables ();
for (var entry : tables.entrySet ()) {
final var dimens = entry.getValue ().getTable ("dimen");
if (dimens != null) {
list.addAll (
dimens.keySet ().stream ()
.map ("@dimen/"::concat)
.collect (Collectors.toSet ()));
}
}
list.addAll (
FrameworkValues.listDimens ().stream ()
.map ("@android:dimen/"::concat)
.collect (Collectors.toList ()));
return list;
}
}
@@ -26,28 +26,39 @@ import com.google.android.material.chip.Chip;
import com.google.android.material.chip.ChipGroup;
import com.itsaky.androidide.utils.Logger;
/** @author Akash Yadav */
/**
* @author Akash Yadav
*/
public class EnumEditor extends FixedValueEditor {
private static final Logger LOG = Logger.instance("EnumEditor");
private static final Logger LOG = Logger.instance ("EnumEditor");
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
this.chipGroup.setSingleSelection(true);
this.chipGroup.setSelectionRequired(true);
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated (view, savedInstanceState);
this.chipGroup.setSingleSelection (true);
this.chipGroup.setSelectionRequired (true);
final var val = this.attribute.getValue ();
for (int i = 0; i < this.chipGroup.getChildCount (); i++) {
final var chip = (Chip) this.chipGroup.getChildAt (i);
if (val.equals (chip.getText ().toString ().trim ())) {
chip.setChecked (true);
break;
}
}
}
@Override
protected void onCheckChanged(@NonNull ChipGroup group, int checkedId) {
final var chip = (Chip) group.findViewById(group.getCheckedChipId());
protected void onCheckChanged (@NonNull ChipGroup group, int checkedId) {
final var chip = (Chip) group.findViewById (group.getCheckedChipId ());
if (chip == null) {
LOG.error("Unable to update enum value. Checked Chip view is null.");
LOG.error ("Unable to update enum value. Checked Chip view is null.");
return;
}
final var val = chip.getText().toString().trim();
notifyValueChanged(val);
final var val = chip.getText ().toString ().trim ();
notifyValueChanged (val);
}
}
@@ -32,6 +32,7 @@ import com.google.android.material.chip.ChipGroup;
import com.itsaky.androidide.utils.Logger;
import java.util.ArrayList;
import java.util.Arrays;
/**
* @author Akash Yadav
@@ -47,6 +48,14 @@ public class FlagEditor extends FixedValueEditor {
this.chipGroup.setSingleSelection(false);
this.chipGroup.setSelectionRequired(true);
this.chipGroup.setOnCheckedChangeListener(null); // does not work for multiple selections
final var values = Arrays.asList (this.attribute.getValue ().split ("\\|"));
for (int i = 0; i < this.chipGroup.getChildCount (); i++) {
final var chip = (Chip) this.chipGroup.getChildAt (i);
if (values.contains (chip.getText ().toString ().trim ())) {
chip.setChecked (true);
}
}
}
@Override
@@ -14,91 +14,72 @@
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.fragments.attr;
import static com.itsaky.androidide.utils.Logger.instance;
import android.text.Editable;
import android.text.TextUtils;
import android.widget.AutoCompleteTextView;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.blankj.utilcode.util.ThreadUtils;
import com.google.android.material.textfield.MaterialAutoCompleteTextView;
import com.itsaky.androidide.adapters.AttrValueCompletionAdapter;
import com.itsaky.androidide.utils.Logger;
import com.itsaky.androidide.utils.TextWatcherAdapter;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.databinding.LayoutReferenceAttrEditorBinding;
import com.itsaky.inflater.values.FrameworkValues;
import com.itsaky.inflater.values.ValuesTableFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
/**
* @author Akash Yadav
*/
public class ReferenceEditor extends BaseValueEditorFragment {
public class ReferenceEditor extends AbstractReferenceEditor {
private static final Logger LOG = instance ("ReferenceEditor");
public TextWatcherAdapter resInputWatcher;
private LayoutReferenceAttrEditorBinding binding;
@Nullable
@Override
public View onCreateView (@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
this.binding = LayoutReferenceAttrEditorBinding.inflate (inflater, container, false);
return this.binding.getRoot ();
}
@Override
protected void notifyValueChanged (@NonNull String newValue) {
try {
super.notifyValueChanged (newValue);
} catch (Throwable e) {
LOG.error ("Unable to update resource value to '" + newValue + "'", e);
}
}
protected void setupReferenceInput (MaterialAutoCompleteTextView referenceInput) {
Objects.requireNonNull (referenceInput);
if (resInputWatcher != null) {
referenceInput.removeTextChangedListener (resInputWatcher);
}
resInputWatcher =
new TextWatcherAdapter () {
@Override
public void afterTextChanged (@NonNull Editable s) {
final var text = s.toString ().trim ();
if (TextUtils.isEmpty (text)) {
return;
}
notifyValueChanged (text);
}
};
referenceInput.addTextChangedListener (resInputWatcher);
CompletableFuture.supplyAsync (this::computeReferenceItems)
.whenComplete (
(list, error) -> handleAutoCompleteResult (list, error, referenceInput));
}
protected void handleAutoCompleteResult (
List<String> list, Throwable error, AutoCompleteTextView referenceInput) {
if (list == null || list.isEmpty ()) {
LOG.error ("No completion items found");
LOG.error ("Error was:", error);
return;
}
LOG.debug ("Found", list.size (), "resource items for completion");
ThreadUtils.runOnUiThread (
() -> {
final var adapter = new AttrValueCompletionAdapter (requireContext (), list);
referenceInput.setThreshold (1);
referenceInput.setAdapter (adapter);
adapter.notifyDataSetChanged ();
});
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated (view, savedInstanceState);
setupReferenceInput ((MaterialAutoCompleteTextView) this.binding.resInput.getEditText ());
}
@Override
protected List<String> computeReferenceItems () {
return new ArrayList<> ();
final var resTable = StudioApp.getInstance ().getResourceTable ();
final var list = new ArrayList<> (resTable.listResourceNames (null));
final var tables = ValuesTableFactory.getAllTables ();
for (var entry : tables.entrySet ()) {
final var resourceMap = entry.getValue ().getResourceMap ();
for (var resourceEntries : resourceMap.entrySet ()) {
final var name = resourceEntries.getKey ();
list.addAll (
resourceEntries
.getValue ()
.keySet ()
.stream ()
.map (("@android:" + name + "/")::concat)
.collect (Collectors.toList ()
)
);
}
}
list.addAll (FrameworkValues.listAllResources ());
return list;
}
}
@@ -39,7 +39,7 @@ import java.util.stream.Collectors;
/**
* @author Akash Yadav
*/
public class StringEditor extends ReferenceEditor {
public class StringEditor extends AbstractReferenceEditor {
private LayoutStringAttrEditorBinding binding;
@@ -54,13 +54,15 @@ public class StringEditor extends ReferenceEditor {
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated (view, savedInstanceState);
Objects.requireNonNull (this.binding.stringInput.getEditText ()).addTextChangedListener (new TextWatcherAdapter () {
final var stringInput = Objects.requireNonNull (this.binding.stringInput.getEditText ());
stringInput.addTextChangedListener (new TextWatcherAdapter () {
@Override
public void afterTextChanged (@NonNull Editable s) {
notifyValueChanged (s.toString ());
}
});
stringInput.setText (this.attribute.getValue ());
setupReferenceInput ((MaterialAutoCompleteTextView) this.binding.stringResInput.getEditText ());
}
@@ -79,7 +81,7 @@ public class StringEditor extends ReferenceEditor {
}
list.addAll (
FrameworkValues.listDimens ().stream ()
FrameworkValues.listStrings ().stream ()
.map ("@android:string/"::concat)
.collect (Collectors.toList ()));
return list;
@@ -49,6 +49,7 @@ import com.itsaky.androidide.fragments.attr.FlagEditor;
import com.itsaky.androidide.fragments.attr.FloatEditor;
import com.itsaky.androidide.fragments.attr.FractionEditor;
import com.itsaky.androidide.fragments.attr.IntegerEditor;
import com.itsaky.androidide.fragments.attr.AbstractReferenceEditor;
import com.itsaky.androidide.fragments.attr.ReferenceEditor;
import com.itsaky.androidide.fragments.attr.StringEditor;
import com.itsaky.androidide.models.XMLAttribute;
@@ -18,6 +18,7 @@
package com.itsaky.androidide.project;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.itsaky.androidide.utils.Logger;
import com.itsaky.inflater.IResourceTable;
@@ -25,229 +26,287 @@ import com.itsaky.inflater.values.ValuesTableFactory;
import java.io.File;
import java.io.FileFilter;
import java.util.Collection;
import java.util.HashSet;
import java.util.Locale;
public class ProjectResourceTable implements IResourceTable {
private static final Logger LOG = Logger.instance ("ProjectResourceFinder");
private File resDir;
private File color;
private File layout;
private File[] drawables;
private File[] mipmaps;
private static final Logger LOG = Logger.instance("ProjectResourceFinder");
@Override
public File findDrawable(@NonNull String name) {
public File findDrawable (@NonNull String name) {
for (File drawable : drawables) {
final File file = findFileWithName(drawable.listFiles(), name);
final File file = findFileWithName (drawable.listFiles (), name);
if (file != null) {
return file;
}
}
return null;
}
@Override
public File findLayout(@NonNull String name) {
return findFileWithName(this.layout.listFiles(), name);
public File findLayout (@NonNull String name) {
return findFileWithName (this.layout.listFiles (), name);
}
@Override
public String findString(@NonNull String name) {
final var table = ValuesTableFactory.getTable(resDir);
public String findString (@NonNull String name) {
final var table = ValuesTableFactory.getTable (resDir);
if (table == null) {
return name;
}
final var resource = table.findString(name);
final var resource = table.findString (name);
if (resource == null) {
return name;
}
final var value = resource.getValue();
if (value.startsWith("@string/")) {
var ref = value.substring("@string/".length());
if (name.equals(ref)) {
final var value = resource.getValue ();
if (value.startsWith ("@string/")) {
var ref = value.substring ("@string/".length ());
if (name.equals (ref)) {
return name;
}
}
return value;
}
@Override
public String findBoolean(@NonNull String name) {
final var table = ValuesTableFactory.getTable(resDir);
public String findBoolean (@NonNull String name) {
final var table = ValuesTableFactory.getTable (resDir);
if (table == null) {
return name;
}
final var resource = table.findBoolean(name);
final var resource = table.findBoolean (name);
if (resource == null) {
return name;
}
final var val = resource.getValue();
if (val.startsWith("@bool/")) {
final var ref = val.substring("@bool/".length());
if (ref.equals(name)) {
final var val = resource.getValue ();
if (val.startsWith ("@bool/")) {
final var ref = val.substring ("@bool/".length ());
if (ref.equals (name)) {
// recursive reference
return name;
}
}
return val;
}
@Override
public String findInteger(@NonNull String name) {
final var table = ValuesTableFactory.getTable(resDir);
public String findInteger (@NonNull String name) {
final var table = ValuesTableFactory.getTable (resDir);
if (table == null) {
return name;
}
final var resource = table.findInteger(name);
final var resource = table.findInteger (name);
if (resource == null) {
return name;
}
final var val = resource.getValue();
if (val.startsWith("@integer/")) {
final var ref = val.substring("@integer/".length());
if (ref.equals(name)) {
final var val = resource.getValue ();
if (val.startsWith ("@integer/")) {
final var ref = val.substring ("@integer/".length ());
if (ref.equals (name)) {
// recursive reference
return name;
}
}
return val;
}
@Override
public String findColor(@NonNull String name) {
final var table = ValuesTableFactory.getTable(resDir);
public String findColor (@NonNull String name) {
final var table = ValuesTableFactory.getTable (resDir);
if (table == null) {
return name;
}
final var resource = table.findColor(name);
final var resource = table.findColor (name);
if (resource == null) {
return name;
}
final var value = resource.getValue();
if (value.startsWith("@color/")) {
var ref = value.substring("@color/".length());
if (name.equals(ref)) {
final var value = resource.getValue ();
if (value.startsWith ("@color/")) {
var ref = value.substring ("@color/".length ());
if (name.equals (ref)) {
return name;
}
}
return value;
}
@Override
public String[] findArray(@NonNull String name) {
final var table = ValuesTableFactory.getTable(resDir);
public String[] findArray (@NonNull String name) {
final var table = ValuesTableFactory.getTable (resDir);
if (table == null) {
return new String[] {name};
return new String[]{name};
}
final var resource = table.findArray(name);
final var resource = table.findArray (name);
if (resource == null) {
return new String[] {name};
return new String[]{name};
}
return resource.getValues();
return resource.getValues ();
}
@Override
public String findDimension(@NonNull String name) {
final var table = ValuesTableFactory.getTable(resDir);
public String findDimension (@NonNull String name) {
final var table = ValuesTableFactory.getTable (resDir);
if (table == null) {
return name;
}
final var resource = table.findDimension(name);
final var resource = table.findDimension (name);
if (resource == null) {
return name;
}
final var value = resource.getValue();
if (value.startsWith("@dimen/")) {
var ref = value.substring("@dimen/".length());
if (name.equals(ref)) {
final var value = resource.getValue ();
if (value.startsWith ("@dimen/")) {
var ref = value.substring ("@dimen/".length ());
if (name.equals (ref)) {
return name;
}
}
return value;
}
@NonNull
@Override
public void setInflatingFile(@NonNull File file) {
if (file.getParentFile() == null) {
throw new IllegalArgumentException("Invalid inflating file");
public Collection<String> listResourceNames (@Nullable String type) {
if (type == null) {
return listAllResourceNames ();
} else {
final var set = new HashSet<String> ();
switch (type) {
case "drawable":
for (var folder : this.drawables) {
listAndAddTo (set, folder, type);
}
break;
case "layout":
listAndAddTo (set, this.layout, type);
break;
case "color":
listAndAddTo (set, this.color, type);
break;
case "mipmap":
for (var folder : this.mipmaps) {
listAndAddTo (set, folder, type);
}
break;
}
return set;
}
setupDirectories(
file // layout file
.getParentFile() // layout dir
.getParentFile()); // res dir
}
private void setupDirectories(File resDir) {
if (resDir == null) {
LOG.error("Null resource directory passed to resource finder. Ignoring.");
private void listAndAddTo (HashSet<String> set, @NonNull File dir, String type) {
final var files = dir.listFiles ();
if (files == null) {
return;
}
for (var file : files) {
var name = file.getName ();
if (!name.contains (".")) {
continue;
}
name = name.substring (0, name.lastIndexOf ('.'));
name = String.format (Locale.ROOT, "@%s/%s", type, name);
set.add (name);
}
}
@NonNull
private Collection<String> listAllResourceNames () {
final var set = new HashSet<> (listResourceNames ("color"));
set.addAll (listResourceNames ("drawable"));
set.addAll (listResourceNames ("mipmap"));
set.addAll (listResourceNames ("layout"));
return set;
}
@Override
public void setInflatingFile (@NonNull File file) {
if (file.getParentFile () == null) {
throw new IllegalArgumentException ("Invalid inflating file");
}
setupDirectories (
file // layout file
.getParentFile () // layout dir
.getParentFile ()); // res dir
}
private void setupDirectories (File resDir) {
if (resDir == null) {
LOG.error ("Null resource directory passed to resource finder. Ignoring.");
return;
}
this.resDir = resDir;
this.color = new File(resDir, "color");
this.layout = new File(resDir, "layout");
final File[] drawables = resDir.listFiles(new NameStartsWith("drawable"));
this.color = new File (resDir, "color");
this.layout = new File (resDir, "layout");
final File[] drawables = resDir.listFiles (new NameStartsWith ("drawable"));
this.drawables = drawables == null ? new File[0] : drawables;
final File[] mipmaps = resDir.listFiles(new NameStartsWith("mipmap"));
final File[] mipmaps = resDir.listFiles (new NameStartsWith ("mipmap"));
this.mipmaps = mipmaps == null ? new File[0] : mipmaps;
}
private File findFileWithName(File[] files, String name) {
private File findFileWithName (File[] files, String name) {
if (files == null || name == null) {
return null;
}
for (File file : files) {
var simpleName = file.getName();
if (simpleName.startsWith(name)) {
var simpleName = file.getName ();
if (simpleName.startsWith (name)) {
return file;
}
}
return null;
}
private static class NameStartsWith implements FileFilter {
private final String name;
public NameStartsWith(String name) {
public NameStartsWith (String name) {
this.name = name;
}
@Override
public boolean accept(@NonNull File file) {
return file.getName().startsWith(name);
public boolean accept (@NonNull File file) {
return file.getName ().startsWith (name);
}
}
}
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ This file is part of AndroidIDE.
~
~ AndroidIDE is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ AndroidIDE is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
-->
<com.google.android.material.textfield.TextInputLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/res_input"
style="@style/AppTheme.TextInputLayout.Outlined"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:hint="@string/hint_resource_reference"
app:hintEnabled="true"
app:hintTextColor="@color/secondaryColor"
tools:layout_margin="16dp">
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:layout_width="match_parent"
android:layout_height="56dp"
android:digits="abcdefghijklmnopqrstwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_/@"
android:inputType="textNoSuggestions"
android:padding="16dp" />
</com.google.android.material.textfield.TextInputLayout>
+1
View File
@@ -342,4 +342,5 @@
<string name="title_color_picker">Color picker</string>
<string name="hint_string_value">String value</string>
<string name="hint_string_resource">String resource</string>
<string name="hint_resource_reference">Any resource reference</string>
</resources>
@@ -18,12 +18,16 @@
package com.itsaky.inflater;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.File;
import java.util.Collection;
/** Finds values in resources of a project */
/**
* Finds values in resources of a project
*/
public interface IResourceTable {
/**
* Find the path of the drawable with the provided name
*
@@ -32,69 +36,79 @@ public interface IResourceTable {
* @param name The name of the drawable
* @return The drawable resource.
*/
File findDrawable(@NonNull String name);
File findDrawable (@NonNull String name);
/**
* Find the layout file with the specified name.
*
* @param name The name of the layout file
* @return The found layout resource file
*/
File findLayout(@NonNull String name);
File findLayout (@NonNull String name);
/**
* Find a string resource. Must be a valid string or {@code null}
*
* @param name The name of the resource
* @return Value of the resource
*/
String findString(@NonNull String name);
String findString (@NonNull String name);
/**
* Find a color resource. The returned value must be a valid color value or {@code -1}
*
* @param name The name of the resource
* @return Value of the resource
*/
String findColor(@NonNull String name);
String findColor (@NonNull String name);
/**
* Find an array resource. Must be a valid array or {@code null}
*
* @param name The name of the resource
* @return Value of the resource
*/
String[] findArray(@NonNull String name);
String[] findArray (@NonNull String name);
/**
* Find the dimension value with the given name.
*
* @param name The name of the dimension resource
* @return The String representation of the dimension. It could one of the following format :
* 1dp, 12sp, 123px, etc.
* 1dp, 12sp, 123px, etc.
*/
String findDimension(@NonNull String name);
String findDimension (@NonNull String name);
/**
* Find the boolean value with the given name.
*
* @param name The name of the boolean resource.
* @return The string value of the boolean resource.
*/
String findBoolean(@NonNull String name);
String findBoolean (@NonNull String name);
/**
* Find the integer value with the given name.
*
* @param name The name of the integer resource.
* @return The string value of the integer resource.
*/
String findInteger(@NonNull String name);
String findInteger (@NonNull String name);
/**
* Set the file which is currently being inflated.
*
* @param file The file.
*/
void setInflatingFile(@NonNull File file);
void setInflatingFile (@NonNull File file);
/**
* Lists the resources with the given type.
*
* @param type The type of the resources to list. Might be <code>null</code>.
* @return A collection of resource names. If <code>type</code> is <code>null</code>,
* all the resources in the resource table must be listed.
*/
@NonNull
Collection<String> listResourceNames (@Nullable String type);
}
@@ -25,6 +25,7 @@ import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* Provides framework resource values.
@@ -32,42 +33,54 @@ import java.util.Objects;
* @author Akash Yadav
*/
public class FrameworkValues {
@NonNull
public static List<String> listDimens() {
return listFields(android.R.dimen.class);
public static List<String> listDimens () {
return listFields (android.R.dimen.class);
}
@NonNull
public static List<String> listBools() {
return listFields(android.R.bool.class);
public static List<String> listBools () {
return listFields (android.R.bool.class);
}
@NonNull
public static List<String> listStrings() {
return listFields(android.R.string.class);
public static List<String> listStrings () {
return listFields (android.R.string.class);
}
@NonNull
public static List<String> listColors() {
return listFields(android.R.color.class);
public static List<String> listColors () {
return listFields (android.R.color.class);
}
@NonNull
private static List<String> listFields(final Class<?> klass) {
Objects.requireNonNull(klass);
final var list = new ArrayList<String>();
final var fields = klass.getDeclaredFields();
private static List<String> listFields (final Class<?> klass) {
Objects.requireNonNull (klass);
final var list = new ArrayList<String> ();
final var fields = klass.getDeclaredFields ();
for (var field : fields) {
final var mods = field.getModifiers();
if (!isPublic(mods) || !isStatic(mods)) {
final var mods = field.getModifiers ();
if (!isPublic (mods) || !isStatic (mods)) {
continue;
}
list.add(field.getName());
list.add (field.getName ());
}
return list;
}
@NonNull
public static List<String> listAllResources () {
final var list = new ArrayList<String> ();
for (var klass : android.R.class.getDeclaredClasses ()) {
list.addAll (listFields (klass).stream ()
.map (("@android:" + klass.getSimpleName () + "/")::concat)
.collect (Collectors.toList ())
);
}
return list;
}
}
@@ -48,8 +48,8 @@ import java.util.Objects;
* @author Akash Yadav
*/
public class ValuesTable {
private static final Logger LOG = Logger.instance("ValuesTable");
private static final Logger LOG = Logger.instance ("ValuesTable");
// TODO Parse styles and styleables
private final Map<String, IResource> arrays;
private final Map<String, IResource> strings;
@@ -57,185 +57,185 @@ public class ValuesTable {
private final Map<String, IResource> integers;
private final Map<String, IResource> dimens;
private final Map<String, IResource> booleans;
private final Map<String, Map<String, IResource>> resourceMap = new HashMap<>();
protected ValuesTable() {
this.arrays = new HashMap<>();
this.strings = new HashMap<>();
this.colors = new HashMap<>();
this.integers = new HashMap<>();
this.dimens = new HashMap<>();
this.booleans = new HashMap<>();
this.resourceMap.put("array", this.arrays);
this.resourceMap.put("string", this.strings);
this.resourceMap.put("color", this.colors);
this.resourceMap.put("integer", this.integers);
this.resourceMap.put("dimen", this.dimens);
this.resourceMap.put("bool", this.booleans);
private final Map<String, Map<String, IResource>> resourceMap = new HashMap<> ();
protected ValuesTable () {
this.arrays = new HashMap<> ();
this.strings = new HashMap<> ();
this.colors = new HashMap<> ();
this.integers = new HashMap<> ();
this.dimens = new HashMap<> ();
this.booleans = new HashMap<> ();
this.resourceMap.put ("array", this.arrays);
this.resourceMap.put ("string", this.strings);
this.resourceMap.put ("color", this.colors);
this.resourceMap.put ("integer", this.integers);
this.resourceMap.put ("dimen", this.dimens);
this.resourceMap.put ("bool", this.booleans);
}
/**
* Create a new table by reading the files from the given values directory.
*
* @param valuesDirectory The "res/values" directory.
* @return The values table or {@code null} if the directory is invalid or there are no files in
* this directory.
* this directory.
* @throws XmlPullParserException Thrown by {@link XmlPullParserFactory}.
* @throws IOException Thrown by {@link XmlPullParser}.
* @throws IOException Thrown by {@link XmlPullParser}.
*/
@Nullable
public static ValuesTable forDirectory(File valuesDirectory) throws Exception {
public static ValuesTable forDirectory (File valuesDirectory) throws Exception {
if (valuesDirectory == null) {
LOG.error("Cannot create values table for directory: ", null);
LOG.error ("Cannot create values table for directory: ", null);
return null;
}
if (!valuesDirectory.exists()) {
LOG.error(
if (!valuesDirectory.exists ()) {
LOG.error (
"Cannot create values table for directory as it does not exists. Directory:",
valuesDirectory);
return null;
}
final var files = valuesDirectory.listFiles(ValuesTable::isValid);
final var files = valuesDirectory.listFiles (ValuesTable::isValid);
if (files == null) {
LOG.error("No files found in the given values directory");
LOG.error ("No files found in the given values directory");
return null;
}
final var start = System.currentTimeMillis();
final var values = new ValuesTable();
final var start = System.currentTimeMillis ();
final var values = new ValuesTable ();
for (var file : files) {
final var factory = XmlPullParserFactory.newInstance();
final var parser = factory.newPullParser();
parser.setInput(new FileInputStream(file), null);
var event = parser.getEventType();
final var factory = XmlPullParserFactory.newInstance ();
final var parser = factory.newPullParser ();
parser.setInput (new FileInputStream (file), null);
var event = parser.getEventType ();
while (event != XmlPullParser.END_DOCUMENT) {
if (event == XmlPullParser.START_TAG) {
final var tag = parser.getName();
if ("resources".equals(tag)) {
event = parser.next();
final var tag = parser.getName ();
if ("resources".equals (tag)) {
event = parser.next ();
continue;
}
readTag(tag, parser, values);
readTag (tag, parser, values);
}
event = parser.next();
event = parser.next ();
}
}
final var duration = System.currentTimeMillis() - start;
LOG.info(
String.format(
Locale.getDefault(),
final var duration = System.currentTimeMillis () - start;
LOG.info (
String.format (
Locale.getDefault (),
"Took %d ms to read %d files in directory: %s",
duration,
files.length,
valuesDirectory));
return values;
}
private static void readTag(String tag, XmlPullParser parser, ValuesTable values)
private static void readTag (String tag, XmlPullParser parser, ValuesTable values)
throws Exception {
if ("style".equals(tag) || "declare-styleable".equals(tag)) {
if ("style".equals (tag) || "declare-styleable".equals (tag)) {
// These tags are not supported yet
// Skip parsing until we reach next tag with same depth as of this tag
skip(parser);
skip (parser);
return;
}
// TODO Should we check if these tags are specified within <resources>?
final var name = getName(parser);
final var name = getName (parser);
if (name == null) {
LOG.error(String.format("A <%s> resource was found with no 'name' attribute", tag));
LOG.error (String.format ("A <%s> resource was found with no 'name' attribute", tag));
return;
}
if ("item".equals(tag)) {
tag = parser.getAttributeValue(null, "type");
if ("item".equals (tag)) {
tag = parser.getAttributeValue (null, "type");
if (tag == null) {
LOG.error("<item> resource value found but no 'type' was specified.");
LOG.error ("<item> resource value found but no 'type' was specified.");
return;
}
}
// check for array before reading value
// because array resources do not have text values
if ("array".equals(tag) || tag.endsWith("-array")) {
readArray(parser, values, name);
if ("array".equals (tag) || tag.endsWith ("-array")) {
readArray (parser, values, name);
return;
}
// Value must be read after all other required values have been read
final var value = parser.nextText();
final var value = parser.nextText ();
IResource resourceValue;
switch (tag) {
case "string":
resourceValue = new StringResource(name, value);
values.strings.put(name, resourceValue);
resourceValue = new StringResource (name, value);
values.strings.put (name, resourceValue);
break;
case "color":
resourceValue = new ColorResource(name, value);
values.colors.put(name, resourceValue);
resourceValue = new ColorResource (name, value);
values.colors.put (name, resourceValue);
break;
case "bool":
resourceValue = new BooleanResource(name, value);
values.booleans.put(name, resourceValue);
resourceValue = new BooleanResource (name, value);
values.booleans.put (name, resourceValue);
break;
case "dimen":
resourceValue = new DimensionResource(name, value);
values.dimens.put(name, resourceValue);
resourceValue = new DimensionResource (name, value);
values.dimens.put (name, resourceValue);
break;
case "integer":
resourceValue = new IntegerResource(name, value);
values.integers.put(name, resourceValue);
resourceValue = new IntegerResource (name, value);
values.integers.put (name, resourceValue);
break;
default:
LOG.warn("Unknown or unsupported resource value tag:", tag);
LOG.warn ("Unknown or unsupported resource value tag:", tag);
break;
}
}
private static void readArray(@NonNull XmlPullParser parser, ValuesTable values, String name)
private static void readArray (@NonNull XmlPullParser parser, ValuesTable values, String name)
throws XmlPullParserException, IOException {
final var minDepth = parser.getDepth();
final var items = new ArrayList<String>();
var event = parser.getEventType();
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT
&& parser.getDepth() >= minDepth) {
final var minDepth = parser.getDepth ();
final var items = new ArrayList<String> ();
var event = parser.getEventType ();
while ((event = parser.next ()) != XmlPullParser.END_DOCUMENT
&& parser.getDepth () >= minDepth) {
if (event == XmlPullParser.START_TAG) {
final var child = parser.getName();
if ("item".equals(child)) {
final var value = parser.nextText();
items.add(value);
final var child = parser.getName ();
if ("item".equals (child)) {
final var value = parser.nextText ();
items.add (value);
}
}
}
final var arr = new String[items.size()];
final var arr = new String[items.size ()];
for (int i = 0; i < arr.length; i++) {
arr[i] = items.get(i);
arr[i] = items.get (i);
}
values.arrays.put(name, new ArrayResource(name, arr));
values.arrays.put (name, new ArrayResource (name, arr));
}
private static void skip(@NonNull XmlPullParser parser)
private static void skip (@NonNull XmlPullParser parser)
throws XmlPullParserException, IOException {
if (parser.getEventType() != XmlPullParser.START_TAG) {
throw new IllegalStateException();
if (parser.getEventType () != XmlPullParser.START_TAG) {
throw new IllegalStateException ();
}
int depth = 1;
while (depth != 0) {
switch (parser.next()) {
switch (parser.next ()) {
case XmlPullParser.END_TAG:
depth--;
break;
@@ -245,21 +245,26 @@ public class ValuesTable {
}
}
}
private static String getName(@NonNull XmlPullParser parser) {
return parser.getAttributeValue(null, "name");
private static String getName (@NonNull XmlPullParser parser) {
return parser.getAttributeValue (null, "name");
}
private static boolean isValid(File file) {
return file != null && file.isFile() && FileUtils.isUtf8(file);
private static boolean isValid (File file) {
return file != null && file.isFile () && FileUtils.isUtf8 (file);
}
@Nullable
public Map<String, IResource> getTable(final String name) {
Objects.requireNonNull(name);
return this.resourceMap.get(name);
public Map<String, IResource> getTable (final String name) {
Objects.requireNonNull (name);
return this.resourceMap.get (name);
}
@NonNull
public Map<String, Map<String, IResource>> getResourceMap () {
return resourceMap;
}
/**
* Finds a resource value of any type.
*
@@ -267,133 +272,133 @@ public class ValuesTable {
* @return The resource value or {@code null} if there is no resource with the given name.
*/
@SuppressWarnings("unused")
public IResource findResource(final String name) {
public IResource findResource (final String name) {
IResource value;
value = findString(name);
value = findString (name);
if (value != null) {
return value;
}
value = findColor(name);
value = findColor (name);
if (value != null) {
return value;
}
value = findInteger(name);
value = findInteger (name);
if (value != null) {
return value;
}
value = findDimension(name);
value = findDimension (name);
if (value != null) {
return value;
}
value = findArray(name);
value = findArray (name);
if (value != null) {
return value;
}
value = findBoolean(name);
value = findBoolean (name);
//noinspection RedundantIfStatement
if (value != null) {
return value;
}
return null;
}
public ArrayResource findArray(final String name) {
return (ArrayResource) arrays.getOrDefault(name, null);
public ArrayResource findArray (final String name) {
return (ArrayResource) arrays.getOrDefault (name, null);
}
/**
* Find a string value with the given name.
*
* @param name The name of the string resource.
* @return The string resource or {@code null} if there is no resource with the given name.
*/
public StringResource findString(final String name) {
return (StringResource) strings.getOrDefault(name, null);
public StringResource findString (final String name) {
return (StringResource) strings.getOrDefault (name, null);
}
/**
* Find a color value with the given name.
*
* @param name The name of the color resource.
* @return The color resource or {@code null} if there is no resource with the given name.
*/
public ColorResource findColor(final String name) {
return (ColorResource) colors.getOrDefault(name, null);
public ColorResource findColor (final String name) {
return (ColorResource) colors.getOrDefault (name, null);
}
/**
* Find a integer value with the given name.
*
* @param name The name of the integer resource.
* @return The integer resource or {@code null} if there is no resource with the given name.
*/
public IntegerResource findInteger(final String name) {
return (IntegerResource) integers.getOrDefault(name, null);
public IntegerResource findInteger (final String name) {
return (IntegerResource) integers.getOrDefault (name, null);
}
/**
* Find a dimension value with the given name.
*
* @param name The name of the dimension resource.
* @return The dimension resource or {@code null} if there is no resource with the given name.
*/
public DimensionResource findDimension(final String name) {
return (DimensionResource) dimens.getOrDefault(name, null);
public DimensionResource findDimension (final String name) {
return (DimensionResource) dimens.getOrDefault (name, null);
}
/**
* Find a boolean value with the given name.
*
* @param name The name of the boolean resource.
* @return The boolean resource or {@code null} if there is no resource with the given name.
*/
public BooleanResource findBoolean(final String name) {
return (BooleanResource) booleans.getOrDefault(name, null);
public BooleanResource findBoolean (final String name) {
return (BooleanResource) booleans.getOrDefault (name, null);
}
/**
* Reads the contents of the given file and updates this table.
*
* @param file The file to sync with.
*/
public void syncWithFile(final File file) throws Exception {
final var start = System.currentTimeMillis();
final var factory = XmlPullParserFactory.newInstance();
final var parser = factory.newPullParser();
parser.setInput(new FileInputStream(file), null);
var event = parser.getEventType();
public void syncWithFile (final File file) throws Exception {
final var start = System.currentTimeMillis ();
final var factory = XmlPullParserFactory.newInstance ();
final var parser = factory.newPullParser ();
parser.setInput (new FileInputStream (file), null);
var event = parser.getEventType ();
while (event != XmlPullParser.END_DOCUMENT) {
if (event == XmlPullParser.START_TAG) {
final var tag = parser.getName();
if ("resources".equals(tag)) {
event = parser.next();
final var tag = parser.getName ();
if ("resources".equals (tag)) {
event = parser.next ();
continue;
}
ValuesTable.readTag(tag, parser, this);
ValuesTable.readTag (tag, parser, this);
}
event = parser.next();
event = parser.next ();
}
LOG.debug(
LOG.debug (
"Syncing with file",
file.getName(),
file.getName (),
"took",
(System.currentTimeMillis() - start),
(System.currentTimeMillis () - start),
"ms");
}
@NonNull
@Override
public String toString() {
public String toString () {
return "ValuesTable{"
+ "strings="
+ strings