Implement basic attribute editor

This commit is contained in:
Akash Yadav
2021-12-25 14:51:57 +05:30
parent 74085b3a31
commit 57e296ee2d
8 changed files with 266 additions and 5 deletions
+1
View File
@@ -24,6 +24,7 @@
<entry key="..\:/Projects/AndroidStudioProjects/android-ide/app/src/main/res/layout/activity_editor.xml" value="0.264" />
<entry key="..\:/Projects/AndroidStudioProjects/android-ide/app/src/main/res/layout/activity_main.xml" value="0.20253164556962025" />
<entry key="..\:/Projects/AndroidStudioProjects/android-ide/app/src/main/res/layout/fragment_editor.xml" value="0.20512820512820512" />
<entry key="..\:/Projects/AndroidStudioProjects/android-ide/app/src/main/res/layout/layour_dimension_editor.xml" value="0.20512820512820512" />
<entry key="..\:/Projects/AndroidStudioProjects/android-ide/app/src/main/res/layout/layout_about_items.xml" value="0.20512820512820512" />
<entry key="..\:/Projects/AndroidStudioProjects/android-ide/app/src/main/res/layout/layout_attr_editor.xml" value="0.2936708860759494" />
<entry key="..\:/Projects/AndroidStudioProjects/android-ide/app/src/main/res/layout/layout_attr_editor_item.xml" value="0.264" />
@@ -17,6 +17,7 @@
package com.itsaky.androidide.fragments.sheets;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -24,6 +25,7 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.transition.ChangeBounds;
import androidx.transition.TransitionManager;
@@ -36,6 +38,7 @@ import com.itsaky.androidide.databinding.LayoutAttrEditorSheetBinding;
import com.itsaky.androidide.databinding.LayoutAttrEditorSheetItemBinding;
import com.itsaky.androidide.models.IconTextListItem;
import com.itsaky.androidide.models.XMLAttribute;
import com.itsaky.androidide.utils.AttributeDialogs;
import com.itsaky.androidide.utils.DialogUtils;
import com.itsaky.androidide.utils.Logger;
import com.itsaky.layoutinflater.IView;
@@ -44,8 +47,11 @@ import com.itsaky.toaster.Toaster;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static com.itsaky.attrinfo.models.Attr.*;
public class AttrEditorSheet extends BottomSheetDialogFragment implements SimpleIconTextAdapter.OnBindListener {
private static final List<IconTextListItem> VIEW_ACTIONS = new ArrayList<> ();
@@ -76,6 +82,14 @@ public class AttrEditorSheet extends BottomSheetDialogFragment implements Simple
}
setupViewData ();
AttributeDialogs.init (getActivity ());
}
@Override
public void onDismiss (@NonNull DialogInterface dialog) {
AttributeDialogs.release ();
super.onDismiss (dialog);
}
public AttrEditorSheet setDeletionFailedListener (OnViewDeletionFailedListener listener) {
@@ -111,6 +125,31 @@ public class AttrEditorSheet extends BottomSheetDialogFragment implements Simple
LOG.error (getString (R.string.msg_no_attr_format), attribute);
return;
}
showEditorDialog (attribute);
}
private void showEditorDialog (@NonNull XMLAttribute attribute) {
final var format = attribute.getFormat ();
final var attr = attribute.getAttr ();
AlertDialog dialog = null;
if (hasFormat (format, DIMENSION)) {
if (attr == null) {
throw new IllegalStateException ("The provided attribute has not been resoleved.");
}
final var possibleValues = attr.possibleValues;
dialog = AttributeDialogs.dimensionEditor (possibleValues.toArray(new String[0]), attribute.getValue (), (dialog1, which) -> {
LOG.debug ("Dimension value changed to : " + attribute.getValue ());
});
}
// TODO Once the user has selected another value, update it for the view too.
}
private boolean hasFormat (int format, int check) {
return (format &check ) != 0;
}
@Override
@@ -21,8 +21,10 @@ import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.attrinfo.models.Attr;
import com.itsaky.layoutinflater.IAttribute;
import com.itsaky.layoutinflater.impl.UiAttribute;
@@ -47,6 +49,8 @@ public class XMLAttribute extends UiAttribute implements Parcelable {
*/
private int format = -1;
private Attr attr;
public XMLAttribute (IAttribute attribute) {
this (attribute, true);
}
@@ -79,14 +83,18 @@ public class XMLAttribute extends UiAttribute implements Parcelable {
}
final var attrs = StudioApp.getInstance ().attrInfo ();
final var attr = attrs.getAttribute (getAttributeName ());
if (attr != null) {
this.format = attr.format;
this.attr = attrs.getAttribute (getAttributeName ());
if (this.attr != null) {
this.format = this.attr.format;
}
return getFormat ();
}
public Attr getAttr () {
return attr;
}
public static final Creator<XMLAttribute> CREATOR = new Creator<XMLAttribute> () {
@NonNull
@Contract("_ -> new")
@@ -0,0 +1,133 @@
/*
* 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.utils;
import android.content.Context;
import android.content.DialogInterface;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import static com.itsaky.androidide.utils.DialogUtils.newMaterialDialogBuilder;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import com.itsaky.androidide.R;
import com.itsaky.androidide.databinding.LayoutDimensionEditorBinding;
/**
* Helper class for creating dialogs for editing view attributes.
*
* TODO Should we merge this into {@link DialogUtils}
* @author Akash Yadav
*/
public class AttributeDialogs {
private static final String DIMENSION_MATCH = "match_parent";
private static final String DIMENSION_WRAP = "wrap_content";
/**
* Context that will be used for creating dialogs.
* This must be initialized by calling {@link #init(Context)}
* and the reference must be released by calling {@link #release()}
*/
private static Context mContext;
/**
* Initialize this class with the provided context.
* @param ctx The context that will be used for creating dialogs.
* @see #release()
*/
public static void init (Context ctx) {
mContext = ctx;
}
/**
* Release the context instance stored in this class.
*
* @see #init(Context)
*/
public static void release () {
mContext = null;
}
public static AlertDialog dimensionEditor (CharSequence[] values, CharSequence value, DialogInterface.OnClickListener onSubmit) {
final var binding = LayoutDimensionEditorBinding.inflate(LayoutInflater.from (mContext));
binding.choices.setOnCheckedChangeListener ((group, checkedId) -> {
binding.otherValue.setVisibility (binding.other.isChecked () ? View.VISIBLE : View.GONE);
});
if (DIMENSION_MATCH.equals (value)) {
binding.choices.check (R.id.match);
} else if (DIMENSION_WRAP.equals (value)) {
binding.choices.check (R.id.wrap);
} else {
binding.choices.check (R.id.other);
}
final var builder = newMaterialDialogBuilder (mContext);
builder.setView (binding.getRoot ());
builder.setPositiveButton (android.R.string.ok, ((dialog, which) -> {
dialog.dismiss ();
if (onSubmit != null) {
onSubmit.onClick (dialog, which);
}
}));
builder.setNegativeButton (android.R.string.cancel, (dialog, which) -> dialog.dismiss ());
return builder.show ();
}
/**
* @see #enumSelector(CharSequence[], int, DialogInterface.OnClickListener)
*/
public static AlertDialog enumSelector (CharSequence[] values, CharSequence value, DialogInterface.OnClickListener onSelected) {
return enumSelector (values, findIndexOf (values, value), onSelected);
}
private static int findIndexOf (@NonNull CharSequence[] values, CharSequence value) {
for (int i=0;i<values.length;i++) {
if (value.equals (values[i])) {
return i;
}
}
throw new IndexOutOfBoundsException ("Cannot find index for value: " + value);
}
/**
* Create a new single choice dialog for selecting enum values.
* @param values The value entries in the enum declaration.
* @param selected The item that will be selected when the dialog is shown.
* @param onSelected The click listener that will be invoked when an item is selected from the list.
* @return The newly created dialog instance.
*/
public static AlertDialog enumSelector (CharSequence[] values, int selected, DialogInterface.OnClickListener onSelected) {
final var builder = newMaterialDialogBuilder (mContext);
builder.setSingleChoiceItems (values, selected, (dialog, which) -> {
dialog.dismiss ();
if (onSelected != null) {
onSelected.onClick (dialog, which);
}
});
return builder.show ();
}
}
@@ -45,6 +45,16 @@ public class DialogUtils {
return new MaterialAlertDialogBuilder (context, R.style.AppTheme_MaterialAlertDialog);
}
/**
* Create a new alert dialog with two buttons: <span>Yes</span> and <span>No</span>.
* This method simply calls {@link #newYesNoDialog(Context, String, String, DialogInterface.OnClickListener, DialogInterface.OnClickListener)}
* with default values for title and message.
*
* @param context The context for the dialog.
* @param positiveClickListener A listener that will be invoked on the <span>Yes</span> button click.
* @param negativeClickListener A listener that will be invoked on the <span>No</span> button click.
* @return The newly created dialog.
*/
@NonNull
public static MaterialAlertDialogBuilder newYesNoDialog (Context context,
DialogInterface.OnClickListener positiveClickListener,
@@ -56,6 +66,16 @@ public class DialogUtils {
negativeClickListener);
}
/**
* Create a new alert dialog with two buttons: <span>Yes</span> and <span>No</span>.
*
* @param context The context for the dialog.
* @param title The title of the dialog.
* @param message The message of the dialog.
* @param positiveClickListener A listener that will be invoked on the <span>Yes</span> button click.
* @param negativeClickListener A listener that will be invoked on the <span>No</span> button click.
* @return The newly created dialog instance.
*/
@NonNull
public static MaterialAlertDialogBuilder newYesNoDialog (Context context,
String title,
@@ -23,7 +23,7 @@
android:paddingTop="16dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp">
<TextView
android:id="@+id/widget_name"
android:layout_width="0dp"
@@ -0,0 +1,60 @@
<?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/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<RadioGroup
android:id="@+id/choices"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
android:id="@+id/match"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/attr_val_match_parent" />
<RadioButton
android:id="@+id/wrap"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/attr_val_wrap_content" />
<RadioButton
android:id="@+id/other"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/attr_val_other" />
</RadioGroup>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/other_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_new_attr_value">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
+1 -1
View File
@@ -316,7 +316,7 @@
<string name="hint_new_attr_value">New value</string>
<string name="attr_val_wrap_content">wrap_content</string>
<string name="attr_val_match_parent">match_parent</string>
<string name="attr_val_fixed">Fixed</string>
<string name="attr_val_other">Other value</string>
<string name="msg_enter_dimension_value">Enter dimension value</string>
<string name="msg_applicable_attrs">Applicable attributes</string>
<string name="msg_attr_editor_actions">Actions</string>