diff --git a/app/src/main/java/com/itsaky/androidide/EditorActivity.java b/app/src/main/java/com/itsaky/androidide/EditorActivity.java index 1854043e3..b341181ea 100644 --- a/app/src/main/java/com/itsaky/androidide/EditorActivity.java +++ b/app/src/main/java/com/itsaky/androidide/EditorActivity.java @@ -184,7 +184,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F loadFragment(mFileTreeFragment); symbolInput = new SymbolInputView(this); - mBinding.inputContainer.addView(symbolInput, 1, new ViewGroup.LayoutParams(-1, -2)); + mBinding.inputContainer.addView(symbolInput, 0, new ViewGroup.LayoutParams(-1, -2)); mBinding.editorViewPager.setOffscreenPageLimit(9); mBinding.editorViewPager.setAdapter(mPagerAdapter); @@ -233,6 +233,26 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F } } + @Override + protected void onPause() { + new Thread(() -> { + + boolean saved = false; + try { + saveAll(); + saved = true; + } catch (Throwable th) { + LOG.error(":onPause(): Failed to save files", th); + saved = false; + } + + if(!saved) { + getApp().toast(R.string.msg_failed_save, Toaster.Type.ERROR); + } + }, "AndroidIDE FileSaver").start(); + super.onPause(); + } + @Override protected void onResume() { super.onResume(); diff --git a/app/src/main/java/com/itsaky/androidide/adapters/EditorPagerAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/EditorPagerAdapter.java index b55d4ff48..ae457c7c5 100644 --- a/app/src/main/java/com/itsaky/androidide/adapters/EditorPagerAdapter.java +++ b/app/src/main/java/com/itsaky/androidide/adapters/EditorPagerAdapter.java @@ -96,7 +96,9 @@ public class EditorPagerAdapter extends FragmentStatePagerAdapter implements Edi public void save(int index, SaveResult result) { if (index >= 0 && index < mFragments.size()) { EditorFragment frag = getFrag(index); - + if (frag == null || frag.getFile() == null) { + return; + } /** * Must be called before frag.save() * Otherwise, it'll always return false diff --git a/app/src/main/java/com/itsaky/androidide/fragments/EditorFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/EditorFragment.java index cbee456ec..d4fa9f1bb 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/EditorFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/EditorFragment.java @@ -140,6 +140,8 @@ public class EditorFragment extends BaseFragment implements EditorEventListener mBinding.editor.setLineColorsEnabled(true); mBinding.editor.setDividerWidth(SizeUtils.dp2px(1)); + mBinding.diagnosticTextContainer.setVisibility(View.GONE); + configureEditorIfNeeded(); final Range range = fromArgs(getArguments()); diff --git a/app/src/main/java/com/itsaky/androidide/handlers/BuildServiceHandler.java b/app/src/main/java/com/itsaky/androidide/handlers/BuildServiceHandler.java index ad56dba91..b61f60bfd 100644 --- a/app/src/main/java/com/itsaky/androidide/handlers/BuildServiceHandler.java +++ b/app/src/main/java/com/itsaky/androidide/handlers/BuildServiceHandler.java @@ -1,6 +1,7 @@ package com.itsaky.androidide.handlers; import android.view.View; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.itsaky.androidide.R; import com.itsaky.androidide.managers.PreferenceManager; import com.itsaky.androidide.models.AndroidProject; @@ -12,12 +13,11 @@ import com.itsaky.androidide.tasks.gradle.build.ApkGeneratingTask; import com.itsaky.androidide.utils.Environment; import java.io.File; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import java.util.Collection; public class BuildServiceHandler extends IDEHandler implements IDEService.BuildListener { diff --git a/app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java b/app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java index 36cc708d3..78e385611 100644 --- a/app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java +++ b/app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java @@ -182,6 +182,7 @@ public abstract class IDELanguageClientImpl implements IDELanguageClient { final FragmentEditorBinding binding = frag.getBinding(); binding.diagnosticTextContainer.setVisibility(View.VISIBLE); + binding.diagnosticText.setVisibility(View.VISIBLE); binding.diagnosticText.setClickable(false); binding.diagnosticText.setText(diagnostic.getMessage()); @@ -221,12 +222,6 @@ public abstract class IDELanguageClientImpl implements IDELanguageClient { return; } - TransitionSet set = new TransitionSet(); - set.addTransition(new ChangeBounds()); - set.addTransition(new Fade()); - set.setDuration(DIAGNOSTIC_TRANSITION_DURATION); - TransitionManager.beginDelayedTransition(activity().getBinding().getRoot(), set); - activity().getDiagnosticBinding().getRoot().setVisibility(View.GONE); } @@ -241,6 +236,7 @@ public abstract class IDELanguageClientImpl implements IDELanguageClient { } frag.getBinding().diagnosticTextContainer.setVisibility(View.GONE); + frag.getBinding().diagnosticText.setVisibility(View.GONE); frag.getBinding().diagnosticText.setClickable(false); } diff --git a/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java b/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java index a05b5b1e5..1182c9717 100644 --- a/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java +++ b/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java @@ -113,7 +113,8 @@ public class ShellServer extends Thread { try { this.process.getOutputStream().write(str.concat("\n").getBytes()); this.process.getOutputStream().flush(); - } catch (IOException e) { + } catch (Throwable e) { + // Ignored } } diff --git a/app/src/main/java/com/itsaky/androidide/ui/inflater/ILayoutInflater.java b/app/src/main/java/com/itsaky/androidide/ui/inflater/ILayoutInflater.java new file mode 100644 index 000000000..25d839ab4 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/ui/inflater/ILayoutInflater.java @@ -0,0 +1,22 @@ +package com.itsaky.androidide.ui.inflater; + +import java.io.File; +import java.util.Set; + +/** + * A layout inflater which inflates layout from a raw XML file + * + * @author Akash Yadav + */ +public abstract class ILayoutInflater { + + /** + * Creates an instance of {@code ILayoutInflater} + * + * @param config The configuration of the LayoutInflater + * @return A new layout inflater instance + */ + public static ILayoutInflater newInstance (LayoutInflaterConfiguration config) { + return new XMLLayoutInflater (config); + } +} diff --git a/app/src/main/java/com/itsaky/androidide/ui/inflater/LayoutInflaterConfiguration.java b/app/src/main/java/com/itsaky/androidide/ui/inflater/LayoutInflaterConfiguration.java new file mode 100644 index 000000000..d99478c04 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/ui/inflater/LayoutInflaterConfiguration.java @@ -0,0 +1,15 @@ +package com.itsaky.androidide.ui.inflater; + +import java.io.File; +import java.util.Set; + +public class LayoutInflaterConfiguration { + + + private final Set resDirs; + + public LayoutInflaterConfiguration(Set resDirs) { + this.resDirs = resDirs; + } + +} diff --git a/app/src/main/java/com/itsaky/androidide/ui/inflater/XMLLayoutInflater.java b/app/src/main/java/com/itsaky/androidide/ui/inflater/XMLLayoutInflater.java new file mode 100644 index 000000000..edff87eca --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/ui/inflater/XMLLayoutInflater.java @@ -0,0 +1,10 @@ +package com.itsaky.androidide.ui.inflater; + +class XMLLayoutInflater extends ILayoutInflater { + + final LayoutInflaterConfiguration config; + + XMLLayoutInflater(LayoutInflaterConfiguration config) { + this.config = config; + } +} diff --git a/app/src/main/java/com/itsaky/androidide/ui/view/AttrType.java b/app/src/main/java/com/itsaky/androidide/ui/view/AttrType.java new file mode 100644 index 000000000..7610b03d9 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/ui/view/AttrType.java @@ -0,0 +1,122 @@ +package com.itsaky.androidide.ui.view; + +/** + * Value type of {@link IAttribute} + * + * @author Akash Yadav + */ +public enum AttrType { + + /** + * Refers to another resource + */ + REFERENCE ("reference"), + + /** + * Color value + */ + COLOR ("color"), + + /** + * Either {@code true} or {@code false} + */ + BOOLEAN ("boolean"), + + /** + * A dimension like 10dp, 14sp etc. + */ + DIMENSION ("dimension"), + + /** + * A floating point number. E.g.: 1.9, 123.45 + */ + FLOAT ("float"), + + /** + * An integer value + */ + INTEGER ("integer"), + + /** + * A string value + */ + STRING ("string"), + + /** + * A fraction + */ + FRACTION ("fraction"), + + /** + * A enum value. Unlike {@link #FLAG}, an attribute can have only one of this. + */ + ENUM ("enum"), + + /** + * A flag value. May contain on or more values + */ + FLAG ("flag"), + + /** + * An unknown value. The layout inflater does not consider this + */ + UNKNOWN (""); + + private final String format; + + public static final String FORMAT_REF = REFERENCE.getFormat(); + public static final String FORMAT_COLOR = COLOR.getFormat(); + public static final String FORMAT_BOOL = BOOLEAN.getFormat(); + public static final String FORMAT_DIMEN = DIMENSION.getFormat(); + public static final String FORMAT_FLOAT = FLOAT.getFormat(); + public static final String FORMAT_INT = INTEGER.getFormat(); + public static final String FORMAT_STRING = STRING.getFormat(); + public static final String FORMAT_FRACTION = FRACTION.getFormat(); + public static final String FORMAT_ENUM = ENUM.getFormat(); + public static final String FORMAT_FLAG = FLAG.getFormat(); + + private AttrType(String format) { + this.format = format; + } + + public String getFormat () { + return this.format; + } + + public boolean hasFixedValues () { + return this == AttrType.ENUM + || this == AttrType.FLAG; + } + + /** + * Get the attribute type from format + * + * @param format Format, as declared in styleables. + * @return The Attribute type + */ + public static AttrType fromFormat (String format) { + if (FORMAT_REF.equals(format)) { + return AttrType.REFERENCE; + } else if (FORMAT_COLOR.equals(format)) { + return AttrType.COLOR; + } else if (FORMAT_BOOL.equals(format)) { + return AttrType.BOOLEAN; + } else if (FORMAT_DIMEN.equals(format)) { + return AttrType.DIMENSION; + } else if (FORMAT_FLOAT.equals(format)) { + return AttrType.FLOAT; + } else if (FORMAT_INT.equals(format)) { + return AttrType.INTEGER; + } else if (FORMAT_STRING.equals(format)) { + return AttrType.STRING; + } else if (FORMAT_FRACTION.equals(format)) { + return AttrType.FRACTION; + } else if (FORMAT_ENUM.equals(format)) { + return AttrType.ENUM; + } else if (FORMAT_FLAG.equals(format)) { + return AttrType.FLAG; + } + + return AttrType.UNKNOWN; + } +} diff --git a/app/src/main/java/com/itsaky/androidide/ui/view/IAttribute.java b/app/src/main/java/com/itsaky/androidide/ui/view/IAttribute.java new file mode 100644 index 000000000..6925916e3 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/ui/view/IAttribute.java @@ -0,0 +1,64 @@ +package com.itsaky.androidide.ui.view; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import java.util.Set; + +/** + * Attribute that can be applied to a {@link IView} + * + * @author Akash Yadav + */ +public interface IAttribute { + + /** + * Set namespace of this attribute + */ + void setNamespace (String namespace); + + /** + * @see setNamespace(String) + */ + @NonNull + String getNamespace (); + + /** + * Set name of this attribute + */ + void setAtrributeName (String name); + + /** + * @see setAtrributeName(String) + */ + @NonNull + String getAttributeName (); + + /** + * Set current value of this attribute + */ + void setValue (String name); + + /** + * @see setValue(String) + */ + @Nullable + String getValue (); + + /** + * Set applicable values of this attribute + * Empty by default + */ + void setApplicableValues(Set name); + + /** + * @see setAtrributeName(String) + */ + @NonNull + String getApplicableValues (); + + /** + * Get the value type of this attribute + */ + @NonNull + AttrType getType (); +} diff --git a/app/src/main/java/com/itsaky/androidide/ui/view/IView.java b/app/src/main/java/com/itsaky/androidide/ui/view/IView.java new file mode 100644 index 000000000..300873694 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/ui/view/IView.java @@ -0,0 +1,39 @@ +package com.itsaky.androidide.ui.view; + +import android.view.View; + +/** + * Represents a view in the UI Designer + * + * @author Akash Yadav + */ +public interface IView { + + /** + * Get this view as android {@link View}. This will be used to display this view in the designer + * + * @return {@link View} of this object. Maybe {@code null} + */ + View asView(); + + /** + * Add this attribute to this view + * + * @param attr The Attribute to add + */ + void addAttribute (IAttribute attr); + + /** + * Remove this attribute + * + * @param attr The Attribute to remove + */ + void removeAttribute (IAttribute attr); + + /** + * Remove attribute at index + * + * @param index Index of the attribute to remove + */ + void removeAttributeAt (int index); +} diff --git a/app/src/main/java/com/itsaky/androidide/ui/view/IViewGroup.java b/app/src/main/java/com/itsaky/androidide/ui/view/IViewGroup.java new file mode 100644 index 000000000..b282a84d6 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/ui/view/IViewGroup.java @@ -0,0 +1,39 @@ +package com.itsaky.androidide.ui.view; + +/** + * Represents a {@link android.view.ViewGroup} + * + * @author Akash Yadav + */ +public interface IViewGroup extends IView { + + /** + * Add this view to this group + * + * @param view The view to add + */ + void addView (IView view); + + /** + * Add this view to this group + * + * @param view The view to add + * @param index Add this view to this index + */ + void addView (IView view, int index); + + /** + * Get the number of child views this layout contains + * + * @return The child count + */ + int getChildCount(); + + /** + * Get child at the provided index + * + * @param index The index of child + * @throws IndexOutOfBoundsException If there is no child at the given index + */ + IView getChildAt (int index); +} diff --git a/app/src/main/java/com/itsaky/toaster/Toaster.java b/app/src/main/java/com/itsaky/toaster/Toaster.java index 747b59aab..6a6ebdd3d 100644 --- a/app/src/main/java/com/itsaky/toaster/Toaster.java +++ b/app/src/main/java/com/itsaky/toaster/Toaster.java @@ -70,6 +70,8 @@ public class Toaster { } public void show() { + + // Always run on UI Thread ThreadUtils.runOnUiThread(() -> { showInternal(); }); diff --git a/app/src/main/java/io/github/rosemoe/editor/text/SpanMapUpdater.java b/app/src/main/java/io/github/rosemoe/editor/text/SpanMapUpdater.java index d5edd776a..7eec6117c 100644 --- a/app/src/main/java/io/github/rosemoe/editor/text/SpanMapUpdater.java +++ b/app/src/main/java/io/github/rosemoe/editor/text/SpanMapUpdater.java @@ -15,11 +15,10 @@ */ package io.github.rosemoe.editor.text; -import java.util.ArrayList; -import java.util.List; - import io.github.rosemoe.editor.struct.Span; import io.github.rosemoe.editor.widget.EditorColorScheme; +import java.util.ArrayList; +import java.util.List; /** * Update spans on text change event @@ -29,6 +28,11 @@ import io.github.rosemoe.editor.widget.EditorColorScheme; public class SpanMapUpdater { public static void shiftSpansOnMultiLineDelete(List> map, int startLine, int startColumn, int endLine, int endColumn) { + + if (map == null || map.isEmpty()) { + return; + } + int lineCount = endLine - startLine - 1; // Remove unrelated lines while (lineCount > 0) { @@ -36,7 +40,7 @@ public class SpanMapUpdater { lineCount--; } // Clean up start line - List startLineSpans = map.get(startLine); + List startLineSpans = emptyIfNull(map.get(startLine)); int index = startLineSpans.size() - 1; while (index > 0) { if (startLineSpans.get(index).column >= startColumn) { @@ -47,7 +51,7 @@ public class SpanMapUpdater { } } // Shift end line - List endLineSpans = map.get(startLine + 1); + List endLineSpans = emptyIfNull(map.get(startLine + 1)); while (endLineSpans.size() > 1) { Span first = endLineSpans.get(0); if (first.column >= endColumn) { @@ -76,7 +80,7 @@ public class SpanMapUpdater { if (map == null || map.isEmpty()) { return; } - List spanList = map.get(line); + List spanList = emptyIfNull(map.get(line)); int startIndex = findSpanIndexFor(spanList, 0, startCol); if (startIndex == -1) { //No span is to be updated @@ -114,7 +118,7 @@ public class SpanMapUpdater { if (map == null || map.isEmpty()) { return; } - List spanList = map.get(line); + List spanList = emptyIfNull( map.get(line)); int index = findSpanIndexFor(spanList, 0, startCol); if (index == -1) { return; @@ -138,7 +142,7 @@ public class SpanMapUpdater { public static void shiftSpansOnMultiLineInsert(List> map, int startLine, int startColumn, int endLine, int endColumn) { // Find extended span - List startLineSpans = map.get(startLine); + List startLineSpans = emptyIfNull( map.get(startLine)); int extendedSpanIndex = findSpanIndexFor(startLineSpans, 0, startColumn); if (extendedSpanIndex == -1) { extendedSpanIndex = startLineSpans.size() - 1; @@ -159,7 +163,7 @@ public class SpanMapUpdater { map.add(startLine + 1, list); } // Add original spans to new line - List endLineSpans = map.get(endLine); + List endLineSpans = emptyIfNull(map.get(endLine)); if (endColumn == 0 && extendedSpanIndex + 1 < startLineSpans.size()) { endLineSpans.clear(); } @@ -174,6 +178,7 @@ public class SpanMapUpdater { } private static int findSpanIndexFor(List spans, int initialPosition, int targetCol) { + spans = emptyIfNull(spans); for (int i = initialPosition; i < spans.size(); i++) { if (spans.get(i).column >= targetCol) { return i; @@ -181,5 +186,13 @@ public class SpanMapUpdater { } return -1; } + + private static List emptyIfNull (List original) { + if (original == null) { + original = new ArrayList(); + } + + return original; + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4e5cce5f7..c5de862ad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -258,7 +258,7 @@ Splash image Checking storage permissions Failed to list project files! - Failed to save some files! + Failed to save files! Service Started Starting services Failed to start Completion Services diff --git a/build.gradle b/build.gradle index 0f2d56ee2..57cdaaca9 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ buildscript { javaSourceVersion = JavaVersion.VERSION_1_8 javaTargetVersion = JavaVersion.VERSION_1_8 - stringfogEnabled = false + stringfogEnabled = true } repositories { diff --git a/treeview/src/main/java/com/unnamed/b/atv/model/TreeNode.java b/treeview/src/main/java/com/unnamed/b/atv/model/TreeNode.java index e60153a8a..0e0a7aa51 100644 --- a/treeview/src/main/java/com/unnamed/b/atv/model/TreeNode.java +++ b/treeview/src/main/java/com/unnamed/b/atv/model/TreeNode.java @@ -47,7 +47,7 @@ public class TreeNode { } public TreeNode(File value) { - children = new ArrayList<>(); + children = Collections.synchronizedList(new ArrayList()); mValue = value; } @@ -93,7 +93,7 @@ public class TreeNode { } public List getChildren() { - return children == null ? new ArrayList() : children; + return children == null ? Collections.synchronizedList(new ArrayList()) : children; } public int size() { diff --git a/treeview/src/main/java/com/unnamed/b/atv/view/AndroidTreeView.java b/treeview/src/main/java/com/unnamed/b/atv/view/AndroidTreeView.java index 3c9287b9c..c09fd7b82 100644 --- a/treeview/src/main/java/com/unnamed/b/atv/view/AndroidTreeView.java +++ b/treeview/src/main/java/com/unnamed/b/atv/view/AndroidTreeView.java @@ -223,10 +223,9 @@ public class AndroidTreeView { node.setExpanded(true); final TreeNode.BaseNodeViewHolder parentViewHolder = getViewHolderForNode(node); parentViewHolder.getNodeItemsView().removeAllViews(); - - + parentViewHolder.toggle(true); - + final Iterator nodes = node.getChildren().iterator(); while (nodes.hasNext()) { final TreeNode n = nodes.next();