From 57e296ee2dafb4de39df3ea08c6f11f3ccc2069f Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Sat, 25 Dec 2021 14:51:57 +0530 Subject: [PATCH] Implement basic attribute editor --- .idea/misc.xml | 1 + .../fragments/sheets/AttrEditorSheet.java | 39 +++++ .../androidide/models/XMLAttribute.java | 14 +- .../androidide/utils/AttributeDialogs.java | 133 ++++++++++++++++++ .../itsaky/androidide/utils/DialogUtils.java | 20 +++ .../res/layout/layout_attr_editor_sheet.xml | 2 +- .../res/layout/layout_dimension_editor.xml | 60 ++++++++ app/src/main/res/values/strings.xml | 2 +- 8 files changed, 266 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/com/itsaky/androidide/utils/AttributeDialogs.java create mode 100644 app/src/main/res/layout/layout_dimension_editor.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index 7119b2011..88818fd3e 100755 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -24,6 +24,7 @@ + diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrEditorSheet.java b/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrEditorSheet.java index 2f7a92258..fd3b8aa5b 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrEditorSheet.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttrEditorSheet.java @@ -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 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 diff --git a/app/src/main/java/com/itsaky/androidide/models/XMLAttribute.java b/app/src/main/java/com/itsaky/androidide/models/XMLAttribute.java index 697914c07..0b2a68e1f 100644 --- a/app/src/main/java/com/itsaky/androidide/models/XMLAttribute.java +++ b/app/src/main/java/com/itsaky/androidide/models/XMLAttribute.java @@ -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 CREATOR = new Creator () { @NonNull @Contract("_ -> new") diff --git a/app/src/main/java/com/itsaky/androidide/utils/AttributeDialogs.java b/app/src/main/java/com/itsaky/androidide/utils/AttributeDialogs.java new file mode 100644 index 000000000..17efddf16 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/utils/AttributeDialogs.java @@ -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 . + */ + +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 { + dialog.dismiss (); + if (onSelected != null) { + onSelected.onClick (dialog, which); + } + }); + return builder.show (); + } + + +} diff --git a/app/src/main/java/com/itsaky/androidide/utils/DialogUtils.java b/app/src/main/java/com/itsaky/androidide/utils/DialogUtils.java index 384e69852..a54c2ec85 100644 --- a/app/src/main/java/com/itsaky/androidide/utils/DialogUtils.java +++ b/app/src/main/java/com/itsaky/androidide/utils/DialogUtils.java @@ -45,6 +45,16 @@ public class DialogUtils { return new MaterialAlertDialogBuilder (context, R.style.AppTheme_MaterialAlertDialog); } + /** + * Create a new alert dialog with two buttons: Yes and No. + * 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 Yes button click. + * @param negativeClickListener A listener that will be invoked on the No 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: Yes and No. + * + * @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 Yes button click. + * @param negativeClickListener A listener that will be invoked on the No button click. + * @return The newly created dialog instance. + */ @NonNull public static MaterialAlertDialogBuilder newYesNoDialog (Context context, String title, diff --git a/app/src/main/res/layout/layout_attr_editor_sheet.xml b/app/src/main/res/layout/layout_attr_editor_sheet.xml index faa55d8dc..b726336ba 100644 --- a/app/src/main/res/layout/layout_attr_editor_sheet.xml +++ b/app/src/main/res/layout/layout_attr_editor_sheet.xml @@ -23,7 +23,7 @@ android:paddingTop="16dp" android:paddingEnd="16dp" android:paddingBottom="8dp"> - + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 62de0ece2..493358788 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -316,7 +316,7 @@ New value wrap_content match_parent - Fixed + Other value Enter dimension value Applicable attributes Actions