Create base classes for attribute editors

This commit is contained in:
Akash Yadav
2021-12-23 16:36:13 +05:30
parent 24ac11231b
commit 04a0f241f2
8 changed files with 392 additions and 2 deletions
+2
View File
@@ -31,7 +31,9 @@
<entry key="..\:/Projects/AndroidStudioProjects/android-ide/app/src/main/res/layout/layout_attr_editor_sheet_item.xml" value="0.264" />
<entry key="..\:/Projects/AndroidStudioProjects/android-ide/app/src/main/res/layout/layout_attr_value_editor.xml" value="0.264" />
<entry key="..\:/Projects/AndroidStudioProjects/android-ide/app/src/main/res/layout/layout_attr_value_editor_dimension.xml" value="0.264" />
<entry key="..\:/Projects/AndroidStudioProjects/android-ide/app/src/main/res/layout/layout_base_attr_editor.xml" value="0.20512820512820512" />
<entry key="..\:/Projects/AndroidStudioProjects/android-ide/app/src/main/res/layout/layout_completion_item.xml" value="0.20512820512820512" />
<entry key="..\:/Projects/AndroidStudioProjects/android-ide/app/src/main/res/layout/layout_dimension_editor.xml" value="0.264" />
<entry key="..\:/Projects/AndroidStudioProjects/android-ide/app/src/main/res/layout/layout_simple_icon_text.xml" value="0.30128205128205127" />
<entry key="..\:/Projects/AndroidStudioProjects/android-ide/app/src/main/res/layout/layout_widget_group_item.xml" value="0.33" />
<entry key="..\:/Projects/AndroidStudioProjects/android-ide/app/src/main/res/layout/layout_widget_item.xml" value="0.33" />
@@ -20,13 +20,16 @@ package com.itsaky.androidide.adapters;
import android.graphics.Typeface;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.itsaky.androidide.R;
import com.itsaky.androidide.databinding.LayoutAttrEditorSheetItemBinding;
import com.itsaky.androidide.models.XMLAttribute;
@@ -57,6 +60,10 @@ public class AttributeListAdapter extends RecyclerView.Adapter <AttributeListAda
if (attr.isApplied ()) {
sb.append (" = ");
sb.append (attr.getValue (), new StyleSpan (Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
final var length = attr.getValue ().length ();
sb.setSpan (new ForegroundColorSpan (ContextCompat.getColor (binding.text.getContext (), R.color.primaryTextColor),
));
}
binding.text.setText (sb);
@@ -0,0 +1,61 @@
/*
* 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.attreditors;
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.itsaky.androidide.databinding.LayoutDimensionEditorBinding;
import com.itsaky.androidide.models.XMLAttribute;
import com.itsaky.attrinfo.models.Attr;
public class DimensionAttrEditor extends IAttrEditorFragment {
private LayoutDimensionEditorBinding binding;
@Override
protected IAttrEditorFragment newInstance (XMLAttribute attribute) {
final var fragment = new DimensionAttrEditor ();
Bundle args = new Bundle ();
args.putParcelable (ATTRIBUTE_KEY, attribute);
fragment.setArguments (args);
return fragment;
}
@Override
protected int[] getSupportedFormats () {
// TODO Should we consider Attr.ENUM as well? Maybe for match_parent and wrap_content.
return new int[] { Attr.DIMENSION };
}
@Override
protected View createEditorView (LayoutInflater inflater, ViewGroup parent) {
this.binding = LayoutDimensionEditorBinding.inflate (inflater, parent, false);
return this.binding.getRoot ();
}
@Override
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated (view, savedInstanceState);
}
}
@@ -0,0 +1,152 @@
/*
* 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.attreditors;
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 androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.Fragment;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.databinding.LayoutBaseAttrEditorBinding;
import com.itsaky.androidide.models.XMLAttribute;
import com.itsaky.androidide.utils.Logger;
/**
* Base class for every attribute editor.
*
* @author Akash Yadav
*/
public abstract class IAttrEditorFragment extends Fragment {
private XMLAttribute attribute;
protected static final String ATTRIBUTE_KEY = "xml_attribute";
protected final Logger LOG = Logger.instance (getClass ().getSimpleName ());
@Nullable
@Override
public View onCreateView (@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
LayoutBaseAttrEditorBinding binding = LayoutBaseAttrEditorBinding.inflate (inflater, container, false);
binding.getRoot ().addView (createEditorView (inflater, binding.getRoot ()), generateEditorLayoutParams ());
return binding.getRoot ();
}
@Override
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated (view, savedInstanceState);
if (getArguments () == null) {
LOG.error ("No arguments found. Use IAttrEditorFragment#newInstance(..) to create these type of fragments");
return;
}
final var args = getArguments ();
final var attribute = (XMLAttribute) args.getParcelable (ATTRIBUTE_KEY);
if (attribute == null) {
throw new IllegalArgumentException ("How can we edit a null attribute?");
}
validate (attribute);
this.attribute = attribute;
}
protected ConstraintLayout.LayoutParams generateEditorLayoutParams () {
final var params = new ConstraintLayout.LayoutParams (0, 0);
params.topToBottom = R.id.editor_divider;
params.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
params.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
params.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;
return params;
}
/**
* Check if the provided attribute is valid for this editor or not.
* If not, an {@link ValidationException} will be thrown.
*
* @param attribute The attribute to validate.
* @return {@code true} if the attribute is valid, {@code false} otherwise.
*/
protected boolean validate (XMLAttribute attribute) {
final var attrs = StudioApp.getInstance ().attrInfo ();
final var attr = attrs.getAttribute (attribute.getAttributeName ());
if (attr == null) {
throw new ValidationException ("Cannot find attribute with the given name: " + attribute.getAttributeName ());
}
for (var format : getSupportedFormats ()) {
if ((attr.format & format) != 0) {
LOG.error ("Error validating given attribute", "attr: " + attr, "attribute: " + attribute);
throw new ValidationException ();
}
}
}
/**
* Subclasses are supposed to inflate the layout in this method.
*
* @param inflater The layout inflater. Same as passed in {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}.
* @param parent The parent group to use when inflating the layout.
* @return The created or inflated attribute editor view.
*/
protected abstract View createEditorView (LayoutInflater inflater, ViewGroup parent);
/**
* Create a new instance of this attribute editor fragment.
* TODO Instead of defining this in every subclass, can we use reflection to create instances by default?
* How would it affect performance?
* As of now, we only set arguments for the newly created fragment. So we can implement any generic method.
*
* @param attribute The attribute to be edited.
* @return The new instance of this fragment.
*/
protected abstract IAttrEditorFragment newInstance (XMLAttribute attribute);
/**
* Get the formats supported by this attribute editor.
* This will be used to valid the attribute passed to this editor fragment.
* in {@link #validate(XMLAttribute)}.
*
* @return The supported formats.
*/
protected abstract int[] getSupportedFormats ();
/**
* An unchecked exception which is thrown if we fail to validate
* the given attribute.
*/
protected static class ValidationException extends RuntimeException {
public ValidationException () {
this("Given attribute is invalid.");
}
public ValidationException (String msg) {
super(msg);
}
}
}
@@ -52,10 +52,11 @@ public class AttrEditorSheet extends BottomSheetDialogFragment implements Simple
private IView selectedView;
private LayoutAttrEditorSheetBinding binding;
private final Logger LOG = Logger.instance ("AttrSheetEditor");
private OnViewDeletionFailedListener mDeletionFailedListener;
private static final Logger LOG = Logger.instance ("AttrBottomSheet");
@Nullable
@Override
public View onCreateView (@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
@@ -104,6 +105,11 @@ public class AttrEditorSheet extends BottomSheetDialogFragment implements Simple
}
private void onAttrClick (LayoutAttrEditorSheetItemBinding binding, XMLAttribute attribute) {
final var inflated = inflateAppropriateEditor (attribute);
}
private View inflateAppropriateEditor (XMLAttribute attribute) {
return null;
}
@Override
@@ -17,6 +17,9 @@
package com.itsaky.androidide.models;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import com.itsaky.layoutinflater.IAttribute;
@@ -29,7 +32,7 @@ import org.jetbrains.annotations.Contract;
*
* @author Akash Yadav
*/
public class XMLAttribute extends UiAttribute {
public class XMLAttribute extends UiAttribute implements Parcelable {
/**
* Is this attribute applied to any view?
@@ -49,6 +52,26 @@ public class XMLAttribute extends UiAttribute {
this.isApplied = isApplied;
}
private XMLAttribute (@NonNull Parcel in) {
this (in.readString (), in.readString (), in.readString (), in.readByte () != 0);
}
public static final Creator<XMLAttribute> CREATOR = new Creator<XMLAttribute> () {
@NonNull
@Contract("_ -> new")
@Override
public XMLAttribute createFromParcel (Parcel in) {
return new XMLAttribute (in);
}
@NonNull
@Contract(value = "_ -> new", pure = true)
@Override
public XMLAttribute[] newArray (int size) {
return new XMLAttribute[size];
}
};
/**
* Set the given string as a value to this attribute.
* This will set {@link #isApplied()} to return {@code true}.
@@ -88,4 +111,17 @@ public class XMLAttribute extends UiAttribute {
public static XMLAttribute newAndroidAttribute (String name) {
return new XMLAttribute ("android", name, "", false);
}
@Override
public int describeContents () {
return 0;
}
@Override
public void writeToParcel (@NonNull Parcel dest, int flags) {
dest.writeString (getNamespace ());
dest.writeString (getAttributeName ());
dest.writeString (getValue ());
dest.writeByte ((byte) (isApplied ? 1 : 0));
}
}
@@ -0,0 +1,52 @@
<?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/>.
-->
<!--Used as a base layout for attribute editor layouts-->
<androidx.constraintlayout.widget.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/attr_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="middle"
android:maxLines="1"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="layout_height" />
<View
android:id="@+id/editor_divider"
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_marginTop="8dp"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/attr_name" />
<!-- Actual editor will be added here after this -->
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,74 @@
<?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/>.
-->
<androidx.constraintlayout.widget.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioGroup
android:id="@+id/dimension_options"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/dimension_match"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/attr_val_match_parent"
/>
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/dimension_wrap"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/attr_val_wrap_content"
/>
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/dimension_fixed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/attr_val_fixed"
/>
</RadioGroup>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="@string/hint_new_attr_value"
android:layout_marginTop="8dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/dimension_options">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>