From 1a2920cb4654149923d7399aadd32181972ed0ac Mon Sep 17 00:00:00 2001
From: github-actions <>
Date: Fri, 4 Mar 2022 08:06:27 +0000
Subject: [PATCH] Format code
---
.../com/itsaky/androidide/AboutActivity.java | 339 +-
.../androidide/CrashHandlerActivity.java | 52 +-
.../itsaky/androidide/DesignerActivity.java | 694 +-
.../itsaky/androidide/DownloadActivity.java | 498 +-
.../com/itsaky/androidide/EditorActivity.java | 2937 +-
.../com/itsaky/androidide/MainActivity.java | 776 +-
.../androidide/PreferencesActivity.java | 54 +-
.../com/itsaky/androidide/SplashActivity.java | 113 +-
.../itsaky/androidide/TerminalActivity.java | 867 +-
.../androidide/adapters/AttrListAdapter.java | 80 +-
.../adapters/CompletionListAdapter.java | 294 +-
.../adapters/DiagnosticItemAdapter.java | 123 +-
.../adapters/DiagnosticsAdapter.java | 106 +-
.../adapters/EditorBottomSheetTabAdapter.java | 173 +-
.../androidide/adapters/IconTextAdapter.java | 134 +-
.../adapters/OptionsSheetAdapter.java | 91 +-
.../adapters/ProjectsListAdapter.java | 226 +-
.../adapters/SearchListAdapter.java | 201 +-
.../adapters/SimpleIconTextAdapter.java | 46 +-
.../adapters/SymbolInputAdapter.java | 168 +-
.../adapters/TextActionItemAdapter.java | 123 +-
.../adapters/WidgetGroupItemAdapter.java | 71 +-
.../adapters/WidgetItemAdapter.java | 75 +-
.../adapters/XMLAttributeListAdapter.java | 124 +-
.../viewholders/FileTreeViewHolder.java | 131 +-
.../itsaky/androidide/app/StudioActivity.java | 186 +-
.../com/itsaky/androidide/app/StudioApp.java | 198 +-
.../androidide/fragments/BaseFragment.java | 7 +-
.../fragments/CrashReportFragment.java | 200 +-
.../fragments/DiagnosticsListFragment.java | 85 +-
.../fragments/FileTreeFragment.java | 397 +-
.../androidide/fragments/IDELogFragment.java | 14 +-
.../androidide/fragments/LogViewFragment.java | 66 +-
.../fragments/NonEditableEditorFragment.java | 69 +-
.../fragments/ProjectsFragment.java | 192 +-
.../fragments/SearchResultFragment.java | 82 +-
.../fragments/SimpleOutputFragment.java | 44 +-
.../preferences/BasePreferenceFragment.java | 38 +-
.../preferences/BuildPreferences.java | 215 +-
.../preferences/EditorPreferences.java | 466 +-
.../preferences/GeneralPreferences.java | 100 +-
.../fragments/preferences/IDEPreferences.java | 294 +-
.../fragments/sheets/AttrEditorSheet.java | 587 +-
.../fragments/sheets/AttributeListSheet.java | 144 +-
.../sheets/BaseBottomSheetFragment.java | 132 +-
.../fragments/sheets/OptionsListFragment.java | 107 +-
.../fragments/sheets/ProgressSheet.java | 166 +-
.../fragments/sheets/TextSheetFragment.java | 184 +-
.../handlers/BuildServiceHandler.java | 383 +-
.../handlers/FileOptionsHandler.java | 716 +-
.../androidide/handlers/IDEHandler.java | 80 +-
.../interfaces/DiagnosticClickListener.java | 5 +-
.../interfaces/EditorActivityProvider.java | 11 +-
.../language/CommonCompletionProvider.java | 171 +-
.../androidide/language/IDELanguage.java | 35 +-
.../language/groovy/GroovyAnalyzer.java | 433 +-
.../language/groovy/GroovyAutoComplete.java | 667 +-
.../language/groovy/GroovyLanguage.java | 241 +-
.../language/java/JavaAnalyzer.java | 623 +-
.../language/java/JavaLanguage.java | 267 +-
.../language/logs/LogLanguageImpl.java | 154 +-
.../androidide/language/xml/XMLAnalyzer.java | 212 +-
.../androidide/language/xml/XMLLanguage.java | 201 +-
.../androidide/lsp/IDELanguageClientImpl.java | 594 +-
.../itsaky/androidide/models/ApkMetadata.java | 375 +-
.../androidide/models/DiagnosticGroup.java | 21 +-
.../androidide/models/IconTextListItem.java | 28 +-
.../com/itsaky/androidide/models/License.java | 22 +-
.../com/itsaky/androidide/models/LogLine.java | 208 +-
.../androidide/models/NewProjectDetails.java | 78 +-
.../models/PrefBasedJavaServerSettings.java | 35 +-
.../androidide/models/ProjectTemplate.java | 91 +-
.../androidide/models/SearchResult.java | 19 +-
.../itsaky/androidide/models/UIWidget.java | 150 +-
.../androidide/models/UIWidgetGroup.java | 237 +-
.../androidide/models/XMLAttribute.java | 248 +-
.../androidide/project/AndroidProject.java | 320 +-
.../itsaky/androidide/project/IDEModule.java | 62 +-
.../itsaky/androidide/project/IDEProject.java | 107 +-
.../itsaky/androidide/project/IDETask.java | 32 +-
.../project/ProjectFileObserver.java | 16 +-
.../project/ProjectResourceTable.java | 430 +-
.../androidide/services/LogReceiver.java | 57 +-
.../androidide/services/MessagingService.java | 175 +-
.../services/builder/BuildListener.java | 95 +-
.../services/builder/IDEService.java | 777 +-
.../builder/MainThreadBuildListener.java | 84 +-
.../syntax/colorschemes/SchemeAndroidIDE.java | 269 +-
.../syntax/highlighters/Highlighter.java | 2 +-
.../syntax/highlighters/JavaHighlighter.java | 348 +-
.../androidide/tasks/BaseGradleTask.java | 32 +-
.../itsaky/androidide/tasks/GradleTask.java | 64 +-
.../itsaky/androidide/tasks/TaskExecutor.java | 82 +-
.../tasks/callables/FileTreeCallable.java | 82 +-
.../tasks/callables/LicenseReader.java | 34 +-
.../callables/ListDirectoryCallable.java | 34 +-
.../callables/ProjectCreatorCallable.java | 29 +-
.../tasks/callables/UnzipCallable.java | 21 +-
.../tasks/gradle/BaseGradleTasks.java | 22 +-
.../tasks/gradle/build/ApkGeneratingTask.java | 187 +-
.../tasks/gradle/build/AssembleDebug.java | 71 +-
.../tasks/gradle/build/AssembleRelease.java | 61 +-
.../androidide/tasks/gradle/build/Build.java | 63 +-
.../androidide/tasks/gradle/build/Bundle.java | 49 +-
.../androidide/tasks/gradle/build/Clean.java | 49 +-
.../tasks/gradle/build/CleanBuild.java | 49 +-
.../androidide/tasks/gradle/lint/Lint.java | 49 +-
.../tasks/gradle/lint/LintDebug.java | 49 +-
.../tasks/gradle/lint/LintRelease.java | 49 +-
.../gradle/plugin/InitializeIDEProject.java | 49 +-
.../DatabindingGenBaseClassesDebug.java | 57 +-
.../resources/ProcessDebugResources.java | 57 +-
.../resources/UpdateResourceClassesTask.java | 89 +-
.../itsaky/androidide/ui/WidgetDragData.java | 42 +-
.../androidide/ui/WidgetDragListener.java | 318 +-
.../ui/WidgetDragShadowBuilder.java | 30 +-
.../androidide/ui/WidgetTouchListener.java | 120 +-
.../ui/edit/BaseSuggestionProvider.java | 37 +-
.../ui/edit/DimensionSuggestionProvider.java | 86 +-
.../ui/edit/IValueSuggestionProvider.java | 36 +-
.../androidide/utils/AttributeDialogs.java | 512 +-
.../itsaky/androidide/utils/DialogUtils.java | 125 +-
.../utils/EditorBottomSheetBehavior.java | 60 +-
.../utils/HighlightRangeHelper.java | 213 +-
.../itsaky/androidide/utils/JSONUtility.java | 4 +-
.../com/itsaky/androidide/utils/LSPUtils.java | 237 +-
.../androidide/utils/ProjectFinder.java | 298 +-
.../androidide/utils/ProjectWriter.java | 330 +-
.../utils/RecursiveFileSearcher.java | 222 +-
.../com/itsaky/androidide/utils/Symbols.java | 148 +-
.../androidide/utils/TextWatcherAdapter.java | 13 +-
.../androidide/utils/TransformUtils.java | 32 +-
.../androidide/viewmodel/EditorViewModel.java | 270 +-
.../views/DesignerLayoutContainer.java | 92 +-
.../itsaky/androidide/views/EmptyView.java | 51 +-
.../androidide/views/MaterialBanner.java | 290 +-
.../androidide/views/SymbolInputView.java | 88 +-
.../androidide/views/ValueEditorTextView.java | 61 +-
.../views/editor/CodeEditorView.java | 584 +-
.../views/editor/DiagnosticWindow.java | 57 +-
.../views/editor/EditorTextActionMode.java | 294 +-
.../views/editor/EditorTextActionWindow.java | 486 +-
.../androidide/views/editor/IDEEditor.java | 1866 +-
.../views/editor/SignatureHelpWindow.java | 251 +-
.../views/editor/SimpleTextWindow.java | 101 +-
.../views/transition/ArcMotionPlus.java | 159 +-
.../views/transition/CubicBezierArc.java | 268 +-
.../views/transition/ProgressTransition.java | 82 +-
.../androidide/app/BaseApplication.java | 422 +-
.../colorpicker/ColorPickerView.java | 323 +-
.../itsaky/androidide/interfaces/Filter.java | 2 +-
.../interfaces/ProjectWriterCallback.java | 8 +-
.../managers/PreferenceManager.java | 250 +-
.../androidide/managers/ToolsManager.java | 194 +-
.../managers/VersionedFileManager.java | 30 +-
.../androidide/models/ConstantsBridge.java | 70 +-
.../com/itsaky/androidide/models/DexPair.java | 60 +-
.../itsaky/androidide/models/SaveResult.java | 18 +-
.../itsaky/androidide/models/SheetOption.java | 58 +-
.../shell/CommonProcessExecutor.java | 180 +-
.../androidide/shell/IProcessExecutor.java | 109 +-
.../shell/IProcessExitListener.java | 2 +-
.../shell/ProcessExecutorFactory.java | 9 +-
.../shell/ProcessStreamsHolder.java | 6 +-
.../itsaky/androidide/shell/ShellServer.java | 216 +-
.../androidide/utils/BootstrapInstaller.java | 257 +-
.../utils/CharSequenceInputStream.java | 471 +-
.../androidide/utils/CharSequenceReader.java | 484 +-
.../itsaky/androidide/utils/Environment.java | 427 +-
.../com/itsaky/androidide/utils/FileUtil.java | 967 +-
.../utils/InputStreamLineReader.java | 42 +-
.../androidide/utils/InputStreamWriter.java | 50 +-
.../itsaky/androidide/utils/IntProperty.java | 24 +-
.../androidide/utils/JavaCharacter.java | 114 +-
.../com/itsaky/androidide/utils/Logger.java | 192 +-
.../androidide/utils/ReaderInputStream.java | 484 +-
.../itsaky/androidide/utils/StudioUtils.java | 59 +-
.../androidide/utils/TypefaceUtils.java | 25 +-
.../java/com/itsaky/toaster/ToastWrapper.java | 94 +-
.../main/java/com/itsaky/toaster/Toaster.java | 259 +-
.../views/virtualkeys/SpecialButton.java | 71 +-
.../views/virtualkeys/SpecialButtonState.java | 79 +-
.../views/virtualkeys/VirtualKeyButton.java | 273 +-
.../virtualkeys/VirtualKeysConstants.java | 448 +-
.../views/virtualkeys/VirtualKeysInfo.java | 261 +-
.../views/virtualkeys/VirtualKeysView.java | 1102 +-
.../java/com/itsaky/terminal/ByteQueue.java | 191 +-
.../main/java/com/itsaky/terminal/JNI.java | 86 +-
.../java/com/itsaky/terminal/KeyHandler.java | 667 +-
.../com/itsaky/terminal/TerminalBuffer.java | 1014 +-
.../itsaky/terminal/TerminalColorScheme.java | 620 +-
.../com/itsaky/terminal/TerminalColors.java | 133 +-
.../com/itsaky/terminal/TerminalEmulator.java | 5464 ++--
.../com/itsaky/terminal/TerminalOutput.java | 40 +-
.../java/com/itsaky/terminal/TerminalRow.java | 522 +-
.../com/itsaky/terminal/TerminalSession.java | 634 +-
.../terminal/TerminalSessionClient.java | 32 +-
.../java/com/itsaky/terminal/TextStyle.java | 127 +-
.../java/com/itsaky/terminal/WcWidth.java | 982 +-
.../view/GestureAndScaleRecognizer.java | 210 +-
.../terminal/view/TerminalRenderer.java | 563 +-
.../itsaky/terminal/view/TerminalView.java | 2490 +-
.../terminal/view/TerminalViewClient.java | 55 +-
.../view/textselection/CursorController.java | 57 +-
.../TextSelectionCursorController.java | 688 +-
.../TextSelectionHandleView.java | 597 +-
.../androidide/lexers/groovy/GroovyLexer.java | 1794 +-
.../androidide/lexers/java/JavaLexer.java | 1775 +-
.../androidide/lexers/java/JavaParser.java | 26692 ++++++++--------
.../lexers/java/JavaParserBaseListener.java | 3024 +-
.../lexers/java/JavaParserBaseVisitor.java | 2113 +-
.../lexers/java/JavaParserListener.java | 2532 +-
.../lexers/java/JavaParserVisitor.java | 1472 +-
.../lexers/javadoc/JavadocLexer.java | 375 +-
.../lexers/javadoc/JavadocParser.java | 4607 +--
.../javadoc/JavadocParserBaseListener.java | 675 +-
.../javadoc/JavadocParserBaseVisitor.java | 444 +-
.../lexers/javadoc/JavadocParserListener.java | 528 +-
.../lexers/javadoc/JavadocParserVisitor.java | 308 +-
.../androidide/lexers/xml/XMLLexer.java | 503 +-
.../androidide/lexers/xml/XMLParser.java | 2150 +-
.../lexers/xml/XMLParserBaseListener.java | 364 +-
.../lexers/xml/XMLParserBaseVisitor.java | 222 +-
.../lexers/xml/XMLParserListener.java | 264 +-
.../lexers/xml/XMLParserVisitor.java | 154 +-
.../lsp/api/AbstractServiceProvider.java | 28 +-
.../lsp/api/ConfigurableServiceProvider.java | 15 +-
.../itsaky/lsp/api/ICodeActionProvider.java | 19 +-
.../itsaky/lsp/api/ICompletionProvider.java | 26 +-
.../itsaky/lsp/api/IDefinitionProvider.java | 17 +-
.../itsaky/lsp/api/IDiagnosticProvider.java | 18 +-
.../com/itsaky/lsp/api/IDocumentHandler.java | 58 +-
.../com/itsaky/lsp/api/ILanguageClient.java | 30 +-
.../com/itsaky/lsp/api/ILanguageServer.java | 227 +-
.../itsaky/lsp/api/IReferenceProvider.java | 18 +-
.../itsaky/lsp/api/ISelectionProvider.java | 21 +-
.../com/itsaky/lsp/api/IServerSettings.java | 99 +-
.../lsp/api/ISignatureHelpProvider.java | 17 +-
.../itsaky/lsp/util/NoDocumentHandler.java | 16 +-
.../java/com/itsaky/lsp/util/StringUtils.java | 51 +-
.../itsaky/androidide/utils/ClassBuilder.java | 85 +-
.../java/com/itsaky/lsp/java/FileStore.java | 536 +-
.../itsaky/lsp/java/JavaLanguageServer.java | 338 +-
.../lsp/java/compiler/CompileBatch.java | 240 +-
.../itsaky/lsp/java/compiler/CompileTask.java | 74 +-
.../lsp/java/compiler/CompilerProvider.java | 28 +-
.../java/compiler/JavaCompilerService.java | 599 +-
.../lsp/java/compiler/ReusableCompiler.java | 410 +-
.../lsp/java/compiler/SourceFileManager.java | 176 +-
.../lsp/java/compiler/SourceFileObject.java | 222 +-
.../lsp/java/compiler/SynchronizedTask.java | 119 +-
.../java/models/JavaServerConfiguration.java | 72 +-
.../com/itsaky/lsp/java/parser/ParseTask.java | 12 +-
.../com/itsaky/lsp/java/parser/Parser.java | 761 +-
.../java/providers/CodeActionProvider.java | 753 +-
.../java/providers/CompletionProvider.java | 1897 +-
.../java/providers/DefinitionProvider.java | 263 +-
.../java/providers/DiagnosticsProvider.java | 387 +-
.../providers/JavaDiagnosticProvider.java | 42 +-
.../java/providers/JavaSelectionProvider.java | 45 +-
.../lsp/java/providers/ReferenceProvider.java | 143 +-
.../providers/SemanticHighlightProvider.java | 55 +-
.../lsp/java/providers/SignatureProvider.java | 531 +-
.../itsaky/lsp/java/rewrite/AddException.java | 99 +-
.../itsaky/lsp/java/rewrite/AddImport.java | 89 +-
.../rewrite/AddSuppressWarningAnnotation.java | 73 +-
.../lsp/java/rewrite/ConvertFieldToBlock.java | 84 +-
.../rewrite/ConvertVariableToStatement.java | 109 +-
.../lsp/java/rewrite/CreateMissingMethod.java | 414 +-
.../rewrite/GenerateRecordConstructor.java | 186 +-
.../rewrite/ImplementAbstractMethods.java | 121 +-
.../java/rewrite/OverrideInheritedMethod.java | 150 +-
.../itsaky/lsp/java/rewrite/RemoveClass.java | 28 +-
.../lsp/java/rewrite/RemoveException.java | 267 +-
.../itsaky/lsp/java/rewrite/RemoveMethod.java | 53 +-
.../com/itsaky/lsp/java/rewrite/Rewrite.java | 11 +-
.../lsp/java/rewrite/RewriteNotSupported.java | 9 +-
.../java/com/itsaky/lsp/java/utils/Cache.java | 105 +-
.../com/itsaky/lsp/java/utils/EditHelper.java | 242 +-
.../com/itsaky/lsp/java/utils/Extractors.java | 35 +-
.../com/itsaky/lsp/java/utils/FindHelper.java | 252 +-
.../itsaky/lsp/java/utils/MarkdownHelper.java | 317 +-
.../lsp/java/utils/NavigationHelper.java | 92 +-
.../itsaky/lsp/java/utils/ScanClassPath.java | 62 +-
.../itsaky/lsp/java/utils/ScopeHelper.java | 113 +-
.../lsp/java/utils/ShortTypePrinter.java | 261 +-
.../itsaky/lsp/java/utils/StringSearch.java | 863 +-
.../lsp/java/visitors/DiagnosticVisitor.java | 462 +-
.../lsp/java/visitors/FindBiggerRange.java | 131 +-
.../lsp/java/visitors/FindCompletionsAt.java | 189 +-
.../lsp/java/visitors/FindInvocationAt.java | 72 +-
.../lsp/java/visitors/FindMethodCallAt.java | 402 +-
.../visitors/FindMethodDeclarationAt.java | 54 +-
.../itsaky/lsp/java/visitors/FindNameAt.java | 161 +-
.../lsp/java/visitors/FindReferences.java | 72 +-
.../java/visitors/FindTypeDeclarationAt.java | 62 +-
.../visitors/FindTypeDeclarationNamed.java | 45 +-
.../java/visitors/FindTypeDeclarations.java | 31 +-
.../java/visitors/FindVariableAtCursor.java | 54 +-
.../lsp/java/visitors/PruneMethodBodies.java | 81 +-
.../java/visitors/SemanticHighlighter.java | 334 +-
.../com/squareup/javapoet/AnnotationSpec.java | 432 +-
.../com/squareup/javapoet/ArrayTypeName.java | 141 +-
.../java/com/squareup/javapoet/ClassName.java | 530 +-
.../java/com/squareup/javapoet/CodeBlock.java | 794 +-
.../com/squareup/javapoet/CodeWriter.java | 940 +-
.../java/com/squareup/javapoet/FieldSpec.java | 245 +-
.../java/com/squareup/javapoet/JavaFile.java | 544 +-
.../com/squareup/javapoet/LineWrapper.java | 255 +-
.../com/squareup/javapoet/MethodSpec.java | 922 +-
.../com/squareup/javapoet/NameAllocator.java | 157 +-
.../com/squareup/javapoet/ParameterSpec.java | 318 +-
.../javapoet/ParameterizedTypeName.java | 208 +-
.../java/com/squareup/javapoet/TypeName.java | 614 +-
.../java/com/squareup/javapoet/TypeSpec.java | 1604 +-
.../squareup/javapoet/TypeVariableName.java | 269 +-
.../main/java/com/squareup/javapoet/Util.java | 195 +-
.../squareup/javapoet/WildcardTypeName.java | 187 +-
.../java/com/sun/tools/javac/file/FSInfo.java | 141 +-
.../com/sun/tools/javac/file/Locations.java | 3888 +--
.../java/javax/lang/model/SourceVersion.java | 622 +-
.../main/java/jdk/internal/jmod/JmodFile.java | 305 +-
.../internal/jrtfs/JrtFileSystemProvider.java | 511 +-
.../java/jdk/internal/jrtfs/SystemImage.java | 122 +-
.../com/itsaky/lsp/xml/XMLLanguageServer.java | 214 +-
.../lsp/xml/providers/CompletionProvider.java | 513 +-
.../com/itsaky/lsp/xml/utils/XmlUtils.java | 38 +-
.../main/java/com/itsaky/apiinfo/ApiInfo.java | 265 +-
.../com/itsaky/apiinfo/models/ClassInfo.java | 111 +-
.../java/com/itsaky/apiinfo/models/Info.java | 16 +-
.../com/itsaky/apiinfo/models/MethodInfo.java | 14 +-
.../java/com/itsaky/attrinfo/AttrInfo.java | 333 +-
.../java/com/itsaky/attrinfo/models/Attr.java | 294 +-
.../com/itsaky/attrinfo/models/Styleable.java | 56 +-
.../src/main/java/com/itsaky/sdk/SDKInfo.java | 39 +-
.../java/com/itsaky/widgets/WidgetInfo.java | 127 +-
.../com/itsaky/widgets/models/Widget.java | 32 +-
.../main/java/com/itsaky/xml/INamespace.java | 45 +-
.../java/com/itsaky/xml/impl/UiNamespace.java | 136 +-
.../java/org/eclipse/jdt/core/Signature.java | 5575 ++--
.../jdt/core/compiler/CharOperation.java | 7751 ++---
.../jdt/internal/compiler/ast/ASTNode.java | 240 +-
.../compiler/classfmt/ClassFileConstants.java | 357 +-
.../compiler/parser/ScannerHelper.java | 1336 +-
.../jdt/internal/compiler/util/Util.java | 1202 +-
.../b/atv/holder/SimpleViewHolder.java | 23 +-
.../com/unnamed/b/atv/model/TreeNode.java | 550 +-
.../unnamed/b/atv/view/AndroidTreeView.java | 904 +-
.../b/atv/view/TreeNodeWrapperView.java | 61 +-
.../unnamed/b/atv/view/TwoDScrollView.java | 2048 +-
.../java/com/itsaky/inflater/AttrType.java | 148 +-
.../itsaky/inflater/BaseLayoutInflater.java | 78 +-
.../java/com/itsaky/inflater/IAttribute.java | 127 +-
.../itsaky/inflater/IAttributeAdapter.java | 46 +-
.../java/com/itsaky/inflater/IDTable.java | 89 +-
.../java/com/itsaky/inflater/IFixedValue.java | 36 +-
.../com/itsaky/inflater/IInflateListener.java | 44 +-
.../com/itsaky/inflater/ILayoutInflater.java | 281 +-
.../com/itsaky/inflater/IResourceTable.java | 131 +-
.../main/java/com/itsaky/inflater/IView.java | 321 +-
.../java/com/itsaky/inflater/IViewGroup.java | 212 +-
.../com/itsaky/inflater/InflateException.java | 18 +-
.../inflater/LayoutInflaterConfiguration.java | 101 +-
.../inflater/NotSupportedException.java | 20 +-
.../itsaky/inflater/XMLLayoutInflater.java | 776 +-
.../android/view/ViewAttrAdapter.java | 1038 +-
.../android/view/ViewGroupAttrAdapter.java | 129 +-
.../android/widget/AbsSeekBarAttrAdapter.java | 86 +-
.../android/widget/AbsSpinnerAttrAdapter.java | 76 +-
.../widget/AdapterViewAttrAdapter.java | 62 +-
.../android/widget/ButtonAttrAdapter.java | 27 +-
.../android/widget/CheckBoxAttrAdapter.java | 28 +-
.../widget/CheckedTextViewAttrAdapter.java | 80 +-
.../widget/CompondButtonAttrAdapter.java | 80 +-
.../android/widget/EditTextAttrAdapter.java | 28 +-
.../widget/FrameLayoutAttrAdapter.java | 70 +-
.../widget/ImageButtonAttrAdapter.java | 28 +-
.../android/widget/ImageViewAttrAdapter.java | 174 +-
.../widget/LinearLayoutAttrAdapter.java | 112 +-
.../widget/ProgressBarAttrAdapter.java | 194 +-
.../widget/RadioButtonAttrAdapter.java | 28 +-
.../widget/RelativeLayoutAttrAdapter.java | 74 +-
.../android/widget/SeekBarAttrAdapter.java | 65 +-
.../android/widget/SpinnerAttrAdapter.java | 95 +-
.../android/widget/TextViewAttrAdapter.java | 377 +-
.../drawable/BitmapDrawableParser.java | 207 +-
.../inflater/drawable/ClipDrawableParser.java | 85 +-
.../drawable/DrawableParserFactory.java | 303 +-
.../inflater/drawable/IDrawableParser.java | 222 +-
.../drawable/InsetDrawableParser.java | 70 +-
.../inflater/drawable/LayerListParser.java | 179 +-
.../drawable/ScaleDrawableParser.java | 127 +-
.../drawable/ShapeDrawableParser.java | 572 +-
.../inflater/drawable/StateListParser.java | 164 +-
.../com/itsaky/inflater/impl/BaseView.java | 566 +-
.../itsaky/inflater/impl/BaseViewGroup.java | 147 +-
.../com/itsaky/inflater/impl/ErrorUiView.java | 115 +-
.../itsaky/inflater/impl/IncludeLayout.java | 20 +-
.../com/itsaky/inflater/impl/UiAttribute.java | 114 +-
.../java/com/itsaky/inflater/impl/UiView.java | 12 +-
.../com/itsaky/inflater/impl/UiViewGroup.java | 141 +-
.../inflater/util/CommonParseUtils.java | 704 +-
.../itsaky/inflater/util/Preconditions.java | 42 +-
.../itsaky/inflater/util/TextDrawable.java | 86 +-
.../inflater/values/IResourceValue.java | 30 +-
.../itsaky/inflater/values/ValuesTable.java | 624 +-
.../inflater/values/ValuesTableFactory.java | 174 +-
.../values/models/AbstractResourceValue.java | 95 +-
.../inflater/values/models/ArrayResource.java | 67 +-
.../inflater/values/models/BooleanValue.java | 42 +-
.../inflater/values/models/ColorValue.java | 31 +-
.../values/models/DimensionValue.java | 6 +-
.../inflater/values/models/IntegerValue.java | 30 +-
.../inflater/values/models/StringValue.java | 6 +-
.../harjot/vectormaster/DefaultValues.java | 78 +-
.../vectormaster/VectorMasterDrawable.java | 1032 +-
.../harjot/vectormaster/VectorMasterView.java | 796 +-
.../harjot/vectormaster/enums/TintMode.java | 12 +-
.../vectormaster/models/ClipPathModel.java | 125 +-
.../vectormaster/models/GroupModel.java | 454 +-
.../harjot/vectormaster/models/PathModel.java | 491 +-
.../vectormaster/models/VectorModel.java | 318 +-
.../harjot/vectormaster/utilities/Utils.java | 154 +-
.../utilities/legacyparser/PathParser.java | 1414 +-
.../utilities/parser/ParserHelper.java | 652 +-
.../utilities/parser/PathParser.java | 645 +-
426 files changed, 86887 insertions(+), 84448 deletions(-)
diff --git a/app/src/main/java/com/itsaky/androidide/AboutActivity.java b/app/src/main/java/com/itsaky/androidide/AboutActivity.java
index 7fb61a03d..fcfa99a49 100755
--- a/app/src/main/java/com/itsaky/androidide/AboutActivity.java
+++ b/app/src/main/java/com/itsaky/androidide/AboutActivity.java
@@ -21,178 +21,211 @@ import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.view.View;
+
import androidx.appcompat.widget.TooltipCompat;
import androidx.core.util.Pair;
+
import com.itsaky.androidide.app.StudioActivity;
import com.itsaky.androidide.databinding.ActivityAboutBinding;
import com.itsaky.androidide.databinding.LayoutAboutItemsBinding;
import com.itsaky.androidide.models.License;
+
import java.util.ArrayList;
import java.util.List;
public class AboutActivity extends StudioActivity {
- private ActivityAboutBinding binding;
+ private ActivityAboutBinding binding;
- public static final String JDK_SOURCE = "https://github.com/itsaky/OpenJDK-Android";
+ public static final String JDK_SOURCE = "https://github.com/itsaky/OpenJDK-Android";
- @Override
- protected View bindLayout() {
- binding = ActivityAboutBinding.inflate(getLayoutInflater());
- return binding.getRoot();
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- binding.items.footerText.setText(getFooter());
-
- setupTranslations();
- setupLicenses();
-
- LayoutAboutItemsBinding items = binding.items;
- TooltipCompat.setTooltipText(items.discuss, getString(R.string.discussions_on_telegram));
- TooltipCompat.setTooltipText(items.github, getString(R.string.title_github));
- TooltipCompat.setTooltipText(items.email, getString(R.string.about_option_email));
- TooltipCompat.setTooltipText(items.website, getString(R.string.about_option_website));
- items.github.setOnClickListener(
- v -> {
- getApp().openGitHub();
- });
- items.discuss.setOnClickListener(
- v -> {
- getApp().openTelegramGroup();
- });
- items.email.setOnClickListener(
- v -> {
- getApp().emailUs();
- });
- items.website.setOnClickListener(
- v -> {
- getApp().openWebsite();
- });
- }
-
- private void setupTranslations() {
- final var translations = createTranslationsList();
- final var sb = new StringBuilder();
-
- for (var translation : translations) {
- final var author = translation.first;
- final var language = translation.second;
-
- sb.append(author);
- sb.append(" - ");
- sb.append(language);
- sb.append("\n");
+ @Override
+ protected View bindLayout() {
+ binding = ActivityAboutBinding.inflate(getLayoutInflater());
+ return binding.getRoot();
}
- binding.items.translations.setText(sb);
- }
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
- /**
- * Create the list of translations. The list items are pairs. The first item of the pair is the
- * name of the language. The second item is the name of the person who made the translations.
- *
- *
If you're adding translations, make sure you add an item to this list. You can choose to
- * display the language in a localized version. For example : Language: हिन्दी - < name of author
- * >
- *
- *
Prefer using unicode escapes for language names. For example, हिन्दी can be written as :
- * Language: \u0939\u093f\u0928\u094d\u0926\u0940 - < name of author >
- *
- * @return The translations list.
- */
- private List> createTranslationsList() {
- final var list = new ArrayList>();
+ binding.items.footerText.setText(getFooter());
- list.add(Pair.create("Bahasa Indonesia", "Fitrah Nuno Syahbani"));
- list.add(Pair.create("\u4e2d\u6587", "Rosemoe")); // 中文
- list.add(Pair.create("Deutsch", "Marvin Stelter"));
- list.add(Pair.create("\u0939\u093f\u0928\u094d\u0926\u0940", "Premjit Chowdhury")); // हिन्दी
- list.add(Pair.create("Russian", "Smooth-E"));
- list.add(Pair.create("French", "Se-Lyan"));
+ setupTranslations();
+ setupLicenses();
- return list;
- }
-
- private void setupLicenses() {
- final var licenses = new ArrayList();
- licenses.add(
- new License(
- "AndroidX Libraries", "Apache License 2.0", "https://github.com/androidx/androidx"));
- licenses.add(
- new License("Gradle Build Tool", "Apache License 2.0", "https://github.com/gradle/gradle"));
- licenses.add(new License("Gson", "Apache License 2.0", "https://github.com/google/gson"));
- licenses.add(
- new License(
- "Material Components for Android",
- "Apache License 2.0",
- "https://github.com/material-components/material-components-android"));
- licenses.add(
- new License("ANTLR4 Runtime", "BSD 3-clause License", "https://github.com/antlr/antlr4"));
- licenses.add(
- new License("Glide", "BSD, part MIT and Apache 2.0", "https://github.com/bumptech/glide"));
- licenses.add(
- new License(
- "Android UtilCode", "Apache License 2.0", "https://github.com/Blankj/AndroidUtilCode"));
- licenses.add(
- new License(
- "UnicornFilePicker",
- "Apache License 2.0",
- "https://github.com/abhishekti7/UnicornFilePicker"));
- licenses.add(
- new License("QuickAction", "Apache License 2.0", "https://github.com/piruin/quickaction"));
- licenses.add(
- new License("XmlToJson", "Apache License 2.0", "https://github.com/smart-fun/XmlToJson"));
- licenses.add(
- new License(
- "AndroidTreeView",
- "Apache License 2.0",
- "https://github.com/bmelnychuk/AndroidTreeView"));
- licenses.add(
- new License(
- "CodeEditor v0.5.2", "Apache License 2.0", "https://github.com/Rosemoe/CodeEditor"));
- licenses.add(
- new License("Guava-Android", "Apache License 2.0", "https://github.com/google/guava"));
- licenses.add(new License("Jsoup", "MIT License", "https://github.com/jhy/jsoup"));
- licenses.add(
- new License("Termux [Terminal Emulator]", "GPL 3", "https://github.com/termux/termux-app"));
- licenses.add(
- new License("JavaPoet", "Apache License 2.0", "https://github.com/square/JavaPoet"));
- licenses.add(
- new License("Zip4j", "Apache License 2.0", "https://github.com/srikanth-lingala/zip4j"));
-
- StringBuilder sb = new StringBuilder();
- for (License license : licenses) {
- sb.append("\u2022 ");
- sb.append(license.name);
- sb.append("
");
- sb.append(getString(R.string.msg_about_licensed_under));
- sb.append(license.license);
- sb.append("
");
- sb.append(license.url);
- sb.append("");
- sb.append("
");
+ LayoutAboutItemsBinding items = binding.items;
+ TooltipCompat.setTooltipText(items.discuss, getString(R.string.discussions_on_telegram));
+ TooltipCompat.setTooltipText(items.github, getString(R.string.title_github));
+ TooltipCompat.setTooltipText(items.email, getString(R.string.about_option_email));
+ TooltipCompat.setTooltipText(items.website, getString(R.string.about_option_website));
+ items.github.setOnClickListener(
+ v -> {
+ getApp().openGitHub();
+ });
+ items.discuss.setOnClickListener(
+ v -> {
+ getApp().openTelegramGroup();
+ });
+ items.email.setOnClickListener(
+ v -> {
+ getApp().emailUs();
+ });
+ items.website.setOnClickListener(
+ v -> {
+ getApp().openWebsite();
+ });
}
- sb.append(getString(R.string.license_jdk));
- sb.append(String.format("
%1$s", JDK_SOURCE));
- sb.append("
");
- binding.items.licenses.setText(Html.fromHtml(sb.toString()));
- binding.items.licenses.setMovementMethod(LinkMovementMethod.getInstance());
- }
- private String getFooter() {
- final String arch = android.os.Build.SUPPORTED_ABIS[0];
- try {
- final String version =
- getPackageManager().getPackageInfo("com.itsaky.androidide", 0).versionName;
- return getString(R.string.about_footer, version, arch);
- } catch (Throwable th) {
- return getString(R.string.about_footer_alternate, arch);
+ private void setupTranslations() {
+ final var translations = createTranslationsList();
+ final var sb = new StringBuilder();
+
+ for (var translation : translations) {
+ final var author = translation.first;
+ final var language = translation.second;
+
+ sb.append(author);
+ sb.append(" - ");
+ sb.append(language);
+ sb.append("\n");
+ }
+
+ binding.items.translations.setText(sb);
+ }
+
+ /**
+ * Create the list of translations. The list items are pairs. The first item of the pair is the
+ * name of the language. The second item is the name of the person who made the translations.
+ *
+ * If you're adding translations, make sure you add an item to this list. You can choose to
+ * display the language in a localized version. For example : Language: हिन्दी - < name of
+ * author >
+ *
+ *
Prefer using unicode escapes for language names. For example, हिन्दी can be written as :
+ * Language: \u0939\u093f\u0928\u094d\u0926\u0940 - < name of author >
+ *
+ * @return The translations list.
+ */
+ private List> createTranslationsList() {
+ final var list = new ArrayList>();
+
+ list.add(Pair.create("Bahasa Indonesia", "Fitrah Nuno Syahbani"));
+ list.add(Pair.create("\u4e2d\u6587", "Rosemoe")); // 中文
+ list.add(Pair.create("Deutsch", "Marvin Stelter"));
+ list.add(
+ Pair.create("\u0939\u093f\u0928\u094d\u0926\u0940", "Premjit Chowdhury")); // हिन्दी
+ list.add(Pair.create("Russian", "Smooth-E"));
+ list.add(Pair.create("French", "Se-Lyan"));
+
+ return list;
+ }
+
+ private void setupLicenses() {
+ final var licenses = new ArrayList();
+ licenses.add(
+ new License(
+ "AndroidX Libraries",
+ "Apache License 2.0",
+ "https://github.com/androidx/androidx"));
+ licenses.add(
+ new License(
+ "Gradle Build Tool",
+ "Apache License 2.0",
+ "https://github.com/gradle/gradle"));
+ licenses.add(new License("Gson", "Apache License 2.0", "https://github.com/google/gson"));
+ licenses.add(
+ new License(
+ "Material Components for Android",
+ "Apache License 2.0",
+ "https://github.com/material-components/material-components-android"));
+ licenses.add(
+ new License(
+ "ANTLR4 Runtime",
+ "BSD 3-clause License",
+ "https://github.com/antlr/antlr4"));
+ licenses.add(
+ new License(
+ "Glide",
+ "BSD, part MIT and Apache 2.0",
+ "https://github.com/bumptech/glide"));
+ licenses.add(
+ new License(
+ "Android UtilCode",
+ "Apache License 2.0",
+ "https://github.com/Blankj/AndroidUtilCode"));
+ licenses.add(
+ new License(
+ "UnicornFilePicker",
+ "Apache License 2.0",
+ "https://github.com/abhishekti7/UnicornFilePicker"));
+ licenses.add(
+ new License(
+ "QuickAction",
+ "Apache License 2.0",
+ "https://github.com/piruin/quickaction"));
+ licenses.add(
+ new License(
+ "XmlToJson",
+ "Apache License 2.0",
+ "https://github.com/smart-fun/XmlToJson"));
+ licenses.add(
+ new License(
+ "AndroidTreeView",
+ "Apache License 2.0",
+ "https://github.com/bmelnychuk/AndroidTreeView"));
+ licenses.add(
+ new License(
+ "CodeEditor v0.5.2",
+ "Apache License 2.0",
+ "https://github.com/Rosemoe/CodeEditor"));
+ licenses.add(
+ new License(
+ "Guava-Android", "Apache License 2.0", "https://github.com/google/guava"));
+ licenses.add(new License("Jsoup", "MIT License", "https://github.com/jhy/jsoup"));
+ licenses.add(
+ new License(
+ "Termux [Terminal Emulator]",
+ "GPL 3",
+ "https://github.com/termux/termux-app"));
+ licenses.add(
+ new License(
+ "JavaPoet", "Apache License 2.0", "https://github.com/square/JavaPoet"));
+ licenses.add(
+ new License(
+ "Zip4j",
+ "Apache License 2.0",
+ "https://github.com/srikanth-lingala/zip4j"));
+
+ StringBuilder sb = new StringBuilder();
+ for (License license : licenses) {
+ sb.append("\u2022 ");
+ sb.append(license.name);
+ sb.append("
");
+ sb.append(getString(R.string.msg_about_licensed_under));
+ sb.append(license.license);
+ sb.append("
");
+ sb.append(license.url);
+ sb.append("");
+ sb.append("
");
+ }
+ sb.append(getString(R.string.license_jdk));
+ sb.append(String.format("
%1$s", JDK_SOURCE));
+ sb.append("
");
+ binding.items.licenses.setText(Html.fromHtml(sb.toString()));
+ binding.items.licenses.setMovementMethod(LinkMovementMethod.getInstance());
+ }
+
+ private String getFooter() {
+ final String arch = android.os.Build.SUPPORTED_ABIS[0];
+ try {
+ final String version =
+ getPackageManager().getPackageInfo("com.itsaky.androidide", 0).versionName;
+ return getString(R.string.about_footer, version, arch);
+ } catch (Throwable th) {
+ return getString(R.string.about_footer_alternate, arch);
+ }
}
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/CrashHandlerActivity.java b/app/src/main/java/com/itsaky/androidide/CrashHandlerActivity.java
index cf252f656..4e88ad6dc 100644
--- a/app/src/main/java/com/itsaky/androidide/CrashHandlerActivity.java
+++ b/app/src/main/java/com/itsaky/androidide/CrashHandlerActivity.java
@@ -17,43 +17,43 @@
package com.itsaky.androidide;
-
import android.os.Bundle;
import android.view.View;
+
import com.itsaky.androidide.app.StudioActivity;
import com.itsaky.androidide.databinding.ActivityCrashHandlerBinding;
import com.itsaky.androidide.fragments.CrashReportFragment;
public class CrashHandlerActivity extends StudioActivity {
- private ActivityCrashHandlerBinding binding;
+ private ActivityCrashHandlerBinding binding;
- public static final String REPORT_ACTION = "com.itsaky.androidide.REPORT_CRASH";
- public static final String TRACE_KEY = "crash_trace";
+ public static final String REPORT_ACTION = "com.itsaky.androidide.REPORT_CRASH";
+ public static final String TRACE_KEY = "crash_trace";
- @Override
- protected View bindLayout() {
- binding = ActivityCrashHandlerBinding.inflate(getLayoutInflater());
- return binding.getRoot();
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- final var extra = getIntent().getExtras();
- if (extra == null) {
- finishAffinity();
- return;
+ @Override
+ protected View bindLayout() {
+ binding = ActivityCrashHandlerBinding.inflate(getLayoutInflater());
+ return binding.getRoot();
}
- final var report = extra.getString(TRACE_KEY, "Unable to get logs.");
- final var fragment = CrashReportFragment.newInstance(report);
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
- getSupportFragmentManager()
- .beginTransaction()
- .replace(binding.getRoot().getId(), fragment, "crash_report_fragment")
- .addToBackStack(null)
- .commit();
- }
+ final var extra = getIntent().getExtras();
+ if (extra == null) {
+ finishAffinity();
+ return;
+ }
+
+ final var report = extra.getString(TRACE_KEY, "Unable to get logs.");
+ final var fragment = CrashReportFragment.newInstance(report);
+
+ getSupportFragmentManager()
+ .beginTransaction()
+ .replace(binding.getRoot().getId(), fragment, "crash_report_fragment")
+ .addToBackStack(null)
+ .commit();
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/DesignerActivity.java b/app/src/main/java/com/itsaky/androidide/DesignerActivity.java
index 3e2a8ec7d..d5323ff69 100755
--- a/app/src/main/java/com/itsaky/androidide/DesignerActivity.java
+++ b/app/src/main/java/com/itsaky/androidide/DesignerActivity.java
@@ -38,6 +38,7 @@ import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView;
+
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.core.content.ContextCompat;
@@ -47,6 +48,7 @@ import androidx.transition.Fade;
import androidx.transition.Slide;
import androidx.transition.TransitionManager;
import androidx.transition.TransitionSet;
+
import com.blankj.utilcode.util.DeviceUtils;
import com.blankj.utilcode.util.ThrowableUtils;
import com.itsaky.androidide.adapters.WidgetGroupItemAdapter;
@@ -69,380 +71,410 @@ import com.itsaky.inflater.ILayoutInflater;
import com.itsaky.inflater.IView;
import com.itsaky.inflater.IViewGroup;
import com.itsaky.inflater.impl.BaseView;
+
+import org.jetbrains.annotations.Contract;
+
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
-import org.jetbrains.annotations.Contract;
public class DesignerActivity extends StudioActivity
- implements WidgetItemAdapter.OnDragStartListener {
+ implements WidgetItemAdapter.OnDragStartListener {
- public static final String KEY_LAYOUT_PATH = "designer_layoutPath";
- public static final String KEY_GENERATED_CODE = "designer_xmlCode";
- public static final String DRAGGING_WIDGET_TAG = "DRAGGING_WIDGET";
- public static final String DRAGGING_WIDGET_MIME = "application/ide_widget";
- private static final Logger LOG = Logger.instance("DesignerActivity");
- private final boolean isTablet = DeviceUtils.isTablet();
- private final List widgetGroups = new ArrayList<>();
- private ActivityDesignerBinding mBinding;
- private UIWidgetGroup checkedWidgetCategory;
- private IViewGroup inflatedRoot;
- private File layout;
- private boolean inflationFailed = false;
- private AttrEditorSheet mEditorSheet;
- // This will make sure to apply listeners, background and data to view that are inflated from XML.
- private final IInflateListener mInflateListener =
- new IInflateListener() {
+ public static final String KEY_LAYOUT_PATH = "designer_layoutPath";
+ public static final String KEY_GENERATED_CODE = "designer_xmlCode";
+ public static final String DRAGGING_WIDGET_TAG = "DRAGGING_WIDGET";
+ public static final String DRAGGING_WIDGET_MIME = "application/ide_widget";
+ private static final Logger LOG = Logger.instance("DesignerActivity");
+ private final boolean isTablet = DeviceUtils.isTablet();
+ private final List widgetGroups = new ArrayList<>();
+ private ActivityDesignerBinding mBinding;
+ private UIWidgetGroup checkedWidgetCategory;
+ private IViewGroup inflatedRoot;
+ private File layout;
+ private boolean inflationFailed = false;
+ private AttrEditorSheet mEditorSheet;
+ // This will make sure to apply listeners, background and data to view that are inflated from
+ // XML.
+ private final IInflateListener mInflateListener =
+ new IInflateListener() {
- @Override
- public void onBeginInflate() {}
+ @Override
+ public void onBeginInflate() {}
- @Override
- public void onInflateView(IView view, IViewGroup parent) {
- if (view instanceof IViewGroup) {
- View v = view.asView();
- v.setBackgroundResource(R.drawable.bg_design_container_background);
- v.setOnDragListener(getOnDragListener((IViewGroup) view));
- }
+ @Override
+ public void onInflateView(IView view, IViewGroup parent) {
+ if (view instanceof IViewGroup) {
+ View v = view.asView();
+ v.setBackgroundResource(R.drawable.bg_design_container_background);
+ v.setOnDragListener(getOnDragListener((IViewGroup) view));
+ }
- setupInflatedView(view);
- setDragDataToInflatedView(view);
- }
+ setupInflatedView(view);
+ setDragDataToInflatedView(view);
+ }
- @Override
- public void onApplyAttribute(IAttribute attr, IView view) {}
+ @Override
+ public void onApplyAttribute(IAttribute attr, IView view) {}
- @Override
- public void onFinishInflate(IView rootView) {
- if (rootView instanceof IViewGroup) {
- inflatedRoot = (IViewGroup) rootView;
- }
- }
- };
+ @Override
+ public void onFinishInflate(IView rootView) {
+ if (rootView instanceof IViewGroup) {
+ inflatedRoot = (IViewGroup) rootView;
+ }
+ }
+ };
- @Override
- protected View bindLayout() {
- this.mBinding = ActivityDesignerBinding.inflate(getLayoutInflater());
- return mBinding.getRoot();
- }
+ @Override
+ protected View bindLayout() {
+ this.mBinding = ActivityDesignerBinding.inflate(getLayoutInflater());
+ return mBinding.getRoot();
+ }
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
- setSupportActionBar(mBinding.toolbar);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ setSupportActionBar(mBinding.toolbar);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- final var toggle =
- new ActionBarDrawerToggle(
- this, mBinding.getRoot(), mBinding.toolbar, R.string.app_name, R.string.app_name) {
+ final var toggle =
+ new ActionBarDrawerToggle(
+ this,
+ mBinding.getRoot(),
+ mBinding.toolbar,
+ R.string.app_name,
+ R.string.app_name) {
- @Override
- public void onDrawerSlide(View drawerView, float slideOffset) {
- super.onDrawerSlide(drawerView, slideOffset);
- if (isTablet) {
- mBinding.mainContainer.setTranslationX(drawerView.getWidth() * slideOffset);
+ @Override
+ public void onDrawerSlide(View drawerView, float slideOffset) {
+ super.onDrawerSlide(drawerView, slideOffset);
+ if (isTablet) {
+ mBinding.mainContainer.setTranslationX(
+ drawerView.getWidth() * slideOffset);
+ }
+ }
+ };
+ mBinding.getRoot().addDrawerListener(toggle);
+ toggle.syncState();
+
+ final var extras = getIntent().getExtras();
+ final var path = extras.getString(KEY_LAYOUT_PATH, null);
+ final var name = path.substring(path.lastIndexOf(File.separator) + 1);
+ getSupportActionBar().setTitle(name);
+
+ try {
+ this.layout = new File(path);
+
+ final ILayoutInflater inflater = getApp().getLayoutInflater();
+ inflater.resetContextProvider(newContextProvider());
+ inflater.registerInflateListener(this.mInflateListener);
+ final IView view = inflater.inflatePath(path, mBinding.layoutContainer);
+
+ if (this.inflatedRoot != null) {
+ this.inflatedRoot.asView().setOnDragListener(getOnDragListener(this.inflatedRoot));
}
- }
- };
- mBinding.getRoot().addDrawerListener(toggle);
- toggle.syncState();
- final var extras = getIntent().getExtras();
- final var path = extras.getString(KEY_LAYOUT_PATH, null);
- final var name = path.substring(path.lastIndexOf(File.separator) + 1);
- getSupportActionBar().setTitle(name);
+ mBinding.layoutContainer.addView(view.asView());
+ mBinding.layoutContainer.setOnClickListener(
+ v -> mBinding.getRoot().openDrawer(GravityCompat.START));
+ } catch (Throwable th) {
+ mBinding.layoutContainer.removeAllViews();
+ mBinding.layoutContainer.addView(createErrorText(th));
+ LOG.error(getString(R.string.err_cannot_inflate_layout), th);
+ inflationFailed = true;
+ }
- try {
- this.layout = new File(path);
+ setupWidgets();
- final ILayoutInflater inflater = getApp().getLayoutInflater();
- inflater.resetContextProvider(newContextProvider());
- inflater.registerInflateListener(this.mInflateListener);
- final IView view = inflater.inflatePath(path, mBinding.layoutContainer);
-
- if (this.inflatedRoot != null) {
- this.inflatedRoot.asView().setOnDragListener(getOnDragListener(this.inflatedRoot));
- }
-
- mBinding.layoutContainer.addView(view.asView());
- mBinding.layoutContainer.setOnClickListener(
- v -> mBinding.getRoot().openDrawer(GravityCompat.START));
- } catch (Throwable th) {
- mBinding.layoutContainer.removeAllViews();
- mBinding.layoutContainer.addView(createErrorText(th));
- LOG.error(getString(R.string.err_cannot_inflate_layout), th);
- inflationFailed = true;
+ // Always open the drawer if this device is a tablet.
+ if (isTablet) {
+ mBinding.getRoot().setScrimColor(Color.TRANSPARENT);
+ mBinding.getRoot().openDrawer(GravityCompat.START);
+ mBinding.getRoot().setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);
+ }
}
- setupWidgets();
+ @Override
+ public void onBackPressed() {
- // Always open the drawer if this device is a tablet.
- if (isTablet) {
- mBinding.getRoot().setScrimColor(Color.TRANSPARENT);
- mBinding.getRoot().openDrawer(GravityCompat.START);
- mBinding.getRoot().setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);
- }
- }
-
- @Override
- public void onBackPressed() {
-
- if (inflationFailed) {
- super.onBackPressed();
- return;
- }
-
- // When the user presses the back button, set the activity result and finish this activity
-
- final var progress = new ProgressSheet();
- progress.setSubMessageEnabled(true);
- progress.setShowTitle(false);
- progress.setMessage(getString(R.string.title_generating_xml));
- progress.setSubMessage(getString(R.string.please_wait));
- progress.show(getSupportFragmentManager(), "generate_code_progress");
-
- final var future =
- CompletableFuture.supplyAsync(
- () -> BaseView.XML_HEADER.concat("\n").concat(inflatedRoot.generateCode()));
- future.whenComplete(
- (s, throwable) -> {
- progress.dismiss();
-
- if (throwable != null) {
- notifyXmlGenerationFailed(throwable);
+ if (inflationFailed) {
+ super.onBackPressed();
return;
- }
+ }
- try {
- finishWithResult(future.get());
- } catch (ExecutionException | InterruptedException e) {
- notifyXmlGenerationFailed(e);
- }
- });
- }
+ // When the user presses the back button, set the activity result and finish this activity
- private void notifyXmlGenerationFailed(Throwable error) {
- final var errorMessage = getString(R.string.msg_generate_xml_failed);
- LOG.error(errorMessage, error);
+ final var progress = new ProgressSheet();
+ progress.setSubMessageEnabled(true);
+ progress.setShowTitle(false);
+ progress.setMessage(getString(R.string.title_generating_xml));
+ progress.setSubMessage(getString(R.string.please_wait));
+ progress.show(getSupportFragmentManager(), "generate_code_progress");
- DialogUtils.newYesNoDialog(
- this,
- getString(R.string.title_code_generation_failed),
- getString(R.string.msg_code_generation_failed),
- (dialog1, which) -> finishWithError(),
- (dialog1, which) -> dialog1.dismiss())
- .show();
- }
+ final var future =
+ CompletableFuture.supplyAsync(
+ () -> BaseView.XML_HEADER.concat("\n").concat(inflatedRoot.generateCode()));
+ future.whenComplete(
+ (s, throwable) -> {
+ progress.dismiss();
- private void finishWithResult(String result) {
- final var intent = new Intent();
- intent.putExtra(KEY_GENERATED_CODE, result);
- setResult(RESULT_OK, intent);
- finish();
- }
+ if (throwable != null) {
+ notifyXmlGenerationFailed(throwable);
+ return;
+ }
- private void finishWithError() {
- final var intent = new Intent();
- setResult(-123, intent);
- finish();
- }
-
- @Override
- protected void onDestroy() {
-
- if (isAttrEditorShowing()) {
- getAttrEditorSheet().dismiss();
+ try {
+ finishWithResult(future.get());
+ } catch (ExecutionException | InterruptedException e) {
+ notifyXmlGenerationFailed(e);
+ }
+ });
}
- this.mEditorSheet = null;
+ private void notifyXmlGenerationFailed(Throwable error) {
+ final var errorMessage = getString(R.string.msg_generate_xml_failed);
+ LOG.error(errorMessage, error);
- // Release the reference to inflate listener and context from the layout inflater
- // Failing to do so will lead getSupportFragmentManager() to return a destroyed fragment manager
- try {
- final var layoutInflater = getApp().getLayoutInflater();
- layoutInflater.unregisterListener(mInflateListener);
- layoutInflater.resetContextProvider(null);
- } catch (Throwable e) {
- e.printStackTrace();
+ DialogUtils.newYesNoDialog(
+ this,
+ getString(R.string.title_code_generation_failed),
+ getString(R.string.msg_code_generation_failed),
+ (dialog1, which) -> finishWithError(),
+ (dialog1, which) -> dialog1.dismiss())
+ .show();
}
- super.onDestroy();
- }
-
- private void setupInflatedView(@NonNull IView view) {
- final var listener =
- new WidgetTouchListener(view, this::onLayoutViewClick, this::onLayoutViewLongClick);
- view.asView().setOnTouchListener(listener);
-
- setDragDataToInflatedView(view);
- }
-
- private boolean onLayoutViewLongClick(@NonNull IView view) {
- final var shadow = new WidgetDragShadowBuilder(view.asView());
- final var item = new ClipData.Item(DesignerActivity.DRAGGING_WIDGET_TAG);
- final var clip =
- new ClipData(
- DesignerActivity.DRAGGING_WIDGET_TAG,
- new String[] {DesignerActivity.DRAGGING_WIDGET_MIME},
- item);
- final var data = view.getExtraData();
-
- view.asView().startDragAndDrop(clip, shadow, data, 0);
- view.removeFromParent();
-
- return true;
- }
-
- private void onLayoutViewClick(@NonNull IView view) {
- if (isAttrEditorShowing()) {
- getAttrEditorSheet().dismiss();
+ private void finishWithResult(String result) {
+ final var intent = new Intent();
+ intent.putExtra(KEY_GENERATED_CODE, result);
+ setResult(RESULT_OK, intent);
+ finish();
}
- getAttrEditorSheet().setSelectedView(view);
-
- if (!getSupportFragmentManager().isDestroyed()) {
- getAttrEditorSheet().show(getSupportFragmentManager(), "attribute_editor_dialog");
+ private void finishWithError() {
+ final var intent = new Intent();
+ setResult(-123, intent);
+ finish();
}
- }
- private boolean isAttrEditorShowing() {
- return getAttrEditorSheet().getDialog() != null && getAttrEditorSheet().getDialog().isShowing();
- }
+ @Override
+ protected void onDestroy() {
- private void setupWidgets() {
- final var android = new UIWidgetGroup(getString(R.string.widget_group_android));
- android.addChild(
- new UIWidget(getString(R.string.widget_button), R.drawable.ic_widget_button, Button.class));
- android.addChild(
- new UIWidget(
- getString(R.string.widget_checkbox), R.drawable.ic_widget_checkbox, CheckBox.class));
- android.addChild(
- new UIWidget(
- getString(R.string.widget_checked_textview),
- R.drawable.ic_widget_checked_textview,
- CheckedTextView.class));
- android.addChild(
- new UIWidget(
- getString(R.string.widget_edittext), R.drawable.ic_widget_edittext, EditText.class));
- android.addChild(
- new UIWidget(
- getString(R.string.widget_image_button),
- R.drawable.ic_widget_image,
- ImageButton.class));
- android.addChild(
- new UIWidget(
- getString(R.string.widget_image_view), R.drawable.ic_widget_image, ImageView.class));
- android.addChild(
- new UIWidget(
- getString(R.string.widget_progressbar),
- R.drawable.ic_widget_progress,
- ProgressBar.class));
- android.addChild(
- new UIWidget(
- getString(R.string.widget_radio_button),
- R.drawable.ic_widget_radio_button,
- RadioButton.class));
- android.addChild(
- new UIWidget(
- getString(R.string.widget_seekbar), R.drawable.ic_widget_seekbar, SeekBar.class));
- android.addChild(
- new UIWidget(
- getString(R.string.widget_spinner), R.drawable.ic_widget_spinner, Spinner.class));
- android.addChild(
- new UIWidget(
- getString(R.string.widget_textview), R.drawable.ic_widget_textview, TextView.class));
+ if (isAttrEditorShowing()) {
+ getAttrEditorSheet().dismiss();
+ }
- // IMPORTANT: Do not select any other groups here
- android.setSelected(true);
- this.checkedWidgetCategory = android;
- widgetGroups.add(android);
+ this.mEditorSheet = null;
- final var layouts = new UIWidgetGroup(getString(R.string.widget_group_layouts));
- layouts.addChild(
- new UIWidget(
- getString(R.string.layout_linear), R.drawable.ic_widget_linear, LinearLayout.class));
- layouts.addChild(
- new UIWidget(
- getString(R.string.layout_relative),
- R.drawable.ic_widget_relative,
- RelativeLayout.class));
+ // Release the reference to inflate listener and context from the layout inflater
+ // Failing to do so will lead getSupportFragmentManager() to return a destroyed fragment
+ // manager
+ try {
+ final var layoutInflater = getApp().getLayoutInflater();
+ layoutInflater.unregisterListener(mInflateListener);
+ layoutInflater.resetContextProvider(null);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
- widgetGroups.add(layouts);
-
- final var adapter = new WidgetGroupItemAdapter(widgetGroups, this::showWidgetGroup);
- mBinding.groupItems.setAdapter(adapter);
-
- showWidgetGroup(android);
- }
-
- @SuppressLint("NotifyDataSetChanged")
- private void showWidgetGroup(@NonNull UIWidgetGroup group) {
- final var children = group.getChildren();
- this.mBinding.widgetItems.setAdapter(new WidgetItemAdapter(children, this));
- this.checkedWidgetCategory.setSelected(false);
- this.checkedWidgetCategory = group;
- this.checkedWidgetCategory.setSelected(true);
-
- TransitionManager.beginDelayedTransition(
- mBinding.navigation,
- new TransitionSet().addTransition(new Slide(Gravity.END)).addTransition(new Fade()));
-
- //noinspection ConstantConditions
- this.mBinding.groupItems.getAdapter().notifyDataSetChanged();
- }
-
- @NonNull
- @Contract(pure = true)
- private ILayoutInflater.ContextProvider newContextProvider() {
- return () -> DesignerActivity.this;
- }
-
- @NonNull
- private TextView createErrorText(Throwable th) {
- final TextView error = new TextView(this);
- error.setText(ThrowableUtils.getFullStackTrace(th));
- error.setLayoutParams(new ViewGroup.LayoutParams(-2, -2));
- error.setTextColor(ContextCompat.getColor(this, android.R.color.black));
- error.setBackgroundColor(ContextCompat.getColor(this, android.R.color.white));
- return error;
- }
-
- @Override
- public void onDragStarted(View view) {
- mBinding.getRoot().closeDrawer(GravityCompat.START);
- }
-
- @NonNull
- @Contract("_ -> new")
- private View.OnDragListener getOnDragListener(IViewGroup group) {
- // WidgetDragListener cannot be reused
- // This is because they keep a reference to the view group in which dragged view will be added
-
- return new WidgetDragListener(this, group, this::setupInflatedView);
- }
-
- private void setDragDataToInflatedView(@NonNull IView view) {
- view.setExtraData(new WidgetDragData(true, view, null));
- }
-
- private AttrEditorSheet getAttrEditorSheet() {
- return this.mEditorSheet == null
- ? mEditorSheet =
- new AttrEditorSheet()
- .setDeletionFailedListener(this::onViewDeletionFailed)
- .setLayout(this.layout)
- : mEditorSheet;
- }
-
- private boolean onViewDeletionFailed(@NonNull IView view) {
- final var v = view.asView();
- if (mBinding.layoutContainer.indexOfChild(v) >= 0) {
- mBinding.layoutContainer.removeView(v);
- return true;
+ super.onDestroy();
+ }
+
+ private void setupInflatedView(@NonNull IView view) {
+ final var listener =
+ new WidgetTouchListener(view, this::onLayoutViewClick, this::onLayoutViewLongClick);
+ view.asView().setOnTouchListener(listener);
+
+ setDragDataToInflatedView(view);
+ }
+
+ private boolean onLayoutViewLongClick(@NonNull IView view) {
+ final var shadow = new WidgetDragShadowBuilder(view.asView());
+ final var item = new ClipData.Item(DesignerActivity.DRAGGING_WIDGET_TAG);
+ final var clip =
+ new ClipData(
+ DesignerActivity.DRAGGING_WIDGET_TAG,
+ new String[] {DesignerActivity.DRAGGING_WIDGET_MIME},
+ item);
+ final var data = view.getExtraData();
+
+ view.asView().startDragAndDrop(clip, shadow, data, 0);
+ view.removeFromParent();
+
+ return true;
+ }
+
+ private void onLayoutViewClick(@NonNull IView view) {
+ if (isAttrEditorShowing()) {
+ getAttrEditorSheet().dismiss();
+ }
+
+ getAttrEditorSheet().setSelectedView(view);
+
+ if (!getSupportFragmentManager().isDestroyed()) {
+ getAttrEditorSheet().show(getSupportFragmentManager(), "attribute_editor_dialog");
+ }
+ }
+
+ private boolean isAttrEditorShowing() {
+ return getAttrEditorSheet().getDialog() != null
+ && getAttrEditorSheet().getDialog().isShowing();
+ }
+
+ private void setupWidgets() {
+ final var android = new UIWidgetGroup(getString(R.string.widget_group_android));
+ android.addChild(
+ new UIWidget(
+ getString(R.string.widget_button),
+ R.drawable.ic_widget_button,
+ Button.class));
+ android.addChild(
+ new UIWidget(
+ getString(R.string.widget_checkbox),
+ R.drawable.ic_widget_checkbox,
+ CheckBox.class));
+ android.addChild(
+ new UIWidget(
+ getString(R.string.widget_checked_textview),
+ R.drawable.ic_widget_checked_textview,
+ CheckedTextView.class));
+ android.addChild(
+ new UIWidget(
+ getString(R.string.widget_edittext),
+ R.drawable.ic_widget_edittext,
+ EditText.class));
+ android.addChild(
+ new UIWidget(
+ getString(R.string.widget_image_button),
+ R.drawable.ic_widget_image,
+ ImageButton.class));
+ android.addChild(
+ new UIWidget(
+ getString(R.string.widget_image_view),
+ R.drawable.ic_widget_image,
+ ImageView.class));
+ android.addChild(
+ new UIWidget(
+ getString(R.string.widget_progressbar),
+ R.drawable.ic_widget_progress,
+ ProgressBar.class));
+ android.addChild(
+ new UIWidget(
+ getString(R.string.widget_radio_button),
+ R.drawable.ic_widget_radio_button,
+ RadioButton.class));
+ android.addChild(
+ new UIWidget(
+ getString(R.string.widget_seekbar),
+ R.drawable.ic_widget_seekbar,
+ SeekBar.class));
+ android.addChild(
+ new UIWidget(
+ getString(R.string.widget_spinner),
+ R.drawable.ic_widget_spinner,
+ Spinner.class));
+ android.addChild(
+ new UIWidget(
+ getString(R.string.widget_textview),
+ R.drawable.ic_widget_textview,
+ TextView.class));
+
+ // IMPORTANT: Do not select any other groups here
+ android.setSelected(true);
+ this.checkedWidgetCategory = android;
+ widgetGroups.add(android);
+
+ final var layouts = new UIWidgetGroup(getString(R.string.widget_group_layouts));
+ layouts.addChild(
+ new UIWidget(
+ getString(R.string.layout_linear),
+ R.drawable.ic_widget_linear,
+ LinearLayout.class));
+ layouts.addChild(
+ new UIWidget(
+ getString(R.string.layout_relative),
+ R.drawable.ic_widget_relative,
+ RelativeLayout.class));
+
+ widgetGroups.add(layouts);
+
+ final var adapter = new WidgetGroupItemAdapter(widgetGroups, this::showWidgetGroup);
+ mBinding.groupItems.setAdapter(adapter);
+
+ showWidgetGroup(android);
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ private void showWidgetGroup(@NonNull UIWidgetGroup group) {
+ final var children = group.getChildren();
+ this.mBinding.widgetItems.setAdapter(new WidgetItemAdapter(children, this));
+ this.checkedWidgetCategory.setSelected(false);
+ this.checkedWidgetCategory = group;
+ this.checkedWidgetCategory.setSelected(true);
+
+ TransitionManager.beginDelayedTransition(
+ mBinding.navigation,
+ new TransitionSet()
+ .addTransition(new Slide(Gravity.END))
+ .addTransition(new Fade()));
+
+ //noinspection ConstantConditions
+ this.mBinding.groupItems.getAdapter().notifyDataSetChanged();
+ }
+
+ @NonNull
+ @Contract(pure = true)
+ private ILayoutInflater.ContextProvider newContextProvider() {
+ return () -> DesignerActivity.this;
+ }
+
+ @NonNull
+ private TextView createErrorText(Throwable th) {
+ final TextView error = new TextView(this);
+ error.setText(ThrowableUtils.getFullStackTrace(th));
+ error.setLayoutParams(new ViewGroup.LayoutParams(-2, -2));
+ error.setTextColor(ContextCompat.getColor(this, android.R.color.black));
+ error.setBackgroundColor(ContextCompat.getColor(this, android.R.color.white));
+ return error;
+ }
+
+ @Override
+ public void onDragStarted(View view) {
+ mBinding.getRoot().closeDrawer(GravityCompat.START);
+ }
+
+ @NonNull
+ @Contract("_ -> new")
+ private View.OnDragListener getOnDragListener(IViewGroup group) {
+ // WidgetDragListener cannot be reused
+ // This is because they keep a reference to the view group in which dragged view will be
+ // added
+
+ return new WidgetDragListener(this, group, this::setupInflatedView);
+ }
+
+ private void setDragDataToInflatedView(@NonNull IView view) {
+ view.setExtraData(new WidgetDragData(true, view, null));
+ }
+
+ private AttrEditorSheet getAttrEditorSheet() {
+ return this.mEditorSheet == null
+ ? mEditorSheet =
+ new AttrEditorSheet()
+ .setDeletionFailedListener(this::onViewDeletionFailed)
+ .setLayout(this.layout)
+ : mEditorSheet;
+ }
+
+ private boolean onViewDeletionFailed(@NonNull IView view) {
+ final var v = view.asView();
+ if (mBinding.layoutContainer.indexOfChild(v) >= 0) {
+ mBinding.layoutContainer.removeView(v);
+ return true;
+ }
+ return false;
}
- return false;
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/DownloadActivity.java b/app/src/main/java/com/itsaky/androidide/DownloadActivity.java
index c75679a62..ad06f6f6c 100755
--- a/app/src/main/java/com/itsaky/androidide/DownloadActivity.java
+++ b/app/src/main/java/com/itsaky/androidide/DownloadActivity.java
@@ -17,15 +17,18 @@
*/
package com.itsaky.androidide;
-import abhishekti7.unicorn.filepicker.UnicornFilePicker;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
+
import androidx.core.content.ContextCompat;
import androidx.transition.TransitionManager;
+
+import abhishekti7.unicorn.filepicker.UnicornFilePicker;
+
import com.blankj.utilcode.util.FileIOUtils;
import com.blankj.utilcode.util.SizeUtils;
import com.blankj.utilcode.util.ThreadUtils;
@@ -45,6 +48,7 @@ import com.itsaky.androidide.utils.Environment;
import com.itsaky.androidide.utils.FileUtil;
import com.itsaky.androidide.utils.InputStreamLineReader;
import com.itsaky.toaster.Toaster;
+
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
@@ -52,266 +56,274 @@ import java.util.ArrayList;
public class DownloadActivity extends StudioActivity {
- private ActivityDownloadBinding binding;
- private ProgressSheet progressSheet;
- private File choosenDir = null;
+ private ActivityDownloadBinding binding;
+ private ProgressSheet progressSheet;
+ private File choosenDir = null;
- @Override
- protected View bindLayout() {
- binding = ActivityDownloadBinding.inflate(getLayoutInflater());
- return binding.getRoot();
- }
+ @Override
+ protected View bindLayout() {
+ binding = ActivityDownloadBinding.inflate(getLayoutInflater());
+ return binding.getRoot();
+ }
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final OnClickListener clickListener = this::handleClick;
- binding.dismiss.setOnClickListener(clickListener);
- binding.choose.setOnClickListener(clickListener);
- binding.install.setOnClickListener(clickListener);
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final OnClickListener clickListener = this::handleClick;
+ binding.dismiss.setOnClickListener(clickListener);
+ binding.choose.setOnClickListener(clickListener);
+ binding.install.setOnClickListener(clickListener);
- binding.install.setEnabled(false);
- }
-
- private void handleClick(View v) {
- final int id = v.getId();
- if (id == binding.dismiss.getId()) {
- TransitionManager.beginDelayedTransition(binding.getRoot());
- binding.card.setVisibility(View.GONE);
- } else if (id == binding.choose.getId()) {
- try {
binding.install.setEnabled(false);
- UnicornFilePicker.from(this)
- .addConfigBuilder()
- .addItemDivider(false)
- .selectMultipleFiles(false)
- .setRootDirectory(FileUtil.getExternalStorageDir())
- .showHiddenFiles(true)
- .showOnlyDirectory(true)
- .theme(R.style.AppTheme_FilePicker)
- .build()
- .forResult(abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE);
- } catch (Throwable th) {
- LOG.error(getString(R.string.err_cannot_start_file_picker), th);
- }
- } else if (id == binding.install.getId()) {
- if (choosenDir == null) {
- getApp().toast(R.string.msg_pick_files, Toaster.Type.ERROR);
- return;
- }
-
- if (!choosenDir.exists()) {
- getApp().toast(R.string.msg_file_doesnt_exist, Toaster.Type.ERROR);
- return;
- }
-
- installAll();
}
- }
- private void installAll() {
- showProgress();
- getApp().getPrefManager().putBoolean(PreferenceManager.KEY_FRAMEWORK_DOWNLOADED, true);
-
- try {
- final File script = createExtractScript();
- final ProcessStreamsHolder holder = new ProcessStreamsHolder();
- final IProcessExecutor executor = ProcessExecutorFactory.commonExecutor();
-
- executor.execAsync(
- holder,
- this::onInstallProcessExit,
- true,
- Environment.BUSYBOX.getAbsolutePath(),
- "sh", // We use busybox's sh, because Environment.SHELL is not installed yet...
- script.getAbsolutePath());
-
- this.output = new StringBuilder();
- final InputStreamLineReader reader =
- new InputStreamLineReader(holder.in, this::onInstallationOutput);
- new Thread(reader).start();
-
- } catch (DownloadActivity.InstallationException e) {
- LOG.error(getString(R.string.err_installation), e);
- onInstallationFailed(e.exitCode);
- } catch (IOException e) {
- LOG.error(getString(R.string.err_installation), e);
- onInstallationFailed(5); // Exit code 5 : I/O Error
- }
- }
-
- private void onInstallationOutput(final String line) {
- ThreadUtils.runOnUiThread(() -> this.appendOut(line));
- }
-
- private void onInstallProcessExit(final int code) {
- ThreadUtils.runOnUiThread(
- () -> {
- if (code == 0) { // 0 = normal execution
- getApp().getPrefManager().putBoolean(PreferenceManager.KEY_FRAMEWORK_INSTALLED, true);
- showRestartNeeded();
- if (getProgressSheet().isShowing()) {
- getProgressSheet().dismiss();
+ private void handleClick(View v) {
+ final int id = v.getId();
+ if (id == binding.dismiss.getId()) {
+ TransitionManager.beginDelayedTransition(binding.getRoot());
+ binding.card.setVisibility(View.GONE);
+ } else if (id == binding.choose.getId()) {
+ try {
+ binding.install.setEnabled(false);
+ UnicornFilePicker.from(this)
+ .addConfigBuilder()
+ .addItemDivider(false)
+ .selectMultipleFiles(false)
+ .setRootDirectory(FileUtil.getExternalStorageDir())
+ .showHiddenFiles(true)
+ .showOnlyDirectory(true)
+ .theme(R.style.AppTheme_FilePicker)
+ .build()
+ .forResult(abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE);
+ } catch (Throwable th) {
+ LOG.error(getString(R.string.err_cannot_start_file_picker), th);
+ }
+ } else if (id == binding.install.getId()) {
+ if (choosenDir == null) {
+ getApp().toast(R.string.msg_pick_files, Toaster.Type.ERROR);
+ return;
}
- } else {
- onInstallationFailed(code);
- }
- });
- }
- private void onInstallationFailed(int code) {
- // TODO Can this be improved by adding some animations?
+ if (!choosenDir.exists()) {
+ getApp().toast(R.string.msg_file_doesnt_exist, Toaster.Type.ERROR);
+ return;
+ }
- if (getProgressSheet().isShowing()) {
- getProgressSheet().dismiss();
- }
-
- final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
- builder.setTitle(R.string.title_installation_failed);
- builder.setMessage(getString(R.string.msg_installation_failed, code, this.output.toString()));
- builder.setPositiveButton(
- android.R.string.ok,
- (d, w) -> {
- d.dismiss();
- finishAffinity();
- });
- builder.setNegativeButton(android.R.string.cancel, null);
- builder.show();
- }
-
- private File createExtractScript() throws DownloadActivity.InstallationException {
- final StringBuilder sb = new StringBuilder();
- sb.append("cd");
- joiner(sb);
- sb.append("echo 'Installing...'");
- joiner(sb);
-
- File[] files = choosenDir.listFiles(ARCHIVE_FILTER);
-
- if (files == null || files.length <= 0) {
- throw new InstallationException(2);
- }
-
- for (File f : files) {
- if (f.getName().endsWith(".tar.xz")) {
- if (f.getName().startsWith("androidide-sysroot")) {
- sb.append("cd $SYSROOT/..");
- joiner(sb);
+ installAll();
}
- sb.append("$BUSYBOX tar xvJf '").append(f.getAbsolutePath()).append("'");
- joiner(sb);
- sb.append("cd $HOME");
- joiner(sb);
- } else if (f.getName().endsWith(".zip")) {
- sb.append("$BUSYBOX unzip '").append(f.getAbsolutePath()).append("'");
- joiner(sb);
- }
}
- sb.append("echo 'Cleaning unsupported flags in binaries...'");
- joiner(sb);
- String DONE = "DONE";
- sb.append("echo ").append(DONE);
+ private void installAll() {
+ showProgress();
+ getApp().getPrefManager().putBoolean(PreferenceManager.KEY_FRAMEWORK_DOWNLOADED, true);
- final File script = new File(Environment.TMP_DIR, "extract_tools.sh");
- if (!FileIOUtils.writeFileFromString(script, sb.toString())) {
- throw new InstallationException(2);
- }
+ try {
+ final File script = createExtractScript();
+ final ProcessStreamsHolder holder = new ProcessStreamsHolder();
+ final IProcessExecutor executor = ProcessExecutorFactory.commonExecutor();
- return script;
- }
+ executor.execAsync(
+ holder,
+ this::onInstallProcessExit,
+ true,
+ Environment.BUSYBOX.getAbsolutePath(),
+ "sh", // We use busybox's sh, because Environment.SHELL is not installed yet...
+ script.getAbsolutePath());
- private void joiner(StringBuilder sb) {
- sb.append(" && ");
- }
+ this.output = new StringBuilder();
+ final InputStreamLineReader reader =
+ new InputStreamLineReader(holder.in, this::onInstallationOutput);
+ new Thread(reader).start();
- private StringBuilder output = new StringBuilder();
-
- private void appendOut(String line) {
- LOG.debug("Installation:", line);
- output.append(line.trim());
- output.append("\n");
-
- getProgressSheet().setSubMessage(line);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE
- && resultCode == RESULT_OK) {
- ArrayList files = data.getStringArrayListExtra("filePaths");
- if (files != null) {
- if (files.size() == 1) {
- File choosenDir = new File(files.get(0));
- if (choosenDir.exists() && choosenDir.isDirectory()) {
- this.choosenDir = choosenDir;
- setDone(binding.choose);
- binding.install.setEnabled(true);
- new TaskExecutor()
- .executeAsync(
- new ListDirectoryCallable(choosenDir),
- __ -> binding.filesText.setText(getString(R.string.msg_installable_files, __)));
- } else {
- getApp().toast(R.string.msg_picked_isnt_dir, Toaster.Type.ERROR);
- }
- } else {
- getApp().toast(R.string.msg_pick_single_file, Toaster.Type.ERROR);
+ } catch (DownloadActivity.InstallationException e) {
+ LOG.error(getString(R.string.err_installation), e);
+ onInstallationFailed(e.exitCode);
+ } catch (IOException e) {
+ LOG.error(getString(R.string.err_installation), e);
+ onInstallationFailed(5); // Exit code 5 : I/O Error
}
- }
}
- }
- private void setDone(MaterialButton button) {
- final int dp8 = SizeUtils.dp2px(8);
- final int dp4 = SizeUtils.dp2px(4);
- final int dp24 = SizeUtils.dp2px(24);
- button.setText("");
- button.setIconResource(R.drawable.ic_ok);
- button.setIconTint(ColorStateList.valueOf(ContextCompat.getColor(this, R.color.bg_green)));
- button.setBackgroundColor(android.graphics.Color.TRANSPARENT);
- button.setPaddingRelative(dp8, dp4, dp8, dp4);
- button.setStrokeWidth(0);
- button.setGravity(Gravity.CENTER);
- button.setIconPadding(0);
- button.setInsetTop(0);
- button.setInsetBottom(0);
- button.setPaddingRelative(dp24, dp4, dp24, dp4);
- }
-
- private void showProgress() {
- getProgressSheet().setCancelable(false);
- getProgressSheet().setShowShadow(false);
- getProgressSheet()
- .setSubMessageEnabled(true)
- .setWelcomeTextEnabled(true)
- .show(getSupportFragmentManager(), "progress_sheet");
- }
-
- private void showRestartNeeded() {
- setDone(binding.install);
- final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
- builder.setTitle(R.string.title_restart);
- builder.setMessage(R.string.msg_restart);
- builder.setCancelable(false);
- builder.setPositiveButton(android.R.string.ok, (p1, p2) -> finishAffinity());
- builder.create().show();
- }
-
- private ProgressSheet getProgressSheet() {
- return progressSheet == null
- ? progressSheet = new ProgressSheet().setMessage(getString(R.string.please_wait))
- : progressSheet;
- }
-
- private final FileFilter ARCHIVE_FILTER =
- p1 -> p1.isFile() && (p1.getName().endsWith(".tar.xz") || p1.getName().endsWith(".zip"));
-
- private static class InstallationException extends Exception {
- private final int exitCode;
-
- public InstallationException(int exitCode) {
- this.exitCode = exitCode;
+ private void onInstallationOutput(final String line) {
+ ThreadUtils.runOnUiThread(() -> this.appendOut(line));
+ }
+
+ private void onInstallProcessExit(final int code) {
+ ThreadUtils.runOnUiThread(
+ () -> {
+ if (code == 0) { // 0 = normal execution
+ getApp().getPrefManager()
+ .putBoolean(PreferenceManager.KEY_FRAMEWORK_INSTALLED, true);
+ showRestartNeeded();
+ if (getProgressSheet().isShowing()) {
+ getProgressSheet().dismiss();
+ }
+ } else {
+ onInstallationFailed(code);
+ }
+ });
+ }
+
+ private void onInstallationFailed(int code) {
+ // TODO Can this be improved by adding some animations?
+
+ if (getProgressSheet().isShowing()) {
+ getProgressSheet().dismiss();
+ }
+
+ final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
+ builder.setTitle(R.string.title_installation_failed);
+ builder.setMessage(
+ getString(R.string.msg_installation_failed, code, this.output.toString()));
+ builder.setPositiveButton(
+ android.R.string.ok,
+ (d, w) -> {
+ d.dismiss();
+ finishAffinity();
+ });
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.show();
+ }
+
+ private File createExtractScript() throws DownloadActivity.InstallationException {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("cd");
+ joiner(sb);
+ sb.append("echo 'Installing...'");
+ joiner(sb);
+
+ File[] files = choosenDir.listFiles(ARCHIVE_FILTER);
+
+ if (files == null || files.length <= 0) {
+ throw new InstallationException(2);
+ }
+
+ for (File f : files) {
+ if (f.getName().endsWith(".tar.xz")) {
+ if (f.getName().startsWith("androidide-sysroot")) {
+ sb.append("cd $SYSROOT/..");
+ joiner(sb);
+ }
+ sb.append("$BUSYBOX tar xvJf '").append(f.getAbsolutePath()).append("'");
+ joiner(sb);
+ sb.append("cd $HOME");
+ joiner(sb);
+ } else if (f.getName().endsWith(".zip")) {
+ sb.append("$BUSYBOX unzip '").append(f.getAbsolutePath()).append("'");
+ joiner(sb);
+ }
+ }
+
+ sb.append("echo 'Cleaning unsupported flags in binaries...'");
+ joiner(sb);
+ String DONE = "DONE";
+ sb.append("echo ").append(DONE);
+
+ final File script = new File(Environment.TMP_DIR, "extract_tools.sh");
+ if (!FileIOUtils.writeFileFromString(script, sb.toString())) {
+ throw new InstallationException(2);
+ }
+
+ return script;
+ }
+
+ private void joiner(StringBuilder sb) {
+ sb.append(" && ");
+ }
+
+ private StringBuilder output = new StringBuilder();
+
+ private void appendOut(String line) {
+ LOG.debug("Installation:", line);
+ output.append(line.trim());
+ output.append("\n");
+
+ getProgressSheet().setSubMessage(line);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE
+ && resultCode == RESULT_OK) {
+ ArrayList files = data.getStringArrayListExtra("filePaths");
+ if (files != null) {
+ if (files.size() == 1) {
+ File choosenDir = new File(files.get(0));
+ if (choosenDir.exists() && choosenDir.isDirectory()) {
+ this.choosenDir = choosenDir;
+ setDone(binding.choose);
+ binding.install.setEnabled(true);
+ new TaskExecutor()
+ .executeAsync(
+ new ListDirectoryCallable(choosenDir),
+ __ ->
+ binding.filesText.setText(
+ getString(
+ R.string.msg_installable_files,
+ __)));
+ } else {
+ getApp().toast(R.string.msg_picked_isnt_dir, Toaster.Type.ERROR);
+ }
+ } else {
+ getApp().toast(R.string.msg_pick_single_file, Toaster.Type.ERROR);
+ }
+ }
+ }
+ }
+
+ private void setDone(MaterialButton button) {
+ final int dp8 = SizeUtils.dp2px(8);
+ final int dp4 = SizeUtils.dp2px(4);
+ final int dp24 = SizeUtils.dp2px(24);
+ button.setText("");
+ button.setIconResource(R.drawable.ic_ok);
+ button.setIconTint(ColorStateList.valueOf(ContextCompat.getColor(this, R.color.bg_green)));
+ button.setBackgroundColor(android.graphics.Color.TRANSPARENT);
+ button.setPaddingRelative(dp8, dp4, dp8, dp4);
+ button.setStrokeWidth(0);
+ button.setGravity(Gravity.CENTER);
+ button.setIconPadding(0);
+ button.setInsetTop(0);
+ button.setInsetBottom(0);
+ button.setPaddingRelative(dp24, dp4, dp24, dp4);
+ }
+
+ private void showProgress() {
+ getProgressSheet().setCancelable(false);
+ getProgressSheet().setShowShadow(false);
+ getProgressSheet()
+ .setSubMessageEnabled(true)
+ .setWelcomeTextEnabled(true)
+ .show(getSupportFragmentManager(), "progress_sheet");
+ }
+
+ private void showRestartNeeded() {
+ setDone(binding.install);
+ final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
+ builder.setTitle(R.string.title_restart);
+ builder.setMessage(R.string.msg_restart);
+ builder.setCancelable(false);
+ builder.setPositiveButton(android.R.string.ok, (p1, p2) -> finishAffinity());
+ builder.create().show();
+ }
+
+ private ProgressSheet getProgressSheet() {
+ return progressSheet == null
+ ? progressSheet = new ProgressSheet().setMessage(getString(R.string.please_wait))
+ : progressSheet;
+ }
+
+ private final FileFilter ARCHIVE_FILTER =
+ p1 ->
+ p1.isFile()
+ && (p1.getName().endsWith(".tar.xz") || p1.getName().endsWith(".zip"));
+
+ private static class InstallationException extends Exception {
+ private final int exitCode;
+
+ public InstallationException(int exitCode) {
+ this.exitCode = exitCode;
+ }
}
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/EditorActivity.java b/app/src/main/java/com/itsaky/androidide/EditorActivity.java
index f7847c1f6..fb1b7c853 100755
--- a/app/src/main/java/com/itsaky/androidide/EditorActivity.java
+++ b/app/src/main/java/com/itsaky/androidide/EditorActivity.java
@@ -35,6 +35,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.LinearLayout;
+
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
@@ -50,6 +51,7 @@ import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.transition.Slide;
import androidx.transition.TransitionManager;
+
import com.blankj.utilcode.util.ClipboardUtils;
import com.blankj.utilcode.util.FileUtils;
import com.blankj.utilcode.util.IntentUtils;
@@ -115,8 +117,15 @@ import com.itsaky.lsp.models.InitializeParams;
import com.itsaky.lsp.models.Range;
import com.itsaky.toaster.Toaster;
import com.unnamed.b.atv.model.TreeNode;
+
import io.github.rosemoe.sora.event.ContentChangeEvent;
import io.github.rosemoe.sora.event.Unsubscribe;
+
+import me.piruin.quickaction.ActionItem;
+import me.piruin.quickaction.QuickAction;
+
+import org.jetbrains.annotations.Contract;
+
import java.io.File;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -130,1523 +139,1561 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
-import me.piruin.quickaction.ActionItem;
-import me.piruin.quickaction.QuickAction;
-import org.jetbrains.annotations.Contract;
public class EditorActivity extends StudioActivity
- implements FileTreeFragment.FileActionListener,
- TabLayout.OnTabSelectedListener,
- NavigationView.OnNavigationItemSelectedListener,
- DiagnosticClickListener,
- IDEHandler.Provider,
- EditorActivityProvider {
+ implements FileTreeFragment.FileActionListener,
+ TabLayout.OnTabSelectedListener,
+ NavigationView.OnNavigationItemSelectedListener,
+ DiagnosticClickListener,
+ IDEHandler.Provider,
+ EditorActivityProvider {
- private ActivityEditorBinding mBinding;
- private LayoutDiagnosticInfoBinding mDiagnosticInfoBinding;
- private EditorBottomSheetTabAdapter bottomSheetTabAdapter;
- private FileTreeFragment mFileTreeFragment;
- private TreeNode mLastHeld;
- private SymbolInputView symbolInput;
- private BuildServiceHandler mBuildServiceHandler;
- private FileOptionsHandler mFileOptionsHandler;
- private QuickAction mTabCloseAction;
- private TextSheetFragment mDaemonStatusFragment;
- private OptionsListFragment mFileOptionsFragment;
- private ProgressSheet mSearchingProgress;
- private AlertDialog mFindInProjectDialog;
- private ActivityResultLauncher mUIDesignerLauncher;
- private EditorBottomSheetBehavior extends View> mEditorBottomSheet;
+ private ActivityEditorBinding mBinding;
+ private LayoutDiagnosticInfoBinding mDiagnosticInfoBinding;
+ private EditorBottomSheetTabAdapter bottomSheetTabAdapter;
+ private FileTreeFragment mFileTreeFragment;
+ private TreeNode mLastHeld;
+ private SymbolInputView symbolInput;
+ private BuildServiceHandler mBuildServiceHandler;
+ private FileOptionsHandler mFileOptionsHandler;
+ private QuickAction mTabCloseAction;
+ private TextSheetFragment mDaemonStatusFragment;
+ private OptionsListFragment mFileOptionsFragment;
+ private ProgressSheet mSearchingProgress;
+ private AlertDialog mFindInProjectDialog;
+ private ActivityResultLauncher mUIDesignerLauncher;
+ private EditorBottomSheetBehavior extends View> mEditorBottomSheet;
- private EditorViewModel mViewModel;
+ private EditorViewModel mViewModel;
- private static final String TAG_FILE_OPTIONS_FRAGMENT = "file_options_fragment";
- private static final int ACTION_ID_CLOSE = 100;
- private static final int ACTION_ID_OTHERS = 101;
- private static final int ACTION_ID_ALL = 102;
+ private static final String TAG_FILE_OPTIONS_FRAGMENT = "file_options_fragment";
+ private static final int ACTION_ID_CLOSE = 100;
+ private static final int ACTION_ID_OTHERS = 101;
+ private static final int ACTION_ID_ALL = 102;
- public static final String EXTRA_PROJECT = "project";
- public static final String KEY_BOTTOM_SHEET_SHOWN = "editor_bottomSheetShown";
+ public static final String EXTRA_PROJECT = "project";
+ public static final String KEY_BOTTOM_SHEET_SHOWN = "editor_bottomSheetShown";
- private final LogReceiver mLogReceiver = new LogReceiver().setLogListener(this::appendApkLog);
+ private final LogReceiver mLogReceiver = new LogReceiver().setLogListener(this::appendApkLog);
- /**
- * MenuItem(s) that are related to the build process
- *
- * These items will be disabled once the build process starts and will be enabled after the
- * execution
- */
- private final int[] BUILD_IDS = {
- R.id.menuEditor_quickRun,
- R.id.menuEditor_runDebug,
- R.id.menuEditor_runRelease,
- R.id.menuEditor_runBuild,
- R.id.menuEditor_runBundle,
- R.id.menuEditor_runClean,
- R.id.menuEditor_runCleanBuild,
- R.id.menuEditor_lint,
- R.id.menuEditor_lintDebug,
- R.id.menuEditor_lintRelease
- };
+ /**
+ * MenuItem(s) that are related to the build process
+ *
+ *
These items will be disabled once the build process starts and will be enabled after the
+ * execution
+ */
+ private final int[] BUILD_IDS = {
+ R.id.menuEditor_quickRun,
+ R.id.menuEditor_runDebug,
+ R.id.menuEditor_runRelease,
+ R.id.menuEditor_runBuild,
+ R.id.menuEditor_runBundle,
+ R.id.menuEditor_runClean,
+ R.id.menuEditor_runCleanBuild,
+ R.id.menuEditor_lint,
+ R.id.menuEditor_lintDebug,
+ R.id.menuEditor_lintRelease
+ };
- @Override
- protected View bindLayout() {
- mBinding = ActivityEditorBinding.inflate(getLayoutInflater());
- mDiagnosticInfoBinding = mBinding.diagnosticInfo;
- return mBinding.getRoot();
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setSupportActionBar(mBinding.editorToolbar);
-
- mViewModel = new ViewModelProvider(this).get(EditorViewModel.class);
- getProjectFromIntent();
-
- mFileTreeFragment =
- FileTreeFragment.newInstance(this.getAndroidProject()).setFileActionListener(this);
- mDaemonStatusFragment = new TextSheetFragment().setTextSelectable(true);
-
- setupDrawerToggle();
- loadFragment(mFileTreeFragment);
-
- symbolInput = new SymbolInputView(this);
- mBinding.bottomSheet.textContainer.addView(symbolInput, 0, new ViewGroup.LayoutParams(-1, -2));
- mBinding.tabs.addOnTabSelectedListener(this);
-
- setupEditorBottomSheet();
- createQuickActions();
-
- mBuildServiceHandler = new BuildServiceHandler(this);
- mFileOptionsHandler = new FileOptionsHandler(this);
-
- startServices();
-
- KeyboardUtils.registerSoftInputChangedListener(this, __ -> onSoftInputChanged());
- registerLogReceiver();
- setupContainers();
- setupDiagnosticInfo();
-
- mUIDesignerLauncher =
- registerForActivityResult(
- new ActivityResultContracts.StartActivityForResult(), this::onGetUIDesignerResult);
- }
-
- private void setupDrawerToggle() {
- ActionBarDrawerToggle toggle =
- new ActionBarDrawerToggle(
- this,
- mBinding.editorDrawerLayout,
- mBinding.editorToolbar,
- R.string.app_name,
- R.string.app_name);
- mBinding.editorDrawerLayout.addDrawerListener(toggle);
- mBinding.startNav.setNavigationItemSelectedListener(this);
- toggle.syncState();
- }
-
- private void setupEditorBottomSheet() {
- bottomSheetTabAdapter = new EditorBottomSheetTabAdapter(this);
- mBinding.bottomSheet.pager.setAdapter(bottomSheetTabAdapter);
-
- final var mediator =
- new TabLayoutMediator(
- mBinding.bottomSheet.tabs,
- mBinding.bottomSheet.pager,
- true,
- true,
- (tab, position) -> tab.setText(bottomSheetTabAdapter.getTitle(position)));
-
- mediator.attach();
- mBinding.bottomSheet.pager.setUserInputEnabled(false);
- mBinding.bottomSheet.pager.setOffscreenPageLimit(
- bottomSheetTabAdapter.getItemCount() - 1); // DO not remove any views
-
- mEditorBottomSheet =
- (EditorBottomSheetBehavior extends View>)
- EditorBottomSheetBehavior.from(mBinding.bottomSheet.getRoot());
- mEditorBottomSheet.setBinding(mBinding.bottomSheet);
- mEditorBottomSheet.addBottomSheetCallback(
- new BottomSheetBehavior.BottomSheetCallback() {
- @Override
- public void onStateChanged(@NonNull View bottomSheet, int newState) {
- mBinding.bottomSheet.textContainer.setVisibility(
- newState == BottomSheetBehavior.STATE_EXPANDED ? View.INVISIBLE : View.VISIBLE);
- }
-
- @Override
- public void onSlide(@NonNull View bottomSheet, float slideOffset) {
- mBinding.bottomSheet.textContainer.setAlpha(1f - slideOffset);
- }
- });
-
- mBinding.bottomSheet.tabs.addOnTabSelectedListener(
- new TabLayout.OnTabSelectedListener() {
-
- @Override
- public void onTabSelected(TabLayout.Tab tab) {
- final var fragment = bottomSheetTabAdapter.getFragmentAtIndex(tab.getPosition());
- if (fragment instanceof NonEditableEditorFragment) {
- mBinding.bottomSheet.clearFab.show();
- } else {
- mBinding.bottomSheet.clearFab.hide();
- }
- }
-
- @Override
- public void onTabUnselected(TabLayout.Tab tab) {}
-
- @Override
- public void onTabReselected(TabLayout.Tab tab) {}
- });
-
- TooltipCompat.setTooltipText(
- mBinding.bottomSheet.clearFab, getString(R.string.title_clear_output));
- mBinding.bottomSheet.clearFab.setOnClickListener(
- v -> {
- final var fragment =
- bottomSheetTabAdapter.getFragmentAtIndex(
- mBinding.bottomSheet.tabs.getSelectedTabPosition());
- if (fragment instanceof NonEditableEditorFragment) {
- final var editor = (NonEditableEditorFragment) fragment;
- if (editor.getEditor() != null) {
- editor.getEditor().setText("");
- }
- }
- });
-
- if (!getApp().getPrefManager().getBoolean(KEY_BOTTOM_SHEET_SHOWN)
- && mEditorBottomSheet.getState() != BottomSheetBehavior.STATE_EXPANDED) {
- mEditorBottomSheet.setState(BottomSheetBehavior.STATE_EXPANDED);
- new Handler(Looper.getMainLooper())
- .postDelayed(
- () -> {
- mEditorBottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED);
- getApp().getPrefManager().putBoolean(KEY_BOTTOM_SHEET_SHOWN, true);
- },
- 1500);
- }
- }
-
- @Override
- public void onBackPressed() {
- if (mBinding.getRoot().isDrawerOpen(GravityCompat.END)) {
- mBinding.getRoot().closeDrawer(GravityCompat.END);
- } else if (mBinding.getRoot().isDrawerOpen(GravityCompat.START)) {
- mBinding.getRoot().closeDrawer(GravityCompat.START);
- } else if (getDaemonStatusFragment().isShowing()) {
- getDaemonStatusFragment().dismiss();
- } else if (mFileOptionsFragment != null && mFileOptionsFragment.isShowing()) {
- mFileOptionsFragment.dismiss();
- } else if (mEditorBottomSheet.getState() == BottomSheetBehavior.STATE_EXPANDED) {
- mEditorBottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED);
- } else {
- confirmProjectClose();
- }
- }
-
- @Override
- protected void onPause() {
- dispatchOnPauseToEditors();
- super.onPause();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- try {
- checkForCompilerModule();
- dispatchOnResumeToEditors();
- mFileTreeFragment.listProjectFiles();
- } catch (Throwable th) {
- LOG.error("Failed to update files list", th);
- getApp().toast(R.string.msg_failed_list_files, Toaster.Type.ERROR);
- }
- }
-
- @Override
- protected void onDestroy() {
- closeProject(false);
- try {
- unregisterReceiver(mLogReceiver);
- } catch (Throwable th) {
- LOG.error("Failed to unregister LogReceiver", th);
- }
- super.onDestroy();
- }
-
- @Override
- @SuppressLint("AlwaysShowAction")
- public boolean onPrepareOptionsMenu(Menu menu) {
- for (int id : BUILD_IDS) {
- MenuItem item = menu.findItem(id);
- if (item != null) {
- boolean enabled = getBuildService() != null && !getBuildService().isBuilding();
- item.setEnabled(enabled);
- item.getIcon().setAlpha(enabled ? 255 : 76);
- }
+ @Override
+ protected View bindLayout() {
+ mBinding = ActivityEditorBinding.inflate(getLayoutInflater());
+ mDiagnosticInfoBinding = mBinding.diagnosticInfo;
+ return mBinding.getRoot();
}
- MenuItem run1 = menu.findItem(R.id.menuEditor_quickRun);
- MenuItem run2 = menu.findItem(R.id.menuEditor_run);
- MenuItem undo = menu.findItem(R.id.menuEditor_undo);
- MenuItem redo = menu.findItem(R.id.menuEditor_redo);
- MenuItem save = menu.findItem(R.id.menuEditor_save);
- MenuItem def = menu.findItem(R.id.menuEditor_gotoDefinition);
- MenuItem ref = menu.findItem(R.id.menuEditor_findReferences);
- MenuItem comment = menu.findItem(R.id.menuEditor_commentLine);
- MenuItem uncomment = menu.findItem(R.id.menuEditor_uncommentLine);
- MenuItem findFile = menu.findItem(R.id.menuEditor_findFile);
- MenuItem viewLayout = menu.findItem(R.id.menuEditor_viewLayout);
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setSupportActionBar(mBinding.editorToolbar);
- if (KeyboardUtils.isSoftInputVisible(this)) {
- run1.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
- run2.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
- undo.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- redo.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ mViewModel = new ViewModelProvider(this).get(EditorViewModel.class);
+ getProjectFromIntent();
- viewLayout.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
- } else {
- run1.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- run2.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- undo.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
- redo.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ mFileTreeFragment =
+ FileTreeFragment.newInstance(this.getAndroidProject()).setFileActionListener(this);
+ mDaemonStatusFragment = new TextSheetFragment().setTextSelectable(true);
- viewLayout.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ setupDrawerToggle();
+ loadFragment(mFileTreeFragment);
+
+ symbolInput = new SymbolInputView(this);
+ mBinding.bottomSheet.textContainer.addView(
+ symbolInput, 0, new ViewGroup.LayoutParams(-1, -2));
+ mBinding.tabs.addOnTabSelectedListener(this);
+
+ setupEditorBottomSheet();
+ createQuickActions();
+
+ mBuildServiceHandler = new BuildServiceHandler(this);
+ mFileOptionsHandler = new FileOptionsHandler(this);
+
+ startServices();
+
+ KeyboardUtils.registerSoftInputChangedListener(this, __ -> onSoftInputChanged());
+ registerLogReceiver();
+ setupContainers();
+ setupDiagnosticInfo();
+
+ mUIDesignerLauncher =
+ registerForActivityResult(
+ new ActivityResultContracts.StartActivityForResult(),
+ this::onGetUIDesignerResult);
}
- final var editor = getCurrentEditor();
- final var file = editor != null ? editor.getFile() : null;
- final var notNull = editor != null && file != null;
- final var isJava = notNull && file.getName().endsWith(".java");
- final var isXml = notNull && file.getName().endsWith(".xml");
- final var filesModified = notNull && mViewModel != null && mViewModel.areFilesModified();
- final var isLayout =
- isXml
- && file.getParentFile() != null
- && Pattern.compile(FileOptionsHandler.LAYOUT_RES_PATH_REGEX)
- .matcher(file.getParentFile().getAbsolutePath())
- .matches();
- final var nullableAlpha = notNull ? 255 : 76;
- final var javaFileAlpha = isJava ? 255 : 76;
- final var layoutFileAlpha = isLayout ? 255 : 76;
- final var saveAlpha = filesModified ? 255 : 76;
-
- undo.setEnabled(notNull);
- redo.setEnabled(notNull);
- save.setEnabled(filesModified);
- comment.setEnabled(notNull);
- uncomment.setEnabled(notNull);
- findFile.setEnabled(notNull);
- def.setEnabled(isJava);
- ref.setEnabled(isJava);
-
- undo.getIcon().setAlpha(nullableAlpha);
- redo.getIcon().setAlpha(nullableAlpha);
- save.getIcon().setAlpha(saveAlpha);
- comment.getIcon().setAlpha(nullableAlpha);
- uncomment.getIcon().setAlpha(nullableAlpha);
- findFile.getIcon().setAlpha(nullableAlpha);
- def.getIcon().setAlpha(javaFileAlpha);
- ref.getIcon().setAlpha(javaFileAlpha);
-
- viewLayout.setEnabled(isLayout);
- viewLayout.getIcon().setAlpha(layoutFileAlpha);
- viewLayout.setShowAsActionFlags(
- isLayout ? MenuItem.SHOW_AS_ACTION_ALWAYS : MenuItem.SHOW_AS_ACTION_NEVER);
-
- return true;
- }
-
- @SuppressLint("RestrictedApi")
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- if (menu instanceof MenuBuilder) {
- MenuBuilder builder = (MenuBuilder) menu;
- builder.setOptionalIconsVisible(true);
+ private void setupDrawerToggle() {
+ ActionBarDrawerToggle toggle =
+ new ActionBarDrawerToggle(
+ this,
+ mBinding.editorDrawerLayout,
+ mBinding.editorToolbar,
+ R.string.app_name,
+ R.string.app_name);
+ mBinding.editorDrawerLayout.addDrawerListener(toggle);
+ mBinding.startNav.setNavigationItemSelectedListener(this);
+ toggle.syncState();
}
- getMenuInflater().inflate(R.menu.menu_editor, menu);
- return true;
- }
+ private void setupEditorBottomSheet() {
+ bottomSheetTabAdapter = new EditorBottomSheetTabAdapter(this);
+ mBinding.bottomSheet.pager.setAdapter(bottomSheetTabAdapter);
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (getBuildService() == null) {
- LOG.error("Build service is null. Cannot perform option menu actions.");
- return false;
+ final var mediator =
+ new TabLayoutMediator(
+ mBinding.bottomSheet.tabs,
+ mBinding.bottomSheet.pager,
+ true,
+ true,
+ (tab, position) -> tab.setText(bottomSheetTabAdapter.getTitle(position)));
+
+ mediator.attach();
+ mBinding.bottomSheet.pager.setUserInputEnabled(false);
+ mBinding.bottomSheet.pager.setOffscreenPageLimit(
+ bottomSheetTabAdapter.getItemCount() - 1); // DO not remove any views
+
+ mEditorBottomSheet =
+ (EditorBottomSheetBehavior extends View>)
+ EditorBottomSheetBehavior.from(mBinding.bottomSheet.getRoot());
+ mEditorBottomSheet.setBinding(mBinding.bottomSheet);
+ mEditorBottomSheet.addBottomSheetCallback(
+ new BottomSheetBehavior.BottomSheetCallback() {
+ @Override
+ public void onStateChanged(@NonNull View bottomSheet, int newState) {
+ mBinding.bottomSheet.textContainer.setVisibility(
+ newState == BottomSheetBehavior.STATE_EXPANDED
+ ? View.INVISIBLE
+ : View.VISIBLE);
+ }
+
+ @Override
+ public void onSlide(@NonNull View bottomSheet, float slideOffset) {
+ mBinding.bottomSheet.textContainer.setAlpha(1f - slideOffset);
+ }
+ });
+
+ mBinding.bottomSheet.tabs.addOnTabSelectedListener(
+ new TabLayout.OnTabSelectedListener() {
+
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ final var fragment =
+ bottomSheetTabAdapter.getFragmentAtIndex(tab.getPosition());
+ if (fragment instanceof NonEditableEditorFragment) {
+ mBinding.bottomSheet.clearFab.show();
+ } else {
+ mBinding.bottomSheet.clearFab.hide();
+ }
+ }
+
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {}
+
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {}
+ });
+
+ TooltipCompat.setTooltipText(
+ mBinding.bottomSheet.clearFab, getString(R.string.title_clear_output));
+ mBinding.bottomSheet.clearFab.setOnClickListener(
+ v -> {
+ final var fragment =
+ bottomSheetTabAdapter.getFragmentAtIndex(
+ mBinding.bottomSheet.tabs.getSelectedTabPosition());
+ if (fragment instanceof NonEditableEditorFragment) {
+ final var editor = (NonEditableEditorFragment) fragment;
+ if (editor.getEditor() != null) {
+ editor.getEditor().setText("");
+ }
+ }
+ });
+
+ if (!getApp().getPrefManager().getBoolean(KEY_BOTTOM_SHEET_SHOWN)
+ && mEditorBottomSheet.getState() != BottomSheetBehavior.STATE_EXPANDED) {
+ mEditorBottomSheet.setState(BottomSheetBehavior.STATE_EXPANDED);
+ new Handler(Looper.getMainLooper())
+ .postDelayed(
+ () -> {
+ mEditorBottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED);
+ getApp().getPrefManager().putBoolean(KEY_BOTTOM_SHEET_SHOWN, true);
+ },
+ 1500);
+ }
}
- int id = item.getItemId();
- if (id == R.id.menuEditor_runDebug || id == R.id.menuEditor_quickRun) {
- getBuildServiceHandler().assembleDebug(true);
- } else if (id == R.id.menuEditor_runRelease) {
- getBuildService().assembleRelease();
- } else if (id == R.id.menuEditor_runClean) {
- getBuildService().clean();
- } else if (id == R.id.menuEditor_runCleanBuild) {
- getBuildService().cleanAndRebuild();
- } else if (id == R.id.menuEditor_runStopDaemons) {
- getBuildService().stopAllDaemons();
- } else if (id == R.id.menuEditor_runBuild) {
- getBuildService().build();
- } else if (id == R.id.menuEditor_runBundle) {
- getBuildService().bundle();
- } else if (id == R.id.menuEditor_lint) {
- getBuildService().lint();
- } else if (id == R.id.menuEditor_lintDebug) {
- getBuildService().lintDebug();
- } else if (id == R.id.menuEditor_lintRelease) {
- getBuildService().lintRelease();
- } else if (id == R.id.menuEditor_save) {
- saveAll(true, true);
- } else if (id == R.id.menuEditor_findProject) {
- AlertDialog d = getFindInProjectDialog();
- if (d != null) {
- d.show();
- }
- } else if (id == R.id.menuEditor_viewLayout) {
- previewLayout();
- } else if (id == R.id.menuEditor_daemonStatus) {
- showDaemonStatus();
- } else if (getCurrentEditor() != null) { // Should be checked at last
- if (id == R.id.menuEditor_undo) {
- this.getCurrentEditor().undo();
- } else if (id == R.id.menuEditor_redo) {
- this.getCurrentEditor().redo();
- } else if (id == R.id.menuEditor_gotoDefinition) {
- this.getCurrentEditor().findDefinition();
- } else if (id == R.id.menuEditor_findReferences) {
- this.getCurrentEditor().findReferences();
- } else if (id == R.id.menuEditor_commentLine) {
- this.getCurrentEditor().commentLine();
- } else if (id == R.id.menuEditor_uncommentLine) {
- this.getCurrentEditor().uncommentLine();
- } else if (id == R.id.menuEditor_findFile) {
- this.getCurrentEditor().beginSearch();
- }
- }
-
- invalidateOptionsMenu();
- return true;
- }
-
- @Override
- public EditorActivity provide() {
- return this;
- }
-
- @Override
- public EditorActivity provideEditorActivity() {
- return this;
- }
-
- @Override
- public AndroidProject provideAndroidProject() {
- return getAndroidProject();
- }
-
- @Override
- public IDEProject provideIDEProject() {
- return getIDEProject();
- }
-
- public void handleSearchResults(Map> results) {
- setSearchResultAdapter(
- new com.itsaky.androidide.adapters.SearchListAdapter(
- results,
- file -> {
- openFile(file);
- hideViewOptions();
- },
- match -> {
- openFileAndSelect(match.file, match);
- hideViewOptions();
- }));
-
- showSearchResults();
-
- if (mSearchingProgress != null && mSearchingProgress.isShowing()) {
- mSearchingProgress.dismiss();
- }
- }
-
- public void appendApkLog(LogLine line) {
- bottomSheetTabAdapter.getLogFragment().appendLog(line);
- }
-
- public void setIDEProject(IDEProject project) {
- if (mViewModel != null) {
- mViewModel.setIDEProject(project);
- }
- }
-
- public void appendBuildOut(final String str) {
- bottomSheetTabAdapter.getBuildOutputFragment().appendOutput(str);
- }
-
- public void showDaemonStatus() {
- ShellServer shell = getApp().newShell(t -> getDaemonStatusFragment().append(t));
- shell.bgAppend(String.format("echo '%s'", getString(R.string.msg_getting_daemom_status)));
- shell.bgAppend(
- String.format(
- "cd '%s' && sh gradlew --status",
- Objects.requireNonNull(getAndroidProject()).getProjectPath()));
- if (!getDaemonStatusFragment().isShowing()) {
- getDaemonStatusFragment().show(getSupportFragmentManager(), "daemon_status");
- }
- }
-
- public void hideViewOptions() {
- if (mEditorBottomSheet.getState() != BottomSheetBehavior.STATE_COLLAPSED) {
- mEditorBottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED);
- }
- }
-
- public void showSearchResults() {
- if (mEditorBottomSheet.getState() != BottomSheetBehavior.STATE_EXPANDED) {
- mEditorBottomSheet.setState(BottomSheetBehavior.STATE_EXPANDED);
- }
-
- final int index = bottomSheetTabAdapter.findIndexOfFragmentByClass(SearchResultFragment.class);
- if (index >= 0 && index < mBinding.bottomSheet.tabs.getTabCount()) {
- final var tab = mBinding.bottomSheet.tabs.getTabAt(index);
- if (tab != null) {
- tab.select();
- }
- }
- }
-
- public void handleDiagnosticsResultVisibility(boolean errorVisible) {
- runOnUiThread(
- () -> bottomSheetTabAdapter.getDiagnosticsFragment().handleResultVisibility(errorVisible));
- }
-
- public void handleSearchResultVisibility(boolean errorVisible) {
- runOnUiThread(
- () -> bottomSheetTabAdapter.getSearchResultFragment().handleResultVisibility(errorVisible));
- }
-
- public void setStatus(final CharSequence text) {
- try {
- runOnUiThread(() -> mBinding.bottomSheet.statusText.setText(text));
- } catch (Throwable th) {
- LOG.error("Failed to update status text", th);
- }
- }
-
- public void showFirstBuildNotice() {
- DialogUtils.newMaterialDialogBuilder(this)
- .setPositiveButton(android.R.string.ok, null)
- .setTitle(R.string.title_first_build)
- .setMessage(R.string.msg_first_build)
- .setCancelable(false)
- .create()
- .show();
- }
-
- @Override
- public void onGroupClick(DiagnosticGroup group) {
- if (group != null
- && group.file != null
- && group.file.exists()
- && FileUtils.isUtf8(group.file)) {
- openFile(group.file);
- hideViewOptions();
- }
- }
-
- @Override
- public void onDiagnosticClick(File file, @NonNull DiagnosticItem diagnostic) {
- openFileAndSelect(file, diagnostic.getRange());
- hideViewOptions();
- }
-
- public ActivityEditorBinding getBinding() {
- return mBinding;
- }
-
- public LayoutDiagnosticInfoBinding getDiagnosticBinding() {
- return mDiagnosticInfoBinding;
- }
-
- public void openFileAndSelect(File file, Range selection) {
- openFile(file, selection);
- final var opened = getEditorForFile(file);
- if (opened != null && opened.getEditor() != null) {
- final var editor = opened.getEditor();
- final var range = editor.validateRange(selection);
- editor.post(
- () -> {
- if (LSPUtils.isEqual(range.getStart(), range.getEnd())) {
- editor.setSelection(range.getStart().getLine(), range.getEnd().getColumn());
- } else {
- editor.setSelectionRegion(
- range.getStart().getLine(),
- range.getStart().getColumn(),
- range.getEnd().getLine(),
- range.getEnd().getColumn());
- }
- });
- }
- }
-
- @Override
- public void onTabSelected(@NonNull TabLayout.Tab tab) {
- final var position = tab.getPosition();
- mBinding.editorContainer.setDisplayedChild(position);
-
- final var editorView = getEditorAtIndex(position);
- mViewModel.setCurrentFile(position, Objects.requireNonNull(editorView).getFile());
- refreshSymbolInput(editorView);
- invalidateOptionsMenu();
- }
-
- @Override
- public void onTabUnselected(@NonNull TabLayout.Tab tab) {
- // unimplemented
- }
-
- @Override
- public void onTabReselected(@NonNull TabLayout.Tab tab) {
- mTabCloseAction.show(tab.view);
- }
-
- @Override
- public boolean onNavigationItemSelected(@NonNull MenuItem p1) {
- final int id = p1.getItemId();
- if (id == R.id.editornav_discuss) {
- getApp().openTelegramGroup();
- } else if (id == R.id.editornav_suggest) {
- getApp().openGitHub();
- } else if (id == R.id.editornav_needHelp) {
- showNeedHelpDialog();
- } else if (id == R.id.editornav_settings) {
- startActivity(new Intent(this, PreferencesActivity.class));
- } else if (id == R.id.editornav_share) {
- startActivity(IntentUtils.getShareTextIntent(getString(R.string.msg_share_app)));
- } else if (id == R.id.editornav_close_project) {
- confirmProjectClose();
- } else if (id == R.id.editornav_terminal) {
- openTerminal();
- }
- mBinding.getRoot().closeDrawer(GravityCompat.START);
- return false;
- }
-
- public void loadFragment(Fragment fragment) {
- super.loadFragment(fragment, mBinding.editorFrameLayout.getId());
- }
-
- public FileTreeFragment getFileTreeFragment() {
- return mFileTreeFragment;
- }
-
- public TreeNode getLastHoldTreeNode() {
- return mLastHeld;
- }
-
- public OptionsListFragment getFileOptionsFragment(File file) {
- mFileOptionsFragment = new OptionsListFragment();
- mFileOptionsFragment.addOption(
- new SheetOption(0, R.drawable.ic_file_copy_path, R.string.copy_path, file));
- mFileOptionsFragment.addOption(
- new SheetOption(1, R.drawable.ic_file_rename, R.string.rename_file, file));
- mFileOptionsFragment.addOption(
- new SheetOption(2, R.drawable.ic_delete, R.string.delete_file, file));
- if (file.isDirectory()) {
- mFileOptionsFragment.addOption(
- new SheetOption(3, R.drawable.ic_new_file, R.string.new_file, file));
- mFileOptionsFragment.addOption(
- new SheetOption(4, R.drawable.ic_new_folder, R.string.new_folder, file));
- }
- mFileOptionsFragment.setOnOptionsClickListener(mFileOptionsHandler);
- return mFileOptionsFragment;
- }
-
- public ProgressSheet getProgressSheet(int msg) {
- if (mSearchingProgress != null && mSearchingProgress.isShowing()) {
- mSearchingProgress.dismiss();
- }
- mSearchingProgress = new ProgressSheet();
- mSearchingProgress.setCancelable(false);
- mSearchingProgress.setWelcomeTextEnabled(false);
- mSearchingProgress.setMessage(getString(msg));
- mSearchingProgress.setSubMessageEnabled(false);
- return mSearchingProgress;
- }
-
- public MaterialBanner getSyncBanner() {
- return mBinding
- .syncBanner
- .setContentTextColor(ContextCompat.getColor(this, R.color.primaryTextColor))
- .setBannerBackgroundColor(ContextCompat.getColor(this, R.color.primaryLightColor))
- .setButtonTextColor(ContextCompat.getColor(this, R.color.secondaryColor))
- .setIcon(R.drawable.ic_sync)
- .setContentText(R.string.msg_sync_needed);
- }
-
- public TextSheetFragment getDaemonStatusFragment() {
- return mDaemonStatusFragment == null
- ? mDaemonStatusFragment =
- new TextSheetFragment()
- .setTextSelectable(true)
- .setTitleText(R.string.gradle_daemon_status)
- : mDaemonStatusFragment;
- }
-
- public void setDiagnosticsAdapter(@NonNull final DiagnosticsAdapter adapter) {
- runOnUiThread(() -> bottomSheetTabAdapter.getDiagnosticsFragment().setAdapter(adapter));
- }
-
- public void setSearchResultAdapter(@NonNull final SearchListAdapter adapter) {
- runOnUiThread(() -> bottomSheetTabAdapter.getSearchResultFragment().setAdapter(adapter));
- }
-
- @Override
- public void showFileOptions(File thisFile, TreeNode node) {
- mLastHeld = node;
- getFileOptionsFragment(thisFile).show(getSupportFragmentManager(), TAG_FILE_OPTIONS_FRAGMENT);
- }
-
- @SuppressWarnings("UnusedReturnValue")
- public boolean saveAll() {
- return saveAll(true);
- }
-
- public boolean saveAll(boolean notify) {
- return saveAll(notify, false);
- }
-
- public boolean saveAll(boolean notify, boolean canProcessResources) {
- SaveResult result = saveAllResult();
-
- if (notify) {
- getApp().toast(R.string.all_saved, Toaster.Type.SUCCESS);
- }
-
- if (result.gradleSaved) {
- notifySyncNeeded();
- }
-
- if (result.xmlSaved && canProcessResources && getBuildService() != null) {
- getBuildService().updateResourceClasses();
- }
-
- return result.gradleSaved;
- }
-
- public SaveResult saveAllResult() {
- SaveResult result = new SaveResult();
- for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) {
- saveResult(i, result);
- }
-
- return result;
- }
-
- public void saveResult(int index, SaveResult result) {
- if (index >= 0 && index < mViewModel.getOpenedFileCount()) {
- var frag = getEditorAtIndex(index);
- if (frag == null || frag.getFile() == null) {
- return;
- }
-
- // Must be called before frag.save()
- // Otherwise, it'll always return false
- final boolean modified = frag.isModified();
-
- frag.save();
-
- final boolean isGradle = frag.getFile().getName().endsWith(".gradle");
- final boolean isXml = frag.getFile().getName().endsWith(".xml");
-
- if (!result.gradleSaved) {
- result.gradleSaved = modified && isGradle;
- }
-
- if (!result.xmlSaved) {
- result.xmlSaved = modified && isXml;
- }
- }
-
- var modified = false;
- for (var file : mViewModel.getOpenedFiles()) {
- var editor = getEditorForFile(file);
- if (editor == null) {
- continue;
- }
- modified = modified || editor.isModified();
- }
-
- mViewModel.setFilesModified(modified);
- }
-
- public void install(@NonNull File apk) {
- if (apk.exists()) {
- Intent i = IntentUtils.getInstallAppIntent(apk);
- if (i != null) {
- startActivity(i);
- } else {
- getApp().toast(R.string.msg_apk_install_intent_failed, Toaster.Type.ERROR);
- }
- }
- }
-
- public void updateServices() {
- new TaskExecutor()
- .executeAsync(
- () -> {
- List cps = Objects.requireNonNull(getAndroidProject()).getClassPaths();
- getApp()
- .getJavaLanguageServer()
- .configurationChanged(
- new JavaServerConfiguration(
- cps.stream().map(Paths::get).collect(Collectors.toSet())));
- return null;
- },
- __ -> setStatus(getString(R.string.msg_service_started)));
- }
-
- @Override
- public CodeEditorView openFile(File file) {
- return openFile(file, null);
- }
-
- public CodeEditorView openFile(File file, com.itsaky.lsp.models.Range selection) {
- if (selection == null) {
- selection = com.itsaky.lsp.models.Range.NONE;
- }
-
- int index = openFileAndGetIndex(file, selection);
- final var tab = mBinding.tabs.getTabAt(index);
- if (tab != null && index >= 0 && !tab.isSelected()) {
- tab.select();
- }
-
- if (mBinding.editorDrawerLayout.isDrawerOpen(GravityCompat.END)) {
- mBinding.editorDrawerLayout.closeDrawer(GravityCompat.END);
- }
-
- mBinding.editorContainer.setDisplayedChild(index);
-
- invalidateOptionsMenu();
- try {
- return getEditorAtIndex(index);
- } catch (Throwable th) {
- LOG.error("Unable to get editor fragment at opened file index", index);
- LOG.error(th);
- return null;
- }
- }
-
- @SuppressLint("NotifyDataSetChanged")
- public int openFileAndGetIndex(File file, com.itsaky.lsp.models.Range selection) {
- final var openedFileIndex = findIndexOfEditorByFile(file);
-
- if (openedFileIndex != -1) {
- LOG.error("File is already opened. File: " + file);
- return openedFileIndex;
- }
-
- final var position = mViewModel.getOpenedFileCount();
-
- LOG.info("Opening file at index:", position, "file: ", file);
-
- final var editor = new CodeEditorView(this, file, selection);
- editor.getEditor().subscribeEvent(ContentChangeEvent.class, this::onEditorContentChanged);
- editor.setLayoutParams(
- new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
-
- mBinding.editorContainer.addView(editor);
- mBinding.editorContainer.setDisplayedChild(position);
- mBinding.tabs.addTab(mBinding.tabs.newTab().setText(file.getName()));
-
- mViewModel.addFile(file);
- mViewModel.setCurrentFile(position, file);
-
- return position;
- }
-
- public void closeFile(int index) {
- if (index >= 0 && index < mViewModel.getOpenedFileCount()) {
- var opened = mViewModel.getOpenedFile(index);
- LOG.info("Closing file:", opened);
- final var editor = getEditorAtIndex(index);
-
- if (editor != null && editor.isModified()) {
- notifyFilesUnsaved(Collections.singletonList(editor), () -> closeFile(index));
- return;
- }
-
- if (editor != null && editor.getEditor() != null) {
- editor.getEditor().close();
- } else {
- LOG.error("Cannot save file before close. Editor instance is null");
- }
-
- mViewModel.removeFile(index);
- mBinding.tabs.removeTabAt(index);
- mBinding.editorContainer.removeViewAt(index);
- } else {
- LOG.error("Invalid file index. Cannot close.");
- return;
- }
-
- mBinding.tabs.requestLayout();
- invalidateOptionsMenu();
- }
-
- public void closeAll() {
- closeAll(null);
- }
-
- /**
- * Close all opened files. If all the files are saved successfully, then the provided {@link
- * Runnable} will be called.
- *
- * @param onSaved The {@link Runnable} to run when all the files are saved.
- */
- public void closeAll(Runnable onSaved) {
- final var count = mViewModel.getOpenedFileCount();
- final var unsavedFiles =
- mViewModel.getOpenedFiles().stream()
- .map(this::getEditorForFile)
- .filter(editor -> editor != null && editor.isModified())
- .collect(Collectors.toList());
-
- if (unsavedFiles.isEmpty()) {
- // Files were already saved, close all files one by one
- for (int i = 0; i < count; i++) {
- final var editor = getEditorAtIndex(i);
- if (editor != null && editor.getEditor() != null) {
- editor.getEditor().close();
+ @Override
+ public void onBackPressed() {
+ if (mBinding.getRoot().isDrawerOpen(GravityCompat.END)) {
+ mBinding.getRoot().closeDrawer(GravityCompat.END);
+ } else if (mBinding.getRoot().isDrawerOpen(GravityCompat.START)) {
+ mBinding.getRoot().closeDrawer(GravityCompat.START);
+ } else if (getDaemonStatusFragment().isShowing()) {
+ getDaemonStatusFragment().dismiss();
+ } else if (mFileOptionsFragment != null && mFileOptionsFragment.isShowing()) {
+ mFileOptionsFragment.dismiss();
+ } else if (mEditorBottomSheet.getState() == BottomSheetBehavior.STATE_EXPANDED) {
+ mEditorBottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED);
} else {
- LOG.error("Unable to close file at index:", i);
+ confirmProjectClose();
}
- }
-
- mViewModel.removeAllFiles();
- mBinding.tabs.removeAllTabs();
- mBinding.tabs.requestLayout();
- mBinding.editorContainer.removeAllViews();
-
- invalidateOptionsMenu();
-
- if (onSaved != null) {
- onSaved.run();
- }
- } else {
- // There are unsaved files
- notifyFilesUnsaved(unsavedFiles, () -> closeAll(onSaved));
}
- }
- private void notifyFilesUnsaved(List unsavedEditors, Runnable invokeAfter) {
- //noinspection ConstantConditions
- final var mapped =
- unsavedEditors.stream()
- .map(CodeEditorView::getFile)
- .filter(Objects::nonNull)
- .map(File::getAbsolutePath)
- .collect(Collectors.toList());
- final var builder =
- DialogUtils.newYesNoDialog(
- this,
- getString(R.string.title_files_unsaved), // title
- getString(R.string.msg_files_unsaved, TextUtils.join("\n", mapped)), // message
- (dialog, which) -> { // 'yes' click
- dialog.dismiss();
- saveAll(true);
- invokeAfter.run();
- },
- (dialog, which) -> { // 'no' click
- dialog.dismiss();
- // Mark all the files as saved, then try to close them all
- for (var editor : unsavedEditors) {
- editor.markAsSaved();
- }
+ @Override
+ protected void onPause() {
+ dispatchOnPauseToEditors();
+ super.onPause();
+ }
- invokeAfter.run();
- });
- builder.show();
- }
+ @Override
+ protected void onResume() {
+ super.onResume();
- public void closeOthers() {
- final var unsavedFiles =
- mViewModel.getOpenedFiles().stream()
- .map(this::getEditorForFile)
- .filter(editor -> editor != null && editor.isModified())
- .collect(Collectors.toList());
+ try {
+ checkForCompilerModule();
+ dispatchOnResumeToEditors();
+ mFileTreeFragment.listProjectFiles();
+ } catch (Throwable th) {
+ LOG.error("Failed to update files list", th);
+ getApp().toast(R.string.msg_failed_list_files, Toaster.Type.ERROR);
+ }
+ }
- if (unsavedFiles.isEmpty()) {
- final var file = mViewModel.getCurrentFile();
- for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) {
- final var editor = getEditorAtIndex(i);
+ @Override
+ protected void onDestroy() {
+ closeProject(false);
+ try {
+ unregisterReceiver(mLogReceiver);
+ } catch (Throwable th) {
+ LOG.error("Failed to unregister LogReceiver", th);
+ }
+ super.onDestroy();
+ }
- // Index of files changes as we keep close files
- // So we compare the files instead of index
- if (editor != null) {
- if (!file.equals(editor.getFile())) {
- closeFile(i);
- }
+ @Override
+ @SuppressLint("AlwaysShowAction")
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ for (int id : BUILD_IDS) {
+ MenuItem item = menu.findItem(id);
+ if (item != null) {
+ boolean enabled = getBuildService() != null && !getBuildService().isBuilding();
+ item.setEnabled(enabled);
+ item.getIcon().setAlpha(enabled ? 255 : 76);
+ }
+ }
+
+ MenuItem run1 = menu.findItem(R.id.menuEditor_quickRun);
+ MenuItem run2 = menu.findItem(R.id.menuEditor_run);
+ MenuItem undo = menu.findItem(R.id.menuEditor_undo);
+ MenuItem redo = menu.findItem(R.id.menuEditor_redo);
+ MenuItem save = menu.findItem(R.id.menuEditor_save);
+ MenuItem def = menu.findItem(R.id.menuEditor_gotoDefinition);
+ MenuItem ref = menu.findItem(R.id.menuEditor_findReferences);
+ MenuItem comment = menu.findItem(R.id.menuEditor_commentLine);
+ MenuItem uncomment = menu.findItem(R.id.menuEditor_uncommentLine);
+ MenuItem findFile = menu.findItem(R.id.menuEditor_findFile);
+ MenuItem viewLayout = menu.findItem(R.id.menuEditor_viewLayout);
+
+ if (KeyboardUtils.isSoftInputVisible(this)) {
+ run1.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ run2.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ undo.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ redo.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+
+ viewLayout.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
} else {
- LOG.error("Unable to save file at index:", i);
+ run1.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ run2.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ undo.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+ redo.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+
+ viewLayout.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
- }
- } else {
- notifyFilesUnsaved(unsavedFiles, this::closeOthers);
- }
- }
- @Nullable
- public CodeEditorView getCurrentEditor() {
- if (mViewModel.getCurrentFileIndex() != -1) {
- return getEditorAtIndex(mViewModel.getCurrentFileIndex());
+ final var editor = getCurrentEditor();
+ final var file = editor != null ? editor.getFile() : null;
+ final var notNull = editor != null && file != null;
+ final var isJava = notNull && file.getName().endsWith(".java");
+ final var isXml = notNull && file.getName().endsWith(".xml");
+ final var filesModified = notNull && mViewModel != null && mViewModel.areFilesModified();
+ final var isLayout =
+ isXml
+ && file.getParentFile() != null
+ && Pattern.compile(FileOptionsHandler.LAYOUT_RES_PATH_REGEX)
+ .matcher(file.getParentFile().getAbsolutePath())
+ .matches();
+ final var nullableAlpha = notNull ? 255 : 76;
+ final var javaFileAlpha = isJava ? 255 : 76;
+ final var layoutFileAlpha = isLayout ? 255 : 76;
+ final var saveAlpha = filesModified ? 255 : 76;
+
+ undo.setEnabled(notNull);
+ redo.setEnabled(notNull);
+ save.setEnabled(filesModified);
+ comment.setEnabled(notNull);
+ uncomment.setEnabled(notNull);
+ findFile.setEnabled(notNull);
+ def.setEnabled(isJava);
+ ref.setEnabled(isJava);
+
+ undo.getIcon().setAlpha(nullableAlpha);
+ redo.getIcon().setAlpha(nullableAlpha);
+ save.getIcon().setAlpha(saveAlpha);
+ comment.getIcon().setAlpha(nullableAlpha);
+ uncomment.getIcon().setAlpha(nullableAlpha);
+ findFile.getIcon().setAlpha(nullableAlpha);
+ def.getIcon().setAlpha(javaFileAlpha);
+ ref.getIcon().setAlpha(javaFileAlpha);
+
+ viewLayout.setEnabled(isLayout);
+ viewLayout.getIcon().setAlpha(layoutFileAlpha);
+ viewLayout.setShowAsActionFlags(
+ isLayout ? MenuItem.SHOW_AS_ACTION_ALWAYS : MenuItem.SHOW_AS_ACTION_NEVER);
+
+ return true;
}
- return null;
- }
-
- @Nullable
- public CodeEditorView getEditorAtIndex(final int index) {
- return (CodeEditorView) mBinding.editorContainer.getChildAt(index);
- }
-
- @Nullable
- public CodeEditorView getEditorForFile(@NonNull final File file) {
- for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) {
- final CodeEditorView editor = (CodeEditorView) mBinding.editorContainer.getChildAt(i);
- if (file.equals(editor.getFile())) {
- return editor;
- }
- }
-
- return null;
- }
-
- public int findIndexOfEditorByFile(File file) {
- if (file == null) {
- LOG.error("Cannot find index of a null file.");
- return -1;
- }
-
- for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) {
- final var opened = mViewModel.getOpenedFile(i);
- if (opened.equals(file)) {
- return i;
- }
- }
-
- return -1;
- }
-
- /////////////////////////////////////////////////
- ////////////// PRIVATE APIS /////////////////////
- /////////////////////////////////////////////////
-
- private void onEditorContentChanged(ContentChangeEvent event, Unsubscribe unsubscribe) {
- if (event.getAction() != ContentChangeEvent.ACTION_SET_NEW_TEXT) {
- mViewModel.setFilesModified(true);
- }
- }
-
- @SuppressWarnings("deprecation")
- private void checkForCompilerModule() {
- if (!Environment.isCompilerModuleInstalled()) {
- final var pd =
- ProgressDialog.show(
- this,
- getString(R.string.title_compiler_module_install),
- getString(R.string.msg_compiler_module_install),
- true,
- false);
-
- final CompletableFuture future =
- CompletableFuture.supplyAsync(
- () -> {
- final var tmpModule = new File(Environment.TMP_DIR, "compiler-module.zip");
- if (!ResourceUtils.copyFileFromAssets(
- ToolsManager.getCommonAsset("compiler-module.zip"),
- tmpModule.getAbsolutePath())) {
- throw new CompletionException(
- new RuntimeException("Unable to copy compiler-module.zip"));
- }
-
- try {
- ZipUtils.unzipFile(tmpModule, Environment.COMPILER_MODULE);
- } catch (Throwable e) {
- throw new CompletionException(e);
- }
-
- if (!Environment.isCompilerModuleInstalled()) {
- throw new CompletionException(new RuntimeException("Unknown error"));
- }
-
- try {
- FileUtils.delete(tmpModule);
- } catch (Exception e) {
- // ignored
- }
-
- return true;
- });
-
- future.whenComplete(
- (result, error) -> {
- pd.dismiss();
-
- if (error != null) {
- showCompilerModuleInstallError(error);
- return;
- }
-
- getApp().toast(getString(R.string.msg_compiler_module_installed), Toaster.Type.SUCCESS);
- });
- }
- }
-
- private void showCompilerModuleInstallError(Throwable error) {
- final var stacktrace = ThrowableUtils.getFullStackTrace(error);
- final var builder = DialogUtils.newMaterialDialogBuilder(this);
- builder.setTitle(R.string.title_installation_failed);
- builder.setMessage(getString(R.string.msg_compiler_module_install_failed, stacktrace));
- builder.setCancelable(false);
- builder.setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
- builder.setNegativeButton(
- R.string.copy,
- (dialog, which) -> {
- ClipboardUtils.copyText(stacktrace);
- dialog.dismiss();
- });
- builder.show();
- }
-
- private void dispatchOnPauseToEditors() {
- CompletableFuture.runAsync(
- () -> {
- for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) {
- final var editor = getEditorAtIndex(i);
- if (editor != null) {
- editor.onPause();
- }
- }
- });
- }
-
- private void dispatchOnResumeToEditors() {
- CompletableFuture.runAsync(
- () -> {
- for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) {
- final var editor = getEditorAtIndex(i);
- if (editor != null) {
- editor.onResume();
- }
- }
- });
- }
-
- @Contract(pure = true)
- private void onGetUIDesignerResult(@NonNull ActivityResult result) {
- final var index = mBinding.editorContainer.getDisplayedChild();
- final var editor = getEditorAtIndex(index);
- if (editor != null && result.getResultCode() == RESULT_OK) {
- final var data = result.getData();
- if (data != null && data.hasExtra(DesignerActivity.KEY_GENERATED_CODE)) {
- final var code = data.getStringExtra(DesignerActivity.KEY_GENERATED_CODE);
- editor.getEditor().setText(code);
- saveAll();
- } else {
- final var msg = getString(R.string.msg_invalid_designer_result);
- getApp().toast(msg, Toaster.Type.ERROR);
- LOG.error(msg, "Data returned by UI Designer is null or is invalid.");
- }
- } else {
- LOG.error(
- "UI Designer returned an invalid result code.", "Result code: " + result.getResultCode());
- }
- }
-
- private void previewLayout() {
- try {
-
- if (getCurrentEditor() == null || getCurrentEditor().getFile() == null) {
- LOG.error("No file is opened. Cannot preview layout.");
- return;
- }
-
- saveAll(false);
- if (getApp().getLayoutInflater() == null) {
- LOG.info("Creating layout inflater instance...");
- getApp()
- .createInflater(
- getApp().createInflaterConfig(getContextProvider(), getResourceDirectories()));
- }
-
- final Intent intent = new Intent(this, DesignerActivity.class);
- intent.putExtra(
- DesignerActivity.KEY_LAYOUT_PATH, getCurrentEditor().getFile().getAbsolutePath());
-
- LOG.info("Launching UI Designer...");
- mUIDesignerLauncher.launch(intent);
- } catch (Throwable th) {
- LOG.error(getString(R.string.err_cannot_preview_layout), th);
- getApp().toast(R.string.msg_cannot_preview_layout, Toaster.Type.ERROR);
- }
- }
-
- @NonNull
- @Contract(" -> new")
- private ILayoutInflater.ContextProvider getContextProvider() {
- return this::provide;
- }
-
- @NonNull
- private Set getResourceDirectories() {
- final Set dirs = new HashSet<>();
- if (getAndroidProject() != null
- && getAndroidProject().getModulePaths() != null
- && !getAndroidProject().getModulePaths().isEmpty()) {
- for (String path : getAndroidProject().getModulePaths()) {
- if (path != null && new File(path).exists()) {
- File res = new File(path, "src/main/res");
- if (res.exists()) {
- dirs.add(res);
- }
+ @SuppressLint("RestrictedApi")
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ if (menu instanceof MenuBuilder) {
+ MenuBuilder builder = (MenuBuilder) menu;
+ builder.setOptionalIconsVisible(true);
}
- }
- }
- return dirs;
- }
- private void openTerminal() {
- final Intent intent = new Intent(this, TerminalActivity.class);
- intent.putExtra(
- TerminalActivity.KEY_WORKING_DIRECTORY,
- Objects.requireNonNull(getAndroidProject()).getProjectPath());
- startActivity(intent);
- }
-
- private void getProjectFromIntent() {
- final var project = (AndroidProject) getIntent().getParcelableExtra(EXTRA_PROJECT);
- if (mViewModel != null) {
- mViewModel.setAndroidProject(project);
- } else {
- LOG.error("ViewModel is null. Cannot set project.");
- }
- getApp()
- .getPrefManager()
- .setOpenedProject(Objects.requireNonNull(this.getAndroidProject()).getProjectPath());
-
- try {
- //noinspection ConstantConditions
- getSupportActionBar().setSubtitle(new File(getAndroidProject().getProjectPath()).getName());
- } catch (Throwable th) {
- // ignored
+ getMenuInflater().inflate(R.menu.menu_editor, menu);
+ return true;
}
- CompletableFuture.runAsync(
- () ->
- ValuesTableFactory.setupWithResDirectories(
- getResourceDirectories().toArray(new File[0])));
- }
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (getBuildService() == null) {
+ LOG.error("Build service is null. Cannot perform option menu actions.");
+ return false;
+ }
- @Nullable
- private AndroidProject getAndroidProject() {
- if (mViewModel == null) {
- return null;
+ int id = item.getItemId();
+ if (id == R.id.menuEditor_runDebug || id == R.id.menuEditor_quickRun) {
+ getBuildServiceHandler().assembleDebug(true);
+ } else if (id == R.id.menuEditor_runRelease) {
+ getBuildService().assembleRelease();
+ } else if (id == R.id.menuEditor_runClean) {
+ getBuildService().clean();
+ } else if (id == R.id.menuEditor_runCleanBuild) {
+ getBuildService().cleanAndRebuild();
+ } else if (id == R.id.menuEditor_runStopDaemons) {
+ getBuildService().stopAllDaemons();
+ } else if (id == R.id.menuEditor_runBuild) {
+ getBuildService().build();
+ } else if (id == R.id.menuEditor_runBundle) {
+ getBuildService().bundle();
+ } else if (id == R.id.menuEditor_lint) {
+ getBuildService().lint();
+ } else if (id == R.id.menuEditor_lintDebug) {
+ getBuildService().lintDebug();
+ } else if (id == R.id.menuEditor_lintRelease) {
+ getBuildService().lintRelease();
+ } else if (id == R.id.menuEditor_save) {
+ saveAll(true, true);
+ } else if (id == R.id.menuEditor_findProject) {
+ AlertDialog d = getFindInProjectDialog();
+ if (d != null) {
+ d.show();
+ }
+ } else if (id == R.id.menuEditor_viewLayout) {
+ previewLayout();
+ } else if (id == R.id.menuEditor_daemonStatus) {
+ showDaemonStatus();
+ } else if (getCurrentEditor() != null) { // Should be checked at last
+ if (id == R.id.menuEditor_undo) {
+ this.getCurrentEditor().undo();
+ } else if (id == R.id.menuEditor_redo) {
+ this.getCurrentEditor().redo();
+ } else if (id == R.id.menuEditor_gotoDefinition) {
+ this.getCurrentEditor().findDefinition();
+ } else if (id == R.id.menuEditor_findReferences) {
+ this.getCurrentEditor().findReferences();
+ } else if (id == R.id.menuEditor_commentLine) {
+ this.getCurrentEditor().commentLine();
+ } else if (id == R.id.menuEditor_uncommentLine) {
+ this.getCurrentEditor().uncommentLine();
+ } else if (id == R.id.menuEditor_findFile) {
+ this.getCurrentEditor().beginSearch();
+ }
+ }
+
+ invalidateOptionsMenu();
+ return true;
}
- return mViewModel.getAndroidProject();
- }
-
- @Nullable
- private IDEProject getIDEProject() {
- if (mViewModel == null) {
- return null;
+ @Override
+ public EditorActivity provide() {
+ return this;
}
- return mViewModel.getIDEProject();
- }
-
- private void setupDiagnosticInfo() {
- GradientDrawable gd = new GradientDrawable();
- gd.setShape(GradientDrawable.RECTANGLE);
- gd.setColor(0xff212121);
- gd.setStroke(1, 0xffffffff);
- gd.setCornerRadius(8);
- mDiagnosticInfoBinding.getRoot().setBackground(gd);
- mDiagnosticInfoBinding.getRoot().setVisibility(View.GONE);
- }
-
- private void setupContainers() {
- handleDiagnosticsResultVisibility(true);
- handleSearchResultVisibility(true);
- }
-
- private void startServices() {
-
- if (!IDELanguageClientImpl.isInitialized()) {
- IDELanguageClientImpl.initialize(this);
+ @Override
+ public EditorActivity provideEditorActivity() {
+ return this;
}
- initializeLanguageServers();
-
- // Actually, we don't need to start FileOptionsHandler
- // Because it would work anyway
- // But still we do...
- mFileOptionsHandler.start();
- mBuildServiceHandler.start();
- getBuildServiceHandler().assembleDebug(false);
- }
-
- private void initializeLanguageServers() {
- final var client = IDELanguageClientImpl.getInstance();
- final var javaLanguageServer = getApp().getJavaLanguageServer();
- final var workspaceRoots =
- Collections.singleton(
- new File(Objects.requireNonNull(getAndroidProject()).getProjectPath()).toPath());
-
- final var params = new InitializeParams(workspaceRoots);
-
- javaLanguageServer.connectClient(client);
- javaLanguageServer.applySettings(PrefBasedJavaServerSettings.getInstance());
- javaLanguageServer.initialize(params);
- }
-
- private void stopServices() {
-
- if (IDELanguageClientImpl.isInitialized()) {
- IDELanguageClientImpl.shutdown();
+ @Override
+ public AndroidProject provideAndroidProject() {
+ return getAndroidProject();
}
- shutdownLanguageServers();
-
- if (getBuildService() != null) {
- getBuildService().setListener(null);
- if (mBuildServiceHandler != null) {
- mBuildServiceHandler.stop();
- } else {
- getBuildService().exit();
- }
+ @Override
+ public IDEProject provideIDEProject() {
+ return getIDEProject();
}
- if (mFileOptionsHandler != null) {
- mFileOptionsHandler.stop();
+ public void handleSearchResults(Map> results) {
+ setSearchResultAdapter(
+ new com.itsaky.androidide.adapters.SearchListAdapter(
+ results,
+ file -> {
+ openFile(file);
+ hideViewOptions();
+ },
+ match -> {
+ openFileAndSelect(match.file, match);
+ hideViewOptions();
+ }));
+
+ showSearchResults();
+
+ if (mSearchingProgress != null && mSearchingProgress.isShowing()) {
+ mSearchingProgress.dismiss();
+ }
}
- }
- private void shutdownLanguageServers() {
- final var javaServer = getApp().getJavaLanguageServer();
- javaServer.shutdown();
- }
-
- private void onSoftInputChanged() {
- invalidateOptionsMenu();
- if (KeyboardUtils.isSoftInputVisible(this)) {
- TransitionManager.beginDelayedTransition(mBinding.getRoot(), new Slide(Gravity.TOP));
- symbolInput.setVisibility(View.VISIBLE);
- mBinding.bottomSheet.statusText.setVisibility(View.GONE);
- mBinding.bottomSheet.swipeHint.setVisibility(View.GONE);
- } else {
- TransitionManager.beginDelayedTransition(mBinding.getRoot(), new Slide(Gravity.BOTTOM));
- symbolInput.setVisibility(View.GONE);
- mBinding.bottomSheet.statusText.setVisibility(View.VISIBLE);
- mBinding.bottomSheet.swipeHint.setVisibility(View.VISIBLE);
+ public void appendApkLog(LogLine line) {
+ bottomSheetTabAdapter.getLogFragment().appendLog(line);
}
- }
- private void refreshSymbolInput(@NonNull CodeEditorView frag) {
- symbolInput.bindEditor(frag.getEditor());
- symbolInput.setSymbols(Symbols.forFile(frag.getFile()));
- }
-
- private void notifySyncNeeded() {
- if (getBuildService() != null && !getBuildService().isBuilding()) {
- getSyncBanner()
- .setNegative(android.R.string.cancel, null)
- .setPositive(android.R.string.ok, v -> getBuildServiceHandler().assembleDebug(false))
- .show();
+ public void setIDEProject(IDEProject project) {
+ if (mViewModel != null) {
+ mViewModel.setIDEProject(project);
+ }
}
- }
- private void closeProject(boolean manualFinish) {
- stopServices();
-
- // Make sure we close files
- // This fill further make sure that file contents are not erased.
- closeAll(
- () -> {
- getApp().getPrefManager().setOpenedProject(PreferenceManager.NO_OPENED_PROJECT);
- getApp().stopAllDaemons();
-
- if (manualFinish) {
- finish();
- }
- });
- }
-
- private void confirmProjectClose() {
- final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
- builder.setTitle(R.string.title_confirm_project_close);
- builder.setMessage(R.string.msg_confirm_project_close);
- builder.setNegativeButton(R.string.no, null);
- builder.setPositiveButton(
- R.string.yes,
- (d, w) -> {
- d.dismiss();
- closeProject(true);
- });
- builder.show();
- }
-
- private AlertDialog getFindInProjectDialog() {
- return mFindInProjectDialog == null ? createFindInProjectDialog() : mFindInProjectDialog;
- }
-
- @Nullable
- private AlertDialog createFindInProjectDialog() {
- if (getAndroidProject() == null
- || getAndroidProject().getModulePaths() == null
- || getAndroidProject().getModulePaths().size() <= 0) {
- getApp().toast(R.string.msg_no_modules, Toaster.Type.ERROR);
- return null;
+ public void appendBuildOut(final String str) {
+ bottomSheetTabAdapter.getBuildOutputFragment().appendOutput(str);
}
- final List modules = getAndroidProject().getModulePaths();
- final List srcDirs = new ArrayList<>();
- final LayoutSearchProjectBinding binding =
- LayoutSearchProjectBinding.inflate(getLayoutInflater());
- binding.modulesContainer.removeAllViews();
- for (int i = 0; i < modules.size(); i++) {
- final File file = new File(modules.get(i));
- final File src = new File(file, "src");
- if (!file.exists() || !file.isDirectory() || !src.exists() || !src.isDirectory()) {
- continue;
- }
- CheckBox check = new CheckBox(this);
- check.setText(file.getName());
- check.setChecked(true);
-
- LinearLayout.MarginLayoutParams params = new LinearLayout.MarginLayoutParams(-2, -2);
- params.bottomMargin = SizeUtils.dp2px(4);
- binding.modulesContainer.addView(check, params);
-
- srcDirs.add(src);
+ public void showDaemonStatus() {
+ ShellServer shell = getApp().newShell(t -> getDaemonStatusFragment().append(t));
+ shell.bgAppend(String.format("echo '%s'", getString(R.string.msg_getting_daemom_status)));
+ shell.bgAppend(
+ String.format(
+ "cd '%s' && sh gradlew --status",
+ Objects.requireNonNull(getAndroidProject()).getProjectPath()));
+ if (!getDaemonStatusFragment().isShowing()) {
+ getDaemonStatusFragment().show(getSupportFragmentManager(), "daemon_status");
+ }
}
- final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
- builder.setTitle(R.string.menu_find_project);
- builder.setView(binding.getRoot());
- builder.setCancelable(false);
- builder.setPositiveButton(
- R.string.menu_find,
- (dialog, which) -> {
- final String text =
- Objects.requireNonNull(binding.input.getEditText()).getText().toString().trim();
- if (text.isEmpty()) {
- getApp().toast(R.string.msg_empty_search_query, Toaster.Type.ERROR);
+
+ public void hideViewOptions() {
+ if (mEditorBottomSheet.getState() != BottomSheetBehavior.STATE_COLLAPSED) {
+ mEditorBottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED);
+ }
+ }
+
+ public void showSearchResults() {
+ if (mEditorBottomSheet.getState() != BottomSheetBehavior.STATE_EXPANDED) {
+ mEditorBottomSheet.setState(BottomSheetBehavior.STATE_EXPANDED);
+ }
+
+ final int index =
+ bottomSheetTabAdapter.findIndexOfFragmentByClass(SearchResultFragment.class);
+ if (index >= 0 && index < mBinding.bottomSheet.tabs.getTabCount()) {
+ final var tab = mBinding.bottomSheet.tabs.getTabAt(index);
+ if (tab != null) {
+ tab.select();
+ }
+ }
+ }
+
+ public void handleDiagnosticsResultVisibility(boolean errorVisible) {
+ runOnUiThread(
+ () ->
+ bottomSheetTabAdapter
+ .getDiagnosticsFragment()
+ .handleResultVisibility(errorVisible));
+ }
+
+ public void handleSearchResultVisibility(boolean errorVisible) {
+ runOnUiThread(
+ () ->
+ bottomSheetTabAdapter
+ .getSearchResultFragment()
+ .handleResultVisibility(errorVisible));
+ }
+
+ public void setStatus(final CharSequence text) {
+ try {
+ runOnUiThread(() -> mBinding.bottomSheet.statusText.setText(text));
+ } catch (Throwable th) {
+ LOG.error("Failed to update status text", th);
+ }
+ }
+
+ public void showFirstBuildNotice() {
+ DialogUtils.newMaterialDialogBuilder(this)
+ .setPositiveButton(android.R.string.ok, null)
+ .setTitle(R.string.title_first_build)
+ .setMessage(R.string.msg_first_build)
+ .setCancelable(false)
+ .create()
+ .show();
+ }
+
+ @Override
+ public void onGroupClick(DiagnosticGroup group) {
+ if (group != null
+ && group.file != null
+ && group.file.exists()
+ && FileUtils.isUtf8(group.file)) {
+ openFile(group.file);
+ hideViewOptions();
+ }
+ }
+
+ @Override
+ public void onDiagnosticClick(File file, @NonNull DiagnosticItem diagnostic) {
+ openFileAndSelect(file, diagnostic.getRange());
+ hideViewOptions();
+ }
+
+ public ActivityEditorBinding getBinding() {
+ return mBinding;
+ }
+
+ public LayoutDiagnosticInfoBinding getDiagnosticBinding() {
+ return mDiagnosticInfoBinding;
+ }
+
+ public void openFileAndSelect(File file, Range selection) {
+ openFile(file, selection);
+ final var opened = getEditorForFile(file);
+ if (opened != null && opened.getEditor() != null) {
+ final var editor = opened.getEditor();
+ final var range = editor.validateRange(selection);
+ editor.post(
+ () -> {
+ if (LSPUtils.isEqual(range.getStart(), range.getEnd())) {
+ editor.setSelection(
+ range.getStart().getLine(), range.getEnd().getColumn());
+ } else {
+ editor.setSelectionRegion(
+ range.getStart().getLine(),
+ range.getStart().getColumn(),
+ range.getEnd().getLine(),
+ range.getEnd().getColumn());
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onTabSelected(@NonNull TabLayout.Tab tab) {
+ final var position = tab.getPosition();
+ mBinding.editorContainer.setDisplayedChild(position);
+
+ final var editorView = getEditorAtIndex(position);
+ mViewModel.setCurrentFile(position, Objects.requireNonNull(editorView).getFile());
+ refreshSymbolInput(editorView);
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onTabUnselected(@NonNull TabLayout.Tab tab) {
+ // unimplemented
+ }
+
+ @Override
+ public void onTabReselected(@NonNull TabLayout.Tab tab) {
+ mTabCloseAction.show(tab.view);
+ }
+
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem p1) {
+ final int id = p1.getItemId();
+ if (id == R.id.editornav_discuss) {
+ getApp().openTelegramGroup();
+ } else if (id == R.id.editornav_suggest) {
+ getApp().openGitHub();
+ } else if (id == R.id.editornav_needHelp) {
+ showNeedHelpDialog();
+ } else if (id == R.id.editornav_settings) {
+ startActivity(new Intent(this, PreferencesActivity.class));
+ } else if (id == R.id.editornav_share) {
+ startActivity(IntentUtils.getShareTextIntent(getString(R.string.msg_share_app)));
+ } else if (id == R.id.editornav_close_project) {
+ confirmProjectClose();
+ } else if (id == R.id.editornav_terminal) {
+ openTerminal();
+ }
+ mBinding.getRoot().closeDrawer(GravityCompat.START);
+ return false;
+ }
+
+ public void loadFragment(Fragment fragment) {
+ super.loadFragment(fragment, mBinding.editorFrameLayout.getId());
+ }
+
+ public FileTreeFragment getFileTreeFragment() {
+ return mFileTreeFragment;
+ }
+
+ public TreeNode getLastHoldTreeNode() {
+ return mLastHeld;
+ }
+
+ public OptionsListFragment getFileOptionsFragment(File file) {
+ mFileOptionsFragment = new OptionsListFragment();
+ mFileOptionsFragment.addOption(
+ new SheetOption(0, R.drawable.ic_file_copy_path, R.string.copy_path, file));
+ mFileOptionsFragment.addOption(
+ new SheetOption(1, R.drawable.ic_file_rename, R.string.rename_file, file));
+ mFileOptionsFragment.addOption(
+ new SheetOption(2, R.drawable.ic_delete, R.string.delete_file, file));
+ if (file.isDirectory()) {
+ mFileOptionsFragment.addOption(
+ new SheetOption(3, R.drawable.ic_new_file, R.string.new_file, file));
+ mFileOptionsFragment.addOption(
+ new SheetOption(4, R.drawable.ic_new_folder, R.string.new_folder, file));
+ }
+ mFileOptionsFragment.setOnOptionsClickListener(mFileOptionsHandler);
+ return mFileOptionsFragment;
+ }
+
+ public ProgressSheet getProgressSheet(int msg) {
+ if (mSearchingProgress != null && mSearchingProgress.isShowing()) {
+ mSearchingProgress.dismiss();
+ }
+ mSearchingProgress = new ProgressSheet();
+ mSearchingProgress.setCancelable(false);
+ mSearchingProgress.setWelcomeTextEnabled(false);
+ mSearchingProgress.setMessage(getString(msg));
+ mSearchingProgress.setSubMessageEnabled(false);
+ return mSearchingProgress;
+ }
+
+ public MaterialBanner getSyncBanner() {
+ return mBinding.syncBanner
+ .setContentTextColor(ContextCompat.getColor(this, R.color.primaryTextColor))
+ .setBannerBackgroundColor(ContextCompat.getColor(this, R.color.primaryLightColor))
+ .setButtonTextColor(ContextCompat.getColor(this, R.color.secondaryColor))
+ .setIcon(R.drawable.ic_sync)
+ .setContentText(R.string.msg_sync_needed);
+ }
+
+ public TextSheetFragment getDaemonStatusFragment() {
+ return mDaemonStatusFragment == null
+ ? mDaemonStatusFragment =
+ new TextSheetFragment()
+ .setTextSelectable(true)
+ .setTitleText(R.string.gradle_daemon_status)
+ : mDaemonStatusFragment;
+ }
+
+ public void setDiagnosticsAdapter(@NonNull final DiagnosticsAdapter adapter) {
+ runOnUiThread(() -> bottomSheetTabAdapter.getDiagnosticsFragment().setAdapter(adapter));
+ }
+
+ public void setSearchResultAdapter(@NonNull final SearchListAdapter adapter) {
+ runOnUiThread(() -> bottomSheetTabAdapter.getSearchResultFragment().setAdapter(adapter));
+ }
+
+ @Override
+ public void showFileOptions(File thisFile, TreeNode node) {
+ mLastHeld = node;
+ getFileOptionsFragment(thisFile)
+ .show(getSupportFragmentManager(), TAG_FILE_OPTIONS_FRAGMENT);
+ }
+
+ @SuppressWarnings("UnusedReturnValue")
+ public boolean saveAll() {
+ return saveAll(true);
+ }
+
+ public boolean saveAll(boolean notify) {
+ return saveAll(notify, false);
+ }
+
+ public boolean saveAll(boolean notify, boolean canProcessResources) {
+ SaveResult result = saveAllResult();
+
+ if (notify) {
+ getApp().toast(R.string.all_saved, Toaster.Type.SUCCESS);
+ }
+
+ if (result.gradleSaved) {
+ notifySyncNeeded();
+ }
+
+ if (result.xmlSaved && canProcessResources && getBuildService() != null) {
+ getBuildService().updateResourceClasses();
+ }
+
+ return result.gradleSaved;
+ }
+
+ public SaveResult saveAllResult() {
+ SaveResult result = new SaveResult();
+ for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) {
+ saveResult(i, result);
+ }
+
+ return result;
+ }
+
+ public void saveResult(int index, SaveResult result) {
+ if (index >= 0 && index < mViewModel.getOpenedFileCount()) {
+ var frag = getEditorAtIndex(index);
+ if (frag == null || frag.getFile() == null) {
+ return;
+ }
+
+ // Must be called before frag.save()
+ // Otherwise, it'll always return false
+ final boolean modified = frag.isModified();
+
+ frag.save();
+
+ final boolean isGradle = frag.getFile().getName().endsWith(".gradle");
+ final boolean isXml = frag.getFile().getName().endsWith(".xml");
+
+ if (!result.gradleSaved) {
+ result.gradleSaved = modified && isGradle;
+ }
+
+ if (!result.xmlSaved) {
+ result.xmlSaved = modified && isXml;
+ }
+ }
+
+ var modified = false;
+ for (var file : mViewModel.getOpenedFiles()) {
+ var editor = getEditorForFile(file);
+ if (editor == null) {
+ continue;
+ }
+ modified = modified || editor.isModified();
+ }
+
+ mViewModel.setFilesModified(modified);
+ }
+
+ public void install(@NonNull File apk) {
+ if (apk.exists()) {
+ Intent i = IntentUtils.getInstallAppIntent(apk);
+ if (i != null) {
+ startActivity(i);
+ } else {
+ getApp().toast(R.string.msg_apk_install_intent_failed, Toaster.Type.ERROR);
+ }
+ }
+ }
+
+ public void updateServices() {
+ new TaskExecutor()
+ .executeAsync(
+ () -> {
+ List cps =
+ Objects.requireNonNull(getAndroidProject()).getClassPaths();
+ getApp().getJavaLanguageServer()
+ .configurationChanged(
+ new JavaServerConfiguration(
+ cps.stream()
+ .map(Paths::get)
+ .collect(Collectors.toSet())));
+ return null;
+ },
+ __ -> setStatus(getString(R.string.msg_service_started)));
+ }
+
+ @Override
+ public CodeEditorView openFile(File file) {
+ return openFile(file, null);
+ }
+
+ public CodeEditorView openFile(File file, com.itsaky.lsp.models.Range selection) {
+ if (selection == null) {
+ selection = com.itsaky.lsp.models.Range.NONE;
+ }
+
+ int index = openFileAndGetIndex(file, selection);
+ final var tab = mBinding.tabs.getTabAt(index);
+ if (tab != null && index >= 0 && !tab.isSelected()) {
+ tab.select();
+ }
+
+ if (mBinding.editorDrawerLayout.isDrawerOpen(GravityCompat.END)) {
+ mBinding.editorDrawerLayout.closeDrawer(GravityCompat.END);
+ }
+
+ mBinding.editorContainer.setDisplayedChild(index);
+
+ invalidateOptionsMenu();
+ try {
+ return getEditorAtIndex(index);
+ } catch (Throwable th) {
+ LOG.error("Unable to get editor fragment at opened file index", index);
+ LOG.error(th);
+ return null;
+ }
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ public int openFileAndGetIndex(File file, com.itsaky.lsp.models.Range selection) {
+ final var openedFileIndex = findIndexOfEditorByFile(file);
+
+ if (openedFileIndex != -1) {
+ LOG.error("File is already opened. File: " + file);
+ return openedFileIndex;
+ }
+
+ final var position = mViewModel.getOpenedFileCount();
+
+ LOG.info("Opening file at index:", position, "file: ", file);
+
+ final var editor = new CodeEditorView(this, file, selection);
+ editor.getEditor().subscribeEvent(ContentChangeEvent.class, this::onEditorContentChanged);
+ editor.setLayoutParams(
+ new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+
+ mBinding.editorContainer.addView(editor);
+ mBinding.editorContainer.setDisplayedChild(position);
+ mBinding.tabs.addTab(mBinding.tabs.newTab().setText(file.getName()));
+
+ mViewModel.addFile(file);
+ mViewModel.setCurrentFile(position, file);
+
+ return position;
+ }
+
+ public void closeFile(int index) {
+ if (index >= 0 && index < mViewModel.getOpenedFileCount()) {
+ var opened = mViewModel.getOpenedFile(index);
+ LOG.info("Closing file:", opened);
+ final var editor = getEditorAtIndex(index);
+
+ if (editor != null && editor.isModified()) {
+ notifyFilesUnsaved(Collections.singletonList(editor), () -> closeFile(index));
+ return;
+ }
+
+ if (editor != null && editor.getEditor() != null) {
+ editor.getEditor().close();
+ } else {
+ LOG.error("Cannot save file before close. Editor instance is null");
+ }
+
+ mViewModel.removeFile(index);
+ mBinding.tabs.removeTabAt(index);
+ mBinding.editorContainer.removeViewAt(index);
+ } else {
+ LOG.error("Invalid file index. Cannot close.");
return;
- }
+ }
- final List searchDirs = new ArrayList<>();
- for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) {
- CheckBox check = (CheckBox) binding.modulesContainer.getChildAt(i);
- if (check.isChecked()) {
- searchDirs.add(srcDirs.get(i));
- }
- }
+ mBinding.tabs.requestLayout();
+ invalidateOptionsMenu();
+ }
- final String extensions =
- Objects.requireNonNull(binding.filter.getEditText()).getText().toString().trim();
- final List extensionList = new ArrayList<>();
- if (!extensions.isEmpty()) {
- if (extensions.contains("|")) {
- for (String str : extensions.split(Pattern.quote("|"))) {
- if (str == null || str.trim().isEmpty()) {
- continue;
+ public void closeAll() {
+ closeAll(null);
+ }
+
+ /**
+ * Close all opened files. If all the files are saved successfully, then the provided {@link
+ * Runnable} will be called.
+ *
+ * @param onSaved The {@link Runnable} to run when all the files are saved.
+ */
+ public void closeAll(Runnable onSaved) {
+ final var count = mViewModel.getOpenedFileCount();
+ final var unsavedFiles =
+ mViewModel.getOpenedFiles().stream()
+ .map(this::getEditorForFile)
+ .filter(editor -> editor != null && editor.isModified())
+ .collect(Collectors.toList());
+
+ if (unsavedFiles.isEmpty()) {
+ // Files were already saved, close all files one by one
+ for (int i = 0; i < count; i++) {
+ final var editor = getEditorAtIndex(i);
+ if (editor != null && editor.getEditor() != null) {
+ editor.getEditor().close();
+ } else {
+ LOG.error("Unable to close file at index:", i);
}
-
- extensionList.add(str);
- }
- } else {
- extensionList.add(extensions);
}
- }
- if (searchDirs.isEmpty()) {
- getApp().toast(R.string.msg_select_search_modules, Toaster.Type.ERROR);
- } else {
- dialog.dismiss();
- getProgressSheet(R.string.msg_searching_project)
- .show(getSupportFragmentManager(), "search_in_project_progress");
- RecursiveFileSearcher.searchRecursiveAsync(
- text, extensionList, searchDirs, this::handleSearchResults);
- }
- });
- builder.setNegativeButton(android.R.string.cancel, (__, ___) -> __.dismiss());
- mFindInProjectDialog = builder.create();
- return mFindInProjectDialog;
- }
+ mViewModel.removeAllFiles();
+ mBinding.tabs.removeAllTabs();
+ mBinding.tabs.requestLayout();
+ mBinding.editorContainer.removeAllViews();
- private void registerLogReceiver() {
- IntentFilter filter = new IntentFilter();
- filter.addAction(LogReceiver.APPEND_LOG);
- registerReceiver(mLogReceiver, filter);
- }
+ invalidateOptionsMenu();
- private IDEService getBuildService() {
- return mBuildServiceHandler.getService();
- }
+ if (onSaved != null) {
+ onSaved.run();
+ }
+ } else {
+ // There are unsaved files
+ notifyFilesUnsaved(unsavedFiles, () -> closeAll(onSaved));
+ }
+ }
- public BuildServiceHandler getBuildServiceHandler() {
- return mBuildServiceHandler;
- }
+ private void notifyFilesUnsaved(List unsavedEditors, Runnable invokeAfter) {
+ //noinspection ConstantConditions
+ final var mapped =
+ unsavedEditors.stream()
+ .map(CodeEditorView::getFile)
+ .filter(Objects::nonNull)
+ .map(File::getAbsolutePath)
+ .collect(Collectors.toList());
+ final var builder =
+ DialogUtils.newYesNoDialog(
+ this,
+ getString(R.string.title_files_unsaved), // title
+ getString(
+ R.string.msg_files_unsaved,
+ TextUtils.join("\n", mapped)), // message
+ (dialog, which) -> { // 'yes' click
+ dialog.dismiss();
+ saveAll(true);
+ invokeAfter.run();
+ },
+ (dialog, which) -> { // 'no' click
+ dialog.dismiss();
+ // Mark all the files as saved, then try to close them all
+ for (var editor : unsavedEditors) {
+ editor.markAsSaved();
+ }
- private void showNeedHelpDialog() {
- MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
- builder.setTitle(R.string.need_help);
- builder.setMessage(R.string.msg_need_help);
- builder.setPositiveButton(android.R.string.ok, null);
- builder.create().show();
- }
+ invokeAfter.run();
+ });
+ builder.show();
+ }
- private void createQuickActions() {
- ActionItem closeThis =
- new ActionItem(
- ACTION_ID_CLOSE, getString(R.string.action_closeThis), R.drawable.ic_close_this);
- ActionItem closeOthers =
- new ActionItem(
- ACTION_ID_OTHERS, getString(R.string.action_closeOthers), R.drawable.ic_close_others);
- ActionItem closeAll =
- new ActionItem(ACTION_ID_ALL, getString(R.string.action_closeAll), R.drawable.ic_close_all);
- mTabCloseAction = new QuickAction(this, QuickAction.HORIZONTAL);
- mTabCloseAction.addActionItem(closeThis, closeOthers, closeAll);
- mTabCloseAction.setColorRes(R.color.tabAction_background);
- mTabCloseAction.setTextColorRes(R.color.tabAction_text);
+ public void closeOthers() {
+ final var unsavedFiles =
+ mViewModel.getOpenedFiles().stream()
+ .map(this::getEditorForFile)
+ .filter(editor -> editor != null && editor.isModified())
+ .collect(Collectors.toList());
- mTabCloseAction.setOnActionItemClickListener(
- (item) -> {
- final int id = item.getActionId();
- if (getApp().getPrefManager().getBoolean(PreferenceManager.KEY_EDITOR_AUTO_SAVE, false)) {
- saveAll();
- }
+ if (unsavedFiles.isEmpty()) {
+ final var file = mViewModel.getCurrentFile();
+ for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) {
+ final var editor = getEditorAtIndex(i);
- if (id == ACTION_ID_CLOSE) {
- closeFile(mBinding.tabs.getSelectedTabPosition());
- }
+ // Index of files changes as we keep close files
+ // So we compare the files instead of index
+ if (editor != null) {
+ if (!file.equals(editor.getFile())) {
+ closeFile(i);
+ }
+ } else {
+ LOG.error("Unable to save file at index:", i);
+ }
+ }
+ } else {
+ notifyFilesUnsaved(unsavedFiles, this::closeOthers);
+ }
+ }
- if (id == ACTION_ID_OTHERS) {
- closeOthers();
- }
+ @Nullable
+ public CodeEditorView getCurrentEditor() {
+ if (mViewModel.getCurrentFileIndex() != -1) {
+ return getEditorAtIndex(mViewModel.getCurrentFileIndex());
+ }
- if (id == ACTION_ID_ALL) {
- closeAll();
- }
- });
- }
+ return null;
+ }
- private static final Logger LOG = Logger.instance("EditorActivity");
+ @Nullable
+ public CodeEditorView getEditorAtIndex(final int index) {
+ return (CodeEditorView) mBinding.editorContainer.getChildAt(index);
+ }
+
+ @Nullable
+ public CodeEditorView getEditorForFile(@NonNull final File file) {
+ for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) {
+ final CodeEditorView editor = (CodeEditorView) mBinding.editorContainer.getChildAt(i);
+ if (file.equals(editor.getFile())) {
+ return editor;
+ }
+ }
+
+ return null;
+ }
+
+ public int findIndexOfEditorByFile(File file) {
+ if (file == null) {
+ LOG.error("Cannot find index of a null file.");
+ return -1;
+ }
+
+ for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) {
+ final var opened = mViewModel.getOpenedFile(i);
+ if (opened.equals(file)) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /////////////////////////////////////////////////
+ ////////////// PRIVATE APIS /////////////////////
+ /////////////////////////////////////////////////
+
+ private void onEditorContentChanged(ContentChangeEvent event, Unsubscribe unsubscribe) {
+ if (event.getAction() != ContentChangeEvent.ACTION_SET_NEW_TEXT) {
+ mViewModel.setFilesModified(true);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private void checkForCompilerModule() {
+ if (!Environment.isCompilerModuleInstalled()) {
+ final var pd =
+ ProgressDialog.show(
+ this,
+ getString(R.string.title_compiler_module_install),
+ getString(R.string.msg_compiler_module_install),
+ true,
+ false);
+
+ final CompletableFuture future =
+ CompletableFuture.supplyAsync(
+ () -> {
+ final var tmpModule =
+ new File(Environment.TMP_DIR, "compiler-module.zip");
+ if (!ResourceUtils.copyFileFromAssets(
+ ToolsManager.getCommonAsset("compiler-module.zip"),
+ tmpModule.getAbsolutePath())) {
+ throw new CompletionException(
+ new RuntimeException(
+ "Unable to copy compiler-module.zip"));
+ }
+
+ try {
+ ZipUtils.unzipFile(tmpModule, Environment.COMPILER_MODULE);
+ } catch (Throwable e) {
+ throw new CompletionException(e);
+ }
+
+ if (!Environment.isCompilerModuleInstalled()) {
+ throw new CompletionException(
+ new RuntimeException("Unknown error"));
+ }
+
+ try {
+ FileUtils.delete(tmpModule);
+ } catch (Exception e) {
+ // ignored
+ }
+
+ return true;
+ });
+
+ future.whenComplete(
+ (result, error) -> {
+ pd.dismiss();
+
+ if (error != null) {
+ showCompilerModuleInstallError(error);
+ return;
+ }
+
+ getApp().toast(
+ getString(R.string.msg_compiler_module_installed),
+ Toaster.Type.SUCCESS);
+ });
+ }
+ }
+
+ private void showCompilerModuleInstallError(Throwable error) {
+ final var stacktrace = ThrowableUtils.getFullStackTrace(error);
+ final var builder = DialogUtils.newMaterialDialogBuilder(this);
+ builder.setTitle(R.string.title_installation_failed);
+ builder.setMessage(getString(R.string.msg_compiler_module_install_failed, stacktrace));
+ builder.setCancelable(false);
+ builder.setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
+ builder.setNegativeButton(
+ R.string.copy,
+ (dialog, which) -> {
+ ClipboardUtils.copyText(stacktrace);
+ dialog.dismiss();
+ });
+ builder.show();
+ }
+
+ private void dispatchOnPauseToEditors() {
+ CompletableFuture.runAsync(
+ () -> {
+ for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) {
+ final var editor = getEditorAtIndex(i);
+ if (editor != null) {
+ editor.onPause();
+ }
+ }
+ });
+ }
+
+ private void dispatchOnResumeToEditors() {
+ CompletableFuture.runAsync(
+ () -> {
+ for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) {
+ final var editor = getEditorAtIndex(i);
+ if (editor != null) {
+ editor.onResume();
+ }
+ }
+ });
+ }
+
+ @Contract(pure = true)
+ private void onGetUIDesignerResult(@NonNull ActivityResult result) {
+ final var index = mBinding.editorContainer.getDisplayedChild();
+ final var editor = getEditorAtIndex(index);
+ if (editor != null && result.getResultCode() == RESULT_OK) {
+ final var data = result.getData();
+ if (data != null && data.hasExtra(DesignerActivity.KEY_GENERATED_CODE)) {
+ final var code = data.getStringExtra(DesignerActivity.KEY_GENERATED_CODE);
+ editor.getEditor().setText(code);
+ saveAll();
+ } else {
+ final var msg = getString(R.string.msg_invalid_designer_result);
+ getApp().toast(msg, Toaster.Type.ERROR);
+ LOG.error(msg, "Data returned by UI Designer is null or is invalid.");
+ }
+ } else {
+ LOG.error(
+ "UI Designer returned an invalid result code.",
+ "Result code: " + result.getResultCode());
+ }
+ }
+
+ private void previewLayout() {
+ try {
+
+ if (getCurrentEditor() == null || getCurrentEditor().getFile() == null) {
+ LOG.error("No file is opened. Cannot preview layout.");
+ return;
+ }
+
+ saveAll(false);
+ if (getApp().getLayoutInflater() == null) {
+ LOG.info("Creating layout inflater instance...");
+ getApp().createInflater(
+ getApp().createInflaterConfig(
+ getContextProvider(), getResourceDirectories()));
+ }
+
+ final Intent intent = new Intent(this, DesignerActivity.class);
+ intent.putExtra(
+ DesignerActivity.KEY_LAYOUT_PATH,
+ getCurrentEditor().getFile().getAbsolutePath());
+
+ LOG.info("Launching UI Designer...");
+ mUIDesignerLauncher.launch(intent);
+ } catch (Throwable th) {
+ LOG.error(getString(R.string.err_cannot_preview_layout), th);
+ getApp().toast(R.string.msg_cannot_preview_layout, Toaster.Type.ERROR);
+ }
+ }
+
+ @NonNull
+ @Contract(" -> new")
+ private ILayoutInflater.ContextProvider getContextProvider() {
+ return this::provide;
+ }
+
+ @NonNull
+ private Set getResourceDirectories() {
+ final Set dirs = new HashSet<>();
+ if (getAndroidProject() != null
+ && getAndroidProject().getModulePaths() != null
+ && !getAndroidProject().getModulePaths().isEmpty()) {
+ for (String path : getAndroidProject().getModulePaths()) {
+ if (path != null && new File(path).exists()) {
+ File res = new File(path, "src/main/res");
+ if (res.exists()) {
+ dirs.add(res);
+ }
+ }
+ }
+ }
+ return dirs;
+ }
+
+ private void openTerminal() {
+ final Intent intent = new Intent(this, TerminalActivity.class);
+ intent.putExtra(
+ TerminalActivity.KEY_WORKING_DIRECTORY,
+ Objects.requireNonNull(getAndroidProject()).getProjectPath());
+ startActivity(intent);
+ }
+
+ private void getProjectFromIntent() {
+ final var project = (AndroidProject) getIntent().getParcelableExtra(EXTRA_PROJECT);
+ if (mViewModel != null) {
+ mViewModel.setAndroidProject(project);
+ } else {
+ LOG.error("ViewModel is null. Cannot set project.");
+ }
+ getApp().getPrefManager()
+ .setOpenedProject(
+ Objects.requireNonNull(this.getAndroidProject()).getProjectPath());
+
+ try {
+ //noinspection ConstantConditions
+ getSupportActionBar()
+ .setSubtitle(new File(getAndroidProject().getProjectPath()).getName());
+ } catch (Throwable th) {
+ // ignored
+ }
+
+ CompletableFuture.runAsync(
+ () ->
+ ValuesTableFactory.setupWithResDirectories(
+ getResourceDirectories().toArray(new File[0])));
+ }
+
+ @Nullable
+ private AndroidProject getAndroidProject() {
+ if (mViewModel == null) {
+ return null;
+ }
+
+ return mViewModel.getAndroidProject();
+ }
+
+ @Nullable
+ private IDEProject getIDEProject() {
+ if (mViewModel == null) {
+ return null;
+ }
+
+ return mViewModel.getIDEProject();
+ }
+
+ private void setupDiagnosticInfo() {
+ GradientDrawable gd = new GradientDrawable();
+ gd.setShape(GradientDrawable.RECTANGLE);
+ gd.setColor(0xff212121);
+ gd.setStroke(1, 0xffffffff);
+ gd.setCornerRadius(8);
+ mDiagnosticInfoBinding.getRoot().setBackground(gd);
+ mDiagnosticInfoBinding.getRoot().setVisibility(View.GONE);
+ }
+
+ private void setupContainers() {
+ handleDiagnosticsResultVisibility(true);
+ handleSearchResultVisibility(true);
+ }
+
+ private void startServices() {
+
+ if (!IDELanguageClientImpl.isInitialized()) {
+ IDELanguageClientImpl.initialize(this);
+ }
+
+ initializeLanguageServers();
+
+ // Actually, we don't need to start FileOptionsHandler
+ // Because it would work anyway
+ // But still we do...
+ mFileOptionsHandler.start();
+ mBuildServiceHandler.start();
+ getBuildServiceHandler().assembleDebug(false);
+ }
+
+ private void initializeLanguageServers() {
+ final var client = IDELanguageClientImpl.getInstance();
+ final var javaLanguageServer = getApp().getJavaLanguageServer();
+ final var workspaceRoots =
+ Collections.singleton(
+ new File(Objects.requireNonNull(getAndroidProject()).getProjectPath())
+ .toPath());
+
+ final var params = new InitializeParams(workspaceRoots);
+
+ javaLanguageServer.connectClient(client);
+ javaLanguageServer.applySettings(PrefBasedJavaServerSettings.getInstance());
+ javaLanguageServer.initialize(params);
+ }
+
+ private void stopServices() {
+
+ if (IDELanguageClientImpl.isInitialized()) {
+ IDELanguageClientImpl.shutdown();
+ }
+
+ shutdownLanguageServers();
+
+ if (getBuildService() != null) {
+ getBuildService().setListener(null);
+ if (mBuildServiceHandler != null) {
+ mBuildServiceHandler.stop();
+ } else {
+ getBuildService().exit();
+ }
+ }
+
+ if (mFileOptionsHandler != null) {
+ mFileOptionsHandler.stop();
+ }
+ }
+
+ private void shutdownLanguageServers() {
+ final var javaServer = getApp().getJavaLanguageServer();
+ javaServer.shutdown();
+ }
+
+ private void onSoftInputChanged() {
+ invalidateOptionsMenu();
+ if (KeyboardUtils.isSoftInputVisible(this)) {
+ TransitionManager.beginDelayedTransition(mBinding.getRoot(), new Slide(Gravity.TOP));
+ symbolInput.setVisibility(View.VISIBLE);
+ mBinding.bottomSheet.statusText.setVisibility(View.GONE);
+ mBinding.bottomSheet.swipeHint.setVisibility(View.GONE);
+ } else {
+ TransitionManager.beginDelayedTransition(mBinding.getRoot(), new Slide(Gravity.BOTTOM));
+ symbolInput.setVisibility(View.GONE);
+ mBinding.bottomSheet.statusText.setVisibility(View.VISIBLE);
+ mBinding.bottomSheet.swipeHint.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private void refreshSymbolInput(@NonNull CodeEditorView frag) {
+ symbolInput.bindEditor(frag.getEditor());
+ symbolInput.setSymbols(Symbols.forFile(frag.getFile()));
+ }
+
+ private void notifySyncNeeded() {
+ if (getBuildService() != null && !getBuildService().isBuilding()) {
+ getSyncBanner()
+ .setNegative(android.R.string.cancel, null)
+ .setPositive(
+ android.R.string.ok, v -> getBuildServiceHandler().assembleDebug(false))
+ .show();
+ }
+ }
+
+ private void closeProject(boolean manualFinish) {
+ stopServices();
+
+ // Make sure we close files
+ // This fill further make sure that file contents are not erased.
+ closeAll(
+ () -> {
+ getApp().getPrefManager().setOpenedProject(PreferenceManager.NO_OPENED_PROJECT);
+ getApp().stopAllDaemons();
+
+ if (manualFinish) {
+ finish();
+ }
+ });
+ }
+
+ private void confirmProjectClose() {
+ final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
+ builder.setTitle(R.string.title_confirm_project_close);
+ builder.setMessage(R.string.msg_confirm_project_close);
+ builder.setNegativeButton(R.string.no, null);
+ builder.setPositiveButton(
+ R.string.yes,
+ (d, w) -> {
+ d.dismiss();
+ closeProject(true);
+ });
+ builder.show();
+ }
+
+ private AlertDialog getFindInProjectDialog() {
+ return mFindInProjectDialog == null ? createFindInProjectDialog() : mFindInProjectDialog;
+ }
+
+ @Nullable
+ private AlertDialog createFindInProjectDialog() {
+ if (getAndroidProject() == null
+ || getAndroidProject().getModulePaths() == null
+ || getAndroidProject().getModulePaths().size() <= 0) {
+ getApp().toast(R.string.msg_no_modules, Toaster.Type.ERROR);
+ return null;
+ }
+ final List modules = getAndroidProject().getModulePaths();
+ final List srcDirs = new ArrayList<>();
+ final LayoutSearchProjectBinding binding =
+ LayoutSearchProjectBinding.inflate(getLayoutInflater());
+ binding.modulesContainer.removeAllViews();
+ for (int i = 0; i < modules.size(); i++) {
+ final File file = new File(modules.get(i));
+ final File src = new File(file, "src");
+ if (!file.exists() || !file.isDirectory() || !src.exists() || !src.isDirectory()) {
+ continue;
+ }
+
+ CheckBox check = new CheckBox(this);
+ check.setText(file.getName());
+ check.setChecked(true);
+
+ LinearLayout.MarginLayoutParams params = new LinearLayout.MarginLayoutParams(-2, -2);
+ params.bottomMargin = SizeUtils.dp2px(4);
+ binding.modulesContainer.addView(check, params);
+
+ srcDirs.add(src);
+ }
+ final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
+ builder.setTitle(R.string.menu_find_project);
+ builder.setView(binding.getRoot());
+ builder.setCancelable(false);
+ builder.setPositiveButton(
+ R.string.menu_find,
+ (dialog, which) -> {
+ final String text =
+ Objects.requireNonNull(binding.input.getEditText())
+ .getText()
+ .toString()
+ .trim();
+ if (text.isEmpty()) {
+ getApp().toast(R.string.msg_empty_search_query, Toaster.Type.ERROR);
+ return;
+ }
+
+ final List searchDirs = new ArrayList<>();
+ for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) {
+ CheckBox check = (CheckBox) binding.modulesContainer.getChildAt(i);
+ if (check.isChecked()) {
+ searchDirs.add(srcDirs.get(i));
+ }
+ }
+
+ final String extensions =
+ Objects.requireNonNull(binding.filter.getEditText())
+ .getText()
+ .toString()
+ .trim();
+ final List extensionList = new ArrayList<>();
+ if (!extensions.isEmpty()) {
+ if (extensions.contains("|")) {
+ for (String str : extensions.split(Pattern.quote("|"))) {
+ if (str == null || str.trim().isEmpty()) {
+ continue;
+ }
+
+ extensionList.add(str);
+ }
+ } else {
+ extensionList.add(extensions);
+ }
+ }
+
+ if (searchDirs.isEmpty()) {
+ getApp().toast(R.string.msg_select_search_modules, Toaster.Type.ERROR);
+ } else {
+ dialog.dismiss();
+ getProgressSheet(R.string.msg_searching_project)
+ .show(getSupportFragmentManager(), "search_in_project_progress");
+ RecursiveFileSearcher.searchRecursiveAsync(
+ text, extensionList, searchDirs, this::handleSearchResults);
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel, (__, ___) -> __.dismiss());
+ mFindInProjectDialog = builder.create();
+ return mFindInProjectDialog;
+ }
+
+ private void registerLogReceiver() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(LogReceiver.APPEND_LOG);
+ registerReceiver(mLogReceiver, filter);
+ }
+
+ private IDEService getBuildService() {
+ return mBuildServiceHandler.getService();
+ }
+
+ public BuildServiceHandler getBuildServiceHandler() {
+ return mBuildServiceHandler;
+ }
+
+ private void showNeedHelpDialog() {
+ MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
+ builder.setTitle(R.string.need_help);
+ builder.setMessage(R.string.msg_need_help);
+ builder.setPositiveButton(android.R.string.ok, null);
+ builder.create().show();
+ }
+
+ private void createQuickActions() {
+ ActionItem closeThis =
+ new ActionItem(
+ ACTION_ID_CLOSE,
+ getString(R.string.action_closeThis),
+ R.drawable.ic_close_this);
+ ActionItem closeOthers =
+ new ActionItem(
+ ACTION_ID_OTHERS,
+ getString(R.string.action_closeOthers),
+ R.drawable.ic_close_others);
+ ActionItem closeAll =
+ new ActionItem(
+ ACTION_ID_ALL,
+ getString(R.string.action_closeAll),
+ R.drawable.ic_close_all);
+ mTabCloseAction = new QuickAction(this, QuickAction.HORIZONTAL);
+ mTabCloseAction.addActionItem(closeThis, closeOthers, closeAll);
+ mTabCloseAction.setColorRes(R.color.tabAction_background);
+ mTabCloseAction.setTextColorRes(R.color.tabAction_text);
+
+ mTabCloseAction.setOnActionItemClickListener(
+ (item) -> {
+ final int id = item.getActionId();
+ if (getApp().getPrefManager()
+ .getBoolean(PreferenceManager.KEY_EDITOR_AUTO_SAVE, false)) {
+ saveAll();
+ }
+
+ if (id == ACTION_ID_CLOSE) {
+ closeFile(mBinding.tabs.getSelectedTabPosition());
+ }
+
+ if (id == ACTION_ID_OTHERS) {
+ closeOthers();
+ }
+
+ if (id == ACTION_ID_ALL) {
+ closeAll();
+ }
+ });
+ }
+
+ private static final Logger LOG = Logger.instance("EditorActivity");
}
diff --git a/app/src/main/java/com/itsaky/androidide/MainActivity.java b/app/src/main/java/com/itsaky/androidide/MainActivity.java
index c87555a0d..fdf28a768 100755
--- a/app/src/main/java/com/itsaky/androidide/MainActivity.java
+++ b/app/src/main/java/com/itsaky/androidide/MainActivity.java
@@ -20,12 +20,15 @@
package com.itsaky.androidide;
-import abhishekti7.unicorn.filepicker.UnicornFilePicker;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
+
import androidx.transition.TransitionManager;
+
+import abhishekti7.unicorn.filepicker.UnicornFilePicker;
+
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.transition.MaterialContainerTransform;
import com.itsaky.androidide.app.StudioActivity;
@@ -45,400 +48,409 @@ import com.itsaky.androidide.utils.DialogUtils;
import com.itsaky.androidide.utils.ProjectFinder;
import com.itsaky.androidide.utils.TransformUtils;
import com.itsaky.toaster.Toaster;
+
import java.io.File;
import java.util.ArrayList;
import java.util.Objects;
import java.util.concurrent.Callable;
public class MainActivity extends StudioActivity
- implements View.OnClickListener, ProjectWriterCallback {
+ implements View.OnClickListener, ProjectWriterCallback {
- private ActivityMainBinding binding;
- private LayoutCreateProjectBinding createBinding;
- private LayoutCreateProjectContentBinding createLayoutBinding;
+ private ActivityMainBinding binding;
+ private LayoutCreateProjectBinding createBinding;
+ private LayoutCreateProjectContentBinding createLayoutBinding;
- private ProgressSheet mProgressSheet;
+ private ProgressSheet mProgressSheet;
- private ArrayList mTemplates = new ArrayList<>();
+ private ArrayList mTemplates = new ArrayList<>();
- private int currentTemplateIndex = 0;
+ private int currentTemplateIndex = 0;
- @Override
- protected View bindLayout() {
- binding = ActivityMainBinding.inflate(getLayoutInflater());
- createBinding = binding.createLayout;
- createLayoutBinding = createBinding.createProjectContent;
- return binding.getRoot();
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- binding.createProject.setOnClickListener(this);
- binding.openProject.setOnClickListener(this);
- binding.openTerminal.setOnClickListener(this);
- createBinding.createprojectClose.setOnClickListener(this);
- createLayoutBinding.createprojectCreate.setOnClickListener(this);
- createLayoutBinding.previousCard.setOnClickListener(this);
- createLayoutBinding.nextCard.setOnClickListener(this);
- binding.createNewCard.setVisibility(View.GONE);
-
- currentTemplateIndex = 0;
- createTemplates();
- createProgressSheet();
- showTemplate(currentTemplateIndex);
-
- binding.gotoPreferences.setOnClickListener(this);
-
- binding
- .getRoot()
- .post(
- () -> {
- PreferenceManager manager = getApp().getPrefManager();
- if (manager.autoOpenProject()
- && manager.wasProjectOpened()
- && ConstantsBridge.SPLASH_TO_MAIN) {
- String path = manager.getOpenedProject();
- if (path == null || path.trim().isEmpty()) {
- getApp().toast(R.string.msg_opened_project_does_not_exist, Toaster.Type.INFO);
- } else {
- File root = new File(manager.getOpenedProject());
- if (!root.exists()) {
- getApp().toast(R.string.msg_opened_project_does_not_exist, Toaster.Type.INFO);
- } else {
- if (manager.confirmProjectOpen()) {
- askProjectOpenPermission(root);
- } else {
- openProject(root);
- }
- }
- }
- }
-
- ConstantsBridge.SPLASH_TO_MAIN = false;
- });
- }
-
- private void askProjectOpenPermission(File root) {
- final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
- builder.setTitle(R.string.title_confirm_open_project);
- builder.setMessage(getString(R.string.msg_confirm_open_project, root.getAbsolutePath()));
- builder.setCancelable(false);
- builder.setPositiveButton(R.string.yes, (d, w) -> openProject(root));
- builder.setNegativeButton(R.string.no, null);
- builder.show();
- }
-
- private void gotoSettings() {
- startActivity(new Intent(this, PreferencesActivity.class));
- }
-
- @Override
- protected void onStorageGranted() {}
-
- @Override
- protected void onStorageDenied() {
- getApp().toast(R.string.msg_storage_denied, Toaster.Type.ERROR);
- finishAffinity();
- }
-
- @Override
- public void onClick(View p1) {
- if (p1.getId() == binding.createProject.getId()) {
- showCreateProject();
- } else if (p1.getId() == createBinding.createprojectClose.getId()) {
- hideCreateProject();
- } else if (p1.getId() == createLayoutBinding.nextCard.getId()) {
- currentTemplateIndex++;
- if (currentTemplateIndex >= mTemplates.size()) {
- currentTemplateIndex = 0;
- }
- showTemplate(currentTemplateIndex);
- } else if (p1.getId() == createLayoutBinding.previousCard.getId()) {
- currentTemplateIndex--;
- if (currentTemplateIndex < 0) {
- currentTemplateIndex = mTemplates.size() - 1;
- }
- showTemplate(currentTemplateIndex);
- } else if (p1.getId() == createLayoutBinding.createprojectCreate.getId()) {
- createNewProject();
- } else if (p1.getId() == binding.gotoPreferences.getId()) {
- gotoSettings();
- } else if (p1.getId() == binding.openProject.getId()) {
- pickProject();
- } else if (p1.getId() == binding.openTerminal.getId()) {
- startActivity(new Intent(this, TerminalActivity.class));
- }
- }
-
- @Override
- public void beforeBegin() {
- if (mProgressSheet == null) {
- createProgressSheet();
- }
-
- setMessage(R.string.msg_begin_project_write);
- }
-
- @Override
- public void onProcessTask(String taskName) {
- setMessage(taskName);
- }
-
- @Override
- public void onSuccess(File root) {
- if (mProgressSheet == null) {
- createProgressSheet();
- }
-
- if (mProgressSheet.isShowing()) {
- mProgressSheet.dismiss();
- }
- getApp().toast(R.string.project_created_successfully, Toaster.Type.SUCCESS);
-
- openProject(root);
- }
-
- @Override
- public void onFailed(String reason) {
- if (mProgressSheet == null) {
- createProgressSheet();
- }
-
- if (mProgressSheet.isShowing()) {
- mProgressSheet.dismiss();
- }
- getApp().toast(reason, Toaster.Type.ERROR);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE
- && resultCode == RESULT_OK) {
- ArrayList files = data.getStringArrayListExtra("filePaths");
- if (files != null) {
- if (files.size() == 1) {
- File choseDir = new File(files.get(0));
- if (choseDir.exists() && choseDir.isDirectory()) {
- openProject(choseDir);
- } else {
- getApp().toast(R.string.msg_picked_isnt_dir, Toaster.Type.ERROR);
- }
- } else {
- getApp().toast(R.string.msg_pick_single_file, Toaster.Type.ERROR);
- }
- }
- }
- }
-
- @Override
- public void onBackPressed() {
- if (binding.createNewCard.getVisibility() == View.VISIBLE) {
- hideCreateProject();
- } else {
- super.onBackPressed();
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- if (mProgressSheet != null && mProgressSheet.isShowing()) {
- mProgressSheet.dismiss();
- }
- }
-
- private void showTemplate(int index) {
- if (index < 0 || index >= mTemplates.size()) {
- return;
- }
- final ProjectTemplate template = mTemplates.get(index);
- createLayoutBinding.createprojectTemplateLabel.setText(template.getName());
- createLayoutBinding.createprojectTemplateDescription.setText(template.getDescription());
- createLayoutBinding.createprojectTemplateImage.setImageResource(template.getImageId());
- }
-
- /** Must be called in/after onViewCreated */
- private void createTemplates() {
- mTemplates = new ArrayList<>();
-
- ProjectTemplate
- empty =
- new ProjectTemplate()
- .setId(0)
- .setName(this, R.string.template_empty)
- .setDescription(this, R.string.template_description_empty)
- .setImageId(R.drawable.template_empty),
- basic =
- new ProjectTemplate()
- .setId(1)
- .setName(this, R.string.template_basic)
- .setDescription(this, R.string.template_description_basic)
- .setImageId(R.drawable.template_basic),
- drawer =
- new ProjectTemplate()
- .setId(2)
- .setName(this, R.string.template_navigation_drawer)
- .setDescription(this, R.string.template_description_navigation_drawer)
- .setImageId(R.drawable.template_navigation_drawer),
- kotlinBasic =
- new ProjectTemplate()
- .setId(3)
- .setName(this, R.string.template_kotlin_basic)
- .setDescription(this, R.string.template_description_kotlin_basic)
- .setImageId(R.drawable.template_basic),
- libgdx =
- new ProjectTemplate()
- .setId(4)
- .setName(this, R.string.template_libgdx)
- .setDescription(this, R.string.template_description_libgdx)
- .setImageId(R.drawable.template_libgdx);
- mTemplates.add(empty);
- mTemplates.add(basic);
- mTemplates.add(drawer);
- mTemplates.add(kotlinBasic);
- mTemplates.add(libgdx);
- }
-
- private void createProgressSheet() {
- mProgressSheet = new ProgressSheet();
- mProgressSheet.setShowShadow(false);
- }
-
- private void setMessage(int msg) {
- setMessage(getString(msg));
- }
-
- private void setMessage(String msg) {
- mProgressSheet.setMessage(msg);
- }
-
- private void openProject(File root) {
- setMessage(R.string.msg_opening_project);
- mProgressSheet.setCancelable(false);
- mProgressSheet.show(getSupportFragmentManager(), "opening_project");
-
- new TaskExecutor().executeAsync(new ProjectReader(root), this::openEditor);
- }
-
- private void pickProject() {
- UnicornFilePicker.from(this)
- .addConfigBuilder()
- .addItemDivider(false)
- .selectMultipleFiles(false)
- .setRootDirectory(Environment.getExternalStorageDirectory().getAbsolutePath())
- .showHiddenFiles(true)
- .showOnlyDirectory(true)
- .theme(R.style.AppTheme_FilePicker)
- .build()
- .forResult(abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE);
- }
-
- private void openEditor(AndroidProject project) {
- if (project == null) {
- getApp().toast(R.string.msg_invalid_project, Toaster.Type.ERROR);
- } else {
- startActivity(
- new Intent(this, EditorActivity.class).putExtra(EditorActivity.EXTRA_PROJECT, project));
- }
- }
-
- private void createNewProject() {
- final String appName =
- Objects.requireNonNull(createLayoutBinding.createprojectTextAppName.getEditText())
- .getText()
- .toString()
- .trim();
- final String packageName =
- Objects.requireNonNull(createLayoutBinding.createprojectTextPackageName.getEditText())
- .getText()
- .toString()
- .trim();
- final int minSdk = getMinSdk();
- final int targetSdk = getTargetSdk();
-
- if (!isValid(appName, packageName, minSdk, targetSdk)) {
- getApp().toast(R.string.invalid_values, Toaster.Type.ERROR);
- return;
- }
-
- createProject(appName, packageName, minSdk, targetSdk);
- }
-
- private void createProject(String appName, String packageName, int minSdk, int targetSdk) {
- new TaskExecutor()
- .executeAsync(
- new ProjectCreatorCallable(
- mTemplates.get(currentTemplateIndex),
- new NewProjectDetails(appName, packageName, minSdk, targetSdk),
- this),
- r -> {});
- }
-
- private boolean isValid(String appName, String packageName, int minSdk, int targetSdk) {
- return appName != null
- && appName.length() > 0
- && packageName != null
- && packageName.length() > 0
- && minSdk > 1
- && minSdk < 99
- && targetSdk > 1
- && targetSdk < 99;
- }
-
- private int getMinSdk() {
- try {
- return Integer.parseInt(
- Objects.requireNonNull(createLayoutBinding.createprojectTextMinSdk.getEditText())
- .getText()
- .toString());
- } catch (Exception e) {
- getApp().toast(e.getMessage(), Toaster.Type.ERROR);
- }
- return -1;
- }
-
- private int getTargetSdk() {
- try {
- return Integer.parseInt(
- Objects.requireNonNull(createLayoutBinding.createprojectTextTargetSdk.getEditText())
- .getText()
- .toString());
- } catch (Exception e) {
- getApp().toast(e.getMessage(), Toaster.Type.ERROR);
- }
- return -1;
- }
-
- private void showCreateProject() {
- MaterialContainerTransform transform =
- TransformUtils.createContainerTransformFor(
- binding.createProject, binding.createNewCard, binding.realContainer);
- TransitionManager.beginDelayedTransition(binding.getRoot(), transform);
- binding.createNewCard.setVisibility(View.VISIBLE);
- }
-
- private void hideCreateProject() {
- MaterialContainerTransform transform =
- TransformUtils.createContainerTransformFor(
- binding.createNewCard, binding.createProject, binding.realContainer);
- TransitionManager.beginDelayedTransition(binding.getRoot(), transform);
- binding.createNewCard.setVisibility(View.GONE);
- }
-
- private static final class ProjectReader implements Callable {
-
- private final File root;
-
- public ProjectReader(File root) {
- this.root = root;
+ @Override
+ protected View bindLayout() {
+ binding = ActivityMainBinding.inflate(getLayoutInflater());
+ createBinding = binding.createLayout;
+ createLayoutBinding = createBinding.createProjectContent;
+ return binding.getRoot();
}
@Override
- public AndroidProject call() throws Exception {
- return ProjectFinder.fromFolder(root);
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ binding.createProject.setOnClickListener(this);
+ binding.openProject.setOnClickListener(this);
+ binding.openTerminal.setOnClickListener(this);
+ createBinding.createprojectClose.setOnClickListener(this);
+ createLayoutBinding.createprojectCreate.setOnClickListener(this);
+ createLayoutBinding.previousCard.setOnClickListener(this);
+ createLayoutBinding.nextCard.setOnClickListener(this);
+ binding.createNewCard.setVisibility(View.GONE);
+
+ currentTemplateIndex = 0;
+ createTemplates();
+ createProgressSheet();
+ showTemplate(currentTemplateIndex);
+
+ binding.gotoPreferences.setOnClickListener(this);
+
+ binding.getRoot()
+ .post(
+ () -> {
+ PreferenceManager manager = getApp().getPrefManager();
+ if (manager.autoOpenProject()
+ && manager.wasProjectOpened()
+ && ConstantsBridge.SPLASH_TO_MAIN) {
+ String path = manager.getOpenedProject();
+ if (path == null || path.trim().isEmpty()) {
+ getApp().toast(
+ R.string.msg_opened_project_does_not_exist,
+ Toaster.Type.INFO);
+ } else {
+ File root = new File(manager.getOpenedProject());
+ if (!root.exists()) {
+ getApp().toast(
+ R.string.msg_opened_project_does_not_exist,
+ Toaster.Type.INFO);
+ } else {
+ if (manager.confirmProjectOpen()) {
+ askProjectOpenPermission(root);
+ } else {
+ openProject(root);
+ }
+ }
+ }
+ }
+
+ ConstantsBridge.SPLASH_TO_MAIN = false;
+ });
+ }
+
+ private void askProjectOpenPermission(File root) {
+ final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
+ builder.setTitle(R.string.title_confirm_open_project);
+ builder.setMessage(getString(R.string.msg_confirm_open_project, root.getAbsolutePath()));
+ builder.setCancelable(false);
+ builder.setPositiveButton(R.string.yes, (d, w) -> openProject(root));
+ builder.setNegativeButton(R.string.no, null);
+ builder.show();
+ }
+
+ private void gotoSettings() {
+ startActivity(new Intent(this, PreferencesActivity.class));
+ }
+
+ @Override
+ protected void onStorageGranted() {}
+
+ @Override
+ protected void onStorageDenied() {
+ getApp().toast(R.string.msg_storage_denied, Toaster.Type.ERROR);
+ finishAffinity();
+ }
+
+ @Override
+ public void onClick(View p1) {
+ if (p1.getId() == binding.createProject.getId()) {
+ showCreateProject();
+ } else if (p1.getId() == createBinding.createprojectClose.getId()) {
+ hideCreateProject();
+ } else if (p1.getId() == createLayoutBinding.nextCard.getId()) {
+ currentTemplateIndex++;
+ if (currentTemplateIndex >= mTemplates.size()) {
+ currentTemplateIndex = 0;
+ }
+ showTemplate(currentTemplateIndex);
+ } else if (p1.getId() == createLayoutBinding.previousCard.getId()) {
+ currentTemplateIndex--;
+ if (currentTemplateIndex < 0) {
+ currentTemplateIndex = mTemplates.size() - 1;
+ }
+ showTemplate(currentTemplateIndex);
+ } else if (p1.getId() == createLayoutBinding.createprojectCreate.getId()) {
+ createNewProject();
+ } else if (p1.getId() == binding.gotoPreferences.getId()) {
+ gotoSettings();
+ } else if (p1.getId() == binding.openProject.getId()) {
+ pickProject();
+ } else if (p1.getId() == binding.openTerminal.getId()) {
+ startActivity(new Intent(this, TerminalActivity.class));
+ }
+ }
+
+ @Override
+ public void beforeBegin() {
+ if (mProgressSheet == null) {
+ createProgressSheet();
+ }
+
+ setMessage(R.string.msg_begin_project_write);
+ }
+
+ @Override
+ public void onProcessTask(String taskName) {
+ setMessage(taskName);
+ }
+
+ @Override
+ public void onSuccess(File root) {
+ if (mProgressSheet == null) {
+ createProgressSheet();
+ }
+
+ if (mProgressSheet.isShowing()) {
+ mProgressSheet.dismiss();
+ }
+ getApp().toast(R.string.project_created_successfully, Toaster.Type.SUCCESS);
+
+ openProject(root);
+ }
+
+ @Override
+ public void onFailed(String reason) {
+ if (mProgressSheet == null) {
+ createProgressSheet();
+ }
+
+ if (mProgressSheet.isShowing()) {
+ mProgressSheet.dismiss();
+ }
+ getApp().toast(reason, Toaster.Type.ERROR);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE
+ && resultCode == RESULT_OK) {
+ ArrayList files = data.getStringArrayListExtra("filePaths");
+ if (files != null) {
+ if (files.size() == 1) {
+ File choseDir = new File(files.get(0));
+ if (choseDir.exists() && choseDir.isDirectory()) {
+ openProject(choseDir);
+ } else {
+ getApp().toast(R.string.msg_picked_isnt_dir, Toaster.Type.ERROR);
+ }
+ } else {
+ getApp().toast(R.string.msg_pick_single_file, Toaster.Type.ERROR);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (binding.createNewCard.getVisibility() == View.VISIBLE) {
+ hideCreateProject();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (mProgressSheet != null && mProgressSheet.isShowing()) {
+ mProgressSheet.dismiss();
+ }
+ }
+
+ private void showTemplate(int index) {
+ if (index < 0 || index >= mTemplates.size()) {
+ return;
+ }
+ final ProjectTemplate template = mTemplates.get(index);
+ createLayoutBinding.createprojectTemplateLabel.setText(template.getName());
+ createLayoutBinding.createprojectTemplateDescription.setText(template.getDescription());
+ createLayoutBinding.createprojectTemplateImage.setImageResource(template.getImageId());
+ }
+
+ /** Must be called in/after onViewCreated */
+ private void createTemplates() {
+ mTemplates = new ArrayList<>();
+
+ ProjectTemplate
+ empty =
+ new ProjectTemplate()
+ .setId(0)
+ .setName(this, R.string.template_empty)
+ .setDescription(this, R.string.template_description_empty)
+ .setImageId(R.drawable.template_empty),
+ basic =
+ new ProjectTemplate()
+ .setId(1)
+ .setName(this, R.string.template_basic)
+ .setDescription(this, R.string.template_description_basic)
+ .setImageId(R.drawable.template_basic),
+ drawer =
+ new ProjectTemplate()
+ .setId(2)
+ .setName(this, R.string.template_navigation_drawer)
+ .setDescription(
+ this, R.string.template_description_navigation_drawer)
+ .setImageId(R.drawable.template_navigation_drawer),
+ kotlinBasic =
+ new ProjectTemplate()
+ .setId(3)
+ .setName(this, R.string.template_kotlin_basic)
+ .setDescription(this, R.string.template_description_kotlin_basic)
+ .setImageId(R.drawable.template_basic),
+ libgdx =
+ new ProjectTemplate()
+ .setId(4)
+ .setName(this, R.string.template_libgdx)
+ .setDescription(this, R.string.template_description_libgdx)
+ .setImageId(R.drawable.template_libgdx);
+ mTemplates.add(empty);
+ mTemplates.add(basic);
+ mTemplates.add(drawer);
+ mTemplates.add(kotlinBasic);
+ mTemplates.add(libgdx);
+ }
+
+ private void createProgressSheet() {
+ mProgressSheet = new ProgressSheet();
+ mProgressSheet.setShowShadow(false);
+ }
+
+ private void setMessage(int msg) {
+ setMessage(getString(msg));
+ }
+
+ private void setMessage(String msg) {
+ mProgressSheet.setMessage(msg);
+ }
+
+ private void openProject(File root) {
+ setMessage(R.string.msg_opening_project);
+ mProgressSheet.setCancelable(false);
+ mProgressSheet.show(getSupportFragmentManager(), "opening_project");
+
+ new TaskExecutor().executeAsync(new ProjectReader(root), this::openEditor);
+ }
+
+ private void pickProject() {
+ UnicornFilePicker.from(this)
+ .addConfigBuilder()
+ .addItemDivider(false)
+ .selectMultipleFiles(false)
+ .setRootDirectory(Environment.getExternalStorageDirectory().getAbsolutePath())
+ .showHiddenFiles(true)
+ .showOnlyDirectory(true)
+ .theme(R.style.AppTheme_FilePicker)
+ .build()
+ .forResult(abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE);
+ }
+
+ private void openEditor(AndroidProject project) {
+ if (project == null) {
+ getApp().toast(R.string.msg_invalid_project, Toaster.Type.ERROR);
+ } else {
+ startActivity(
+ new Intent(this, EditorActivity.class)
+ .putExtra(EditorActivity.EXTRA_PROJECT, project));
+ }
+ }
+
+ private void createNewProject() {
+ final String appName =
+ Objects.requireNonNull(createLayoutBinding.createprojectTextAppName.getEditText())
+ .getText()
+ .toString()
+ .trim();
+ final String packageName =
+ Objects.requireNonNull(
+ createLayoutBinding.createprojectTextPackageName.getEditText())
+ .getText()
+ .toString()
+ .trim();
+ final int minSdk = getMinSdk();
+ final int targetSdk = getTargetSdk();
+
+ if (!isValid(appName, packageName, minSdk, targetSdk)) {
+ getApp().toast(R.string.invalid_values, Toaster.Type.ERROR);
+ return;
+ }
+
+ createProject(appName, packageName, minSdk, targetSdk);
+ }
+
+ private void createProject(String appName, String packageName, int minSdk, int targetSdk) {
+ new TaskExecutor()
+ .executeAsync(
+ new ProjectCreatorCallable(
+ mTemplates.get(currentTemplateIndex),
+ new NewProjectDetails(appName, packageName, minSdk, targetSdk),
+ this),
+ r -> {});
+ }
+
+ private boolean isValid(String appName, String packageName, int minSdk, int targetSdk) {
+ return appName != null
+ && appName.length() > 0
+ && packageName != null
+ && packageName.length() > 0
+ && minSdk > 1
+ && minSdk < 99
+ && targetSdk > 1
+ && targetSdk < 99;
+ }
+
+ private int getMinSdk() {
+ try {
+ return Integer.parseInt(
+ Objects.requireNonNull(
+ createLayoutBinding.createprojectTextMinSdk.getEditText())
+ .getText()
+ .toString());
+ } catch (Exception e) {
+ getApp().toast(e.getMessage(), Toaster.Type.ERROR);
+ }
+ return -1;
+ }
+
+ private int getTargetSdk() {
+ try {
+ return Integer.parseInt(
+ Objects.requireNonNull(
+ createLayoutBinding.createprojectTextTargetSdk.getEditText())
+ .getText()
+ .toString());
+ } catch (Exception e) {
+ getApp().toast(e.getMessage(), Toaster.Type.ERROR);
+ }
+ return -1;
+ }
+
+ private void showCreateProject() {
+ MaterialContainerTransform transform =
+ TransformUtils.createContainerTransformFor(
+ binding.createProject, binding.createNewCard, binding.realContainer);
+ TransitionManager.beginDelayedTransition(binding.getRoot(), transform);
+ binding.createNewCard.setVisibility(View.VISIBLE);
+ }
+
+ private void hideCreateProject() {
+ MaterialContainerTransform transform =
+ TransformUtils.createContainerTransformFor(
+ binding.createNewCard, binding.createProject, binding.realContainer);
+ TransitionManager.beginDelayedTransition(binding.getRoot(), transform);
+ binding.createNewCard.setVisibility(View.GONE);
+ }
+
+ private static final class ProjectReader implements Callable {
+
+ private final File root;
+
+ public ProjectReader(File root) {
+ this.root = root;
+ }
+
+ @Override
+ public AndroidProject call() throws Exception {
+ return ProjectFinder.fromFolder(root);
+ }
}
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/PreferencesActivity.java b/app/src/main/java/com/itsaky/androidide/PreferencesActivity.java
index ba208d436..a3e99d7a1 100755
--- a/app/src/main/java/com/itsaky/androidide/PreferencesActivity.java
+++ b/app/src/main/java/com/itsaky/androidide/PreferencesActivity.java
@@ -22,45 +22,47 @@ package com.itsaky.androidide;
import android.os.Bundle;
import android.view.View;
+
import androidx.fragment.app.Fragment;
+
import com.itsaky.androidide.app.StudioActivity;
import com.itsaky.androidide.databinding.ActivityPreferencesBinding;
import com.itsaky.androidide.fragments.preferences.IDEPreferences;
public class PreferencesActivity extends StudioActivity {
- private ActivityPreferencesBinding binding;
- private IDEPreferences mPref;
+ private ActivityPreferencesBinding binding;
+ private IDEPreferences mPref;
- @Override
- protected View bindLayout() {
- binding = ActivityPreferencesBinding.inflate(getLayoutInflater());
- return binding.getRoot();
- }
+ @Override
+ protected View bindLayout() {
+ binding = ActivityPreferencesBinding.inflate(getLayoutInflater());
+ return binding.getRoot();
+ }
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
- setSupportActionBar(binding.toolbar);
- getSupportActionBar().setTitle(R.string.ide_preferences);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ setSupportActionBar(binding.toolbar);
+ getSupportActionBar().setTitle(R.string.ide_preferences);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- binding.toolbar.setNavigationOnClickListener(v -> onBackPressed());
+ binding.toolbar.setNavigationOnClickListener(v -> onBackPressed());
- loadFragment(getPrefsFragment());
- }
+ loadFragment(getPrefsFragment());
+ }
- private IDEPreferences getPrefsFragment() {
- return mPref == null ? mPref = new IDEPreferences() : mPref;
- }
+ private IDEPreferences getPrefsFragment() {
+ return mPref == null ? mPref = new IDEPreferences() : mPref;
+ }
- private void loadFragment(Fragment frag) {
- super.loadFragment(frag, binding.fragmentContainer.getId());
- }
+ private void loadFragment(Fragment frag) {
+ super.loadFragment(frag, binding.fragmentContainer.getId());
+ }
- @Override
- public void onBackPressed() {
- super.onBackPressed();
- }
+ @Override
+ public void onBackPressed() {
+ super.onBackPressed();
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/SplashActivity.java b/app/src/main/java/com/itsaky/androidide/SplashActivity.java
index f1ecc7755..bdbe13296 100755
--- a/app/src/main/java/com/itsaky/androidide/SplashActivity.java
+++ b/app/src/main/java/com/itsaky/androidide/SplashActivity.java
@@ -24,6 +24,7 @@ import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
+
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.itsaky.androidide.app.StudioActivity;
import com.itsaky.androidide.app.StudioApp;
@@ -32,65 +33,65 @@ import com.itsaky.androidide.models.ConstantsBridge;
import com.itsaky.androidide.utils.DialogUtils;
public class SplashActivity extends StudioActivity {
- private ActivitySplashBinding binding;
+ private ActivitySplashBinding binding;
- private static final Handler mHandler = new Handler();
- private final Runnable mRunnable =
- new Runnable() {
+ private static final Handler mHandler = new Handler();
+ private final Runnable mRunnable =
+ new Runnable() {
- @Override
- public void run() {
- ConstantsBridge.SPLASH_TO_MAIN = true;
- startActivity(new Intent(SplashActivity.this, MainActivity.class));
- finish();
- }
- };
+ @Override
+ public void run() {
+ ConstantsBridge.SPLASH_TO_MAIN = true;
+ startActivity(new Intent(SplashActivity.this, MainActivity.class));
+ finish();
+ }
+ };
- @Override
- protected View bindLayout() {
- binding = ActivitySplashBinding.inflate(getLayoutInflater());
- return binding.getRoot();
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- binding.splashText.setText(getString(R.string.msg_checking_storage_permissions));
- }
-
- @Override
- protected void onStorageGranted() {
- super.onStorageGranted();
- proceed();
- }
-
- @Override
- protected void onStorageAlreadyGranted() {
- super.onStorageAlreadyGranted();
- proceed();
- }
-
- private void proceed() {
- if (!StudioApp.isAbiSupported()) {
- final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
- builder.setTitle(R.string.title_device_not_supported);
- builder.setMessage(R.string.msg_device_not_supported);
- builder.setCancelable(false);
- builder.setPositiveButton(android.R.string.ok, (p1, p2) -> finishAffinity());
- builder.create().show();
- } else {
- binding.splashText.setText(getString(R.string.msg_storage_granted));
- if (getApp().isFrameworkInstalled()) {
- goToMain();
- } else {
- startActivity(new Intent(this, DownloadActivity.class));
- finish();
- }
+ @Override
+ protected View bindLayout() {
+ binding = ActivitySplashBinding.inflate(getLayoutInflater());
+ return binding.getRoot();
}
- }
- private void goToMain() {
- mHandler.removeCallbacks(mRunnable);
- mHandler.postDelayed(mRunnable, 1000);
- }
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ binding.splashText.setText(getString(R.string.msg_checking_storage_permissions));
+ }
+
+ @Override
+ protected void onStorageGranted() {
+ super.onStorageGranted();
+ proceed();
+ }
+
+ @Override
+ protected void onStorageAlreadyGranted() {
+ super.onStorageAlreadyGranted();
+ proceed();
+ }
+
+ private void proceed() {
+ if (!StudioApp.isAbiSupported()) {
+ final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
+ builder.setTitle(R.string.title_device_not_supported);
+ builder.setMessage(R.string.msg_device_not_supported);
+ builder.setCancelable(false);
+ builder.setPositiveButton(android.R.string.ok, (p1, p2) -> finishAffinity());
+ builder.create().show();
+ } else {
+ binding.splashText.setText(getString(R.string.msg_storage_granted));
+ if (getApp().isFrameworkInstalled()) {
+ goToMain();
+ } else {
+ startActivity(new Intent(this, DownloadActivity.class));
+ finish();
+ }
+ }
+ }
+
+ private void goToMain() {
+ mHandler.removeCallbacks(mRunnable);
+ mHandler.postDelayed(mRunnable, 1000);
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/TerminalActivity.java b/app/src/main/java/com/itsaky/androidide/TerminalActivity.java
index 385f3f978..2e9d96aa2 100755
--- a/app/src/main/java/com/itsaky/androidide/TerminalActivity.java
+++ b/app/src/main/java/com/itsaky/androidide/TerminalActivity.java
@@ -29,7 +29,9 @@ import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
+
import androidx.annotation.NonNull;
+
import com.blankj.utilcode.util.ClipboardUtils;
import com.blankj.utilcode.util.KeyboardUtils;
import com.blankj.utilcode.util.SizeUtils;
@@ -54,459 +56,466 @@ import com.itsaky.terminal.TerminalSessionClient;
import com.itsaky.terminal.TextStyle;
import com.itsaky.terminal.view.TerminalView;
import com.itsaky.terminal.view.TerminalViewClient;
+
+import org.json.JSONException;
+
import java.io.File;
import java.io.FileOutputStream;
import java.util.Map;
-import org.json.JSONException;
public class TerminalActivity extends StudioActivity {
- private ActivityTerminalBinding binding;
- private TerminalView terminal;
- private TerminalSession session;
+ private ActivityTerminalBinding binding;
+ private TerminalView terminal;
+ private TerminalSession session;
- private boolean isVisible = false;
+ private boolean isVisible = false;
- private KeyListener listener;
- private final Client client = new Client();
- private static final Logger LOG = Logger.instance("TerminalActivity");
+ private KeyListener listener;
+ private final Client client = new Client();
+ private static final Logger LOG = Logger.instance("TerminalActivity");
- public static final String KEY_WORKING_DIRECTORY = "terminal_workingDirectory";
+ public static final String KEY_WORKING_DIRECTORY = "terminal_workingDirectory";
- private static final byte[] SOURCES_LIST_CONTENT =
- "deb https://androidide.com/packages/ stable main".getBytes();
+ private static final byte[] SOURCES_LIST_CONTENT =
+ "deb https://androidide.com/packages/ stable main".getBytes();
- @Override
- protected View bindLayout() {
- binding = ActivityTerminalBinding.inflate(getLayoutInflater());
- return binding.getRoot();
- }
+ @Override
+ protected View bindLayout() {
+ binding = ActivityTerminalBinding.inflate(getLayoutInflater());
+ return binding.getRoot();
+ }
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
- final var bash = new File(BIN_DIR, "bash");
- final var useSystemShell =
- getApp().getPrefManager().getBoolean(PreferenceManager.KEY_TERMINAL_USE_SYSTEM_SHELL);
- if ((SYSROOT.exists()
- && SYSROOT.isDirectory()
- && bash.exists()
- && bash.isFile()
- && bash.canExecute())
- || useSystemShell) {
- setupTerminalView();
- } else {
- LOG.info("Bootstrap is not installed.");
+ final var bash = new File(BIN_DIR, "bash");
+ final var useSystemShell =
+ getApp().getPrefManager()
+ .getBoolean(PreferenceManager.KEY_TERMINAL_USE_SYSTEM_SHELL);
+ if ((SYSROOT.exists()
+ && SYSROOT.isDirectory()
+ && bash.exists()
+ && bash.isFile()
+ && bash.canExecute())
+ || useSystemShell) {
+ setupTerminalView();
+ } else {
+ LOG.info("Bootstrap is not installed.");
- // Show the progress sheet
- final var progress = new ProgressSheet();
- progress.setShowShadow(false);
- progress.setSubMessageEnabled(true);
- progress.setShowTitle(false);
- progress.setMessage(getString(R.string.please_wait));
- progress.setSubMessage(getString(R.string.msg_reading_bootstrap));
- progress.setCancelable(false);
- progress.show(getSupportFragmentManager(), "extract_bootstrap_progress");
+ // Show the progress sheet
+ final var progress = new ProgressSheet();
+ progress.setShowShadow(false);
+ progress.setSubMessageEnabled(true);
+ progress.setShowTitle(false);
+ progress.setMessage(getString(R.string.please_wait));
+ progress.setSubMessage(getString(R.string.msg_reading_bootstrap));
+ progress.setCancelable(false);
+ progress.show(getSupportFragmentManager(), "extract_bootstrap_progress");
- LOG.debug("Starting installation...");
- // Install bootstrap asynchronously
- final var future =
- BootstrapInstaller.doInstall(
- this, message -> runOnUiThread(() -> progress.setSubMessage(message)));
+ LOG.debug("Starting installation...");
+ // Install bootstrap asynchronously
+ final var future =
+ BootstrapInstaller.doInstall(
+ this, message -> runOnUiThread(() -> progress.setSubMessage(message)));
- future.whenComplete(
- (voidResult, throwable) -> {
- LOG.debug("Completable future has been complete.", throwable);
+ future.whenComplete(
+ (voidResult, throwable) -> {
+ LOG.debug("Completable future has been complete.", throwable);
- runOnUiThread(
- () -> {
- progress.dismissAllowingStateLoss();
+ runOnUiThread(
+ () -> {
+ progress.dismissAllowingStateLoss();
- if (future.isCompletedExceptionally() || throwable != null) {
- LOG.error("Future has been completed exceptionally.");
- showInstallationError(throwable);
+ if (future.isCompletedExceptionally() || throwable != null) {
+ LOG.error("Future has been completed exceptionally.");
+ showInstallationError(throwable);
+ return;
+ }
+
+ setupTerminalView();
+ });
+ });
+ }
+ }
+
+ private void showInstallationError(Throwable throwable) {
+
+ binding.virtualKeyTable.setVisibility(View.GONE);
+
+ final var frag =
+ CrashReportFragment.newInstance(
+ getString(R.string.title_installation_failed),
+ getString(R.string.msg_bootstrap_installation_error),
+ ThrowableUtils.getFullStackTrace(throwable))
+ .setCloseAppOnClick(false);
+
+ getSupportFragmentManager()
+ .beginTransaction()
+ .replace(binding.root.getId(), frag, "bootstrap_install_error_fragment")
+ .addToBackStack(null)
+ .commit();
+ }
+
+ private void setupTerminalView() {
+ terminal = new TerminalView(this, null);
+ terminal.setTerminalViewClient(client);
+ terminal.attachSession(createSession(getWorkingDirectory()));
+ terminal.setKeepScreenOn(true);
+ terminal.setTextSize(SizeUtils.dp2px(10));
+ terminal.setTypeface(TypefaceUtils.jetbrainsMono());
+
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(-1, 0);
+ params.weight = 1f;
+
+ binding.getRoot().addView(terminal, 0, params);
+ try {
+ binding.virtualKeyTable.setVirtualKeysViewClient(getKeyListener());
+ binding.virtualKeyTable.reload(
+ new VirtualKeysInfo(
+ ConstantsBridge.VIRTUAL_KEYS,
+ "",
+ VirtualKeysConstants.CONTROL_CHARS_ALIASES));
+ } catch (JSONException e) {
+ LOG.error("Unable to parse terminal virtual keys json data", e);
+ }
+ }
+
+ @NonNull
+ private String getWorkingDirectory() {
+ final Bundle extras = getIntent().getExtras();
+ if (extras != null && extras.containsKey(KEY_WORKING_DIRECTORY)) {
+ String directory = extras.getString(KEY_WORKING_DIRECTORY, null);
+ if (directory == null || directory.trim().length() <= 0) {
+ directory = HOME.getAbsolutePath();
+ }
+ return directory;
+ }
+ return HOME.getAbsolutePath();
+ }
+
+ private KeyListener getKeyListener() {
+ return listener == null ? listener = new KeyListener(terminal) : listener;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ setTerminalCursorBlinkingState(true);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ isVisible = true;
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ isVisible = false;
+ setTerminalCursorBlinkingState(false);
+ }
+
+ public boolean isVisible() {
+ return isVisible;
+ }
+
+ private void setTerminalCursorBlinkingState(boolean start) {
+ if (terminal != null && terminal.mEmulator != null) {
+ terminal.setTerminalCursorBlinkerState(start, true);
+ }
+ }
+
+ private TerminalSession createSession(final String workingDirectory) {
+ final Map envs = getEnvironment();
+ final String[] env = new String[envs.size()];
+ int i = 0;
+ for (Map.Entry entry : envs.entrySet()) {
+ env[i] = entry.getKey() + "=" + entry.getValue();
+ i++;
+ }
+
+ session =
+ new TerminalSession(
+ getShellPath(), // Shell command
+ workingDirectory, // Working directory
+ new String[] {}, // Arguments
+ env, // Environment variables
+ TerminalEmulator.DEFAULT_TERMINAL_TRANSCRIPT_ROWS, // Transcript rows
+ client // TerminalSessionClient
+ );
+
+ try {
+ final var file = new File(SYSROOT, "etc/apt/sources.list");
+ final var out = new FileOutputStream(file);
+ out.write(SOURCES_LIST_CONTENT);
+ out.flush();
+ out.close();
+ } catch (Throwable th) {
+ LOG.error("Unable to update sources.list", th);
+ }
+
+ return session;
+ }
+
+ @NonNull
+ private String getShellPath() {
+ final var useSystemShell =
+ getApp().getPrefManager()
+ .getBoolean(PreferenceManager.KEY_TERMINAL_USE_SYSTEM_SHELL);
+ if (!useSystemShell && LOGIN_SHELL.exists() && LOGIN_SHELL.isFile()) {
+ return LOGIN_SHELL.getAbsolutePath();
+ }
+
+ if (!useSystemShell) {
+ LOG.error(
+ "Default shell does not exist. Falling back to '/system/bin/sh'.",
+ "This should not happen in normal circumstances.");
+ }
+
+ return "/system/bin/sh";
+ }
+
+ private static final class KeyListener implements VirtualKeysView.IVirtualKeysView {
+
+ private final TerminalView terminal;
+
+ public KeyListener(TerminalView terminal) {
+ this.terminal = terminal;
+ }
+
+ @Override
+ public void onVirtualKeyButtonClick(View view, VirtualKeyButton buttonInfo, Button button) {
+ if (terminal == null) {
+ return;
+ }
+ if (buttonInfo.isMacro()) {
+ String[] keys = buttonInfo.getKey().split(" ");
+ boolean ctrlDown = false;
+ boolean altDown = false;
+ boolean shiftDown = false;
+ boolean fnDown = false;
+ for (String key : keys) {
+ if (SpecialButton.CTRL.getKey().equals(key)) {
+ ctrlDown = true;
+ } else if (SpecialButton.ALT.getKey().equals(key)) {
+ altDown = true;
+ } else if (SpecialButton.SHIFT.getKey().equals(key)) {
+ shiftDown = true;
+ } else if (SpecialButton.FN.getKey().equals(key)) {
+ fnDown = true;
+ } else {
+ onTerminalExtraKeyButtonClick(key, ctrlDown, altDown, shiftDown, fnDown);
+ ctrlDown = false;
+ altDown = false;
+ shiftDown = false;
+ fnDown = false;
+ }
+ }
+ } else {
+ onTerminalExtraKeyButtonClick(buttonInfo.getKey(), false, false, false, false);
+ }
+ }
+
+ protected void onTerminalExtraKeyButtonClick(
+ String key, boolean ctrlDown, boolean altDown, boolean shiftDown, boolean fnDown) {
+ if (VirtualKeysConstants.PRIMARY_KEY_CODES_FOR_STRINGS.containsKey(key)) {
+ Integer keyCode = VirtualKeysConstants.PRIMARY_KEY_CODES_FOR_STRINGS.get(key);
+ if (keyCode == null) {
return;
- }
+ }
+ int metaState = 0;
+ if (ctrlDown) {
+ metaState |= KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON;
+ }
+ if (altDown) {
+ metaState |= KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON;
+ }
+ if (shiftDown) {
+ metaState |= KeyEvent.META_SHIFT_ON | KeyEvent.META_SHIFT_LEFT_ON;
+ }
+ if (fnDown) {
+ metaState |= KeyEvent.META_FUNCTION_ON;
+ }
- setupTerminalView();
- });
- });
- }
- }
-
- private void showInstallationError(Throwable throwable) {
-
- binding.virtualKeyTable.setVisibility(View.GONE);
-
- final var frag =
- CrashReportFragment.newInstance(
- getString(R.string.title_installation_failed),
- getString(R.string.msg_bootstrap_installation_error),
- ThrowableUtils.getFullStackTrace(throwable))
- .setCloseAppOnClick(false);
-
- getSupportFragmentManager()
- .beginTransaction()
- .replace(binding.root.getId(), frag, "bootstrap_install_error_fragment")
- .addToBackStack(null)
- .commit();
- }
-
- private void setupTerminalView() {
- terminal = new TerminalView(this, null);
- terminal.setTerminalViewClient(client);
- terminal.attachSession(createSession(getWorkingDirectory()));
- terminal.setKeepScreenOn(true);
- terminal.setTextSize(SizeUtils.dp2px(10));
- terminal.setTypeface(TypefaceUtils.jetbrainsMono());
-
- LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(-1, 0);
- params.weight = 1f;
-
- binding.getRoot().addView(terminal, 0, params);
- try {
- binding.virtualKeyTable.setVirtualKeysViewClient(getKeyListener());
- binding.virtualKeyTable.reload(
- new VirtualKeysInfo(
- ConstantsBridge.VIRTUAL_KEYS, "", VirtualKeysConstants.CONTROL_CHARS_ALIASES));
- } catch (JSONException e) {
- LOG.error("Unable to parse terminal virtual keys json data", e);
- }
- }
-
- @NonNull
- private String getWorkingDirectory() {
- final Bundle extras = getIntent().getExtras();
- if (extras != null && extras.containsKey(KEY_WORKING_DIRECTORY)) {
- String directory = extras.getString(KEY_WORKING_DIRECTORY, null);
- if (directory == null || directory.trim().length() <= 0) {
- directory = HOME.getAbsolutePath();
- }
- return directory;
- }
- return HOME.getAbsolutePath();
- }
-
- private KeyListener getKeyListener() {
- return listener == null ? listener = new KeyListener(terminal) : listener;
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- setTerminalCursorBlinkingState(true);
- }
-
- @Override
- protected void onStart() {
- super.onStart();
- isVisible = true;
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- isVisible = false;
- setTerminalCursorBlinkingState(false);
- }
-
- public boolean isVisible() {
- return isVisible;
- }
-
- private void setTerminalCursorBlinkingState(boolean start) {
- if (terminal != null && terminal.mEmulator != null) {
- terminal.setTerminalCursorBlinkerState(start, true);
- }
- }
-
- private TerminalSession createSession(final String workingDirectory) {
- final Map envs = getEnvironment();
- final String[] env = new String[envs.size()];
- int i = 0;
- for (Map.Entry entry : envs.entrySet()) {
- env[i] = entry.getKey() + "=" + entry.getValue();
- i++;
- }
-
- session =
- new TerminalSession(
- getShellPath(), // Shell command
- workingDirectory, // Working directory
- new String[] {}, // Arguments
- env, // Environment variables
- TerminalEmulator.DEFAULT_TERMINAL_TRANSCRIPT_ROWS, // Transcript rows
- client // TerminalSessionClient
- );
-
- try {
- final var file = new File(SYSROOT, "etc/apt/sources.list");
- final var out = new FileOutputStream(file);
- out.write(SOURCES_LIST_CONTENT);
- out.flush();
- out.close();
- } catch (Throwable th) {
- LOG.error("Unable to update sources.list", th);
- }
-
- return session;
- }
-
- @NonNull
- private String getShellPath() {
- final var useSystemShell =
- getApp().getPrefManager().getBoolean(PreferenceManager.KEY_TERMINAL_USE_SYSTEM_SHELL);
- if (!useSystemShell && LOGIN_SHELL.exists() && LOGIN_SHELL.isFile()) {
- return LOGIN_SHELL.getAbsolutePath();
- }
-
- if (!useSystemShell) {
- LOG.error(
- "Default shell does not exist. Falling back to '/system/bin/sh'.",
- "This should not happen in normal circumstances.");
- }
-
- return "/system/bin/sh";
- }
-
- private static final class KeyListener implements VirtualKeysView.IVirtualKeysView {
-
- private final TerminalView terminal;
-
- public KeyListener(TerminalView terminal) {
- this.terminal = terminal;
- }
-
- @Override
- public void onVirtualKeyButtonClick(View view, VirtualKeyButton buttonInfo, Button button) {
- if (terminal == null) {
- return;
- }
- if (buttonInfo.isMacro()) {
- String[] keys = buttonInfo.getKey().split(" ");
- boolean ctrlDown = false;
- boolean altDown = false;
- boolean shiftDown = false;
- boolean fnDown = false;
- for (String key : keys) {
- if (SpecialButton.CTRL.getKey().equals(key)) {
- ctrlDown = true;
- } else if (SpecialButton.ALT.getKey().equals(key)) {
- altDown = true;
- } else if (SpecialButton.SHIFT.getKey().equals(key)) {
- shiftDown = true;
- } else if (SpecialButton.FN.getKey().equals(key)) {
- fnDown = true;
- } else {
- onTerminalExtraKeyButtonClick(key, ctrlDown, altDown, shiftDown, fnDown);
- ctrlDown = false;
- altDown = false;
- shiftDown = false;
- fnDown = false;
- }
- }
- } else {
- onTerminalExtraKeyButtonClick(buttonInfo.getKey(), false, false, false, false);
- }
- }
-
- protected void onTerminalExtraKeyButtonClick(
- String key, boolean ctrlDown, boolean altDown, boolean shiftDown, boolean fnDown) {
- if (VirtualKeysConstants.PRIMARY_KEY_CODES_FOR_STRINGS.containsKey(key)) {
- Integer keyCode = VirtualKeysConstants.PRIMARY_KEY_CODES_FOR_STRINGS.get(key);
- if (keyCode == null) {
- return;
- }
- int metaState = 0;
- if (ctrlDown) {
- metaState |= KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON;
- }
- if (altDown) {
- metaState |= KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON;
- }
- if (shiftDown) {
- metaState |= KeyEvent.META_SHIFT_ON | KeyEvent.META_SHIFT_LEFT_ON;
- }
- if (fnDown) {
- metaState |= KeyEvent.META_FUNCTION_ON;
+ KeyEvent keyEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP, keyCode, 0, metaState);
+ terminal.onKeyDown(keyCode, keyEvent);
+ } else {
+ // not a control char
+ for (int off = 0; off < key.length(); ) {
+ int codePoint = key.codePointAt(off);
+ terminal.inputCodePoint(codePoint, ctrlDown, altDown);
+ off += Character.charCount(codePoint);
+ }
+ }
}
- KeyEvent keyEvent = new KeyEvent(0, 0, KeyEvent.ACTION_UP, keyCode, 0, metaState);
- terminal.onKeyDown(keyCode, keyEvent);
- } else {
- // not a control char
- for (int off = 0; off < key.length(); ) {
- int codePoint = key.codePointAt(off);
- terminal.inputCodePoint(codePoint, ctrlDown, altDown);
- off += Character.charCount(codePoint);
+ @Override
+ public boolean performVirtualKeyButtonHapticFeedback(
+ View view, VirtualKeyButton buttonInfo, Button button) {
+ // No need to handle this
+ // VirtualKeysView will take care of performing haptic feedback
+ return false;
}
- }
}
- @Override
- public boolean performVirtualKeyButtonHapticFeedback(
- View view, VirtualKeyButton buttonInfo, Button button) {
- // No need to handle this
- // VirtualKeysView will take care of performing haptic feedback
- return false;
+ private final class Client implements TerminalViewClient, TerminalSessionClient {
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent e) {
+ return false;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent e, TerminalSession session) {
+ if (keyCode == KeyEvent.KEYCODE_ENTER && !session.isRunning()) {
+ finish();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onCodePoint(int codePoint, boolean ctrlDown, TerminalSession session) {
+ return false;
+ }
+
+ @Override
+ public void onTextChanged(TerminalSession changedSession) {
+ terminal.onScreenUpdated();
+ }
+
+ @Override
+ public void onTitleChanged(TerminalSession changedSession) {}
+
+ @Override
+ public void onSessionFinished(TerminalSession finishedSession) {
+ finish();
+ }
+
+ @Override
+ public void onCopyTextToClipboard(TerminalSession session, String text) {
+ ClipboardUtils.copyText("AndroidIDE Terminal", text);
+ }
+
+ @Override
+ public void onPasteTextFromClipboard(TerminalSession session) {
+ String clip = ClipboardUtils.getText().toString();
+ if (clip.trim().length() > 0 && terminal != null && terminal.mEmulator != null) {
+ terminal.mEmulator.paste(clip);
+ }
+ }
+
+ @Override
+ public void onBell(TerminalSession session) {}
+
+ @Override
+ public void onColorsChanged(TerminalSession session) {}
+
+ @Override
+ public void onTerminalCursorStateChange(boolean state) {}
+
+ @Override
+ public Integer getTerminalCursorStyle() {
+ return TerminalEmulator.DEFAULT_TERMINAL_CURSOR_STYLE;
+ }
+
+ @Override
+ public float onScale(float scale) {
+ return 1.0f;
+ }
+
+ @Override
+ public void onSingleTapUp(MotionEvent e) {
+ KeyboardUtils.showSoftInput(terminal);
+ }
+
+ @Override
+ public boolean shouldBackButtonBeMappedToEscape() {
+ return false;
+ }
+
+ @Override
+ public boolean shouldEnforceCharBasedInput() {
+ return true;
+ }
+
+ @Override
+ public boolean shouldUseCtrlSpaceWorkaround() {
+ return false;
+ }
+
+ @Override
+ public boolean isTerminalViewSelected() {
+ return true;
+ }
+
+ @Override
+ public void copyModeChanged(boolean copyMode) {}
+
+ @Override
+ public boolean onLongPress(MotionEvent event) {
+ return false;
+ }
+
+ @Override
+ public boolean readControlKey() {
+ Boolean state = binding.virtualKeyTable.readSpecialButton(SpecialButton.CTRL, true);
+ return state != null && state;
+ }
+
+ @Override
+ public boolean readAltKey() {
+ Boolean state = binding.virtualKeyTable.readSpecialButton(SpecialButton.ALT, true);
+ return state != null && state;
+ }
+
+ @Override
+ public boolean readFnKey() {
+ return false;
+ }
+
+ @Override
+ public boolean readShiftKey() {
+ return false;
+ }
+
+ @Override
+ public void onEmulatorSet() {
+ setTerminalCursorBlinkingState(true);
+
+ if (session != null) {
+ binding.getRoot()
+ .setBackgroundColor(
+ session.getEmulator()
+ .mColors
+ .mCurrentColors[TextStyle.COLOR_INDEX_BACKGROUND]);
+ }
+ }
+
+ @Override
+ public void logError(String tag, String message) {
+ // No logs
+ }
+
+ @Override
+ public void logWarn(String tag, String message) {}
+
+ @Override
+ public void logInfo(String tag, String message) {}
+
+ @Override
+ public void logDebug(String tag, String message) {}
+
+ @Override
+ public void logVerbose(String tag, String message) {}
+
+ @Override
+ public void logStackTraceWithMessage(String tag, String message, Exception e) {}
+
+ @Override
+ public void logStackTrace(String tag, Exception e) {}
}
- }
-
- private final class Client implements TerminalViewClient, TerminalSessionClient {
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent e) {
- return false;
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent e, TerminalSession session) {
- if (keyCode == KeyEvent.KEYCODE_ENTER && !session.isRunning()) {
- finish();
- return true;
- }
- return false;
- }
-
- @Override
- public boolean onCodePoint(int codePoint, boolean ctrlDown, TerminalSession session) {
- return false;
- }
-
- @Override
- public void onTextChanged(TerminalSession changedSession) {
- terminal.onScreenUpdated();
- }
-
- @Override
- public void onTitleChanged(TerminalSession changedSession) {}
-
- @Override
- public void onSessionFinished(TerminalSession finishedSession) {
- finish();
- }
-
- @Override
- public void onCopyTextToClipboard(TerminalSession session, String text) {
- ClipboardUtils.copyText("AndroidIDE Terminal", text);
- }
-
- @Override
- public void onPasteTextFromClipboard(TerminalSession session) {
- String clip = ClipboardUtils.getText().toString();
- if (clip.trim().length() > 0 && terminal != null && terminal.mEmulator != null) {
- terminal.mEmulator.paste(clip);
- }
- }
-
- @Override
- public void onBell(TerminalSession session) {}
-
- @Override
- public void onColorsChanged(TerminalSession session) {}
-
- @Override
- public void onTerminalCursorStateChange(boolean state) {}
-
- @Override
- public Integer getTerminalCursorStyle() {
- return TerminalEmulator.DEFAULT_TERMINAL_CURSOR_STYLE;
- }
-
- @Override
- public float onScale(float scale) {
- return 1.0f;
- }
-
- @Override
- public void onSingleTapUp(MotionEvent e) {
- KeyboardUtils.showSoftInput(terminal);
- }
-
- @Override
- public boolean shouldBackButtonBeMappedToEscape() {
- return false;
- }
-
- @Override
- public boolean shouldEnforceCharBasedInput() {
- return true;
- }
-
- @Override
- public boolean shouldUseCtrlSpaceWorkaround() {
- return false;
- }
-
- @Override
- public boolean isTerminalViewSelected() {
- return true;
- }
-
- @Override
- public void copyModeChanged(boolean copyMode) {}
-
- @Override
- public boolean onLongPress(MotionEvent event) {
- return false;
- }
-
- @Override
- public boolean readControlKey() {
- Boolean state = binding.virtualKeyTable.readSpecialButton(SpecialButton.CTRL, true);
- return state != null && state;
- }
-
- @Override
- public boolean readAltKey() {
- Boolean state = binding.virtualKeyTable.readSpecialButton(SpecialButton.ALT, true);
- return state != null && state;
- }
-
- @Override
- public boolean readFnKey() {
- return false;
- }
-
- @Override
- public boolean readShiftKey() {
- return false;
- }
-
- @Override
- public void onEmulatorSet() {
- setTerminalCursorBlinkingState(true);
-
- if (session != null) {
- binding
- .getRoot()
- .setBackgroundColor(
- session.getEmulator().mColors.mCurrentColors[TextStyle.COLOR_INDEX_BACKGROUND]);
- }
- }
-
- @Override
- public void logError(String tag, String message) {
- // No logs
- }
-
- @Override
- public void logWarn(String tag, String message) {}
-
- @Override
- public void logInfo(String tag, String message) {}
-
- @Override
- public void logDebug(String tag, String message) {}
-
- @Override
- public void logVerbose(String tag, String message) {}
-
- @Override
- public void logStackTraceWithMessage(String tag, String message, Exception e) {}
-
- @Override
- public void logStackTrace(String tag, Exception e) {}
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/AttrListAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/AttrListAdapter.java
index ab1ac9b49..de3f2c998 100644
--- a/app/src/main/java/com/itsaky/androidide/adapters/AttrListAdapter.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/AttrListAdapter.java
@@ -18,8 +18,10 @@
package com.itsaky.androidide.adapters;
import androidx.annotation.NonNull;
+
import com.itsaky.androidide.R;
import com.itsaky.attrinfo.models.Attr;
+
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
@@ -32,46 +34,48 @@ import java.util.function.Consumer;
*/
public class AttrListAdapter extends IconTextAdapter {
- private final Consumer clickConsumer;
- private final List attrs;
+ private final Consumer clickConsumer;
+ private final List attrs;
- public AttrListAdapter(Consumer clickConsumer, List attrs) {
- this.clickConsumer = clickConsumer;
- if (attrs == null) {
- attrs = new ArrayList<>();
+ public AttrListAdapter(Consumer clickConsumer, List attrs) {
+ this.clickConsumer = clickConsumer;
+ if (attrs == null) {
+ attrs = new ArrayList<>();
+ }
+
+ this.attrs = attrs;
}
- this.attrs = attrs;
- }
-
- @NonNull
- @Override
- public Attr getItemAt(int index) {
- return attrs.get(index);
- }
-
- @Override
- public int getIconResource(int index) {
- return R.drawable.ic_xml_attribute;
- }
-
- @NonNull
- @Override
- public String getItemText(int index) {
- return getItemAt(index).name;
- }
-
- @Override
- public int getItemCount() {
- return attrs.size();
- }
-
- @Override
- public void onBindViewHolder(@NonNull VH holder, int position) {
- super.onBindViewHolder(holder, position);
-
- if (clickConsumer != null) {
- holder.binding.getRoot().setOnClickListener(v -> clickConsumer.accept(getItemAt(position)));
+ @NonNull
+ @Override
+ public Attr getItemAt(int index) {
+ return attrs.get(index);
+ }
+
+ @Override
+ public int getIconResource(int index) {
+ return R.drawable.ic_xml_attribute;
+ }
+
+ @NonNull
+ @Override
+ public String getItemText(int index) {
+ return getItemAt(index).name;
+ }
+
+ @Override
+ public int getItemCount() {
+ return attrs.size();
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull VH holder, int position) {
+ super.onBindViewHolder(holder, position);
+
+ if (clickConsumer != null) {
+ holder.binding
+ .getRoot()
+ .setOnClickListener(v -> clickConsumer.accept(getItemAt(position)));
+ }
}
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/CompletionListAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/CompletionListAdapter.java
index 1df41f5e6..e1f69cd91 100755
--- a/app/src/main/java/com/itsaky/androidide/adapters/CompletionListAdapter.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/CompletionListAdapter.java
@@ -24,7 +24,9 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
+
import androidx.core.content.ContextCompat;
+
import com.blankj.utilcode.util.ThreadUtils;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
@@ -42,167 +44,181 @@ import com.itsaky.apiinfo.models.Info;
import com.itsaky.apiinfo.models.MethodInfo;
import com.itsaky.lsp.models.CompletionItem;
import com.itsaky.lsp.models.CompletionItemKind;
+
import io.github.rosemoe.sora.widget.component.EditorCompletionAdapter;
public class CompletionListAdapter extends EditorCompletionAdapter {
- private static final Logger LOG = Logger.instance("CompletionListAdapter");
+ private static final Logger LOG = Logger.instance("CompletionListAdapter");
- @Override
- public int getItemHeight() {
- return (int)
- TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP, 40, Resources.getSystem().getDisplayMetrics());
- }
-
- @Override
- protected View getView(
- int position, View convertView, ViewGroup parent, boolean isCurrentCursorPosition) {
- final LayoutCompletionItemBinding binding =
- LayoutCompletionItemBinding.inflate(LayoutInflater.from(getContext()), parent, false);
-
- var item = (CompletionItem) getItem(position);
- String label = item.getLabel(), desc = item.getDetail(), type = item.getKind().toString();
- String header = type.length() <= 0 ? "O" : String.valueOf(type.charAt(0));
-
- binding.completionIconText.setText(header);
- binding.completionLabel.setText(label);
- binding.completionType.setText(type);
- binding.completionDetail.setText(desc);
- binding.completionIconText.setTypeface(TypefaceUtils.jetbrainsMono(), Typeface.BOLD);
- if (desc.isEmpty()) {
- binding.completionDetail.setVisibility(View.GONE);
+ @Override
+ public int getItemHeight() {
+ return (int)
+ TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, 40, Resources.getSystem().getDisplayMetrics());
}
- if (isCurrentCursorPosition) {
- binding
- .getRoot()
- .setBackgroundColor(
- ContextCompat.getColor(getContext(), R.color.completionList_backgroundSelected));
- } else {
- binding
- .getRoot()
- .setBackgroundColor(
- ContextCompat.getColor(getContext(), R.color.completionList_background));
+ @Override
+ protected View getView(
+ int position, View convertView, ViewGroup parent, boolean isCurrentCursorPosition) {
+ final LayoutCompletionItemBinding binding =
+ LayoutCompletionItemBinding.inflate(
+ LayoutInflater.from(getContext()), parent, false);
+
+ var item = (CompletionItem) getItem(position);
+ String label = item.getLabel(), desc = item.getDetail(), type = item.getKind().toString();
+ String header = type.length() <= 0 ? "O" : String.valueOf(type.charAt(0));
+
+ binding.completionIconText.setText(header);
+ binding.completionLabel.setText(label);
+ binding.completionType.setText(type);
+ binding.completionDetail.setText(desc);
+ binding.completionIconText.setTypeface(TypefaceUtils.jetbrainsMono(), Typeface.BOLD);
+ if (desc.isEmpty()) {
+ binding.completionDetail.setVisibility(View.GONE);
+ }
+
+ if (isCurrentCursorPosition) {
+ binding.getRoot()
+ .setBackgroundColor(
+ ContextCompat.getColor(
+ getContext(), R.color.completionList_backgroundSelected));
+ } else {
+ binding.getRoot()
+ .setBackgroundColor(
+ ContextCompat.getColor(
+ getContext(), R.color.completionList_background));
+ }
+
+ binding.completionApiInfo.setVisibility(View.GONE);
+
+ showApiInfoIfNeeded(item, binding.completionApiInfo);
+
+ return binding.getRoot();
}
- binding.completionApiInfo.setVisibility(View.GONE);
+ private void showApiInfoIfNeeded(final CompletionItem item, final TextView completionApiInfo) {
- showApiInfoIfNeeded(item, binding.completionApiInfo);
+ new Thread(
+ () -> {
+ final ApiInfo apiInfo = StudioApp.getInstance().apiInfo();
+ boolean hasRead = apiInfo != null && apiInfo.hasRead();
+ boolean isValid = isValidForApiVersion(item);
- return binding.getRoot();
- }
+ if (hasRead && isValid) {
- private void showApiInfoIfNeeded(final CompletionItem item, final TextView completionApiInfo) {
+ JsonElement element = new Gson().toJsonTree(item.getData());
+ if (element == null || !element.isJsonObject()) return;
+ JsonObject data = element.getAsJsonObject();
+ if (!data.has("className")) return;
- new Thread(
- () -> {
- final ApiInfo apiInfo = StudioApp.getInstance().apiInfo();
- boolean hasRead = apiInfo != null && apiInfo.hasRead();
- boolean isValid = isValidForApiVersion(item);
+ final String className = data.get("className").getAsString();
+ CompletionItemKind kind = item.getKind();
- if (hasRead && isValid) {
+ ClassInfo clazz = apiInfo.getClassByName(className);
+ if (clazz == null) return;
- JsonElement element = new Gson().toJsonTree(item.getData());
- if (element == null || !element.isJsonObject()) return;
- JsonObject data = element.getAsJsonObject();
- if (!data.has("className")) return;
+ Info info = clazz;
- final String className = data.get("className").getAsString();
- CompletionItemKind kind = item.getKind();
+ // If this Info is not a class info, find the right member
+ if (kind == CompletionItemKind.METHOD
+ && data.has("erasedParameterTypes")
+ && data.has("memberName")) {
+ JsonElement erasedParameterTypesElement =
+ data.get("erasedParameterTypes");
+ if (erasedParameterTypesElement.isJsonArray()) {
+ String simpleName = data.get("memberName").getAsString();
+ JsonArray erasedParameterTypes =
+ erasedParameterTypesElement.getAsJsonArray();
+ String[] paramTypes =
+ new String[erasedParameterTypes.size()];
+ for (int i = 0; i < erasedParameterTypes.size(); i++) {
+ paramTypes[i] =
+ erasedParameterTypes.get(i).getAsString();
+ }
- ClassInfo clazz = apiInfo.getClassByName(className);
- if (clazz == null) return;
+ MethodInfo method = clazz.getMethod(simpleName, paramTypes);
- Info info = clazz;
+ if (method != null) {
+ info = method;
+ }
+ }
+ } else if (kind == CompletionItemKind.FIELD
+ && data.has("memberName")) {
+ String simpleName = data.get("memberName").getAsString();
+ FieldInfo field = clazz.getFieldByName(simpleName);
- // If this Info is not a class info, find the right member
- if (kind == CompletionItemKind.METHOD
- && data.has("erasedParameterTypes")
- && data.has("memberName")) {
- JsonElement erasedParameterTypesElement = data.get("erasedParameterTypes");
- if (erasedParameterTypesElement.isJsonArray()) {
- String simpleName = data.get("memberName").getAsString();
- JsonArray erasedParameterTypes = erasedParameterTypesElement.getAsJsonArray();
- String[] paramTypes = new String[erasedParameterTypes.size()];
- for (int i = 0; i < erasedParameterTypes.size(); i++) {
- paramTypes[i] = erasedParameterTypes.get(i).getAsString();
- }
+ if (field != null) {
+ info = field;
+ }
+ }
- MethodInfo method = clazz.getMethod(simpleName, paramTypes);
+ final StringBuilder infoBuilder = new StringBuilder();
+ if (info.since > 1) {
+ infoBuilder.append(
+ completionApiInfo
+ .getContext()
+ .getString(
+ R.string.msg_api_info_since,
+ info.since));
+ infoBuilder.append("\n");
+ }
- if (method != null) {
- info = method;
- }
- }
- } else if (kind == CompletionItemKind.FIELD && data.has("memberName")) {
- String simpleName = data.get("memberName").getAsString();
- FieldInfo field = clazz.getFieldByName(simpleName);
+ if (info.removed > 0) {
+ infoBuilder.append(
+ completionApiInfo
+ .getContext()
+ .getString(
+ R.string.msg_api_info_removed,
+ info.removed));
+ infoBuilder.append("\n");
+ }
- if (field != null) {
- info = field;
- }
- }
+ if (info.deprecated > 0) {
+ infoBuilder.append(
+ completionApiInfo
+ .getContext()
+ .getString(
+ R.string.msg_api_info_deprecated,
+ info.deprecated));
+ infoBuilder.append("\n");
+ }
- final StringBuilder infoBuilder = new StringBuilder();
- if (info.since > 1) {
- infoBuilder.append(
- completionApiInfo
- .getContext()
- .getString(R.string.msg_api_info_since, info.since));
- infoBuilder.append("\n");
- }
-
- if (info.removed > 0) {
- infoBuilder.append(
- completionApiInfo
- .getContext()
- .getString(R.string.msg_api_info_removed, info.removed));
- infoBuilder.append("\n");
- }
-
- if (info.deprecated > 0) {
- infoBuilder.append(
- completionApiInfo
- .getContext()
- .getString(R.string.msg_api_info_deprecated, info.deprecated));
- infoBuilder.append("\n");
- }
-
- ThreadUtils.runOnUiThread(
- () -> {
- if (infoBuilder.toString().trim().length() > 0) {
- completionApiInfo.setText(infoBuilder.toString().trim());
- completionApiInfo.setVisibility(View.VISIBLE);
- } else completionApiInfo.setVisibility(View.GONE);
- });
- }
- })
- .start();
- }
-
- private boolean isValidForApiVersion(CompletionItem item) {
- if (item == null) return false;
- final CompletionItemKind type = item.getKind();
- JsonElement element = new Gson().toJsonTree(item.getData());
- if (
-
- // These represent a class type
- (type == CompletionItemKind.CLASS
- || type == CompletionItemKind.INTERFACE
- || type == CompletionItemKind.ENUM
-
- // These represent a method type
- || type == CompletionItemKind.METHOD
- || type == CompletionItemKind.CONSTRUCTOR
-
- // A field type
- || type == CompletionItemKind.FIELD)
- && element != null
- && element.isJsonObject()) {
- JsonObject data = element.getAsJsonObject();
- return data.has("className");
+ ThreadUtils.runOnUiThread(
+ () -> {
+ if (infoBuilder.toString().trim().length() > 0) {
+ completionApiInfo.setText(
+ infoBuilder.toString().trim());
+ completionApiInfo.setVisibility(View.VISIBLE);
+ } else completionApiInfo.setVisibility(View.GONE);
+ });
+ }
+ })
+ .start();
+ }
+
+ private boolean isValidForApiVersion(CompletionItem item) {
+ if (item == null) return false;
+ final CompletionItemKind type = item.getKind();
+ JsonElement element = new Gson().toJsonTree(item.getData());
+ if (
+
+ // These represent a class type
+ (type == CompletionItemKind.CLASS
+ || type == CompletionItemKind.INTERFACE
+ || type == CompletionItemKind.ENUM
+
+ // These represent a method type
+ || type == CompletionItemKind.METHOD
+ || type == CompletionItemKind.CONSTRUCTOR
+
+ // A field type
+ || type == CompletionItemKind.FIELD)
+ && element != null
+ && element.isJsonObject()) {
+ JsonObject data = element.getAsJsonObject();
+ return data.has("className");
+ }
+ return false;
}
- return false;
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticItemAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticItemAdapter.java
index ea21d644f..d38bc038f 100644
--- a/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticItemAdapter.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticItemAdapter.java
@@ -20,79 +20,82 @@ package com.itsaky.androidide.adapters;
import android.graphics.PorterDuff;
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.LayoutDiagnosticItemBinding;
import com.itsaky.androidide.interfaces.DiagnosticClickListener;
import com.itsaky.lsp.models.DiagnosticItem;
import com.itsaky.lsp.models.DiagnosticSeverity;
+
import java.io.File;
import java.util.List;
public class DiagnosticItemAdapter extends RecyclerView.Adapter {
- private final List diags;
- private final File file;
- private final DiagnosticClickListener listener;
+ private final List diags;
+ private final File file;
+ private final DiagnosticClickListener listener;
- public DiagnosticItemAdapter(
- List diags, File file, DiagnosticClickListener listener) {
- this.diags = diags;
- this.file = file;
- this.listener = listener;
- }
-
- @NonNull
- @Override
- public DiagnosticItemAdapter.VH onCreateViewHolder(@NonNull ViewGroup p1, int p2) {
- return new VH(
- LayoutDiagnosticItemBinding.inflate(LayoutInflater.from(p1.getContext()), p1, false));
- }
-
- @Override
- public void onBindViewHolder(DiagnosticItemAdapter.VH p1, int p2) {
- final DiagnosticItem diagnostic = diags.get(p2);
- final LayoutDiagnosticItemBinding binding = p1.binding;
-
- binding.icon.setImageResource(getDiagnosticIconId(diagnostic));
- binding.icon.setColorFilter(
- ContextCompat.getColor(
- binding.icon.getContext(),
- diagnostic.getSeverity() == DiagnosticSeverity.ERROR
- ? R.color.diagnostic_error
- : R.color.diagnostic_warning),
- PorterDuff.Mode.SRC_ATOP);
- binding.title.setText(diagnostic.getMessage());
-
- binding
- .getRoot()
- .setOnClickListener(
- v -> {
- if (listener != null) {
- listener.onDiagnosticClick(file, diagnostic);
- }
- });
- }
-
- @Override
- public int getItemCount() {
- return diags.size();
- }
-
- private int getDiagnosticIconId(DiagnosticItem diagnostic) {
- if (diagnostic.getSeverity() == DiagnosticSeverity.ERROR)
- return R.drawable.ic_compilation_error;
- return R.drawable.ic_info;
- }
-
- public static class VH extends RecyclerView.ViewHolder {
- private LayoutDiagnosticItemBinding binding;
-
- public VH(LayoutDiagnosticItemBinding binding) {
- super(binding.getRoot());
- this.binding = binding;
+ public DiagnosticItemAdapter(
+ List diags, File file, DiagnosticClickListener listener) {
+ this.diags = diags;
+ this.file = file;
+ this.listener = listener;
+ }
+
+ @NonNull
+ @Override
+ public DiagnosticItemAdapter.VH onCreateViewHolder(@NonNull ViewGroup p1, int p2) {
+ return new VH(
+ LayoutDiagnosticItemBinding.inflate(
+ LayoutInflater.from(p1.getContext()), p1, false));
+ }
+
+ @Override
+ public void onBindViewHolder(DiagnosticItemAdapter.VH p1, int p2) {
+ final DiagnosticItem diagnostic = diags.get(p2);
+ final LayoutDiagnosticItemBinding binding = p1.binding;
+
+ binding.icon.setImageResource(getDiagnosticIconId(diagnostic));
+ binding.icon.setColorFilter(
+ ContextCompat.getColor(
+ binding.icon.getContext(),
+ diagnostic.getSeverity() == DiagnosticSeverity.ERROR
+ ? R.color.diagnostic_error
+ : R.color.diagnostic_warning),
+ PorterDuff.Mode.SRC_ATOP);
+ binding.title.setText(diagnostic.getMessage());
+
+ binding.getRoot()
+ .setOnClickListener(
+ v -> {
+ if (listener != null) {
+ listener.onDiagnosticClick(file, diagnostic);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return diags.size();
+ }
+
+ private int getDiagnosticIconId(DiagnosticItem diagnostic) {
+ if (diagnostic.getSeverity() == DiagnosticSeverity.ERROR)
+ return R.drawable.ic_compilation_error;
+ return R.drawable.ic_info;
+ }
+
+ public static class VH extends RecyclerView.ViewHolder {
+ private LayoutDiagnosticItemBinding binding;
+
+ public VH(LayoutDiagnosticItemBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
}
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticsAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticsAdapter.java
index ad2cc5ac4..fa1a06c41 100755
--- a/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticsAdapter.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/DiagnosticsAdapter.java
@@ -23,68 +23,72 @@ package com.itsaky.androidide.adapters;
import android.graphics.PorterDuff;
import android.view.LayoutInflater;
import android.view.ViewGroup;
+
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+
import com.itsaky.androidide.R;
import com.itsaky.androidide.databinding.LayoutDiagnosticGroupBinding;
import com.itsaky.androidide.interfaces.DiagnosticClickListener;
import com.itsaky.androidide.models.DiagnosticGroup;
+
import java.util.List;
public class DiagnosticsAdapter extends RecyclerView.Adapter {
- private final List diags;
- private final DiagnosticClickListener listener;
+ private final List diags;
+ private final DiagnosticClickListener listener;
- public DiagnosticsAdapter(List diags, DiagnosticClickListener listener) {
- this.diags = diags;
- this.listener = listener;
- }
-
- @Override
- public DiagnosticsAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
- return new VH(
- LayoutDiagnosticGroupBinding.inflate(LayoutInflater.from(p1.getContext()), p1, false));
- }
-
- @Override
- public void onBindViewHolder(DiagnosticsAdapter.VH p1, int p2) {
- final DiagnosticGroup group = diags.get(p2);
- final LayoutDiagnosticGroupBinding binding = p1.binding;
-
- final int color =
- ContextCompat.getColor(binding.info.icon.getContext(), R.color.secondaryColor);
- binding.info.icon.setImageResource(group.icon);
- binding.info.icon.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
- binding.info.title.setText(group.text);
- binding.info.title.setTextColor(color);
- binding.diagnostics.setLayoutManager(new LinearLayoutManager(binding.diagnostics.getContext()));
- binding.diagnostics.setAdapter(
- new DiagnosticItemAdapter(group.diagnostics, group.file, listener));
-
- binding
- .info
- .getRoot()
- .setOnClickListener(
- v -> {
- if (listener != null) {
- listener.onGroupClick(group);
- }
- });
- }
-
- @Override
- public int getItemCount() {
- return diags.size();
- }
-
- public class VH extends RecyclerView.ViewHolder {
- private LayoutDiagnosticGroupBinding binding;
-
- public VH(LayoutDiagnosticGroupBinding binding) {
- super(binding.getRoot());
- this.binding = binding;
+ public DiagnosticsAdapter(List diags, DiagnosticClickListener listener) {
+ this.diags = diags;
+ this.listener = listener;
+ }
+
+ @Override
+ public DiagnosticsAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
+ return new VH(
+ LayoutDiagnosticGroupBinding.inflate(
+ LayoutInflater.from(p1.getContext()), p1, false));
+ }
+
+ @Override
+ public void onBindViewHolder(DiagnosticsAdapter.VH p1, int p2) {
+ final DiagnosticGroup group = diags.get(p2);
+ final LayoutDiagnosticGroupBinding binding = p1.binding;
+
+ final int color =
+ ContextCompat.getColor(binding.info.icon.getContext(), R.color.secondaryColor);
+ binding.info.icon.setImageResource(group.icon);
+ binding.info.icon.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+ binding.info.title.setText(group.text);
+ binding.info.title.setTextColor(color);
+ binding.diagnostics.setLayoutManager(
+ new LinearLayoutManager(binding.diagnostics.getContext()));
+ binding.diagnostics.setAdapter(
+ new DiagnosticItemAdapter(group.diagnostics, group.file, listener));
+
+ binding.info
+ .getRoot()
+ .setOnClickListener(
+ v -> {
+ if (listener != null) {
+ listener.onGroupClick(group);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return diags.size();
+ }
+
+ public class VH extends RecyclerView.ViewHolder {
+ private LayoutDiagnosticGroupBinding binding;
+
+ public VH(LayoutDiagnosticGroupBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
}
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/EditorBottomSheetTabAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/EditorBottomSheetTabAdapter.java
index bbc662848..98fb6595f 100644
--- a/app/src/main/java/com/itsaky/androidide/adapters/EditorBottomSheetTabAdapter.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/EditorBottomSheetTabAdapter.java
@@ -22,105 +22,112 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
+
import com.itsaky.androidide.R;
import com.itsaky.androidide.fragments.DiagnosticsListFragment;
import com.itsaky.androidide.fragments.IDELogFragment;
import com.itsaky.androidide.fragments.LogViewFragment;
import com.itsaky.androidide.fragments.SearchResultFragment;
import com.itsaky.androidide.fragments.SimpleOutputFragment;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class EditorBottomSheetTabAdapter extends FragmentStateAdapter {
- private final List fragments;
+ private final List fragments;
- public EditorBottomSheetTabAdapter(@NonNull FragmentActivity fragmentActivity) {
- super(fragmentActivity);
+ public EditorBottomSheetTabAdapter(@NonNull FragmentActivity fragmentActivity) {
+ super(fragmentActivity);
- this.fragments = new ArrayList<>();
- this.fragments.add(
- new Tab(fragmentActivity.getString(R.string.build_output), new SimpleOutputFragment()));
- this.fragments.add(
- new Tab(fragmentActivity.getString(R.string.app_logs), new LogViewFragment()));
- this.fragments.add(
- new Tab(fragmentActivity.getString(R.string.ide_logs), new IDELogFragment()));
- this.fragments.add(
- new Tab(fragmentActivity.getString(R.string.view_diags), new DiagnosticsListFragment()));
- this.fragments.add(
- new Tab(
- fragmentActivity.getString(R.string.view_search_results), new SearchResultFragment()));
- }
-
- public Fragment getFragmentAtIndex(int index) {
- return this.fragments.get(index).fragment;
- }
-
- @NonNull
- @Override
- public Fragment createFragment(int position) {
- return this.fragments.get(position).fragment;
- }
-
- @Override
- public int getItemCount() {
- return fragments.size();
- }
-
- public String getTitle(int position) {
- return fragments.get(position).title;
- }
-
- @NonNull
- public SimpleOutputFragment getBuildOutputFragment() {
- return Objects.requireNonNull(findFragmentByClass(SimpleOutputFragment.class));
- }
-
- @NonNull
- public LogViewFragment getLogFragment() {
- return Objects.requireNonNull(findFragmentByClass(LogViewFragment.class));
- }
-
- @NonNull
- public DiagnosticsListFragment getDiagnosticsFragment() {
- return Objects.requireNonNull(findFragmentByClass(DiagnosticsListFragment.class));
- }
-
- @NonNull
- public SearchResultFragment getSearchResultFragment() {
- return Objects.requireNonNull(findFragmentByClass(SearchResultFragment.class));
- }
-
- @Nullable
- private T findFragmentByClass(Class clazz) {
- for (final var tab : this.fragments) {
- if (tab.fragment.getClass() == clazz) {
- return (T) tab.fragment;
- }
- }
- return null;
- }
-
- public int findIndexOfFragmentByClass(Class tClass) {
- for (int i = 0; i < this.fragments.size(); i++) {
- final var tab = this.fragments.get(i);
- if (tab.fragment.getClass() == tClass) {
- return i;
- }
+ this.fragments = new ArrayList<>();
+ this.fragments.add(
+ new Tab(
+ fragmentActivity.getString(R.string.build_output),
+ new SimpleOutputFragment()));
+ this.fragments.add(
+ new Tab(fragmentActivity.getString(R.string.app_logs), new LogViewFragment()));
+ this.fragments.add(
+ new Tab(fragmentActivity.getString(R.string.ide_logs), new IDELogFragment()));
+ this.fragments.add(
+ new Tab(
+ fragmentActivity.getString(R.string.view_diags),
+ new DiagnosticsListFragment()));
+ this.fragments.add(
+ new Tab(
+ fragmentActivity.getString(R.string.view_search_results),
+ new SearchResultFragment()));
}
- return -1;
- }
-
- static class Tab {
-
- public Tab(String title, Fragment fragment) {
- this.title = title;
- this.fragment = fragment;
+ public Fragment getFragmentAtIndex(int index) {
+ return this.fragments.get(index).fragment;
}
- String title;
- Fragment fragment;
- }
+ @NonNull
+ @Override
+ public Fragment createFragment(int position) {
+ return this.fragments.get(position).fragment;
+ }
+
+ @Override
+ public int getItemCount() {
+ return fragments.size();
+ }
+
+ public String getTitle(int position) {
+ return fragments.get(position).title;
+ }
+
+ @NonNull
+ public SimpleOutputFragment getBuildOutputFragment() {
+ return Objects.requireNonNull(findFragmentByClass(SimpleOutputFragment.class));
+ }
+
+ @NonNull
+ public LogViewFragment getLogFragment() {
+ return Objects.requireNonNull(findFragmentByClass(LogViewFragment.class));
+ }
+
+ @NonNull
+ public DiagnosticsListFragment getDiagnosticsFragment() {
+ return Objects.requireNonNull(findFragmentByClass(DiagnosticsListFragment.class));
+ }
+
+ @NonNull
+ public SearchResultFragment getSearchResultFragment() {
+ return Objects.requireNonNull(findFragmentByClass(SearchResultFragment.class));
+ }
+
+ @Nullable
+ private T findFragmentByClass(Class clazz) {
+ for (final var tab : this.fragments) {
+ if (tab.fragment.getClass() == clazz) {
+ return (T) tab.fragment;
+ }
+ }
+ return null;
+ }
+
+ public int findIndexOfFragmentByClass(Class tClass) {
+ for (int i = 0; i < this.fragments.size(); i++) {
+ final var tab = this.fragments.get(i);
+ if (tab.fragment.getClass() == tClass) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ static class Tab {
+
+ public Tab(String title, Fragment fragment) {
+ this.title = title;
+ this.fragment = fragment;
+ }
+
+ String title;
+ Fragment fragment;
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/IconTextAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/IconTextAdapter.java
index 8f67b9483..0b1b92bde 100644
--- a/app/src/main/java/com/itsaky/androidide/adapters/IconTextAdapter.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/IconTextAdapter.java
@@ -20,8 +20,10 @@ package com.itsaky.androidide.adapters;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
+
import com.itsaky.androidide.databinding.LayoutSimpleIconTextBinding;
/**
@@ -31,86 +33,86 @@ import com.itsaky.androidide.databinding.LayoutSimpleIconTextBinding;
*/
public abstract class IconTextAdapter extends RecyclerView.Adapter {
- private OnBindListener bindListener;
+ private OnBindListener bindListener;
- public IconTextAdapter setOnBindListener(OnBindListener listener) {
- this.bindListener = listener;
- return this;
- }
-
- @NonNull
- @Override
- public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- return new VH(
- LayoutSimpleIconTextBinding.inflate(
- LayoutInflater.from(parent.getContext()), parent, false));
- }
-
- @Override
- public void onBindViewHolder(@NonNull VH holder, int position) {
- final var binding = holder.binding;
- final var item = getItemAt(position);
-
- if (this.bindListener != null && this.bindListener.onBind(item, holder, position)) {
- return;
+ public IconTextAdapter setOnBindListener(OnBindListener listener) {
+ this.bindListener = listener;
+ return this;
}
- final var icon = getIconResource(position);
-
- if (icon == -1) {
- binding.icon.setVisibility(View.GONE);
- } else {
- binding.icon.setImageResource(icon);
+ @NonNull
+ @Override
+ public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new VH(
+ LayoutSimpleIconTextBinding.inflate(
+ LayoutInflater.from(parent.getContext()), parent, false));
}
- binding.text.setText(getItemText(position));
+ @Override
+ public void onBindViewHolder(@NonNull VH holder, int position) {
+ final var binding = holder.binding;
+ final var item = getItemAt(position);
- if (this.bindListener != null) {
- this.bindListener.postBind(item, holder, position);
- }
- }
+ if (this.bindListener != null && this.bindListener.onBind(item, holder, position)) {
+ return;
+ }
- /**
- * Get the list item at the given position.
- *
- * @param index The index of the item to retrieve.
- * @return The item at the given index. Must not be null.
- */
- @NonNull
- public abstract E getItemAt(int index);
+ final var icon = getIconResource(position);
- /**
- * Get the icon resource ID of the item the given index.
- *
- * @param index The index of the item.
- * @return The icon resource id or -1 to hide the icon.
- */
- public abstract int getIconResource(int index);
+ if (icon == -1) {
+ binding.icon.setVisibility(View.GONE);
+ } else {
+ binding.icon.setImageResource(icon);
+ }
- /**
- * Get the title of the item at the given index.
- *
- * @param index The index of the item.
- * @return The title of the item.
- */
- @NonNull
- public abstract String getItemText(int index);
+ binding.text.setText(getItemText(position));
- public interface OnBindListener {
- default boolean onBind(T item, VH holder, int position) {
- return false;
+ if (this.bindListener != null) {
+ this.bindListener.postBind(item, holder, position);
+ }
}
- default void postBind(T item, VH holder, int position) {}
- }
+ /**
+ * Get the list item at the given position.
+ *
+ * @param index The index of the item to retrieve.
+ * @return The item at the given index. Must not be null.
+ */
+ @NonNull
+ public abstract E getItemAt(int index);
- public static class VH extends RecyclerView.ViewHolder {
+ /**
+ * Get the icon resource ID of the item the given index.
+ *
+ * @param index The index of the item.
+ * @return The icon resource id or -1 to hide the icon.
+ */
+ public abstract int getIconResource(int index);
- public final LayoutSimpleIconTextBinding binding;
+ /**
+ * Get the title of the item at the given index.
+ *
+ * @param index The index of the item.
+ * @return The title of the item.
+ */
+ @NonNull
+ public abstract String getItemText(int index);
- public VH(@NonNull LayoutSimpleIconTextBinding binding) {
- super(binding.getRoot());
- this.binding = binding;
+ public interface OnBindListener {
+ default boolean onBind(T item, VH holder, int position) {
+ return false;
+ }
+
+ default void postBind(T item, VH holder, int position) {}
+ }
+
+ public static class VH extends RecyclerView.ViewHolder {
+
+ public final LayoutSimpleIconTextBinding binding;
+
+ public VH(@NonNull LayoutSimpleIconTextBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
}
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/OptionsSheetAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/OptionsSheetAdapter.java
index f0cfc5e57..a3425a86d 100755
--- a/app/src/main/java/com/itsaky/androidide/adapters/OptionsSheetAdapter.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/OptionsSheetAdapter.java
@@ -22,58 +22,61 @@ package com.itsaky.androidide.adapters;
import android.view.LayoutInflater;
import android.view.ViewGroup;
+
import androidx.recyclerview.widget.RecyclerView;
+
import com.itsaky.androidide.databinding.LayoutOptionssheetItemBinding;
import com.itsaky.androidide.models.SheetOption;
+
import java.util.List;
public class OptionsSheetAdapter extends RecyclerView.Adapter {
- private OnOptionsClickListener listener;
- private List options;
+ private OnOptionsClickListener listener;
+ private List options;
- public OptionsSheetAdapter(List options, OnOptionsClickListener listener) {
- this.options = options;
- this.listener = listener;
- }
-
- @Override
- public OptionsSheetAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
- return new VH(
- LayoutOptionssheetItemBinding.inflate(LayoutInflater.from(p1.getContext()), p1, false));
- }
-
- @Override
- public void onBindViewHolder(OptionsSheetAdapter.VH p1, int p2) {
- final LayoutOptionssheetItemBinding binding = p1.binding;
- final SheetOption option = options.get(p2);
-
- binding.text.setText(option.titleRes);
- binding.icon.setImageResource(option.iconRes);
-
- binding
- .getRoot()
- .setOnClickListener(
- v -> {
- if (listener != null) listener.onOptionClick(option);
- });
- }
-
- @Override
- public int getItemCount() {
- return options.size();
- }
-
- public class VH extends RecyclerView.ViewHolder {
- private LayoutOptionssheetItemBinding binding;
-
- public VH(LayoutOptionssheetItemBinding binding) {
- super(binding.getRoot());
- this.binding = binding;
+ public OptionsSheetAdapter(List options, OnOptionsClickListener listener) {
+ this.options = options;
+ this.listener = listener;
}
- }
- public static interface OnOptionsClickListener {
- public void onOptionClick(SheetOption option);
- }
+ @Override
+ public OptionsSheetAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
+ return new VH(
+ LayoutOptionssheetItemBinding.inflate(
+ LayoutInflater.from(p1.getContext()), p1, false));
+ }
+
+ @Override
+ public void onBindViewHolder(OptionsSheetAdapter.VH p1, int p2) {
+ final LayoutOptionssheetItemBinding binding = p1.binding;
+ final SheetOption option = options.get(p2);
+
+ binding.text.setText(option.titleRes);
+ binding.icon.setImageResource(option.iconRes);
+
+ binding.getRoot()
+ .setOnClickListener(
+ v -> {
+ if (listener != null) listener.onOptionClick(option);
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return options.size();
+ }
+
+ public class VH extends RecyclerView.ViewHolder {
+ private LayoutOptionssheetItemBinding binding;
+
+ public VH(LayoutOptionssheetItemBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
+ }
+
+ public static interface OnOptionsClickListener {
+ public void onOptionClick(SheetOption option);
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/ProjectsListAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/ProjectsListAdapter.java
index 7e7801f16..3db235826 100755
--- a/app/src/main/java/com/itsaky/androidide/adapters/ProjectsListAdapter.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/ProjectsListAdapter.java
@@ -24,9 +24,11 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+
import androidx.recyclerview.widget.RecyclerView;
import androidx.transition.ChangeBounds;
import androidx.transition.TransitionManager;
+
import com.blankj.utilcode.util.FileUtils;
import com.bumptech.glide.Glide;
import com.itsaky.androidide.R;
@@ -35,131 +37,135 @@ import com.itsaky.androidide.databinding.LayoutProjectsItemBinding;
import com.itsaky.androidide.project.AndroidProject;
import com.itsaky.androidide.tasks.TaskExecutor;
import com.itsaky.toaster.Toaster;
+
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class ProjectsListAdapter extends RecyclerView.Adapter {
- private ArrayList mProjects;
- private OnProjectClickListener listener;
+ private ArrayList mProjects;
+ private OnProjectClickListener listener;
- private final Comparator PROJECTS_SORTER =
- new Comparator() {
+ private final Comparator PROJECTS_SORTER =
+ new Comparator() {
- @Override
- public int compare(AndroidProject p1, AndroidProject p2) {
- return p1.getAppName().compareTo(p2.getAppName());
+ @Override
+ public int compare(AndroidProject p1, AndroidProject p2) {
+ return p1.getAppName().compareTo(p2.getAppName());
+ }
+ };
+
+ public ProjectsListAdapter(
+ ArrayList mProjects, OnProjectClickListener listener) {
+ this.mProjects = mProjects;
+ this.listener = listener;
+ Collections.sort(this.mProjects, PROJECTS_SORTER);
+ }
+
+ @Override
+ public ProjectsListAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
+ return new VH(
+ LayoutProjectsItemBinding.inflate(LayoutInflater.from(p1.getContext()), p1, false));
+ }
+
+ @Override
+ public void onBindViewHolder(ProjectsListAdapter.VH holder, int p2) {
+ final AndroidProject project = mProjects.get(p2);
+ holder.binding.projecsitemName.setText(project.getAppName());
+ holder.binding.projecsitemPkgName.setText(project.getPackageName());
+ holder.binding.deleteMsg.setText(
+ holder.binding
+ .deleteMsg
+ .getContext()
+ .getString(R.string.msg_confirm_delete, project.getAppName()));
+ if (!TextUtils.isEmpty(project.getIconPath()))
+ Glide.with(holder.itemView.getContext().getApplicationContext())
+ .load(new File(project.getIconPath()))
+ .into(holder.binding.projecsitemIcon);
+ else holder.binding.projecsitemIcon.setImageResource(R.drawable.ic_android);
+
+ holder.binding.projectsitemClickableContainer.setOnClickListener(
+ v -> sendClickEvent(project));
+ holder.binding.projectsitemClickableContainer.setOnLongClickListener(
+ v -> holder.toggleDelete());
+ holder.binding.delete.setOnClickListener(v -> holder.showConfirmDelete());
+ holder.binding.deleteCancel.setOnClickListener(v -> holder.hideAllDelete());
+ holder.binding.deleteOk.setOnClickListener(v -> deleteProject(project, p2));
+ }
+
+ private void deleteProject(AndroidProject project, int index) {
+ index = index >= mProjects.size() ? mProjects.size() - 1 : index;
+ if (index < 0) return;
+ mProjects.remove(index);
+ notifyItemRemoved(index);
+
+ new TaskExecutor()
+ .executeAsync(
+ () -> FileUtils.delete(project.getProjectPath()),
+ __ -> {
+ StudioApp.getInstance()
+ .toast(
+ __
+ ? R.string.msg_project_deleted
+ : R.string.msg_project_delete_failed,
+ Toaster.Type.SUCCESS);
+ });
+ }
+
+ private void sendClickEvent(AndroidProject project) {
+ if (listener != null) listener.onProjectClick(project);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mProjects.size();
+ }
+
+ public class VH extends RecyclerView.ViewHolder {
+ LayoutProjectsItemBinding binding;
+
+ public VH(LayoutProjectsItemBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
}
- };
- public ProjectsListAdapter(ArrayList mProjects, OnProjectClickListener listener) {
- this.mProjects = mProjects;
- this.listener = listener;
- Collections.sort(this.mProjects, PROJECTS_SORTER);
- }
+ public void showConfirmDelete() {
+ binding.confirmDeleteContainer.setEnabled(true);
+ TransitionManager.beginDelayedTransition(
+ binding.getRoot(), new ChangeBounds().setDuration(85));
+ binding.confirmDeleteContainer.setVisibility(View.VISIBLE);
+ }
- @Override
- public ProjectsListAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
- return new VH(
- LayoutProjectsItemBinding.inflate(LayoutInflater.from(p1.getContext()), p1, false));
- }
+ public void hideAllDelete() {
+ hideDelete();
+ hideConfirmDelete();
+ }
- @Override
- public void onBindViewHolder(ProjectsListAdapter.VH holder, int p2) {
- final AndroidProject project = mProjects.get(p2);
- holder.binding.projecsitemName.setText(project.getAppName());
- holder.binding.projecsitemPkgName.setText(project.getPackageName());
- holder.binding.deleteMsg.setText(
- holder
- .binding
- .deleteMsg
- .getContext()
- .getString(R.string.msg_confirm_delete, project.getAppName()));
- if (!TextUtils.isEmpty(project.getIconPath()))
- Glide.with(holder.itemView.getContext().getApplicationContext())
- .load(new File(project.getIconPath()))
- .into(holder.binding.projecsitemIcon);
- else holder.binding.projecsitemIcon.setImageResource(R.drawable.ic_android);
+ public boolean toggleDelete() {
+ binding.delete.setEnabled(!binding.delete.isEnabled());
+ TransitionManager.beginDelayedTransition(
+ binding.getRoot(), new ChangeBounds().setDuration(85));
+ binding.delete.setVisibility(binding.delete.isEnabled() ? View.VISIBLE : View.GONE);
+ return true;
+ }
- holder.binding.projectsitemClickableContainer.setOnClickListener(v -> sendClickEvent(project));
- holder.binding.projectsitemClickableContainer.setOnLongClickListener(
- v -> holder.toggleDelete());
- holder.binding.delete.setOnClickListener(v -> holder.showConfirmDelete());
- holder.binding.deleteCancel.setOnClickListener(v -> holder.hideAllDelete());
- holder.binding.deleteOk.setOnClickListener(v -> deleteProject(project, p2));
- }
+ public void hideDelete() {
+ binding.delete.setEnabled(false);
+ TransitionManager.beginDelayedTransition(
+ binding.getRoot(), new ChangeBounds().setDuration(85));
+ binding.delete.setVisibility(View.GONE);
+ }
- private void deleteProject(AndroidProject project, int index) {
- index = index >= mProjects.size() ? mProjects.size() - 1 : index;
- if (index < 0) return;
- mProjects.remove(index);
- notifyItemRemoved(index);
-
- new TaskExecutor()
- .executeAsync(
- () -> FileUtils.delete(project.getProjectPath()),
- __ -> {
- StudioApp.getInstance()
- .toast(
- __ ? R.string.msg_project_deleted : R.string.msg_project_delete_failed,
- Toaster.Type.SUCCESS);
- });
- }
-
- private void sendClickEvent(AndroidProject project) {
- if (listener != null) listener.onProjectClick(project);
- }
-
- @Override
- public int getItemCount() {
- return mProjects.size();
- }
-
- public class VH extends RecyclerView.ViewHolder {
- LayoutProjectsItemBinding binding;
-
- public VH(LayoutProjectsItemBinding binding) {
- super(binding.getRoot());
- this.binding = binding;
+ public void hideConfirmDelete() {
+ binding.confirmDeleteContainer.setEnabled(false);
+ TransitionManager.beginDelayedTransition(
+ binding.getRoot(), new ChangeBounds().setDuration(85));
+ binding.confirmDeleteContainer.setVisibility(View.GONE);
+ }
}
- public void showConfirmDelete() {
- binding.confirmDeleteContainer.setEnabled(true);
- TransitionManager.beginDelayedTransition(
- binding.getRoot(), new ChangeBounds().setDuration(85));
- binding.confirmDeleteContainer.setVisibility(View.VISIBLE);
+ public interface OnProjectClickListener {
+ public void onProjectClick(AndroidProject project);
}
-
- public void hideAllDelete() {
- hideDelete();
- hideConfirmDelete();
- }
-
- public boolean toggleDelete() {
- binding.delete.setEnabled(!binding.delete.isEnabled());
- TransitionManager.beginDelayedTransition(
- binding.getRoot(), new ChangeBounds().setDuration(85));
- binding.delete.setVisibility(binding.delete.isEnabled() ? View.VISIBLE : View.GONE);
- return true;
- }
-
- public void hideDelete() {
- binding.delete.setEnabled(false);
- TransitionManager.beginDelayedTransition(
- binding.getRoot(), new ChangeBounds().setDuration(85));
- binding.delete.setVisibility(View.GONE);
- }
-
- public void hideConfirmDelete() {
- binding.confirmDeleteContainer.setEnabled(false);
- TransitionManager.beginDelayedTransition(
- binding.getRoot(), new ChangeBounds().setDuration(85));
- binding.confirmDeleteContainer.setVisibility(View.GONE);
- }
- }
-
- public interface OnProjectClickListener {
- public void onProjectClick(AndroidProject project);
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/SearchListAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/SearchListAdapter.java
index 5178f691b..fd3868eed 100755
--- a/app/src/main/java/com/itsaky/androidide/adapters/SearchListAdapter.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/SearchListAdapter.java
@@ -24,14 +24,17 @@ import android.graphics.PorterDuff;
import android.text.SpannableStringBuilder;
import android.view.LayoutInflater;
import android.view.ViewGroup;
+
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+
import com.blankj.utilcode.util.ThreadUtils;
import com.itsaky.androidide.databinding.LayoutSearchResultGroupBinding;
import com.itsaky.androidide.databinding.LayoutSearchResultItemBinding;
import com.itsaky.androidide.models.SearchResult;
import com.itsaky.androidide.syntax.highlighters.JavaHighlighter;
+
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
@@ -40,124 +43,124 @@ import java.util.Map;
public class SearchListAdapter extends RecyclerView.Adapter {
- private final Map> results;
- private final List keys;
+ private final Map> results;
+ private final List keys;
- private final OnFileClickListener fileListener;
- private final OnMatchClickListener matchListener;
+ private final OnFileClickListener fileListener;
+ private final OnMatchClickListener matchListener;
- public SearchListAdapter(
- Map> results,
- OnFileClickListener fileListener,
- OnMatchClickListener matchListener) {
- if (results == null) results = new HashMap<>();
- this.results = results;
- this.keys = new ArrayList(results.keySet());
- this.fileListener = fileListener;
- this.matchListener = matchListener;
- }
-
- @Override
- public SearchListAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
- return new VH(LayoutSearchResultGroupBinding.inflate(LayoutInflater.from(p1.getContext())));
- }
-
- @Override
- public void onBindViewHolder(SearchListAdapter.VH p1, int p2) {
- final LayoutSearchResultGroupBinding binding = p1.binding;
- final File file = keys.get(p2);
- final List matches = results.get(file);
- final int color =
- ContextCompat.getColor(
- binding.icon.getContext(), com.itsaky.androidide.R.color.secondaryColor);
- binding.title.setText(file.getName());
- binding.icon.setImageResource(com.itsaky.androidide.R.drawable.ic_language_java);
- binding.icon.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
- binding.items.setLayoutManager(new LinearLayoutManager(binding.items.getContext()));
- binding.items.setAdapter(new ChildAdapter(matches));
-
- binding
- .getRoot()
- .setOnClickListener(
- v -> {
- if (fileListener != null) fileListener.onClick(file);
- });
- }
-
- @Override
- public int getItemCount() {
- return results.size();
- }
-
- public class ChildAdapter extends RecyclerView.Adapter {
-
- private final List matches;
-
- public ChildAdapter(List matches) {
- if (matches == null) matches = new ArrayList<>();
- this.matches = matches;
+ public SearchListAdapter(
+ Map> results,
+ OnFileClickListener fileListener,
+ OnMatchClickListener matchListener) {
+ if (results == null) results = new HashMap<>();
+ this.results = results;
+ this.keys = new ArrayList(results.keySet());
+ this.fileListener = fileListener;
+ this.matchListener = matchListener;
}
@Override
- public SearchListAdapter.ChildVH onCreateViewHolder(ViewGroup p1, int p2) {
- return new ChildVH(
- LayoutSearchResultItemBinding.inflate(LayoutInflater.from(p1.getContext())));
+ public SearchListAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
+ return new VH(LayoutSearchResultGroupBinding.inflate(LayoutInflater.from(p1.getContext())));
}
@Override
- public void onBindViewHolder(SearchListAdapter.ChildVH p1, int p2) {
- final SearchResult match = matches.get(p2);
- final LayoutSearchResultItemBinding binding = p1.binding;
- new Thread(
- () -> {
- try {
- final SpannableStringBuilder sb =
- new JavaHighlighter().highlight(match.line, match.match);
- ThreadUtils.runOnUiThread(() -> binding.text.setText(sb));
- } catch (Exception e) {
- ThreadUtils.runOnUiThread(() -> binding.text.setText(match.match));
- }
- })
- .start();
- binding
- .getRoot()
- .setOnClickListener(
- v -> {
- if (matchListener != null) {
- matchListener.onClick(match);
- }
- });
+ public void onBindViewHolder(SearchListAdapter.VH p1, int p2) {
+ final LayoutSearchResultGroupBinding binding = p1.binding;
+ final File file = keys.get(p2);
+ final List matches = results.get(file);
+ final int color =
+ ContextCompat.getColor(
+ binding.icon.getContext(), com.itsaky.androidide.R.color.secondaryColor);
+ binding.title.setText(file.getName());
+ binding.icon.setImageResource(com.itsaky.androidide.R.drawable.ic_language_java);
+ binding.icon.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+ binding.items.setLayoutManager(new LinearLayoutManager(binding.items.getContext()));
+ binding.items.setAdapter(new ChildAdapter(matches));
+
+ binding.getRoot()
+ .setOnClickListener(
+ v -> {
+ if (fileListener != null) fileListener.onClick(file);
+ });
}
@Override
public int getItemCount() {
- return matches.size();
+ return results.size();
}
- }
- public class VH extends RecyclerView.ViewHolder {
- private LayoutSearchResultGroupBinding binding;
+ public class ChildAdapter extends RecyclerView.Adapter {
- public VH(LayoutSearchResultGroupBinding binding) {
- super(binding.getRoot());
- this.binding = binding;
+ private final List matches;
+
+ public ChildAdapter(List matches) {
+ if (matches == null) matches = new ArrayList<>();
+ this.matches = matches;
+ }
+
+ @Override
+ public SearchListAdapter.ChildVH onCreateViewHolder(ViewGroup p1, int p2) {
+ return new ChildVH(
+ LayoutSearchResultItemBinding.inflate(LayoutInflater.from(p1.getContext())));
+ }
+
+ @Override
+ public void onBindViewHolder(SearchListAdapter.ChildVH p1, int p2) {
+ final SearchResult match = matches.get(p2);
+ final LayoutSearchResultItemBinding binding = p1.binding;
+ new Thread(
+ () -> {
+ try {
+ final SpannableStringBuilder sb =
+ new JavaHighlighter()
+ .highlight(match.line, match.match);
+ ThreadUtils.runOnUiThread(() -> binding.text.setText(sb));
+ } catch (Exception e) {
+ ThreadUtils.runOnUiThread(
+ () -> binding.text.setText(match.match));
+ }
+ })
+ .start();
+ binding.getRoot()
+ .setOnClickListener(
+ v -> {
+ if (matchListener != null) {
+ matchListener.onClick(match);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return matches.size();
+ }
}
- }
- public class ChildVH extends RecyclerView.ViewHolder {
- private LayoutSearchResultItemBinding binding;
+ public class VH extends RecyclerView.ViewHolder {
+ private LayoutSearchResultGroupBinding binding;
- public ChildVH(LayoutSearchResultItemBinding binding) {
- super(binding.getRoot());
- this.binding = binding;
+ public VH(LayoutSearchResultGroupBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
}
- }
- public static interface OnFileClickListener {
- void onClick(File file);
- }
+ public class ChildVH extends RecyclerView.ViewHolder {
+ private LayoutSearchResultItemBinding binding;
- public static interface OnMatchClickListener {
- void onClick(SearchResult match);
- }
+ public ChildVH(LayoutSearchResultItemBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
+ }
+
+ public static interface OnFileClickListener {
+ void onClick(File file);
+ }
+
+ public static interface OnMatchClickListener {
+ void onClick(SearchResult match);
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/SimpleIconTextAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/SimpleIconTextAdapter.java
index a61f5a1b0..be64d6d00 100755
--- a/app/src/main/java/com/itsaky/androidide/adapters/SimpleIconTextAdapter.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/SimpleIconTextAdapter.java
@@ -18,7 +18,9 @@
package com.itsaky.androidide.adapters;
import androidx.annotation.NonNull;
+
import com.itsaky.androidide.models.IconTextListItem;
+
import java.util.List;
/**
@@ -28,31 +30,31 @@ import java.util.List;
*/
public class SimpleIconTextAdapter extends IconTextAdapter {
- private final List extends IconTextListItem> items;
+ private final List extends IconTextListItem> items;
- public SimpleIconTextAdapter(List extends IconTextListItem> items) {
- this.items = items;
- }
+ public SimpleIconTextAdapter(List extends IconTextListItem> items) {
+ this.items = items;
+ }
- @NonNull
- @Override
- public IconTextListItem getItemAt(int index) {
- return items.get(index);
- }
+ @NonNull
+ @Override
+ public IconTextListItem getItemAt(int index) {
+ return items.get(index);
+ }
- @Override
- public int getIconResource(int index) {
- return getItemAt(index).getIconResource();
- }
+ @Override
+ public int getIconResource(int index) {
+ return getItemAt(index).getIconResource();
+ }
- @NonNull
- @Override
- public String getItemText(int index) {
- return getItemAt(index).getText();
- }
+ @NonNull
+ @Override
+ public String getItemText(int index) {
+ return getItemAt(index).getText();
+ }
- @Override
- public int getItemCount() {
- return items.size();
- }
+ @Override
+ public int getItemCount() {
+ return items.size();
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/SymbolInputAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/SymbolInputAdapter.java
index b984358df..b9a254ec6 100755
--- a/app/src/main/java/com/itsaky/androidide/adapters/SymbolInputAdapter.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/SymbolInputAdapter.java
@@ -20,100 +20,108 @@ package com.itsaky.androidide.adapters;
import android.annotation.SuppressLint;
import android.view.LayoutInflater;
import android.view.ViewGroup;
+
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
+
import com.itsaky.androidide.databinding.LayoutSymbolItemBinding;
import com.itsaky.androidide.views.SymbolInputView.Symbol;
import com.itsaky.androidide.views.editor.IDEEditor;
+
import java.util.ArrayList;
import java.util.List;
public class SymbolInputAdapter extends RecyclerView.Adapter {
- private final IDEEditor editor;
- private Symbol[] symbols;
+ private final IDEEditor editor;
+ private Symbol[] symbols;
- public SymbolInputAdapter(IDEEditor editor) {
- this(editor, null);
- }
-
- public SymbolInputAdapter(IDEEditor editor, Symbol[] symbols) {
- this.editor = editor;
- this.symbols = symbols == null ? new Symbol[0] : symbols;
- }
-
- @SuppressLint("NotifyDataSetChanged")
- public void setSymbols(boolean notify, Symbol... symbols) {
- this.symbols = symbols;
- if (notify) {
- notifyDataSetChanged();
- }
- }
-
- @NonNull
- @Override
- public VH onCreateViewHolder(@NonNull ViewGroup parent, int itemType) {
- return new VH(
- LayoutSymbolItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
- }
-
- @Override
- public void onBindViewHolder(@NonNull VH holder, int position) {
- if (symbols == null || symbols[position] == null) return;
- final Symbol symbol = symbols[position];
- holder.binding.symbol.setText(symbol.label);
- holder.binding.symbol.setOnClickListener(__ -> insertSymbol(symbol.commit, symbol.offset));
- }
-
- @Override
- public int getItemCount() {
- return symbols == null ? 0 : symbols.length;
- }
-
- public static class VH extends RecyclerView.ViewHolder {
- LayoutSymbolItemBinding binding;
-
- public VH(LayoutSymbolItemBinding binding) {
- super(binding.getRoot());
- this.binding = binding;
- }
- }
-
- void insertSymbol(String text, int selectionOffset) {
- if (selectionOffset < 0 || selectionOffset > text.length()) {
- return;
- }
- var cur = editor.getText().getCursor();
- if (cur.isSelected()) {
- editor
- .getText()
- .delete(cur.getLeftLine(), cur.getLeftColumn(), cur.getRightLine(), cur.getRightColumn());
- editor.notifyIMEExternalCursorChange();
+ public SymbolInputAdapter(IDEEditor editor) {
+ this(editor, null);
}
- if (cur.getLeftColumn() < editor.getText().getColumnCount(cur.getLeftLine())
- && text.length() == 1
- && text.charAt(0) == editor.getText().charAt(cur.getLeftLine(), cur.getLeftColumn())
- && pairs.contains(text.charAt(0))) {
- editor.moveSelectionRight();
- } else {
- editor.commitText(text);
- if (selectionOffset != text.length()) {
- editor.setSelection(
- cur.getRightLine(), cur.getRightColumn() - (text.length() - selectionOffset));
- }
+ public SymbolInputAdapter(IDEEditor editor, Symbol[] symbols) {
+ this.editor = editor;
+ this.symbols = symbols == null ? new Symbol[0] : symbols;
}
- }
- private static final List pairs;
+ @SuppressLint("NotifyDataSetChanged")
+ public void setSymbols(boolean notify, Symbol... symbols) {
+ this.symbols = symbols;
+ if (notify) {
+ notifyDataSetChanged();
+ }
+ }
- static {
- pairs = new ArrayList<>();
- pairs.add('}');
- pairs.add(')');
- pairs.add(']');
- pairs.add('"');
- pairs.add('\'');
- pairs.add('>');
- }
+ @NonNull
+ @Override
+ public VH onCreateViewHolder(@NonNull ViewGroup parent, int itemType) {
+ return new VH(
+ LayoutSymbolItemBinding.inflate(
+ LayoutInflater.from(parent.getContext()), parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull VH holder, int position) {
+ if (symbols == null || symbols[position] == null) return;
+ final Symbol symbol = symbols[position];
+ holder.binding.symbol.setText(symbol.label);
+ holder.binding.symbol.setOnClickListener(__ -> insertSymbol(symbol.commit, symbol.offset));
+ }
+
+ @Override
+ public int getItemCount() {
+ return symbols == null ? 0 : symbols.length;
+ }
+
+ public static class VH extends RecyclerView.ViewHolder {
+ LayoutSymbolItemBinding binding;
+
+ public VH(LayoutSymbolItemBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
+ }
+
+ void insertSymbol(String text, int selectionOffset) {
+ if (selectionOffset < 0 || selectionOffset > text.length()) {
+ return;
+ }
+ var cur = editor.getText().getCursor();
+ if (cur.isSelected()) {
+ editor.getText()
+ .delete(
+ cur.getLeftLine(),
+ cur.getLeftColumn(),
+ cur.getRightLine(),
+ cur.getRightColumn());
+ editor.notifyIMEExternalCursorChange();
+ }
+
+ if (cur.getLeftColumn() < editor.getText().getColumnCount(cur.getLeftLine())
+ && text.length() == 1
+ && text.charAt(0) == editor.getText().charAt(cur.getLeftLine(), cur.getLeftColumn())
+ && pairs.contains(text.charAt(0))) {
+ editor.moveSelectionRight();
+ } else {
+ editor.commitText(text);
+ if (selectionOffset != text.length()) {
+ editor.setSelection(
+ cur.getRightLine(),
+ cur.getRightColumn() - (text.length() - selectionOffset));
+ }
+ }
+ }
+
+ private static final List pairs;
+
+ static {
+ pairs = new ArrayList<>();
+ pairs.add('}');
+ pairs.add(')');
+ pairs.add(']');
+ pairs.add('"');
+ pairs.add('\'');
+ pairs.add('>');
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/TextActionItemAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/TextActionItemAdapter.java
index 0b132ed46..f95e9f95d 100644
--- a/app/src/main/java/com/itsaky/androidide/adapters/TextActionItemAdapter.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/TextActionItemAdapter.java
@@ -20,12 +20,15 @@ package com.itsaky.androidide.adapters;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.ViewGroup;
+
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
+
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.databinding.LayoutTextActionItemBinding;
import com.itsaky.androidide.managers.PreferenceManager;
import com.itsaky.androidide.views.editor.IDEEditor;
+
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
@@ -37,68 +40,68 @@ import java.util.function.Consumer;
*/
public class TextActionItemAdapter extends RecyclerView.Adapter {
- private final List actions;
- private final Consumer onClick;
- private final boolean isHorizontal;
+ private final List actions;
+ private final Consumer onClick;
+ private final boolean isHorizontal;
- public TextActionItemAdapter(
- List actions, Consumer onClick) {
- Objects.requireNonNull(actions);
- this.actions = actions;
- this.onClick = onClick;
- this.isHorizontal =
- StudioApp.getInstance()
- .getPrefManager()
- .getBoolean(PreferenceManager.KEY_EDITOR_HORIZONTAL_POPUP, false);
- }
-
- @NonNull
- @Override
- public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- return new VH(
- LayoutTextActionItemBinding.inflate(
- LayoutInflater.from(parent.getContext()), parent, false));
- }
-
- @Override
- public void onBindViewHolder(@NonNull VH holder, int position) {
- final var binding = holder.binding;
- final var action = actions.get(position);
- final var button = binding.getRoot();
-
- if (isHorizontal) {
- // If not set to wrap_content, one item will take whole screen width.
- final var params = button.getLayoutParams();
- params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
- button.setLayoutParams(params);
-
- // looks better
- button.setGravity(Gravity.CENTER);
+ public TextActionItemAdapter(
+ List actions, Consumer onClick) {
+ Objects.requireNonNull(actions);
+ this.actions = actions;
+ this.onClick = onClick;
+ this.isHorizontal =
+ StudioApp.getInstance()
+ .getPrefManager()
+ .getBoolean(PreferenceManager.KEY_EDITOR_HORIZONTAL_POPUP, false);
}
- button.setText(action.titleId);
- button.setCompoundDrawablesRelative(action.icon, null, null, null);
-
- button.setOnClickListener(
- v -> {
- if (onClick != null) {
- onClick.accept(action);
- }
- });
- }
-
- @Override
- public int getItemCount() {
- return actions.size();
- }
-
- static class VH extends RecyclerView.ViewHolder {
-
- private final LayoutTextActionItemBinding binding;
-
- public VH(@NonNull LayoutTextActionItemBinding binding) {
- super(binding.getRoot());
- this.binding = binding;
+ @NonNull
+ @Override
+ public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new VH(
+ LayoutTextActionItemBinding.inflate(
+ LayoutInflater.from(parent.getContext()), parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull VH holder, int position) {
+ final var binding = holder.binding;
+ final var action = actions.get(position);
+ final var button = binding.getRoot();
+
+ if (isHorizontal) {
+ // If not set to wrap_content, one item will take whole screen width.
+ final var params = button.getLayoutParams();
+ params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+ button.setLayoutParams(params);
+
+ // looks better
+ button.setGravity(Gravity.CENTER);
+ }
+
+ button.setText(action.titleId);
+ button.setCompoundDrawablesRelative(action.icon, null, null, null);
+
+ button.setOnClickListener(
+ v -> {
+ if (onClick != null) {
+ onClick.accept(action);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return actions.size();
+ }
+
+ static class VH extends RecyclerView.ViewHolder {
+
+ private final LayoutTextActionItemBinding binding;
+
+ public VH(@NonNull LayoutTextActionItemBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
}
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/WidgetGroupItemAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/WidgetGroupItemAdapter.java
index ae7a58fd7..5c1d85431 100755
--- a/app/src/main/java/com/itsaky/androidide/adapters/WidgetGroupItemAdapter.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/WidgetGroupItemAdapter.java
@@ -18,54 +18,57 @@
package com.itsaky.androidide.adapters;
import android.graphics.Color;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
+
import com.itsaky.androidide.R;
import com.itsaky.androidide.models.IconTextListItem;
import com.itsaky.androidide.models.UIWidgetGroup;
+
import java.util.List;
public class WidgetGroupItemAdapter extends SimpleIconTextAdapter
- implements SimpleIconTextAdapter.OnBindListener {
+ implements SimpleIconTextAdapter.OnBindListener {
- private final OnGroupClickListener clickListener;
+ private final OnGroupClickListener clickListener;
- public WidgetGroupItemAdapter(
- @NonNull List groups, @Nullable OnGroupClickListener clickListener) {
- super(groups);
- this.clickListener = clickListener;
+ public WidgetGroupItemAdapter(
+ @NonNull List groups, @Nullable OnGroupClickListener clickListener) {
+ super(groups);
+ this.clickListener = clickListener;
- super.setOnBindListener(this);
- }
-
- @Override
- public boolean onBind(IconTextListItem item, VH holder, int position) {
- // Ignored
- return false;
- }
-
- @Override
- public void postBind(IconTextListItem item, @NonNull VH holder, int position) {
- final var binding = holder.binding;
- final var group = (UIWidgetGroup) item;
-
- if (group.isSelected()) {
- binding
- .getRoot()
- .setBackground(
- ContextCompat.getDrawable(
- binding.getRoot().getContext(), R.drawable.bg_widget_group_selected));
- } else {
- binding.getRoot().setBackgroundColor(Color.TRANSPARENT);
+ super.setOnBindListener(this);
}
- if (this.clickListener != null) {
- binding.getRoot().setOnClickListener(v -> this.clickListener.onGroupClick(group));
+ @Override
+ public boolean onBind(IconTextListItem item, VH holder, int position) {
+ // Ignored
+ return false;
}
- }
- public interface OnGroupClickListener {
- void onGroupClick(UIWidgetGroup group);
- }
+ @Override
+ public void postBind(IconTextListItem item, @NonNull VH holder, int position) {
+ final var binding = holder.binding;
+ final var group = (UIWidgetGroup) item;
+
+ if (group.isSelected()) {
+ binding.getRoot()
+ .setBackground(
+ ContextCompat.getDrawable(
+ binding.getRoot().getContext(),
+ R.drawable.bg_widget_group_selected));
+ } else {
+ binding.getRoot().setBackgroundColor(Color.TRANSPARENT);
+ }
+
+ if (this.clickListener != null) {
+ binding.getRoot().setOnClickListener(v -> this.clickListener.onGroupClick(group));
+ }
+ }
+
+ public interface OnGroupClickListener {
+ void onGroupClick(UIWidgetGroup group);
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/WidgetItemAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/WidgetItemAdapter.java
index 506347f52..62502ed49 100755
--- a/app/src/main/java/com/itsaky/androidide/adapters/WidgetItemAdapter.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/WidgetItemAdapter.java
@@ -19,58 +19,61 @@ package com.itsaky.androidide.adapters;
import android.content.ClipData;
import android.view.View;
+
import androidx.annotation.NonNull;
+
import com.itsaky.androidide.DesignerActivity;
import com.itsaky.androidide.models.IconTextListItem;
import com.itsaky.androidide.models.UIWidget;
import com.itsaky.androidide.ui.WidgetDragData;
import com.itsaky.androidide.ui.WidgetDragShadowBuilder;
+
import java.util.List;
public class WidgetItemAdapter extends SimpleIconTextAdapter
- implements SimpleIconTextAdapter.OnBindListener {
+ implements SimpleIconTextAdapter.OnBindListener {
- private final OnDragStartListener dragStartListener;
+ private final OnDragStartListener dragStartListener;
- public WidgetItemAdapter(List widgets, OnDragStartListener dragStartListener) {
- super(widgets);
- this.dragStartListener = dragStartListener;
+ public WidgetItemAdapter(List widgets, OnDragStartListener dragStartListener) {
+ super(widgets);
+ this.dragStartListener = dragStartListener;
- setOnBindListener(this);
- }
+ setOnBindListener(this);
+ }
- @Override
- public boolean onBind(IconTextListItem item, @NonNull VH holder, int position) {
- return false; // Returning true will not set the text and icon
- }
+ @Override
+ public boolean onBind(IconTextListItem item, @NonNull VH holder, int position) {
+ return false; // Returning true will not set the text and icon
+ }
- @Override
- public void postBind(IconTextListItem item, VH holder, int position) {
- final var binding = holder.binding;
- final var root = binding.getRoot();
- final var widget = (UIWidget) getItemAt(position);
+ @Override
+ public void postBind(IconTextListItem item, VH holder, int position) {
+ final var binding = holder.binding;
+ final var root = binding.getRoot();
+ final var widget = (UIWidget) getItemAt(position);
- root.setOnLongClickListener(
- v -> {
- final var dragData = new WidgetDragData(false, null, widget);
- final var shadow = new WidgetDragShadowBuilder(binding.icon);
- final var dataItem = new ClipData.Item(DesignerActivity.DRAGGING_WIDGET_TAG);
- final var data =
- new ClipData(
- DesignerActivity.DRAGGING_WIDGET_TAG,
- new String[] {DesignerActivity.DRAGGING_WIDGET_MIME},
- dataItem);
- binding.icon.startDragAndDrop(data, shadow, dragData, 0);
+ root.setOnLongClickListener(
+ v -> {
+ final var dragData = new WidgetDragData(false, null, widget);
+ final var shadow = new WidgetDragShadowBuilder(binding.icon);
+ final var dataItem = new ClipData.Item(DesignerActivity.DRAGGING_WIDGET_TAG);
+ final var data =
+ new ClipData(
+ DesignerActivity.DRAGGING_WIDGET_TAG,
+ new String[] {DesignerActivity.DRAGGING_WIDGET_MIME},
+ dataItem);
+ binding.icon.startDragAndDrop(data, shadow, dragData, 0);
- if (this.dragStartListener != null) {
- this.dragStartListener.onDragStarted(v);
- }
+ if (this.dragStartListener != null) {
+ this.dragStartListener.onDragStarted(v);
+ }
- return true;
- });
- }
+ return true;
+ });
+ }
- public interface OnDragStartListener {
- void onDragStarted(View view);
- }
+ public interface OnDragStartListener {
+ void onDragStarted(View view);
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/XMLAttributeListAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/XMLAttributeListAdapter.java
index 1abf22b2e..060504b47 100644
--- a/app/src/main/java/com/itsaky/androidide/adapters/XMLAttributeListAdapter.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/XMLAttributeListAdapter.java
@@ -24,77 +24,83 @@ 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;
+
import java.util.List;
public class XMLAttributeListAdapter extends RecyclerView.Adapter {
- private final List attributes;
- private final OnClickListener clickListener;
+ private final List attributes;
+ private final OnClickListener clickListener;
- public XMLAttributeListAdapter(List attributes, OnClickListener clickListener) {
- this.attributes = attributes;
- this.clickListener = clickListener;
- }
-
- @NonNull
- @Override
- public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- return new VH(
- LayoutAttrEditorSheetItemBinding.inflate(
- LayoutInflater.from(parent.getContext()), parent, false));
- }
-
- @Override
- public void onBindViewHolder(@NonNull VH holder, int position) {
- final var binding = holder.binding;
- final var attr = attributes.get(position);
- final var sb = new SpannableStringBuilder();
- sb.append(attr.getAttributeName());
- 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)),
- sb.length() - length,
- sb.length() - 1,
- SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
+ public XMLAttributeListAdapter(List attributes, OnClickListener clickListener) {
+ this.attributes = attributes;
+ this.clickListener = clickListener;
}
- binding.text.setText(sb);
- binding
- .getRoot()
- .setOnClickListener(
- v -> {
- if (this.clickListener != null) {
- this.clickListener.onClick(binding, attr);
- }
- });
- }
-
- @Override
- public int getItemCount() {
- return attributes.size();
- }
-
- public interface OnClickListener {
- void onClick(LayoutAttrEditorSheetItemBinding binding, XMLAttribute attribute);
- }
-
- static class VH extends RecyclerView.ViewHolder {
- LayoutAttrEditorSheetItemBinding binding;
-
- VH(@NonNull LayoutAttrEditorSheetItemBinding binding) {
- super(binding.getRoot());
- this.binding = binding;
+ @NonNull
+ @Override
+ public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new VH(
+ LayoutAttrEditorSheetItemBinding.inflate(
+ LayoutInflater.from(parent.getContext()), parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull VH holder, int position) {
+ final var binding = holder.binding;
+ final var attr = attributes.get(position);
+ final var sb = new SpannableStringBuilder();
+ sb.append(attr.getAttributeName());
+ 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)),
+ sb.length() - length,
+ sb.length() - 1,
+ SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ binding.text.setText(sb);
+ binding.getRoot()
+ .setOnClickListener(
+ v -> {
+ if (this.clickListener != null) {
+ this.clickListener.onClick(binding, attr);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return attributes.size();
+ }
+
+ public interface OnClickListener {
+ void onClick(LayoutAttrEditorSheetItemBinding binding, XMLAttribute attribute);
+ }
+
+ static class VH extends RecyclerView.ViewHolder {
+ LayoutAttrEditorSheetItemBinding binding;
+
+ VH(@NonNull LayoutAttrEditorSheetItemBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
}
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/viewholders/FileTreeViewHolder.java b/app/src/main/java/com/itsaky/androidide/adapters/viewholders/FileTreeViewHolder.java
index 33ce5ea15..8462d8183 100755
--- a/app/src/main/java/com/itsaky/androidide/adapters/viewholders/FileTreeViewHolder.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/viewholders/FileTreeViewHolder.java
@@ -26,83 +26,88 @@ import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
+
import androidx.core.content.ContextCompat;
+
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.StudioActivity;
import com.itsaky.androidide.databinding.LayoutFiletreeItemBinding;
import com.itsaky.androidide.utils.Environment;
import com.unnamed.b.atv.model.TreeNode;
+
import java.io.File;
public class FileTreeViewHolder extends TreeNode.BaseNodeViewHolder {
- private Context context;
- private ImageView chevron;
- private LayoutFiletreeItemBinding binding;
+ private Context context;
+ private ImageView chevron;
+ private LayoutFiletreeItemBinding binding;
- public FileTreeViewHolder(Context context) {
- super(context);
- this.context = context;
- }
+ public FileTreeViewHolder(Context context) {
+ super(context);
+ this.context = context;
+ }
- @Override
- public View createNodeView(TreeNode node, File file) {
- binding = LayoutFiletreeItemBinding.inflate(LayoutInflater.from(context));
- int dp15 =
- context instanceof StudioActivity
- ? ((StudioActivity) context).dpToPx(15)
- : (int)
- (TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP, 15, context.getResources().getDisplayMetrics()));
- int icon = 0;
- if (file.isDirectory()) icon = R.drawable.ic_folder;
- else if (file.getName().endsWith(".java")) icon = R.drawable.ic_language_java;
- else if (file.getName().endsWith(".kt")) icon = R.drawable.ic_language_kotlin;
- else if (file.getName().endsWith(".xml")) icon = R.drawable.ic_language_xml;
- else if (file.getName().endsWith(".gradle")) icon = R.drawable.ic_language_gradle;
- else if (file.getName().endsWith(".json")) icon = R.drawable.ic_language_json;
- else icon = R.drawable.ic_file_unknown;
+ @Override
+ public View createNodeView(TreeNode node, File file) {
+ binding = LayoutFiletreeItemBinding.inflate(LayoutInflater.from(context));
+ int dp15 =
+ context instanceof StudioActivity
+ ? ((StudioActivity) context).dpToPx(15)
+ : (int)
+ (TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ 15,
+ context.getResources().getDisplayMetrics()));
+ int icon = 0;
+ if (file.isDirectory()) icon = R.drawable.ic_folder;
+ else if (file.getName().endsWith(".java")) icon = R.drawable.ic_language_java;
+ else if (file.getName().endsWith(".kt")) icon = R.drawable.ic_language_kotlin;
+ else if (file.getName().endsWith(".xml")) icon = R.drawable.ic_language_xml;
+ else if (file.getName().endsWith(".gradle")) icon = R.drawable.ic_language_gradle;
+ else if (file.getName().endsWith(".json")) icon = R.drawable.ic_language_json;
+ else icon = R.drawable.ic_file_unknown;
- final boolean isGradle =
- file.getAbsolutePath().equals(Environment.GRADLE_USER_HOME.getAbsolutePath());
- chevron = binding.getRoot().findViewById(R.id.filetree_chevron);
- binding.filetreeName.setText(isGradle ? "GRADLE_HOME" : file.getName());
- binding.filetreeIcon.setImageResource(icon);
- binding
- .getRoot()
- .setPadding(
- binding.getRoot().getPaddingLeft() + (dp15 * (node.getLevel() - 1)),
- binding.getRoot().getPaddingTop(),
- binding.getRoot().getPaddingRight(),
- binding.getRoot().getPaddingBottom());
- chevron.setVisibility(file.isFile() ? View.INVISIBLE : View.VISIBLE);
- chevron.setImageResource(
- node.isExpanded() ? R.drawable.ic_chevron_down : R.drawable.ic_chevron_right);
- chevron
- .getDrawable()
- .setColorFilter(
- ContextCompat.getColor(context, R.color.secondaryLightColor), PorterDuff.Mode.SRC_ATOP);
- binding
- .filetreeIcon
- .getDrawable()
- .setColorFilter(
- ContextCompat.getColor(context, R.color.secondaryLightColor), PorterDuff.Mode.SRC_ATOP);
+ final boolean isGradle =
+ file.getAbsolutePath().equals(Environment.GRADLE_USER_HOME.getAbsolutePath());
+ chevron = binding.getRoot().findViewById(R.id.filetree_chevron);
+ binding.filetreeName.setText(isGradle ? "GRADLE_HOME" : file.getName());
+ binding.filetreeIcon.setImageResource(icon);
+ binding.getRoot()
+ .setPadding(
+ binding.getRoot().getPaddingLeft() + (dp15 * (node.getLevel() - 1)),
+ binding.getRoot().getPaddingTop(),
+ binding.getRoot().getPaddingRight(),
+ binding.getRoot().getPaddingBottom());
+ chevron.setVisibility(file.isFile() ? View.INVISIBLE : View.VISIBLE);
+ chevron.setImageResource(
+ node.isExpanded() ? R.drawable.ic_chevron_down : R.drawable.ic_chevron_right);
+ chevron.getDrawable()
+ .setColorFilter(
+ ContextCompat.getColor(context, R.color.secondaryLightColor),
+ PorterDuff.Mode.SRC_ATOP);
+ binding.filetreeIcon
+ .getDrawable()
+ .setColorFilter(
+ ContextCompat.getColor(context, R.color.secondaryLightColor),
+ PorterDuff.Mode.SRC_ATOP);
- return binding.getRoot();
- }
+ return binding.getRoot();
+ }
- public void setLoading() {
- chevron
- .getDrawable()
- .setColorFilter(
- ContextCompat.getColor(context, R.color.secondaryLightColor), PorterDuff.Mode.SRC_ATOP);
- }
+ public void setLoading() {
+ chevron.getDrawable()
+ .setColorFilter(
+ ContextCompat.getColor(context, R.color.secondaryLightColor),
+ PorterDuff.Mode.SRC_ATOP);
+ }
- public void updateChevron(boolean isExpanded) {
- chevron.setImageResource(isExpanded ? R.drawable.ic_chevron_down : R.drawable.ic_chevron_right);
- chevron
- .getDrawable()
- .setColorFilter(
- ContextCompat.getColor(context, R.color.secondaryLightColor), PorterDuff.Mode.SRC_ATOP);
- }
+ public void updateChevron(boolean isExpanded) {
+ chevron.setImageResource(
+ isExpanded ? R.drawable.ic_chevron_down : R.drawable.ic_chevron_right);
+ chevron.getDrawable()
+ .setColorFilter(
+ ContextCompat.getColor(context, R.color.secondaryLightColor),
+ PorterDuff.Mode.SRC_ATOP);
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/app/StudioActivity.java b/app/src/main/java/com/itsaky/androidide/app/StudioActivity.java
index 9417a211e..0d0488907 100755
--- a/app/src/main/java/com/itsaky/androidide/app/StudioActivity.java
+++ b/app/src/main/java/com/itsaky/androidide/app/StudioActivity.java
@@ -22,121 +22,125 @@ import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.View;
+
import androidx.annotation.AttrRes;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
+
import com.itsaky.androidide.R;
import com.itsaky.androidide.utils.Logger;
public abstract class StudioActivity extends AppCompatActivity {
- private boolean toRequestStorage = true;
+ private boolean toRequestStorage = true;
- public static final String TAG = "StudioActivity";
- public static final int REQCODE_STORAGE = 1009;
+ public static final String TAG = "StudioActivity";
+ public static final int REQCODE_STORAGE = 1009;
- protected static Logger LOG = Logger.instance("StudioActivity");
+ protected static Logger LOG = Logger.instance("StudioActivity");
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(bindLayout());
- onSetContentView();
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(bindLayout());
+ onSetContentView();
- if (toRequestStorage && !isStoragePermissionGranted()) {
- requestStorage();
+ if (toRequestStorage && !isStoragePermissionGranted()) {
+ requestStorage();
+ }
+
+ if (isStoragePermissionGranted()) {
+ onStorageAlreadyGranted();
+ } else {
+ onStorageDenied();
+ }
}
- if (isStoragePermissionGranted()) {
- onStorageAlreadyGranted();
- } else {
- onStorageDenied();
+ public boolean isStoragePermissionGranted() {
+ return ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
+ == PackageManager.PERMISSION_GRANTED
+ && ContextCompat.checkSelfPermission(
+ this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
+ == PackageManager.PERMISSION_GRANTED;
}
- }
- public boolean isStoragePermissionGranted() {
- return ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
- == PackageManager.PERMISSION_GRANTED
- && ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- protected void doNotRequestStorage() {
- this.toRequestStorage = false;
- }
-
- protected void requestStorage() {
- if (isStoragePermissionGranted()) {
- onStorageGranted();
- return;
+ protected void doNotRequestStorage() {
+ this.toRequestStorage = false;
}
- ActivityCompat.requestPermissions(
- this,
- new String[] {
- Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE
- },
- REQCODE_STORAGE);
- }
- public void loadFrament(Fragment frag, View view) {
- loadFragment(frag, view.getId());
- }
-
- public void loadFragment(Fragment fragment, int id) {
- FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- transaction.replace(id, fragment);
- transaction.commit();
- }
-
- public StudioApp getApp() {
- return (StudioApp) getApplication();
- }
-
- public TypedValue getTypedValueForAttr(@AttrRes int attrRes) {
- TypedValue typedValue = new TypedValue();
- getTheme().resolveAttribute(attrRes, typedValue, true);
- return typedValue;
- }
-
- public int getColorPrimary() {
- return getTypedValueForAttr(R.attr.colorPrimary).data;
- }
-
- public int getColorPrimaryDark() {
- return getTypedValueForAttr(R.attr.colorPrimaryDark).data;
- }
-
- public int getColorAccent() {
- return getTypedValueForAttr(R.attr.colorAccent).data;
- }
-
- public int dpToPx(int dp) {
- return (int)
- TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
- }
-
- @Override
- public void onRequestPermissionsResult(
- int requestCode, String[] permissions, int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- if (requestCode == REQCODE_STORAGE) {
- if (grantResults != null
- && grantResults.length > 0
- && grantResults[0] == PackageManager.PERMISSION_GRANTED) onStorageGranted();
- else onStorageDenied();
+ protected void requestStorage() {
+ if (isStoragePermissionGranted()) {
+ onStorageGranted();
+ return;
+ }
+ ActivityCompat.requestPermissions(
+ this,
+ new String[] {
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ },
+ REQCODE_STORAGE);
}
- }
- protected void onStorageAlreadyGranted() {}
+ public void loadFrament(Fragment frag, View view) {
+ loadFragment(frag, view.getId());
+ }
- protected void onStorageGranted() {}
+ public void loadFragment(Fragment fragment, int id) {
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ transaction.replace(id, fragment);
+ transaction.commit();
+ }
- protected void onStorageDenied() {}
+ public StudioApp getApp() {
+ return (StudioApp) getApplication();
+ }
- protected void onSetContentView() {}
+ public TypedValue getTypedValueForAttr(@AttrRes int attrRes) {
+ TypedValue typedValue = new TypedValue();
+ getTheme().resolveAttribute(attrRes, typedValue, true);
+ return typedValue;
+ }
- protected abstract View bindLayout();
+ public int getColorPrimary() {
+ return getTypedValueForAttr(R.attr.colorPrimary).data;
+ }
+
+ public int getColorPrimaryDark() {
+ return getTypedValueForAttr(R.attr.colorPrimaryDark).data;
+ }
+
+ public int getColorAccent() {
+ return getTypedValueForAttr(R.attr.colorAccent).data;
+ }
+
+ public int dpToPx(int dp) {
+ return (int)
+ TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
+ }
+
+ @Override
+ public void onRequestPermissionsResult(
+ int requestCode, String[] permissions, int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (requestCode == REQCODE_STORAGE) {
+ if (grantResults != null
+ && grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED) onStorageGranted();
+ else onStorageDenied();
+ }
+ }
+
+ protected void onStorageAlreadyGranted() {}
+
+ protected void onStorageGranted() {}
+
+ protected void onStorageDenied() {}
+
+ protected void onSetContentView() {}
+
+ protected abstract View bindLayout();
}
diff --git a/app/src/main/java/com/itsaky/androidide/app/StudioApp.java b/app/src/main/java/com/itsaky/androidide/app/StudioApp.java
index 4290f2ae2..151b5133f 100755
--- a/app/src/main/java/com/itsaky/androidide/app/StudioApp.java
+++ b/app/src/main/java/com/itsaky/androidide/app/StudioApp.java
@@ -18,7 +18,9 @@
package com.itsaky.androidide.app;
import android.content.Intent;
+
import androidx.annotation.NonNull;
+
import com.blankj.utilcode.util.ThrowableUtils;
import com.google.firebase.messaging.FirebaseMessaging;
import com.itsaky.androidide.CrashHandlerActivity;
@@ -36,117 +38,119 @@ import com.itsaky.lsp.java.JavaLanguageServer;
import com.itsaky.lsp.xml.XMLLanguageServer;
import com.itsaky.sdk.SDKInfo;
import com.itsaky.widgets.WidgetInfo;
+
import java.io.File;
import java.util.Set;
public class StudioApp extends BaseApplication {
- private static final Logger LOG = Logger.instance("StudioApp");
- private static StudioApp instance;
- private static SDKInfo sdkInfo;
- private final ILanguageServer mJavaLanguageServer = new JavaLanguageServer();
- private final ILanguageServer mXMLLanguageServer = new XMLLanguageServer();
- private IResourceTable mResTable;
- private ILayoutInflater mLayoutInflater;
- private Thread.UncaughtExceptionHandler uncaughtExceptionHandler;
+ private static final Logger LOG = Logger.instance("StudioApp");
+ private static StudioApp instance;
+ private static SDKInfo sdkInfo;
+ private final ILanguageServer mJavaLanguageServer = new JavaLanguageServer();
+ private final ILanguageServer mXMLLanguageServer = new XMLLanguageServer();
+ private IResourceTable mResTable;
+ private ILayoutInflater mLayoutInflater;
+ private Thread.UncaughtExceptionHandler uncaughtExceptionHandler;
- public static StudioApp getInstance() {
- return instance;
- }
-
- @Override
- public void onCreate() {
- instance = this;
- this.uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
- Thread.setDefaultUncaughtExceptionHandler(this::handleCrash);
- super.onCreate();
-
- FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_UPDATE);
- FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_DEV_MSGS);
-
- initializeApiInformation();
- }
-
- @NonNull
- public ILanguageServer getJavaLanguageServer() {
- return mJavaLanguageServer;
- }
-
- @NonNull
- public ILanguageServer getXMLLanguageServer() {
- return mXMLLanguageServer;
- }
-
- private void handleCrash(Thread thread, Throwable th) {
- writeException(th);
-
- try {
- final var intent = new Intent();
- intent.setAction(CrashHandlerActivity.REPORT_ACTION);
- intent.putExtra(CrashHandlerActivity.TRACE_KEY, ThrowableUtils.getFullStackTrace(th));
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
-
- if (this.uncaughtExceptionHandler != null) {
- this.uncaughtExceptionHandler.uncaughtException(thread, th);
- }
- System.exit(1);
- } catch (Throwable error) {
- LOG.error("Unable to show crash handler activity", error);
+ public static StudioApp getInstance() {
+ return instance;
}
- }
- public void createInflater(LayoutInflaterConfiguration config) {
- this.mLayoutInflater = ILayoutInflater.newInstance(config);
- }
+ @Override
+ public void onCreate() {
+ instance = this;
+ this.uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
+ Thread.setDefaultUncaughtExceptionHandler(this::handleCrash);
+ super.onCreate();
- public LayoutInflaterConfiguration createInflaterConfig(
- ILayoutInflater.ContextProvider contextProvider, Set resDirs) {
- return new LayoutInflaterConfiguration.Builder()
- .setAttrInfo(this.attrInfo())
- .setWidgetInfo(this.widgetInfo())
- .setResourceFinder(mResTable == null ? mResTable = new ProjectResourceTable() : mResTable)
- .setResourceDirectories(resDirs)
- .setContextProvider(contextProvider)
- .create();
- }
+ FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_UPDATE);
+ FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_DEV_MSGS);
- public ILayoutInflater getLayoutInflater() {
- return mLayoutInflater;
- }
+ initializeApiInformation();
+ }
- public IResourceTable getResourceTable() {
- return this.mResTable;
- }
+ @NonNull
+ public ILanguageServer getJavaLanguageServer() {
+ return mJavaLanguageServer;
+ }
- /** Reads API version information from api-versions.xml */
- public void initializeApiInformation() {
- new Thread(
- () -> {
- try {
- sdkInfo = new SDKInfo(StudioApp.this);
- ((XMLLanguageServer) mXMLLanguageServer).setupSDK(sdkInfo);
- } catch (Throwable th) {
- LOG.error(getString(R.string.err_init_sdkinfo), th);
- }
- },
- "SDKInfo Loader")
- .start();
- }
+ @NonNull
+ public ILanguageServer getXMLLanguageServer() {
+ return mXMLLanguageServer;
+ }
- public void stopAllDaemons() {
- newShell(null).bgAppend("gradle --stop");
- }
+ private void handleCrash(Thread thread, Throwable th) {
+ writeException(th);
- public ApiInfo apiInfo() {
- return sdkInfo.getApiInfo();
- }
+ try {
+ final var intent = new Intent();
+ intent.setAction(CrashHandlerActivity.REPORT_ACTION);
+ intent.putExtra(CrashHandlerActivity.TRACE_KEY, ThrowableUtils.getFullStackTrace(th));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
- public AttrInfo attrInfo() {
- return sdkInfo.getAttrInfo();
- }
+ if (this.uncaughtExceptionHandler != null) {
+ this.uncaughtExceptionHandler.uncaughtException(thread, th);
+ }
+ System.exit(1);
+ } catch (Throwable error) {
+ LOG.error("Unable to show crash handler activity", error);
+ }
+ }
- public WidgetInfo widgetInfo() {
- return sdkInfo.getWidgetInfo();
- }
+ public void createInflater(LayoutInflaterConfiguration config) {
+ this.mLayoutInflater = ILayoutInflater.newInstance(config);
+ }
+
+ public LayoutInflaterConfiguration createInflaterConfig(
+ ILayoutInflater.ContextProvider contextProvider, Set resDirs) {
+ return new LayoutInflaterConfiguration.Builder()
+ .setAttrInfo(this.attrInfo())
+ .setWidgetInfo(this.widgetInfo())
+ .setResourceFinder(
+ mResTable == null ? mResTable = new ProjectResourceTable() : mResTable)
+ .setResourceDirectories(resDirs)
+ .setContextProvider(contextProvider)
+ .create();
+ }
+
+ public ILayoutInflater getLayoutInflater() {
+ return mLayoutInflater;
+ }
+
+ public IResourceTable getResourceTable() {
+ return this.mResTable;
+ }
+
+ /** Reads API version information from api-versions.xml */
+ public void initializeApiInformation() {
+ new Thread(
+ () -> {
+ try {
+ sdkInfo = new SDKInfo(StudioApp.this);
+ ((XMLLanguageServer) mXMLLanguageServer).setupSDK(sdkInfo);
+ } catch (Throwable th) {
+ LOG.error(getString(R.string.err_init_sdkinfo), th);
+ }
+ },
+ "SDKInfo Loader")
+ .start();
+ }
+
+ public void stopAllDaemons() {
+ newShell(null).bgAppend("gradle --stop");
+ }
+
+ public ApiInfo apiInfo() {
+ return sdkInfo.getApiInfo();
+ }
+
+ public AttrInfo attrInfo() {
+ return sdkInfo.getAttrInfo();
+ }
+
+ public WidgetInfo widgetInfo() {
+ return sdkInfo.getWidgetInfo();
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/BaseFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/BaseFragment.java
index d48112e3e..7319e9287 100755
--- a/app/src/main/java/com/itsaky/androidide/fragments/BaseFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/BaseFragment.java
@@ -21,10 +21,11 @@
package com.itsaky.androidide.fragments;
import androidx.fragment.app.Fragment;
+
import com.itsaky.androidide.app.StudioActivity;
public class BaseFragment extends Fragment {
- protected StudioActivity getStudioActivity() {
- return (StudioActivity) getActivity();
- }
+ protected StudioActivity getStudioActivity() {
+ return (StudioActivity) getActivity();
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/CrashReportFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/CrashReportFragment.java
index d8a5fe66d..58eadca46 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/CrashReportFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/CrashReportFragment.java
@@ -27,9 +27,11 @@ 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.fragment.app.Fragment;
+
import com.blankj.utilcode.util.ClipboardUtils;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.BaseApplication;
@@ -37,116 +39,118 @@ import com.itsaky.androidide.databinding.LayoutCrashReportBinding;
public class CrashReportFragment extends Fragment {
- private LayoutCrashReportBinding binding;
- private boolean closeAppOnClick = true;
+ private LayoutCrashReportBinding binding;
+ private boolean closeAppOnClick = true;
- public static final String KEY_TITLE = "crash_title";
- public static final String KEY_MESSAGE = "crash_message";
- public static final String KEY_TRACE = "crash_trace";
+ public static final String KEY_TITLE = "crash_title";
+ public static final String KEY_MESSAGE = "crash_message";
+ public static final String KEY_TRACE = "crash_trace";
- @NonNull
- public static CrashReportFragment newInstance(@NonNull final String trace) {
- return CrashReportFragment.newInstance(null, null, trace);
- }
-
- @NonNull
- public static CrashReportFragment newInstance(
- @Nullable final String title, @Nullable final String message, @NonNull final String trace) {
- final var frag = new CrashReportFragment();
- final var args = new Bundle();
-
- args.putString(KEY_TRACE, trace);
-
- if (title != null) {
- args.putString(KEY_TITLE, title);
+ @NonNull
+ public static CrashReportFragment newInstance(@NonNull final String trace) {
+ return CrashReportFragment.newInstance(null, null, trace);
}
- if (message != null) {
- args.putString(KEY_MESSAGE, message);
+ @NonNull
+ public static CrashReportFragment newInstance(
+ @Nullable final String title,
+ @Nullable final String message,
+ @NonNull final String trace) {
+ final var frag = new CrashReportFragment();
+ final var args = new Bundle();
+
+ args.putString(KEY_TRACE, trace);
+
+ if (title != null) {
+ args.putString(KEY_TITLE, title);
+ }
+
+ if (message != null) {
+ args.putString(KEY_MESSAGE, message);
+ }
+
+ frag.setArguments(args);
+ return frag;
}
- frag.setArguments(args);
- return frag;
- }
-
- public CrashReportFragment setCloseAppOnClick(boolean closeAppOnClick) {
- this.closeAppOnClick = closeAppOnClick;
- return this;
- }
-
- @Nullable
- @Override
- public View onCreateView(
- @NonNull LayoutInflater inflater,
- @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- return (binding = LayoutCrashReportBinding.inflate(inflater, container, false)).getRoot();
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- final var args = requireArguments();
- var title = getString(R.string.msg_ide_crashed);
- var message = getString(R.string.msg_report_crash);
- var trace = "";
- if (args.containsKey(KEY_TITLE)) {
- title = args.getString(KEY_TITLE);
+ public CrashReportFragment setCloseAppOnClick(boolean closeAppOnClick) {
+ this.closeAppOnClick = closeAppOnClick;
+ return this;
}
- if (args.containsKey(KEY_MESSAGE)) {
- message = args.getString(KEY_MESSAGE);
+ @Nullable
+ @Override
+ public View onCreateView(
+ @NonNull LayoutInflater inflater,
+ @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ return (binding = LayoutCrashReportBinding.inflate(inflater, container, false)).getRoot();
}
- if (args.containsKey(KEY_TRACE)) {
- trace = args.getString(KEY_TRACE);
- trace = buildReportText(trace);
- } else {
- trace = "No stack strace was provided for the report";
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ final var args = requireArguments();
+ var title = getString(R.string.msg_ide_crashed);
+ var message = getString(R.string.msg_report_crash);
+ var trace = "";
+ if (args.containsKey(KEY_TITLE)) {
+ title = args.getString(KEY_TITLE);
+ }
+
+ if (args.containsKey(KEY_MESSAGE)) {
+ message = args.getString(KEY_MESSAGE);
+ }
+
+ if (args.containsKey(KEY_TRACE)) {
+ trace = args.getString(KEY_TRACE);
+ trace = buildReportText(trace);
+ } else {
+ trace = "No stack strace was provided for the report";
+ }
+
+ binding.crashTitle.setText(title);
+ binding.crashSubtitle.setText(message);
+ binding.logText.setText(trace);
+
+ final var report = trace;
+ binding.closeButton.setOnClickListener(
+ v -> {
+ if (closeAppOnClick) {
+ requireActivity().finishAffinity();
+ } else {
+ requireActivity().finish();
+ }
+ });
+ binding.reportButton.setOnClickListener(v -> reportTrace(report));
}
- binding.crashTitle.setText(title);
- binding.crashSubtitle.setText(message);
- binding.logText.setText(trace);
+ private void reportTrace(String report) {
+ ClipboardUtils.copyText("AndroidIDE CrashLog", report);
- final var report = trace;
- binding.closeButton.setOnClickListener(
- v -> {
- if (closeAppOnClick) {
- requireActivity().finishAffinity();
- } else {
- requireActivity().finish();
- }
- });
- binding.reportButton.setOnClickListener(v -> reportTrace(report));
- }
+ final var url = BaseApplication.GITHUB_URL.concat("/issues");
+ final var intent = new Intent();
+ intent.setAction(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(url));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
- private void reportTrace(String report) {
- ClipboardUtils.copyText("AndroidIDE CrashLog", report);
-
- final var url = BaseApplication.GITHUB_URL.concat("/issues");
- final var intent = new Intent();
- intent.setAction(Intent.ACTION_VIEW);
- intent.setData(Uri.parse(url));
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- }
-
- @NonNull
- private String buildReportText(String trace) {
- return "AndroidIDE crash report\n"
- + "Manufacturer: "
- + getManufacturer()
- + "\n"
- + "Device: "
- + getModel()
- + "\n"
- + "App version: "
- + getAppVersionName()
- + " ("
- + getAppVersionCode()
- + ")\n\n Stacktrace: \n"
- + trace;
- }
+ @NonNull
+ private String buildReportText(String trace) {
+ return "AndroidIDE crash report\n"
+ + "Manufacturer: "
+ + getManufacturer()
+ + "\n"
+ + "Device: "
+ + getModel()
+ + "\n"
+ + "App version: "
+ + getAppVersionName()
+ + " ("
+ + getAppVersionCode()
+ + ")\n\n Stacktrace: \n"
+ + trace;
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/DiagnosticsListFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/DiagnosticsListFragment.java
index 0ca758683..58e7cb9b4 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/DiagnosticsListFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/DiagnosticsListFragment.java
@@ -22,63 +22,68 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+
import com.itsaky.androidide.adapters.DiagnosticsAdapter;
import com.itsaky.androidide.views.EmptyView;
+
import java.util.ArrayList;
public class DiagnosticsListFragment extends Fragment {
- private RecyclerView list;
- private EmptyView emptyView;
+ private RecyclerView list;
+ private EmptyView emptyView;
- private DiagnosticsAdapter unsavedAdapter;
+ private DiagnosticsAdapter unsavedAdapter;
- @Nullable
- @Override
- public View onCreateView(
- @NonNull LayoutInflater inflater,
- @Nullable ViewGroup parent,
- @Nullable Bundle savedInstanceState) {
- final var container = new LinearLayout(inflater.getContext());
- container.setOrientation(LinearLayout.VERTICAL);
- container.setLayoutParams(new ViewGroup.LayoutParams(-1, -1));
+ @Nullable
+ @Override
+ public View onCreateView(
+ @NonNull LayoutInflater inflater,
+ @Nullable ViewGroup parent,
+ @Nullable Bundle savedInstanceState) {
+ final var container = new LinearLayout(inflater.getContext());
+ container.setOrientation(LinearLayout.VERTICAL);
+ container.setLayoutParams(new ViewGroup.LayoutParams(-1, -1));
- this.list = new RecyclerView(inflater.getContext());
- this.emptyView = new EmptyView(inflater.getContext());
+ this.list = new RecyclerView(inflater.getContext());
+ this.emptyView = new EmptyView(inflater.getContext());
- container.addView(list, new LinearLayout.LayoutParams(-1, -1));
- container.addView(emptyView, new LinearLayout.LayoutParams(-1, -1));
+ container.addView(list, new LinearLayout.LayoutParams(-1, -1));
+ container.addView(emptyView, new LinearLayout.LayoutParams(-1, -1));
- return container;
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- list.setLayoutManager(new LinearLayoutManager(list.getContext()));
- list.setAdapter(
- unsavedAdapter != null ? unsavedAdapter : new DiagnosticsAdapter(new ArrayList<>(), null));
- unsavedAdapter = null;
- }
-
- public void setAdapter(@NonNull DiagnosticsAdapter adapter) {
- if (list != null) {
- list.setAdapter(adapter);
- } else {
- unsavedAdapter = adapter;
+ return container;
}
- }
- public void handleResultVisibility(boolean error) {
- if (emptyView != null && list != null) {
- emptyView.setVisibility(error ? View.VISIBLE : View.GONE);
- list.setVisibility(error ? View.GONE : View.VISIBLE);
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ list.setLayoutManager(new LinearLayoutManager(list.getContext()));
+ list.setAdapter(
+ unsavedAdapter != null
+ ? unsavedAdapter
+ : new DiagnosticsAdapter(new ArrayList<>(), null));
+ unsavedAdapter = null;
+ }
+
+ public void setAdapter(@NonNull DiagnosticsAdapter adapter) {
+ if (list != null) {
+ list.setAdapter(adapter);
+ } else {
+ unsavedAdapter = adapter;
+ }
+ }
+
+ public void handleResultVisibility(boolean error) {
+ if (emptyView != null && list != null) {
+ emptyView.setVisibility(error ? View.VISIBLE : View.GONE);
+ list.setVisibility(error ? View.GONE : View.VISIBLE);
+ }
}
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java
index 882d856e1..3bb002b3b 100755
--- a/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java
@@ -27,9 +27,11 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ProgressBar;
+
import androidx.annotation.NonNull;
import androidx.transition.ChangeBounds;
import androidx.transition.TransitionManager;
+
import com.blankj.utilcode.util.FileIOUtils;
import com.blankj.utilcode.util.FileUtils;
import com.blankj.utilcode.util.IntentUtils;
@@ -46,223 +48,232 @@ import com.itsaky.androidide.views.editor.CodeEditorView;
import com.itsaky.toaster.Toaster;
import com.unnamed.b.atv.model.TreeNode;
import com.unnamed.b.atv.view.AndroidTreeView;
+
import java.io.File;
import java.util.Arrays;
public class FileTreeFragment extends BottomSheetDialogFragment
- implements TreeNode.TreeNodeClickListener, TreeNode.TreeNodeLongClickListener {
+ implements TreeNode.TreeNodeClickListener, TreeNode.TreeNodeLongClickListener {
- private LayoutEditorFileTreeBinding binding;
- private AndroidTreeView mFileTreeView;
- private FileActionListener mFileActionListener;
- private AndroidProject mProject;
- private TreeNode mRoot;
+ private LayoutEditorFileTreeBinding binding;
+ private AndroidTreeView mFileTreeView;
+ private FileActionListener mFileActionListener;
+ private AndroidProject mProject;
+ private TreeNode mRoot;
- public FileTreeFragment() {}
+ public FileTreeFragment() {}
- public FileTreeFragment setFileActionListener(FileActionListener listener) {
- this.mFileActionListener = listener;
- return this;
- }
-
- @NonNull
- public static FileTreeFragment newInstance(AndroidProject project) {
- Bundle bundle = new Bundle();
- bundle.putParcelable("project", project);
- FileTreeFragment frag = new FileTreeFragment();
- frag.setArguments(bundle);
- return frag;
- }
-
- @Override
- public View onCreateView(
- LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- binding = LayoutEditorFileTreeBinding.inflate(inflater, container, false);
- return binding.getRoot();
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- mProject = requireArguments().getParcelable("project");
-
- listProjectFiles();
- }
-
- @Override
- public void onClick(TreeNode node, Object p2) {
- final File f = (File) p2;
- if (!f.exists()) {
- return;
+ public FileTreeFragment setFileActionListener(FileActionListener listener) {
+ this.mFileActionListener = listener;
+ return this;
}
- if (f.isFile()) {
- if (f.getName().endsWith(".apk")) {
- final var intent = IntentUtils.getInstallAppIntent(f);
- if (intent != null) {
- requireContext().startActivity(intent);
- } else {
- StudioApp.getInstance()
- .toast(getString(R.string.msg_apk_install_intent_failed), Toaster.Type.ERROR);
+ @NonNull
+ public static FileTreeFragment newInstance(AndroidProject project) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable("project", project);
+ FileTreeFragment frag = new FileTreeFragment();
+ frag.setArguments(bundle);
+ return frag;
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ binding = LayoutEditorFileTreeBinding.inflate(inflater, container, false);
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ mProject = requireArguments().getParcelable("project");
+
+ listProjectFiles();
+ }
+
+ @Override
+ public void onClick(TreeNode node, Object p2) {
+ final File f = (File) p2;
+ if (!f.exists()) {
+ return;
}
- } else if (mFileActionListener != null && (FileUtils.isUtf8(f) || f.length() == 0)) {
- mFileActionListener.openFile(f);
- }
- } else if (f.isDirectory() && f.exists()) {
- if (node.isExpanded()) {
- collapseNode(node);
- } else if (f.getAbsolutePath().equals(Environment.GRADLE_USER_HOME.getAbsolutePath())
- && !node.isExpanded()) {
- expandNode(node);
- } else {
- setLoading(node);
- listNode(node);
- }
- }
- }
- private void listNode(@NonNull TreeNode node) {
- node.getChildren().clear();
- node.setExpanded(false);
- new TaskExecutor()
- .executeAsync(
- () -> {
- getNodeFromFiles(
- node.getValue().listFiles(/* new FileTreeCallable.HiddenFilesFilter() */ ), node);
- TreeNode temp = node;
- while (temp.size() == 1) {
- temp = temp.childAt(0);
- if (!temp.getValue().isDirectory()) {
- break;
+ if (f.isFile()) {
+ if (f.getName().endsWith(".apk")) {
+ final var intent = IntentUtils.getInstallAppIntent(f);
+ if (intent != null) {
+ requireContext().startActivity(intent);
+ } else {
+ StudioApp.getInstance()
+ .toast(
+ getString(R.string.msg_apk_install_intent_failed),
+ Toaster.Type.ERROR);
}
- getNodeFromFiles(
- temp.getValue().listFiles(/* new FileTreeCallable.HiddenFilesFilter() */ ),
- temp);
- temp.setExpanded(true);
- }
- return null;
- },
- __ -> {
- updateChevron(node);
- expandNode(node);
- });
- }
-
- private void getNodeFromFiles(File[] files, TreeNode parent) {
- Arrays.sort(files, new FileTreeCallable.SortFileName());
- Arrays.sort(files, new FileTreeCallable.SortFolder());
- for (File file : files) {
- TreeNode node = new TreeNode(file);
- node.setViewHolder(new FileTreeViewHolder(getContext()));
- parent.addChild(node);
- }
- }
-
- private void setLoading(@NonNull TreeNode node) {
- if (node.getViewHolder() instanceof FileTreeViewHolder) {
- ((FileTreeViewHolder) node.getViewHolder()).setLoading();
- }
- }
-
- private void updateChevron(@NonNull TreeNode node) {
- if (node.getViewHolder() instanceof FileTreeViewHolder) {
- ((FileTreeViewHolder) node.getViewHolder()).updateChevron(!node.isExpanded());
- }
- }
-
- @Override
- public boolean onLongClick(TreeNode node, Object value) {
- if (mFileActionListener != null) {
- mFileActionListener.showFileOptions((File) value, node);
- }
- return true;
- }
-
- public void expandNode(TreeNode node) {
- if (mFileTreeView == null) {
- return;
+ } else if (mFileActionListener != null && (FileUtils.isUtf8(f) || f.length() == 0)) {
+ mFileActionListener.openFile(f);
+ }
+ } else if (f.isDirectory() && f.exists()) {
+ if (node.isExpanded()) {
+ collapseNode(node);
+ } else if (f.getAbsolutePath().equals(Environment.GRADLE_USER_HOME.getAbsolutePath())
+ && !node.isExpanded()) {
+ expandNode(node);
+ } else {
+ setLoading(node);
+ listNode(node);
+ }
+ }
}
- TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds());
- mFileTreeView.expandNode(node);
- }
-
- public void collapseNode(TreeNode node) {
- if (mFileTreeView == null) {
- return;
+ private void listNode(@NonNull TreeNode node) {
+ node.getChildren().clear();
+ node.setExpanded(false);
+ new TaskExecutor()
+ .executeAsync(
+ () -> {
+ getNodeFromFiles(
+ node.getValue()
+ .listFiles(
+ /* new FileTreeCallable.HiddenFilesFilter() */ ),
+ node);
+ TreeNode temp = node;
+ while (temp.size() == 1) {
+ temp = temp.childAt(0);
+ if (!temp.getValue().isDirectory()) {
+ break;
+ }
+ getNodeFromFiles(
+ temp.getValue()
+ .listFiles(
+ /* new FileTreeCallable.HiddenFilesFilter() */ ),
+ temp);
+ temp.setExpanded(true);
+ }
+ return null;
+ },
+ __ -> {
+ updateChevron(node);
+ expandNode(node);
+ });
}
- TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds());
- mFileTreeView.collapseNode(node);
- }
-
- public void listProjectFiles() {
- if (mProject == null) {
- return;
+ private void getNodeFromFiles(File[] files, TreeNode parent) {
+ Arrays.sort(files, new FileTreeCallable.SortFileName());
+ Arrays.sort(files, new FileTreeCallable.SortFolder());
+ for (File file : files) {
+ TreeNode node = new TreeNode(file);
+ node.setViewHolder(new FileTreeViewHolder(getContext()));
+ parent.addChild(node);
+ }
}
- final File gradleProps = Environment.GRADLE_PROPS;
- final File gradleHome = Environment.GRADLE_USER_HOME;
- File projectDir = new File(mProject.getProjectPath());
- mRoot = TreeNode.root(projectDir);
- if (gradleHome.exists() && gradleHome.isDirectory()) {
- if (!gradleProps.exists()) {
- FileIOUtils.writeFileFromString(gradleProps, Environment.SAMPLE_GRADLE_PROP_CONTENTS);
- }
- TreeNode home = new TreeNode(gradleHome);
- home.setViewHolder(new FileTreeViewHolder(getContext()));
- TreeNode prop = new TreeNode(gradleProps);
- prop.setViewHolder(new FileTreeViewHolder(getContext()));
- home.addChild(prop);
- mRoot.addChild(home);
- }
- mRoot.setViewHolder(new FileTreeViewHolder(getContext()));
-
- getScrollView().setVisibility(View.GONE);
- getLoadingProgress().setVisibility(View.VISIBLE);
- new TaskExecutor()
- .executeAsync(
- new FileTreeCallable(getContext(), mRoot, projectDir),
- (result) -> {
- getScrollView().setVisibility(View.VISIBLE);
- getLoadingProgress().setVisibility(View.GONE);
- AndroidTreeView tree = createTreeView(mRoot);
- if (tree != null) {
- tree.setUseAutoToggle(false);
- tree.setDefaultNodeClickListener(FileTreeFragment.this);
- tree.setDefaultNodeLongClickListener(FileTreeFragment.this);
- getScrollView().removeAllViews();
- getScrollView().addView(tree.getView());
- }
- });
- }
-
- public AndroidTreeView createTreeView(TreeNode node) {
- Context ctx = null;
- if (getActivity() != null) {
- ctx = getActivity();
- } else if (getContext() != null) {
- ctx = getContext();
+ private void setLoading(@NonNull TreeNode node) {
+ if (node.getViewHolder() instanceof FileTreeViewHolder) {
+ ((FileTreeViewHolder) node.getViewHolder()).setLoading();
+ }
}
- if (ctx == null) {
- return null;
+ private void updateChevron(@NonNull TreeNode node) {
+ if (node.getViewHolder() instanceof FileTreeViewHolder) {
+ ((FileTreeViewHolder) node.getViewHolder()).updateChevron(!node.isExpanded());
+ }
}
- return mFileTreeView = new AndroidTreeView(ctx, node, R.drawable.bg_ripple);
- }
+ @Override
+ public boolean onLongClick(TreeNode node, Object value) {
+ if (mFileActionListener != null) {
+ mFileActionListener.showFileOptions((File) value, node);
+ }
+ return true;
+ }
- public HorizontalScrollView getScrollView() {
- return binding.filetreeHorizontalScrollView;
- }
+ public void expandNode(TreeNode node) {
+ if (mFileTreeView == null) {
+ return;
+ }
- public ProgressBar getLoadingProgress() {
- return binding.fileTreeLoadingProgress;
- }
+ TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds());
+ mFileTreeView.expandNode(node);
+ }
- public interface FileActionListener {
- CodeEditorView openFile(File file);
+ public void collapseNode(TreeNode node) {
+ if (mFileTreeView == null) {
+ return;
+ }
- void showFileOptions(File file, TreeNode node);
- }
+ TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds());
+ mFileTreeView.collapseNode(node);
+ }
+
+ public void listProjectFiles() {
+ if (mProject == null) {
+ return;
+ }
+
+ final File gradleProps = Environment.GRADLE_PROPS;
+ final File gradleHome = Environment.GRADLE_USER_HOME;
+ File projectDir = new File(mProject.getProjectPath());
+ mRoot = TreeNode.root(projectDir);
+ if (gradleHome.exists() && gradleHome.isDirectory()) {
+ if (!gradleProps.exists()) {
+ FileIOUtils.writeFileFromString(
+ gradleProps, Environment.SAMPLE_GRADLE_PROP_CONTENTS);
+ }
+ TreeNode home = new TreeNode(gradleHome);
+ home.setViewHolder(new FileTreeViewHolder(getContext()));
+ TreeNode prop = new TreeNode(gradleProps);
+ prop.setViewHolder(new FileTreeViewHolder(getContext()));
+ home.addChild(prop);
+ mRoot.addChild(home);
+ }
+ mRoot.setViewHolder(new FileTreeViewHolder(getContext()));
+
+ getScrollView().setVisibility(View.GONE);
+ getLoadingProgress().setVisibility(View.VISIBLE);
+ new TaskExecutor()
+ .executeAsync(
+ new FileTreeCallable(getContext(), mRoot, projectDir),
+ (result) -> {
+ getScrollView().setVisibility(View.VISIBLE);
+ getLoadingProgress().setVisibility(View.GONE);
+ AndroidTreeView tree = createTreeView(mRoot);
+ if (tree != null) {
+ tree.setUseAutoToggle(false);
+ tree.setDefaultNodeClickListener(FileTreeFragment.this);
+ tree.setDefaultNodeLongClickListener(FileTreeFragment.this);
+ getScrollView().removeAllViews();
+ getScrollView().addView(tree.getView());
+ }
+ });
+ }
+
+ public AndroidTreeView createTreeView(TreeNode node) {
+ Context ctx = null;
+ if (getActivity() != null) {
+ ctx = getActivity();
+ } else if (getContext() != null) {
+ ctx = getContext();
+ }
+
+ if (ctx == null) {
+ return null;
+ }
+
+ return mFileTreeView = new AndroidTreeView(ctx, node, R.drawable.bg_ripple);
+ }
+
+ public HorizontalScrollView getScrollView() {
+ return binding.filetreeHorizontalScrollView;
+ }
+
+ public ProgressBar getLoadingProgress() {
+ return binding.fileTreeLoadingProgress;
+ }
+
+ public interface FileActionListener {
+ CodeEditorView openFile(File file);
+
+ void showFileOptions(File file, TreeNode node);
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/IDELogFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/IDELogFragment.java
index 8d778bc3e..afd2d1af8 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/IDELogFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/IDELogFragment.java
@@ -21,13 +21,13 @@ import com.itsaky.androidide.utils.Logger;
public class IDELogFragment extends SimpleOutputFragment {
- private final Logger.LogListener listener = this::log;
+ private final Logger.LogListener listener = this::log;
- public IDELogFragment() {
- Logger.addLogListener(listener);
- }
+ public IDELogFragment() {
+ Logger.addLogListener(listener);
+ }
- private void log(int priority, String tag, String message) {
- appendOutput(String.format("%s: %s", tag, message));
- }
+ private void log(int priority, String tag, String message) {
+ appendOutput(String.format("%s: %s", tag, message));
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/LogViewFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/LogViewFragment.java
index 592e7cb1a..2c05531e9 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/LogViewFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/LogViewFragment.java
@@ -19,55 +19,59 @@ package com.itsaky.androidide.fragments;
import android.os.Bundle;
import android.view.View;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+
import com.blankj.utilcode.util.ThreadUtils;
import com.itsaky.androidide.language.logs.LogLanguageImpl;
import com.itsaky.androidide.models.LogLine;
+
import io.github.rosemoe.sora.lang.Language;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class LogViewFragment extends NonEditableEditorFragment {
- private final Language language = getLanguage();
- private final List unsavedLines = new ArrayList<>();
+ private final Language language = getLanguage();
+ private final List unsavedLines = new ArrayList<>();
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- Objects.requireNonNull(getEditor()).setEditorLanguage(language);
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ Objects.requireNonNull(getEditor()).setEditorLanguage(language);
+
+ if (!unsavedLines.isEmpty()) {
+ for (var line : unsavedLines) {
+ applyToLanguage(line);
+ getEditor().append(line.toString().trim() + "\n");
+ }
+ unsavedLines.clear();
+ }
+ }
+
+ public void appendLog(LogLine line) {
+ if (getEditor() == null) {
+ unsavedLines.add(line);
+ return;
+ }
- if (!unsavedLines.isEmpty()) {
- for (var line : unsavedLines) {
applyToLanguage(line);
- getEditor().append(line.toString().trim() + "\n");
- }
- unsavedLines.clear();
- }
- }
- public void appendLog(LogLine line) {
- if (getEditor() == null) {
- unsavedLines.add(line);
- return;
+ final var lineString = line.toString();
+ final var msg = lineString.endsWith("\n") ? lineString : lineString + "\n";
+ ThreadUtils.runOnUiThread(() -> getEditor().append(msg));
}
- applyToLanguage(line);
-
- final var lineString = line.toString();
- final var msg = lineString.endsWith("\n") ? lineString : lineString + "\n";
- ThreadUtils.runOnUiThread(() -> getEditor().append(msg));
- }
-
- private void applyToLanguage(LogLine line) {
- if (language instanceof LogLanguageImpl) {
- ((LogLanguageImpl) this.language).addLine(line);
+ private void applyToLanguage(LogLine line) {
+ if (language instanceof LogLanguageImpl) {
+ ((LogLanguageImpl) this.language).addLine(line);
+ }
}
- }
- protected Language getLanguage() {
- return new LogLanguageImpl();
- }
+ protected Language getLanguage() {
+ return new LogLanguageImpl();
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/NonEditableEditorFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/NonEditableEditorFragment.java
index 0524306f5..59dc8b5fc 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/NonEditableEditorFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/NonEditableEditorFragment.java
@@ -21,50 +21,53 @@ 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.fragment.app.Fragment;
+
import com.itsaky.androidide.databinding.FragmentNonEditableEditorBinding;
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
import com.itsaky.androidide.utils.TypefaceUtils;
import com.itsaky.androidide.views.editor.IDEEditor;
+
import io.github.rosemoe.sora.lang.EmptyLanguage;
public abstract class NonEditableEditorFragment extends Fragment {
- private FragmentNonEditableEditorBinding binding;
+ private FragmentNonEditableEditorBinding binding;
- @Nullable
- @Override
- public View onCreateView(
- @NonNull LayoutInflater inflater,
- @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- this.binding = FragmentNonEditableEditorBinding.inflate(inflater, container, false);
- return binding.getRoot();
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- final var editor = binding.getRoot();
- editor.setEditable(false);
- editor.setDividerWidth(0);
- editor.setEditorLanguage(new EmptyLanguage());
- editor.setWordwrap(false);
- editor.setUndoEnabled(false);
- editor.setTypefaceLineNumber(TypefaceUtils.jetbrainsMono());
- editor.setTypefaceText(TypefaceUtils.jetbrainsMono());
- editor.setTextSize(12);
- editor.setColorScheme(new SchemeAndroidIDE());
- }
-
- @Nullable
- public IDEEditor getEditor() {
- if (binding == null) {
- return null;
+ @Nullable
+ @Override
+ public View onCreateView(
+ @NonNull LayoutInflater inflater,
+ @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ this.binding = FragmentNonEditableEditorBinding.inflate(inflater, container, false);
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ final var editor = binding.getRoot();
+ editor.setEditable(false);
+ editor.setDividerWidth(0);
+ editor.setEditorLanguage(new EmptyLanguage());
+ editor.setWordwrap(false);
+ editor.setUndoEnabled(false);
+ editor.setTypefaceLineNumber(TypefaceUtils.jetbrainsMono());
+ editor.setTypefaceText(TypefaceUtils.jetbrainsMono());
+ editor.setTextSize(12);
+ editor.setColorScheme(new SchemeAndroidIDE());
+ }
+
+ @Nullable
+ public IDEEditor getEditor() {
+ if (binding == null) {
+ return null;
+ }
+ return binding.editor;
}
- return binding.editor;
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/ProjectsFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/ProjectsFragment.java
index 63556ff61..ce401b0cd 100755
--- a/app/src/main/java/com/itsaky/androidide/fragments/ProjectsFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/ProjectsFragment.java
@@ -26,8 +26,10 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
+
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+
import com.itsaky.androidide.EditorActivity;
import com.itsaky.androidide.adapters.ProjectsListAdapter;
import com.itsaky.androidide.app.StudioApp;
@@ -35,6 +37,7 @@ import com.itsaky.androidide.databinding.LayoutProjectsBinding;
import com.itsaky.androidide.project.AndroidProject;
import com.itsaky.androidide.tasks.TaskExecutor;
import com.itsaky.androidide.utils.ProjectFinder;
+
import java.io.File;
import java.util.ArrayList;
import java.util.List;
@@ -42,99 +45,104 @@ import java.util.concurrent.Callable;
public class ProjectsFragment extends BaseFragment {
- private LayoutProjectsBinding binding;
- private ArrayList mProjects;
- private boolean isLoading = false;
+ private LayoutProjectsBinding binding;
+ private ArrayList mProjects;
+ private boolean isLoading = false;
- @Override
- public View onCreateView(
- LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- binding = LayoutProjectsBinding.inflate(inflater, container, false);
- return binding.getRoot();
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- if (getActivity() == null) return;
-
- loadProjects();
- }
-
- public void loadProjects() {
- if (!isLoading && getStudioActivity().isStoragePermissionGranted()) {
- getProgressBar().setVisibility(View.VISIBLE);
- getRecyclerView().setVisibility(View.GONE);
- setLoading(true);
- final ProjectsFragment frag = this;
- new TaskExecutor()
- .executeAsync(
- new GetProjectsTask(),
- (result) -> {
- frag.setLoading(false);
-
- if (frag.getProjectsList() == null || frag.getProjectsList().size() == 0) {
- frag.getProgressBar().setVisibility(View.GONE);
- frag.getRecyclerView().setVisibility(View.GONE);
- frag.getNothingLayout().setVisibility(View.VISIBLE);
- } else {
- frag.getNothingLayout().setVisibility(View.GONE);
- frag.getProgressBar().setVisibility(View.GONE);
- frag.getRecyclerView().setVisibility(View.VISIBLE);
- frag.getRecyclerView().setLayoutManager(new LinearLayoutManager(getActivity()));
- frag.getRecyclerView()
- .setAdapter(
- new ProjectsListAdapter(mProjects, (project) -> openProject(project)));
- }
- });
- }
- }
-
- private void openProject(AndroidProject project) {
- startActivity(new Intent(getActivity(), EditorActivity.class).putExtra("project", project));
- }
-
- public void getProjects() throws Exception {
- mProjects = new ArrayList<>();
- File[] files = StudioApp.getInstance().listProjects();
- for (File file : files) {
- AndroidProject project = ProjectFinder.fromFolder(file);
-
- if (project == null) continue;
-
- mProjects.add(project);
- }
- }
-
- public ProgressBar getProgressBar() {
- return binding.projectsProgressBar;
- }
-
- public RecyclerView getRecyclerView() {
- return binding.mainProjectsRecyclerView;
- }
-
- public boolean isLoading() {
- return isLoading;
- }
-
- public void setLoading(boolean isLoading) {
- this.isLoading = isLoading;
- }
-
- public ArrayList getProjectsList() {
- return mProjects;
- }
-
- public View getNothingLayout() {
- return binding.projectsNothingLayout;
- }
-
- private class GetProjectsTask implements Callable> {
@Override
- public List call() throws Exception {
- getProjects();
- return null;
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ binding = LayoutProjectsBinding.inflate(inflater, container, false);
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ if (getActivity() == null) return;
+
+ loadProjects();
+ }
+
+ public void loadProjects() {
+ if (!isLoading && getStudioActivity().isStoragePermissionGranted()) {
+ getProgressBar().setVisibility(View.VISIBLE);
+ getRecyclerView().setVisibility(View.GONE);
+ setLoading(true);
+ final ProjectsFragment frag = this;
+ new TaskExecutor()
+ .executeAsync(
+ new GetProjectsTask(),
+ (result) -> {
+ frag.setLoading(false);
+
+ if (frag.getProjectsList() == null
+ || frag.getProjectsList().size() == 0) {
+ frag.getProgressBar().setVisibility(View.GONE);
+ frag.getRecyclerView().setVisibility(View.GONE);
+ frag.getNothingLayout().setVisibility(View.VISIBLE);
+ } else {
+ frag.getNothingLayout().setVisibility(View.GONE);
+ frag.getProgressBar().setVisibility(View.GONE);
+ frag.getRecyclerView().setVisibility(View.VISIBLE);
+ frag.getRecyclerView()
+ .setLayoutManager(
+ new LinearLayoutManager(getActivity()));
+ frag.getRecyclerView()
+ .setAdapter(
+ new ProjectsListAdapter(
+ mProjects,
+ (project) -> openProject(project)));
+ }
+ });
+ }
+ }
+
+ private void openProject(AndroidProject project) {
+ startActivity(new Intent(getActivity(), EditorActivity.class).putExtra("project", project));
+ }
+
+ public void getProjects() throws Exception {
+ mProjects = new ArrayList<>();
+ File[] files = StudioApp.getInstance().listProjects();
+ for (File file : files) {
+ AndroidProject project = ProjectFinder.fromFolder(file);
+
+ if (project == null) continue;
+
+ mProjects.add(project);
+ }
+ }
+
+ public ProgressBar getProgressBar() {
+ return binding.projectsProgressBar;
+ }
+
+ public RecyclerView getRecyclerView() {
+ return binding.mainProjectsRecyclerView;
+ }
+
+ public boolean isLoading() {
+ return isLoading;
+ }
+
+ public void setLoading(boolean isLoading) {
+ this.isLoading = isLoading;
+ }
+
+ public ArrayList getProjectsList() {
+ return mProjects;
+ }
+
+ public View getNothingLayout() {
+ return binding.projectsNothingLayout;
+ }
+
+ private class GetProjectsTask implements Callable> {
+ @Override
+ public List call() throws Exception {
+ getProjects();
+ return null;
+ }
}
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/SearchResultFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/SearchResultFragment.java
index bb0344eaa..551587a49 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/SearchResultFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/SearchResultFragment.java
@@ -22,62 +22,64 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+
import com.itsaky.androidide.adapters.SearchListAdapter;
import com.itsaky.androidide.views.EmptyView;
public class SearchResultFragment extends Fragment {
- private RecyclerView list;
- private EmptyView emptyView;
+ private RecyclerView list;
+ private EmptyView emptyView;
- private SearchListAdapter unsavedAdapter;
+ private SearchListAdapter unsavedAdapter;
- @Nullable
- @Override
- public View onCreateView(
- @NonNull LayoutInflater inflater,
- @Nullable ViewGroup parent,
- @Nullable Bundle savedInstanceState) {
- final var container = new LinearLayout(inflater.getContext());
- container.setOrientation(LinearLayout.VERTICAL);
- container.setLayoutParams(new ViewGroup.LayoutParams(-1, -1));
+ @Nullable
+ @Override
+ public View onCreateView(
+ @NonNull LayoutInflater inflater,
+ @Nullable ViewGroup parent,
+ @Nullable Bundle savedInstanceState) {
+ final var container = new LinearLayout(inflater.getContext());
+ container.setOrientation(LinearLayout.VERTICAL);
+ container.setLayoutParams(new ViewGroup.LayoutParams(-1, -1));
- this.list = new RecyclerView(inflater.getContext());
- this.emptyView = new EmptyView(inflater.getContext());
+ this.list = new RecyclerView(inflater.getContext());
+ this.emptyView = new EmptyView(inflater.getContext());
- container.addView(list, new LinearLayout.LayoutParams(-1, -1));
- container.addView(emptyView, new LinearLayout.LayoutParams(-1, -1));
+ container.addView(list, new LinearLayout.LayoutParams(-1, -1));
+ container.addView(emptyView, new LinearLayout.LayoutParams(-1, -1));
- return container;
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- list.setLayoutManager(new LinearLayoutManager(list.getContext()));
- list.setAdapter(
- unsavedAdapter != null ? unsavedAdapter : new SearchListAdapter(null, null, null));
- unsavedAdapter = null;
- }
-
- public void setAdapter(@NonNull final SearchListAdapter adapter) {
- if (list == null) {
- unsavedAdapter = adapter;
- return;
+ return container;
}
- this.list.setAdapter(adapter);
- }
- public void handleResultVisibility(boolean error) {
- if (emptyView != null && list != null) {
- emptyView.setVisibility(error ? View.VISIBLE : View.GONE);
- list.setVisibility(error ? View.GONE : View.VISIBLE);
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ list.setLayoutManager(new LinearLayoutManager(list.getContext()));
+ list.setAdapter(
+ unsavedAdapter != null ? unsavedAdapter : new SearchListAdapter(null, null, null));
+ unsavedAdapter = null;
+ }
+
+ public void setAdapter(@NonNull final SearchListAdapter adapter) {
+ if (list == null) {
+ unsavedAdapter = adapter;
+ return;
+ }
+ this.list.setAdapter(adapter);
+ }
+
+ public void handleResultVisibility(boolean error) {
+ if (emptyView != null && list != null) {
+ emptyView.setVisibility(error ? View.VISIBLE : View.GONE);
+ list.setVisibility(error ? View.GONE : View.VISIBLE);
+ }
}
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/SimpleOutputFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/SimpleOutputFragment.java
index daae22835..19c848477 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/SimpleOutputFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/SimpleOutputFragment.java
@@ -19,38 +19,42 @@ package com.itsaky.androidide.fragments;
import android.os.Bundle;
import android.view.View;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+
import com.blankj.utilcode.util.ThreadUtils;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class SimpleOutputFragment extends NonEditableEditorFragment {
- private final List unsavedLines = new ArrayList<>();
+ private final List unsavedLines = new ArrayList<>();
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
- if (!unsavedLines.isEmpty()) {
- for (String line : unsavedLines) {
- Objects.requireNonNull(getEditor()).append(line.trim() + "\n");
- }
- unsavedLines.clear();
+ if (!unsavedLines.isEmpty()) {
+ for (String line : unsavedLines) {
+ Objects.requireNonNull(getEditor()).append(line.trim() + "\n");
+ }
+ unsavedLines.clear();
+ }
}
- }
- public void appendOutput(String output) {
- if (getEditor() == null) {
- unsavedLines.add(output);
- return;
+ public void appendOutput(String output) {
+ if (getEditor() == null) {
+ unsavedLines.add(output);
+ return;
+ }
+ ThreadUtils.runOnUiThread(
+ () -> {
+ final var message =
+ output == null || output.endsWith("\n") ? output : output + "\n";
+ getEditor().append(message);
+ });
}
- ThreadUtils.runOnUiThread(
- () -> {
- final var message = output == null || output.endsWith("\n") ? output : output + "\n";
- getEditor().append(message);
- });
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/preferences/BasePreferenceFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/preferences/BasePreferenceFragment.java
index eae850770..f0b7cc9d6 100755
--- a/app/src/main/java/com/itsaky/androidide/fragments/preferences/BasePreferenceFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/preferences/BasePreferenceFragment.java
@@ -24,31 +24,35 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceFragmentCompat;
+
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.managers.PreferenceManager;
public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
- protected PreferenceManager mPrefManager;
+ protected PreferenceManager mPrefManager;
- @Override
- public View onCreateView(
- LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View v = super.onCreateView(inflater, container, savedInstanceState);
- v.setBackgroundColor(
- ContextCompat.getColor(
- StudioApp.getInstance(), com.itsaky.androidide.R.color.content_background));
- return v;
- }
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View v = super.onCreateView(inflater, container, savedInstanceState);
+ v.setBackgroundColor(
+ ContextCompat.getColor(
+ StudioApp.getInstance(), com.itsaky.androidide.R.color.content_background));
+ return v;
+ }
- @Override
- public void onCreatePreferences(Bundle p1, String p2) {
- setPreferencesFromResource(com.itsaky.androidide.R.xml.ide_prefs, p2);
- }
+ @Override
+ public void onCreatePreferences(Bundle p1, String p2) {
+ setPreferencesFromResource(com.itsaky.androidide.R.xml.ide_prefs, p2);
+ }
- protected PreferenceManager getPrefManager() {
- return mPrefManager == null ? mPrefManager = new PreferenceManager(getContext()) : mPrefManager;
- }
+ protected PreferenceManager getPrefManager() {
+ return mPrefManager == null
+ ? mPrefManager = new PreferenceManager(getContext())
+ : mPrefManager;
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java b/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java
index 76ffe318d..5e3edc636 100755
--- a/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java
@@ -21,9 +21,11 @@
package com.itsaky.androidide.fragments.preferences;
import android.os.Bundle;
+
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
+
import com.blankj.utilcode.util.FileUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.itsaky.androidide.R;
@@ -31,124 +33,129 @@ import com.itsaky.androidide.fragments.sheets.ProgressSheet;
import com.itsaky.androidide.tasks.TaskExecutor;
import com.itsaky.androidide.utils.DialogUtils;
import com.itsaky.androidide.utils.Environment;
+
import java.io.File;
public class BuildPreferences extends BasePreferenceFragment
- implements Preference.OnPreferenceClickListener {
+ implements Preference.OnPreferenceClickListener {
- public static final String KEY_GRADLE_COMMAMDS = "idepref_build_gradleCommands";
- public static final String KEY_GRADLE_CLEAR_CACHE = "idepref_build_gradleClearCache";
+ public static final String KEY_GRADLE_COMMAMDS = "idepref_build_gradleCommands";
+ public static final String KEY_GRADLE_CLEAR_CACHE = "idepref_build_gradleClearCache";
- private ProgressSheet progressSheet;
+ private ProgressSheet progressSheet;
- @Override
- public void onCreatePreferences(Bundle p1, String p2) {
- super.onCreatePreferences(p1, p2);
- if (getContext() == null) return;
- final PreferenceScreen screen = getPreferenceScreen();
- final PreferenceCategory categoryGradle = new PreferenceCategory(getContext());
- final Preference customCommands = new Preference(getContext());
- final Preference clearCache = new Preference(getContext());
+ @Override
+ public void onCreatePreferences(Bundle p1, String p2) {
+ super.onCreatePreferences(p1, p2);
+ if (getContext() == null) return;
+ final PreferenceScreen screen = getPreferenceScreen();
+ final PreferenceCategory categoryGradle = new PreferenceCategory(getContext());
+ final Preference customCommands = new Preference(getContext());
+ final Preference clearCache = new Preference(getContext());
- screen.addPreference(categoryGradle);
+ screen.addPreference(categoryGradle);
- customCommands.setKey(KEY_GRADLE_COMMAMDS);
- customCommands.setIcon(R.drawable.ic_bash_commands);
- customCommands.setTitle(R.string.idepref_build_customgradlecommands_title);
- customCommands.setSummary(R.string.idepref_build_customgradlecommands_summary);
+ customCommands.setKey(KEY_GRADLE_COMMAMDS);
+ customCommands.setIcon(R.drawable.ic_bash_commands);
+ customCommands.setTitle(R.string.idepref_build_customgradlecommands_title);
+ customCommands.setSummary(R.string.idepref_build_customgradlecommands_summary);
- clearCache.setKey(KEY_GRADLE_CLEAR_CACHE);
- clearCache.setIcon(R.drawable.ic_delete);
- clearCache.setTitle(R.string.idepref_build_clearCache_title);
- clearCache.setSummary(R.string.idepref_build_clearCache_summary);
+ clearCache.setKey(KEY_GRADLE_CLEAR_CACHE);
+ clearCache.setIcon(R.drawable.ic_delete);
+ clearCache.setTitle(R.string.idepref_build_clearCache_title);
+ clearCache.setSummary(R.string.idepref_build_clearCache_summary);
- categoryGradle.setTitle(R.string.gradle);
- categoryGradle.addPreference(customCommands);
- categoryGradle.addPreference(clearCache);
+ categoryGradle.setTitle(R.string.gradle);
+ categoryGradle.addPreference(customCommands);
+ categoryGradle.addPreference(clearCache);
- setPreferenceScreen(screen);
+ setPreferenceScreen(screen);
- customCommands.setOnPreferenceClickListener(this);
- clearCache.setOnPreferenceClickListener(this);
- }
-
- @Override
- public boolean onPreferenceClick(Preference p1) {
- final String key = p1.getKey();
- if (key.equals(KEY_GRADLE_COMMAMDS)) {
- showGradleCommandsDialog();
- } else if (key.equals(KEY_GRADLE_CLEAR_CACHE)) {
- showClearCacheDialog();
+ customCommands.setOnPreferenceClickListener(this);
+ clearCache.setOnPreferenceClickListener(this);
}
- return true;
- }
- private void showGradleCommandsDialog() {
- final String[] labels = {"--stacktrace", "--info", "--debug", "--scan", "--warning-mode all"};
- final boolean[] checked = {
- getPrefManager().isStackTraceEnabled(),
- getPrefManager().isGradleInfoEnabled(),
- getPrefManager().isGradleDebugEnabled(),
- getPrefManager().isGradleScanEnabled(),
- getPrefManager().isGradleWarningEnabled()
- };
- final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(getContext());
- builder.setTitle(R.string.idepref_build_customgradlecommands_title);
- builder.setMultiChoiceItems(
- labels,
- checked,
- (p1, p2, p3) -> {
- boolean isChecked = p3;
- int pos = p2;
- if (pos == 0) {
- getPrefManager().setGradleStacktraceEnabled(isChecked);
- } else if (pos == 1) {
- getPrefManager().setGradleInfoEnabled(isChecked);
- } else if (pos == 2) {
- getPrefManager().setGradleDebugEnabled(isChecked);
- } else if (pos == 3) {
- getPrefManager().setGradleScanEnabled(isChecked);
- } else if (pos == 4) {
- getPrefManager().setGradleWarningEnabled(isChecked);
- }
- });
- builder.setPositiveButton(android.R.string.ok, null);
- builder.setCancelable(false);
- builder.create().show();
- }
-
- private void showClearCacheDialog() {
- final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(getContext());
- builder.setTitle(R.string.idepref_build_clearCache_title);
- builder.setMessage(R.string.msg_clear_cache);
- builder.setCancelable(false);
- builder.setPositiveButton(
- R.string.yes,
- (p1, p2) -> {
- p1.dismiss();
- getProgressSheet().show(getChildFragmentManager(), "progress_sheet");
- new TaskExecutor()
- .executeAsync(
- this::deleteCaches,
- __ -> {
- getProgressSheet().dismiss();
- });
- });
- builder.setNegativeButton(R.string.no, null);
- builder.create().show();
- }
-
- private Object deleteCaches() {
- File file = new File(Environment.GRADLE_USER_HOME, "caches");
- if (file.exists()) {
- FileUtils.delete(file);
+ @Override
+ public boolean onPreferenceClick(Preference p1) {
+ final String key = p1.getKey();
+ if (key.equals(KEY_GRADLE_COMMAMDS)) {
+ showGradleCommandsDialog();
+ } else if (key.equals(KEY_GRADLE_CLEAR_CACHE)) {
+ showClearCacheDialog();
+ }
+ return true;
}
- return null;
- }
- private ProgressSheet getProgressSheet() {
- return progressSheet == null
- ? progressSheet = new ProgressSheet().setMessage(getString(R.string.please_wait))
- : progressSheet;
- }
+ private void showGradleCommandsDialog() {
+ final String[] labels = {
+ "--stacktrace", "--info", "--debug", "--scan", "--warning-mode all"
+ };
+ final boolean[] checked = {
+ getPrefManager().isStackTraceEnabled(),
+ getPrefManager().isGradleInfoEnabled(),
+ getPrefManager().isGradleDebugEnabled(),
+ getPrefManager().isGradleScanEnabled(),
+ getPrefManager().isGradleWarningEnabled()
+ };
+ final MaterialAlertDialogBuilder builder =
+ DialogUtils.newMaterialDialogBuilder(getContext());
+ builder.setTitle(R.string.idepref_build_customgradlecommands_title);
+ builder.setMultiChoiceItems(
+ labels,
+ checked,
+ (p1, p2, p3) -> {
+ boolean isChecked = p3;
+ int pos = p2;
+ if (pos == 0) {
+ getPrefManager().setGradleStacktraceEnabled(isChecked);
+ } else if (pos == 1) {
+ getPrefManager().setGradleInfoEnabled(isChecked);
+ } else if (pos == 2) {
+ getPrefManager().setGradleDebugEnabled(isChecked);
+ } else if (pos == 3) {
+ getPrefManager().setGradleScanEnabled(isChecked);
+ } else if (pos == 4) {
+ getPrefManager().setGradleWarningEnabled(isChecked);
+ }
+ });
+ builder.setPositiveButton(android.R.string.ok, null);
+ builder.setCancelable(false);
+ builder.create().show();
+ }
+
+ private void showClearCacheDialog() {
+ final MaterialAlertDialogBuilder builder =
+ DialogUtils.newMaterialDialogBuilder(getContext());
+ builder.setTitle(R.string.idepref_build_clearCache_title);
+ builder.setMessage(R.string.msg_clear_cache);
+ builder.setCancelable(false);
+ builder.setPositiveButton(
+ R.string.yes,
+ (p1, p2) -> {
+ p1.dismiss();
+ getProgressSheet().show(getChildFragmentManager(), "progress_sheet");
+ new TaskExecutor()
+ .executeAsync(
+ this::deleteCaches,
+ __ -> {
+ getProgressSheet().dismiss();
+ });
+ });
+ builder.setNegativeButton(R.string.no, null);
+ builder.create().show();
+ }
+
+ private Object deleteCaches() {
+ File file = new File(Environment.GRADLE_USER_HOME, "caches");
+ if (file.exists()) {
+ FileUtils.delete(file);
+ }
+ return null;
+ }
+
+ private ProgressSheet getProgressSheet() {
+ return progressSheet == null
+ ? progressSheet = new ProgressSheet().setMessage(getString(R.string.please_wait))
+ : progressSheet;
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java b/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java
index db23f0b19..5e62cfe30 100755
--- a/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java
@@ -34,10 +34,12 @@ import static com.itsaky.androidide.models.PrefBasedJavaServerSettings.KEY_JAVA_
import android.os.Bundle;
import android.view.LayoutInflater;
+
import androidx.annotation.NonNull;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.SwitchPreference;
+
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.StudioApp;
@@ -48,254 +50,258 @@ import com.itsaky.androidide.utils.DialogUtils;
public class EditorPreferences extends BasePreferenceFragment {
- @Override
- public void onCreatePreferences(Bundle p1, String p2) {
- super.onCreatePreferences(p1, p2);
+ @Override
+ public void onCreatePreferences(Bundle p1, String p2) {
+ super.onCreatePreferences(p1, p2);
- if (getContext() == null) {
- return;
+ if (getContext() == null) {
+ return;
+ }
+
+ final var screen = getPreferenceScreen();
+ final var commonCategory = new PreferenceCategory(getContext());
+ final var javaCategory = new PreferenceCategory(getContext());
+
+ final var fontSize = new Preference(getContext());
+ final var nonPrintable = new Preference(getContext());
+ final var tabSize = new Preference(getContext());
+ final var drawHex = new SwitchPreference(getContext());
+ final var fontLigatures = new SwitchPreference(getContext());
+ final var autoSave = new SwitchPreference(getContext());
+ final var usePopupActions = new SwitchPreference(getContext());
+ final var horizontalPopup = new SwitchPreference(getContext());
+
+ final var javaMatchLower = new SwitchPreference(getContext());
+
+ screen.addPreference(commonCategory);
+ screen.addPreference(javaCategory);
+
+ fontSize.setIcon(R.drawable.ic_text_size);
+ fontSize.setKey(KEY_EDITOR_FONT_SIZE);
+ fontSize.setTitle(R.string.idepref_editor_fontsize_title);
+ fontSize.setSummary(R.string.idepref_editor_fontsize_summary);
+
+ nonPrintable.setIcon(R.drawable.ic_drawing);
+ nonPrintable.setKey(KEY_EDITOR_PRINTABLE_CHARS);
+ nonPrintable.setTitle(R.string.idepref_editor_paintingflags_title);
+ nonPrintable.setSummary(R.string.idepref_editor_paintingflags_summary);
+
+ tabSize.setIcon(R.drawable.ic_tab);
+ tabSize.setKey(KEY_EDITOR_TAB_SIZE);
+ tabSize.setTitle(R.string.title_tab_size);
+ tabSize.setSummary(R.string.msg_tab_size);
+
+ drawHex.setIcon(R.drawable.ic_hexadecimal);
+ drawHex.setKey(KEY_EDITOR_DRAW_HEX);
+ drawHex.setTitle(R.string.idepref_editor_drawhexcolors_title);
+ drawHex.setSummary(R.string.idepref_editor_drawhexcolors_summary);
+
+ fontLigatures.setIcon(R.drawable.ic_font_ligatures);
+ fontLigatures.setKey(KEY_EDITOR_FONT_LIGATURES);
+ fontLigatures.setTitle(getString(R.string.idepref_editor_ligatures_title));
+ fontLigatures.setSummary(getString(R.string.idepref_editor_ligatures_summary));
+
+ usePopupActions.setIcon(R.drawable.ic_text_actions);
+ usePopupActions.setKey(KEY_EDITOR_USE_POPUP);
+ usePopupActions.setTitle(getString(R.string.title_use_popup_actions));
+ usePopupActions.setSummary(getString(R.string.msg_use_popup_actions));
+
+ horizontalPopup.setIcon(R.drawable.ic_text_actions_horizontal);
+ horizontalPopup.setKey(KEY_EDITOR_HORIZONTAL_POPUP);
+ horizontalPopup.setTitle(getString(R.string.title_use_horizontal_popup));
+ horizontalPopup.setSummary(getString(R.string.msg_use_horizontal_popup));
+
+ autoSave.setIcon(R.drawable.ic_save);
+ autoSave.setKey(KEY_EDITOR_AUTO_SAVE);
+ autoSave.setTitle(getString(R.string.idepref_editor_autoSave_title));
+ autoSave.setSummary(getString(R.string.idepref_editor_autoSave_summary));
+
+ javaMatchLower.setKey(KEY_JAVA_PREF_MATCH_LOWER);
+ javaMatchLower.setIcon(R.drawable.ic_text_lower);
+ javaMatchLower.setTitle(getString(R.string.idepref_java_matchLower_title));
+ javaMatchLower.setSummary(getString(R.string.idepref_java_matchLower_summary));
+
+ commonCategory.setTitle(getString(R.string.idepref_editor_category_common));
+ commonCategory.addPreference(fontSize);
+ commonCategory.addPreference(fontLigatures);
+ commonCategory.addPreference(usePopupActions);
+ commonCategory.addPreference(horizontalPopup);
+ commonCategory.addPreference(nonPrintable);
+ commonCategory.addPreference(tabSize);
+ commonCategory.addPreference(drawHex);
+ commonCategory.addPreference(autoSave);
+
+ javaCategory.setTitle(getString(R.string.idepref_editor_category_java));
+ javaCategory.addPreference(javaMatchLower);
+
+ setPreferenceScreen(screen);
+
+ fontSize.setOnPreferenceClickListener(this::onPreferenceClick);
+ fontLigatures.setOnPreferenceChangeListener(this::onPreferenceChange);
+ usePopupActions.setOnPreferenceChangeListener(this::onPreferenceChange);
+ horizontalPopup.setOnPreferenceChangeListener(this::onPreferenceChange);
+ nonPrintable.setOnPreferenceClickListener(this::onPreferenceClick);
+ tabSize.setOnPreferenceClickListener(this::onPreferenceClick);
+ drawHex.setOnPreferenceChangeListener(this::onPreferenceChange);
+ autoSave.setOnPreferenceChangeListener(this::onPreferenceChange);
+ javaMatchLower.setOnPreferenceChangeListener(this::onPreferenceChange);
+
+ fontLigatures.setChecked(getPrefManager().getBoolean(KEY_EDITOR_FONT_LIGATURES, true));
+ usePopupActions.setChecked(getPrefManager().getBoolean(KEY_EDITOR_USE_POPUP, false));
+ horizontalPopup.setChecked(getPrefManager().getBoolean(KEY_EDITOR_HORIZONTAL_POPUP, false));
+ drawHex.setChecked(getPrefManager().getBoolean(KEY_EDITOR_DRAW_HEX, true));
+ autoSave.setChecked(getPrefManager().getBoolean(KEY_EDITOR_AUTO_SAVE, false));
+ javaMatchLower.setChecked(getPrefManager().getBoolean(KEY_JAVA_PREF_MATCH_LOWER, false));
}
- final var screen = getPreferenceScreen();
- final var commonCategory = new PreferenceCategory(getContext());
- final var javaCategory = new PreferenceCategory(getContext());
+ public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) {
+ final boolean value = (boolean) newValue;
- final var fontSize = new Preference(getContext());
- final var nonPrintable = new Preference(getContext());
- final var tabSize = new Preference(getContext());
- final var drawHex = new SwitchPreference(getContext());
- final var fontLigatures = new SwitchPreference(getContext());
- final var autoSave = new SwitchPreference(getContext());
- final var usePopupActions = new SwitchPreference(getContext());
- final var horizontalPopup = new SwitchPreference(getContext());
+ switch (preference.getKey()) {
+ case KEY_EDITOR_DRAW_HEX:
+ ConstantsBridge.EDITOR_PREF_DRAW_HEX_CHANGED = true;
+ break;
+ case KEY_EDITOR_FONT_LIGATURES:
+ ConstantsBridge.EDITOR_PREF_LIGATURES_CHANGED = true;
+ break;
+ case KEY_JAVA_PREF_MATCH_LOWER:
+ final var javaServer = StudioApp.getInstance().getJavaLanguageServer();
+ javaServer.applySettings(PrefBasedJavaServerSettings.getInstance());
+ break;
+ }
- final var javaMatchLower = new SwitchPreference(getContext());
+ getPrefManager().putBoolean(preference.getKey(), value);
- screen.addPreference(commonCategory);
- screen.addPreference(javaCategory);
+ final var horizontalPopup = findPreference(KEY_EDITOR_HORIZONTAL_POPUP);
+ if (horizontalPopup != null) {
+ horizontalPopup.setEnabled(getPrefManager().getBoolean(KEY_EDITOR_USE_POPUP));
+ }
- fontSize.setIcon(R.drawable.ic_text_size);
- fontSize.setKey(KEY_EDITOR_FONT_SIZE);
- fontSize.setTitle(R.string.idepref_editor_fontsize_title);
- fontSize.setSummary(R.string.idepref_editor_fontsize_summary);
-
- nonPrintable.setIcon(R.drawable.ic_drawing);
- nonPrintable.setKey(KEY_EDITOR_PRINTABLE_CHARS);
- nonPrintable.setTitle(R.string.idepref_editor_paintingflags_title);
- nonPrintable.setSummary(R.string.idepref_editor_paintingflags_summary);
-
- tabSize.setIcon(R.drawable.ic_tab);
- tabSize.setKey(KEY_EDITOR_TAB_SIZE);
- tabSize.setTitle(R.string.title_tab_size);
- tabSize.setSummary(R.string.msg_tab_size);
-
- drawHex.setIcon(R.drawable.ic_hexadecimal);
- drawHex.setKey(KEY_EDITOR_DRAW_HEX);
- drawHex.setTitle(R.string.idepref_editor_drawhexcolors_title);
- drawHex.setSummary(R.string.idepref_editor_drawhexcolors_summary);
-
- fontLigatures.setIcon(R.drawable.ic_font_ligatures);
- fontLigatures.setKey(KEY_EDITOR_FONT_LIGATURES);
- fontLigatures.setTitle(getString(R.string.idepref_editor_ligatures_title));
- fontLigatures.setSummary(getString(R.string.idepref_editor_ligatures_summary));
-
- usePopupActions.setIcon(R.drawable.ic_text_actions);
- usePopupActions.setKey(KEY_EDITOR_USE_POPUP);
- usePopupActions.setTitle(getString(R.string.title_use_popup_actions));
- usePopupActions.setSummary(getString(R.string.msg_use_popup_actions));
-
- horizontalPopup.setIcon(R.drawable.ic_text_actions_horizontal);
- horizontalPopup.setKey(KEY_EDITOR_HORIZONTAL_POPUP);
- horizontalPopup.setTitle(getString(R.string.title_use_horizontal_popup));
- horizontalPopup.setSummary(getString(R.string.msg_use_horizontal_popup));
-
- autoSave.setIcon(R.drawable.ic_save);
- autoSave.setKey(KEY_EDITOR_AUTO_SAVE);
- autoSave.setTitle(getString(R.string.idepref_editor_autoSave_title));
- autoSave.setSummary(getString(R.string.idepref_editor_autoSave_summary));
-
- javaMatchLower.setKey(KEY_JAVA_PREF_MATCH_LOWER);
- javaMatchLower.setIcon(R.drawable.ic_text_lower);
- javaMatchLower.setTitle(getString(R.string.idepref_java_matchLower_title));
- javaMatchLower.setSummary(getString(R.string.idepref_java_matchLower_summary));
-
- commonCategory.setTitle(getString(R.string.idepref_editor_category_common));
- commonCategory.addPreference(fontSize);
- commonCategory.addPreference(fontLigatures);
- commonCategory.addPreference(usePopupActions);
- commonCategory.addPreference(horizontalPopup);
- commonCategory.addPreference(nonPrintable);
- commonCategory.addPreference(tabSize);
- commonCategory.addPreference(drawHex);
- commonCategory.addPreference(autoSave);
-
- javaCategory.setTitle(getString(R.string.idepref_editor_category_java));
- javaCategory.addPreference(javaMatchLower);
-
- setPreferenceScreen(screen);
-
- fontSize.setOnPreferenceClickListener(this::onPreferenceClick);
- fontLigatures.setOnPreferenceChangeListener(this::onPreferenceChange);
- usePopupActions.setOnPreferenceChangeListener(this::onPreferenceChange);
- horizontalPopup.setOnPreferenceChangeListener(this::onPreferenceChange);
- nonPrintable.setOnPreferenceClickListener(this::onPreferenceClick);
- tabSize.setOnPreferenceClickListener(this::onPreferenceClick);
- drawHex.setOnPreferenceChangeListener(this::onPreferenceChange);
- autoSave.setOnPreferenceChangeListener(this::onPreferenceChange);
- javaMatchLower.setOnPreferenceChangeListener(this::onPreferenceChange);
-
- fontLigatures.setChecked(getPrefManager().getBoolean(KEY_EDITOR_FONT_LIGATURES, true));
- usePopupActions.setChecked(getPrefManager().getBoolean(KEY_EDITOR_USE_POPUP, false));
- horizontalPopup.setChecked(getPrefManager().getBoolean(KEY_EDITOR_HORIZONTAL_POPUP, false));
- drawHex.setChecked(getPrefManager().getBoolean(KEY_EDITOR_DRAW_HEX, true));
- autoSave.setChecked(getPrefManager().getBoolean(KEY_EDITOR_AUTO_SAVE, false));
- javaMatchLower.setChecked(getPrefManager().getBoolean(KEY_JAVA_PREF_MATCH_LOWER, false));
- }
-
- public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) {
- final boolean value = (boolean) newValue;
-
- switch (preference.getKey()) {
- case KEY_EDITOR_DRAW_HEX:
- ConstantsBridge.EDITOR_PREF_DRAW_HEX_CHANGED = true;
- break;
- case KEY_EDITOR_FONT_LIGATURES:
- ConstantsBridge.EDITOR_PREF_LIGATURES_CHANGED = true;
- break;
- case KEY_JAVA_PREF_MATCH_LOWER:
- final var javaServer = StudioApp.getInstance().getJavaLanguageServer();
- javaServer.applySettings(PrefBasedJavaServerSettings.getInstance());
- break;
+ return true;
}
- getPrefManager().putBoolean(preference.getKey(), value);
-
- final var horizontalPopup = findPreference(KEY_EDITOR_HORIZONTAL_POPUP);
- if (horizontalPopup != null) {
- horizontalPopup.setEnabled(getPrefManager().getBoolean(KEY_EDITOR_USE_POPUP));
+ public boolean onPreferenceClick(@NonNull Preference preference) {
+ final String key = preference.getKey();
+ switch (key) {
+ case KEY_EDITOR_FONT_SIZE:
+ showTextSizeDialog();
+ break;
+ case KEY_EDITOR_PRINTABLE_CHARS:
+ showPrintableCharsDialog();
+ break;
+ case KEY_EDITOR_TAB_SIZE:
+ showTabSizeDialog();
+ break;
+ }
+ return true;
}
- return true;
- }
+ private void showTabSizeDialog() {
+ final MaterialAlertDialogBuilder builder =
+ DialogUtils.newMaterialDialogBuilder(getContext());
+ final String[] sizes = new String[] {"2", "4", "6", "8"};
- public boolean onPreferenceClick(@NonNull Preference preference) {
- final String key = preference.getKey();
- switch (key) {
- case KEY_EDITOR_FONT_SIZE:
- showTextSizeDialog();
- break;
- case KEY_EDITOR_PRINTABLE_CHARS:
- showPrintableCharsDialog();
- break;
- case KEY_EDITOR_TAB_SIZE:
- showTabSizeDialog();
- break;
+ // We apply simple maths,
+ // assuming that tab size options have a difference of 2
+ // And the array is sorted
+ // Like, 2, 4, 6, 8, 10, etc
+ //
+ // If current tab size if 4, 4/2 = 2 and 2 - 1 = 1, so option at index 1 will be selected
+ // which
+ // is current tab size (4)!
+ int current = (getPrefManager().getEditorTabSize() / 2) - 1;
+ if (current < 0 || current >= sizes.length) {
+ current = 1;
+ }
+ builder.setTitle(R.string.title_tab_size);
+ builder.setSingleChoiceItems(
+ sizes,
+ current,
+ (d, i) -> {
+ d.dismiss();
+
+ // Reversing the logic applied above...
+ int tabSize = (i + 1) * 2;
+ if (tabSize < 2 || tabSize > 8) {
+ tabSize = 4;
+ }
+ getPrefManager().putInt(KEY_EDITOR_TAB_SIZE, tabSize);
+ });
+ builder.setCancelable(true);
+ builder.show();
}
- return true;
- }
- private void showTabSizeDialog() {
- final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(getContext());
- final String[] sizes = new String[] {"2", "4", "6", "8"};
+ private void showTextSizeDialog() {
+ final LayoutTextSizeSliderBinding binding =
+ LayoutTextSizeSliderBinding.inflate(LayoutInflater.from(getContext()));
+ final MaterialAlertDialogBuilder builder =
+ DialogUtils.newMaterialDialogBuilder(getContext());
+ float size = getPrefManager().getFloat(KEY_EDITOR_FONT_SIZE, 14);
+ if (size < 6 || size > 32) {
+ size = 14;
+ }
+ changeTextSize(binding, size);
- // We apply simple maths,
- // assuming that tab size options have a difference of 2
- // And the array is sorted
- // Like, 2, 4, 6, 8, 10, etc
- //
- // If current tab size if 4, 4/2 = 2 and 2 - 1 = 1, so option at index 1 will be selected which
- // is current tab size (4)!
- int current = (getPrefManager().getEditorTabSize() / 2) - 1;
- if (current < 0 || current >= sizes.length) {
- current = 1;
+ binding.slider.setLabelFormatter(p1 -> String.valueOf((int) p1));
+ builder.setTitle(R.string.title_change_text_size);
+ builder.setMessage(R.string.msg_editor_font_size);
+ builder.setView(binding.getRoot());
+ builder.setPositiveButton(
+ android.R.string.ok,
+ (p1, p2) -> {
+ p1.dismiss();
+ float s = binding.slider.getValue();
+ changeTextSize(binding, s);
+ });
+ builder.setNeutralButton(R.string.reset, (p1, p2) -> changeTextSize(binding, 14));
+ builder.setCancelable(false);
+ builder.show();
}
- builder.setTitle(R.string.title_tab_size);
- builder.setSingleChoiceItems(
- sizes,
- current,
- (d, i) -> {
- d.dismiss();
- // Reversing the logic applied above...
- int tabSize = (i + 1) * 2;
- if (tabSize < 2 || tabSize > 8) {
- tabSize = 4;
- }
- getPrefManager().putInt(KEY_EDITOR_TAB_SIZE, tabSize);
- });
- builder.setCancelable(true);
- builder.show();
- }
-
- private void showTextSizeDialog() {
- final LayoutTextSizeSliderBinding binding =
- LayoutTextSizeSliderBinding.inflate(LayoutInflater.from(getContext()));
- final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(getContext());
- float size = getPrefManager().getFloat(KEY_EDITOR_FONT_SIZE, 14);
- if (size < 6 || size > 32) {
- size = 14;
+ private void changeTextSize(LayoutTextSizeSliderBinding binding, float size) {
+ getPrefManager().putFloat(KEY_EDITOR_FONT_SIZE, size);
+ binding.slider.setValue(size);
+ ConstantsBridge.EDITOR_PREF_SIZE_CHANGED = true;
}
- changeTextSize(binding, size);
- binding.slider.setLabelFormatter(p1 -> String.valueOf((int) p1));
- builder.setTitle(R.string.title_change_text_size);
- builder.setMessage(R.string.msg_editor_font_size);
- builder.setView(binding.getRoot());
- builder.setPositiveButton(
- android.R.string.ok,
- (p1, p2) -> {
- p1.dismiss();
- float s = binding.slider.getValue();
- changeTextSize(binding, s);
- });
- builder.setNeutralButton(R.string.reset, (p1, p2) -> changeTextSize(binding, 14));
- builder.setCancelable(false);
- builder.show();
- }
+ private void showPrintableCharsDialog() {
+ String[] labels = {
+ "Leading whitespaces",
+ "Trailing whitespaces",
+ "Inner whitespaces",
+ "Whitespaces in empty lines",
+ "Line breaks"
+ };
+ final boolean[] checked = {
+ getPrefManager().getBoolean(KEY_EDITOR_FLAG_WS_LEADING, true),
+ getPrefManager().getBoolean(KEY_EDITOR_FLAG_WS_TRAILING, false),
+ getPrefManager().getBoolean(KEY_EDITOR_FLAG_WS_INNER, true),
+ getPrefManager().getBoolean(KEY_EDITOR_FLAG_WS_EMPTY_LINE, true),
+ getPrefManager().getBoolean(KEY_EDITOR_FLAG_LINE_BREAK, true)
+ };
+ final MaterialAlertDialogBuilder builder =
+ DialogUtils.newMaterialDialogBuilder(getContext());
+ builder.setTitle(R.string.idepref_editor_paintingflags_title);
+ builder.setMultiChoiceItems(
+ labels,
+ checked,
+ (dialog, which, isChecked) -> {
+ if (which == 0) {
+ getPrefManager().putBoolean(KEY_EDITOR_FLAG_WS_LEADING, isChecked);
+ } else if (which == 1) {
+ getPrefManager().putBoolean(KEY_EDITOR_FLAG_WS_TRAILING, isChecked);
+ } else if (which == 2) {
+ getPrefManager().putBoolean(KEY_EDITOR_FLAG_WS_INNER, isChecked);
+ } else if (which == 3) {
+ getPrefManager().putBoolean(KEY_EDITOR_FLAG_WS_EMPTY_LINE, isChecked);
+ } else if (which == 4) {
+ getPrefManager().putBoolean(KEY_EDITOR_FLAG_LINE_BREAK, isChecked);
+ }
- private void changeTextSize(LayoutTextSizeSliderBinding binding, float size) {
- getPrefManager().putFloat(KEY_EDITOR_FONT_SIZE, size);
- binding.slider.setValue(size);
- ConstantsBridge.EDITOR_PREF_SIZE_CHANGED = true;
- }
-
- private void showPrintableCharsDialog() {
- String[] labels = {
- "Leading whitespaces",
- "Trailing whitespaces",
- "Inner whitespaces",
- "Whitespaces in empty lines",
- "Line breaks"
- };
- final boolean[] checked = {
- getPrefManager().getBoolean(KEY_EDITOR_FLAG_WS_LEADING, true),
- getPrefManager().getBoolean(KEY_EDITOR_FLAG_WS_TRAILING, false),
- getPrefManager().getBoolean(KEY_EDITOR_FLAG_WS_INNER, true),
- getPrefManager().getBoolean(KEY_EDITOR_FLAG_WS_EMPTY_LINE, true),
- getPrefManager().getBoolean(KEY_EDITOR_FLAG_LINE_BREAK, true)
- };
- final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(getContext());
- builder.setTitle(R.string.idepref_editor_paintingflags_title);
- builder.setMultiChoiceItems(
- labels,
- checked,
- (dialog, which, isChecked) -> {
- if (which == 0) {
- getPrefManager().putBoolean(KEY_EDITOR_FLAG_WS_LEADING, isChecked);
- } else if (which == 1) {
- getPrefManager().putBoolean(KEY_EDITOR_FLAG_WS_TRAILING, isChecked);
- } else if (which == 2) {
- getPrefManager().putBoolean(KEY_EDITOR_FLAG_WS_INNER, isChecked);
- } else if (which == 3) {
- getPrefManager().putBoolean(KEY_EDITOR_FLAG_WS_EMPTY_LINE, isChecked);
- } else if (which == 4) {
- getPrefManager().putBoolean(KEY_EDITOR_FLAG_LINE_BREAK, isChecked);
- }
-
- ConstantsBridge.EDITOR_PREF_FLAGS_CHANGED = true;
- });
- builder.setPositiveButton(android.R.string.ok, null);
- builder.setCancelable(false);
- builder.show();
- }
+ ConstantsBridge.EDITOR_PREF_FLAGS_CHANGED = true;
+ });
+ builder.setPositiveButton(android.R.string.ok, null);
+ builder.setCancelable(false);
+ builder.show();
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/preferences/GeneralPreferences.java b/app/src/main/java/com/itsaky/androidide/fragments/preferences/GeneralPreferences.java
index fa60e9f30..f5e8f5d97 100755
--- a/app/src/main/java/com/itsaky/androidide/fragments/preferences/GeneralPreferences.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/preferences/GeneralPreferences.java
@@ -20,70 +20,72 @@ package com.itsaky.androidide.fragments.preferences;
import static com.itsaky.androidide.managers.PreferenceManager.*;
import android.os.Bundle;
+
import androidx.annotation.NonNull;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
+
import com.itsaky.androidide.R;
public class GeneralPreferences extends BasePreferenceFragment
- implements Preference.OnPreferenceChangeListener {
+ implements Preference.OnPreferenceChangeListener {
- @Override
- public void onCreatePreferences(Bundle p1, String p2) {
- super.onCreatePreferences(p1, p2);
- if (getContext() == null) return;
+ @Override
+ public void onCreatePreferences(Bundle p1, String p2) {
+ super.onCreatePreferences(p1, p2);
+ if (getContext() == null) return;
- final PreferenceScreen screen = getPreferenceScreen();
- final SwitchPreference openProjects = new SwitchPreference(getContext());
- final SwitchPreference confirmProjectOpen = new SwitchPreference(getContext());
- final SwitchPreference shell = new SwitchPreference(getContext());
+ final PreferenceScreen screen = getPreferenceScreen();
+ final SwitchPreference openProjects = new SwitchPreference(getContext());
+ final SwitchPreference confirmProjectOpen = new SwitchPreference(getContext());
+ final SwitchPreference shell = new SwitchPreference(getContext());
- openProjects.setKey(KEY_OPEN_PROJECTS);
- openProjects.setTitle(R.string.title_open_projects);
- openProjects.setIcon(R.drawable.ic_open_project);
- openProjects.setSummary(R.string.msg_open_projects);
+ openProjects.setKey(KEY_OPEN_PROJECTS);
+ openProjects.setTitle(R.string.title_open_projects);
+ openProjects.setIcon(R.drawable.ic_open_project);
+ openProjects.setSummary(R.string.msg_open_projects);
- confirmProjectOpen.setKey(KEY_CONFIRM_PROJECT_OPEN);
- confirmProjectOpen.setTitle(R.string.title_confirm_project_open);
- confirmProjectOpen.setSummary(R.string.msg_confirm_project_open);
- confirmProjectOpen.setIcon(R.drawable.ic_open_project);
+ confirmProjectOpen.setKey(KEY_CONFIRM_PROJECT_OPEN);
+ confirmProjectOpen.setTitle(R.string.title_confirm_project_open);
+ confirmProjectOpen.setSummary(R.string.msg_confirm_project_open);
+ confirmProjectOpen.setIcon(R.drawable.ic_open_project);
- shell.setKey(KEY_TERMINAL_USE_SYSTEM_SHELL);
- shell.setTitle(getString(R.string.title_default_shell));
- shell.setSummary(getString(R.string.msg_default_shell));
- shell.setIcon(R.drawable.ic_bash_commands);
+ shell.setKey(KEY_TERMINAL_USE_SYSTEM_SHELL);
+ shell.setTitle(getString(R.string.title_default_shell));
+ shell.setSummary(getString(R.string.msg_default_shell));
+ shell.setIcon(R.drawable.ic_bash_commands);
- openProjects.setChecked(getPrefManager().autoOpenProject());
- confirmProjectOpen.setChecked(getPrefManager().confirmProjectOpen());
- shell.setChecked(getPrefManager().getBoolean(KEY_TERMINAL_USE_SYSTEM_SHELL));
+ openProjects.setChecked(getPrefManager().autoOpenProject());
+ confirmProjectOpen.setChecked(getPrefManager().confirmProjectOpen());
+ shell.setChecked(getPrefManager().getBoolean(KEY_TERMINAL_USE_SYSTEM_SHELL));
- screen.addPreference(openProjects);
- screen.addPreference(confirmProjectOpen);
- screen.addPreference(shell);
+ screen.addPreference(openProjects);
+ screen.addPreference(confirmProjectOpen);
+ screen.addPreference(shell);
- setPreferenceScreen(screen);
+ setPreferenceScreen(screen);
- openProjects.setOnPreferenceChangeListener(this);
- confirmProjectOpen.setOnPreferenceChangeListener(this);
- shell.setOnPreferenceChangeListener(this);
- }
-
- @Override
- public boolean onPreferenceChange(@NonNull Preference p1, Object p2) {
- boolean checked = (Boolean) p2;
- final var key = p1.getKey();
- switch (key) {
- case KEY_OPEN_PROJECTS:
- getPrefManager().putBoolean(KEY_OPEN_PROJECTS, checked);
- break;
- case KEY_CONFIRM_PROJECT_OPEN:
- getPrefManager().putBoolean(KEY_CONFIRM_PROJECT_OPEN, checked);
- break;
- case KEY_TERMINAL_USE_SYSTEM_SHELL:
- getPrefManager().putBoolean(KEY_TERMINAL_USE_SYSTEM_SHELL, checked);
- break;
+ openProjects.setOnPreferenceChangeListener(this);
+ confirmProjectOpen.setOnPreferenceChangeListener(this);
+ shell.setOnPreferenceChangeListener(this);
+ }
+
+ @Override
+ public boolean onPreferenceChange(@NonNull Preference p1, Object p2) {
+ boolean checked = (Boolean) p2;
+ final var key = p1.getKey();
+ switch (key) {
+ case KEY_OPEN_PROJECTS:
+ getPrefManager().putBoolean(KEY_OPEN_PROJECTS, checked);
+ break;
+ case KEY_CONFIRM_PROJECT_OPEN:
+ getPrefManager().putBoolean(KEY_CONFIRM_PROJECT_OPEN, checked);
+ break;
+ case KEY_TERMINAL_USE_SYSTEM_SHELL:
+ getPrefManager().putBoolean(KEY_TERMINAL_USE_SYSTEM_SHELL, checked);
+ break;
+ }
+ return true;
}
- return true;
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/preferences/IDEPreferences.java b/app/src/main/java/com/itsaky/androidide/fragments/preferences/IDEPreferences.java
index 0cf26d891..83fb55afa 100755
--- a/app/src/main/java/com/itsaky/androidide/fragments/preferences/IDEPreferences.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/preferences/IDEPreferences.java
@@ -31,9 +31,11 @@ import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ScrollView;
import android.widget.TextView;
+
import androidx.core.content.ContextCompat;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
+
import com.blankj.utilcode.util.ResourceUtils;
import com.blankj.utilcode.util.SizeUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
@@ -41,171 +43,173 @@ import com.itsaky.androidide.AboutActivity;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.utils.DialogUtils;
+
import org.json.JSONArray;
import org.json.JSONObject;
public class IDEPreferences extends BasePreferenceFragment {
- private GeneralPreferences mGeneralPrefs;
- private BuildPreferences mBuildPreferences;
- private EditorPreferences mEditorPreferences;
+ private GeneralPreferences mGeneralPrefs;
+ private BuildPreferences mBuildPreferences;
+ private EditorPreferences mEditorPreferences;
- private String CHANGELOG = null;
+ private String CHANGELOG = null;
- public static final String KEY_GENERAL = "idepref_general";
- public static final String KEY_APPEARANCE = "idepref_appearance";
- public static final String KEY_EDITOR = "idepref_editor";
- public static final String KEY_BUILD = "idepref_build";
- public static final String KEY_TELEGRAM = "idepref_telegram";
- public static final String KEY_ISSUES = "idepref_issues";
- public static final String KEY_CHANGELOG = "idepref_changelog";
- public static final String KEY_ABOUT = "idepref_about";
+ public static final String KEY_GENERAL = "idepref_general";
+ public static final String KEY_APPEARANCE = "idepref_appearance";
+ public static final String KEY_EDITOR = "idepref_editor";
+ public static final String KEY_BUILD = "idepref_build";
+ public static final String KEY_TELEGRAM = "idepref_telegram";
+ public static final String KEY_ISSUES = "idepref_issues";
+ public static final String KEY_CHANGELOG = "idepref_changelog";
+ public static final String KEY_ABOUT = "idepref_about";
- @Override
- public void onCreatePreferences(Bundle savedState, String rootKey) {
- super.onCreatePreferences(savedState, rootKey);
- if (getContext() == null) return;
+ @Override
+ public void onCreatePreferences(Bundle savedState, String rootKey) {
+ super.onCreatePreferences(savedState, rootKey);
+ if (getContext() == null) return;
- final PreferenceScreen screen = getPreferenceScreen();
- final Preference general = new Preference(getContext());
- final Preference editor = new Preference(getContext());
- final Preference build = new Preference(getContext());
- final Preference telegram = new Preference(getContext());
- final Preference github = new Preference(getContext());
- final Preference changelog = new Preference(getContext());
- final Preference about = new Preference(getContext());
+ final PreferenceScreen screen = getPreferenceScreen();
+ final Preference general = new Preference(getContext());
+ final Preference editor = new Preference(getContext());
+ final Preference build = new Preference(getContext());
+ final Preference telegram = new Preference(getContext());
+ final Preference github = new Preference(getContext());
+ final Preference changelog = new Preference(getContext());
+ final Preference about = new Preference(getContext());
- general.setKey(KEY_GENERAL);
- general.setIconSpaceReserved(false);
- general.setFragment(getGeneralFrag().getClass().getName());
- general.setTitle(R.string.title_general);
+ general.setKey(KEY_GENERAL);
+ general.setIconSpaceReserved(false);
+ general.setFragment(getGeneralFrag().getClass().getName());
+ general.setTitle(R.string.title_general);
- editor.setKey(KEY_EDITOR);
- editor.setIconSpaceReserved(false);
- editor.setFragment(getEditorFrag().getClass().getName());
- editor.setTitle(R.string.idepref_editor_title);
+ editor.setKey(KEY_EDITOR);
+ editor.setIconSpaceReserved(false);
+ editor.setFragment(getEditorFrag().getClass().getName());
+ editor.setTitle(R.string.idepref_editor_title);
- build.setKey(KEY_BUILD);
- build.setIconSpaceReserved(false);
- build.setFragment(getBuildFrag().getClass().getName());
- build.setTitle(R.string.idepref_build_title);
+ build.setKey(KEY_BUILD);
+ build.setIconSpaceReserved(false);
+ build.setFragment(getBuildFrag().getClass().getName());
+ build.setTitle(R.string.idepref_build_title);
- github.setKey(KEY_ISSUES);
- github.setIconSpaceReserved(false);
- github.setTitle(R.string.title_github);
+ github.setKey(KEY_ISSUES);
+ github.setIconSpaceReserved(false);
+ github.setTitle(R.string.title_github);
- telegram.setKey(KEY_TELEGRAM);
- telegram.setIconSpaceReserved(false);
- telegram.setTitle(R.string.discussions_on_telegram);
+ telegram.setKey(KEY_TELEGRAM);
+ telegram.setIconSpaceReserved(false);
+ telegram.setTitle(R.string.discussions_on_telegram);
- changelog.setKey(KEY_CHANGELOG);
- changelog.setIconSpaceReserved(false);
- changelog.setTitle(R.string.pref_changelog);
+ changelog.setKey(KEY_CHANGELOG);
+ changelog.setIconSpaceReserved(false);
+ changelog.setTitle(R.string.pref_changelog);
- about.setKey(KEY_ABOUT);
- about.setIconSpaceReserved(false);
- about.setTitle(R.string.idepref_about_title);
+ about.setKey(KEY_ABOUT);
+ about.setIconSpaceReserved(false);
+ about.setTitle(R.string.idepref_about_title);
- screen.addPreference(general);
- screen.addPreference(editor);
- screen.addPreference(build);
- screen.addPreference(github);
- screen.addPreference(telegram);
- screen.addPreference(changelog);
- screen.addPreference(about);
+ screen.addPreference(general);
+ screen.addPreference(editor);
+ screen.addPreference(build);
+ screen.addPreference(github);
+ screen.addPreference(telegram);
+ screen.addPreference(changelog);
+ screen.addPreference(about);
- final Preference.OnPreferenceClickListener listener = getListener();
- github.setOnPreferenceClickListener(listener);
- telegram.setOnPreferenceClickListener(listener);
- changelog.setOnPreferenceClickListener(listener);
- about.setOnPreferenceClickListener(listener);
- }
-
- private GeneralPreferences getGeneralFrag() {
- return mGeneralPrefs == null ? mGeneralPrefs = new GeneralPreferences() : mGeneralPrefs;
- }
-
- private EditorPreferences getEditorFrag() {
- return mEditorPreferences == null
- ? mEditorPreferences = new EditorPreferences()
- : mEditorPreferences;
- }
-
- private BuildPreferences getBuildFrag() {
- return mBuildPreferences == null
- ? mBuildPreferences = new BuildPreferences()
- : mBuildPreferences;
- }
-
- private void showChangelog() {
- final int dp8 = SizeUtils.dp2px(8);
- final int dp16 = SizeUtils.dp2px(16);
- final ScrollView vScroll = new ScrollView(getContext());
- vScroll.setFillViewport(true);
- vScroll.setLayoutParams(new ViewGroup.LayoutParams(-1, -1));
-
- final HorizontalScrollView scroll = new HorizontalScrollView(getContext());
- scroll.setLayoutParams(new ViewGroup.LayoutParams(-1, -2));
- scroll.setFillViewport(true);
- scroll.setPadding(dp16, dp8, dp16, dp8);
- scroll.setPaddingRelative(dp16, dp8, dp16, dp8);
-
- final TextView text = new TextView(getContext());
- text.setText(CHANGELOG == null ? CHANGELOG = changelogAsString() : CHANGELOG);
- text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
- text.setTextColor(ContextCompat.getColor(getContext(), R.color.primaryTextColor));
- scroll.removeAllViews();
- vScroll.removeAllViews();
- scroll.addView(text, new ViewGroup.LayoutParams(-1, -1));
- vScroll.addView(scroll);
-
- final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(getContext());
- builder.setTitle(R.string.pref_changelog);
- builder.setView(vScroll);
- builder.setPositiveButton(android.R.string.ok, null);
- builder.create().show();
- }
-
- private String changelogAsString() {
- try {
- final SpannableStringBuilder sb = new SpannableStringBuilder();
- JSONArray arr = new JSONArray(ResourceUtils.readAssets2String("changelog.json"));
- for (int i = 0; i < arr.length(); i++) {
- JSONObject version = arr.getJSONObject(i);
- sb.append(
- "v".concat(version.getString("version")),
- new StyleSpan(Typeface.BOLD),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- JSONArray changes = version.getJSONArray("changes");
- for (int j = 0; j < changes.length(); j++) {
- sb.append("\n ");
- sb.append("\u2022 ".concat(changes.getString(j)));
- }
- sb.append("\n\n");
- }
- return sb.toString();
- } catch (Throwable th) {
- return getString(R.string.msg_failed_get_changelog);
+ final Preference.OnPreferenceClickListener listener = getListener();
+ github.setOnPreferenceClickListener(listener);
+ telegram.setOnPreferenceClickListener(listener);
+ changelog.setOnPreferenceClickListener(listener);
+ about.setOnPreferenceClickListener(listener);
}
- }
- private Preference.OnPreferenceClickListener getListener() {
- return new Preference.OnPreferenceClickListener() {
+ private GeneralPreferences getGeneralFrag() {
+ return mGeneralPrefs == null ? mGeneralPrefs = new GeneralPreferences() : mGeneralPrefs;
+ }
- @Override
- public boolean onPreferenceClick(Preference p1) {
- final String key = p1.getKey();
- if (key.equals(KEY_CHANGELOG)) {
- showChangelog();
- } else if (key.equals(KEY_TELEGRAM)) {
- StudioApp.getInstance().openTelegramGroup();
- } else if (key.equals(KEY_ISSUES)) {
- StudioApp.getInstance().openGitHub();
- } else if (key.equals(KEY_ABOUT)) {
- startActivity(new Intent(getContext(), AboutActivity.class));
+ private EditorPreferences getEditorFrag() {
+ return mEditorPreferences == null
+ ? mEditorPreferences = new EditorPreferences()
+ : mEditorPreferences;
+ }
+
+ private BuildPreferences getBuildFrag() {
+ return mBuildPreferences == null
+ ? mBuildPreferences = new BuildPreferences()
+ : mBuildPreferences;
+ }
+
+ private void showChangelog() {
+ final int dp8 = SizeUtils.dp2px(8);
+ final int dp16 = SizeUtils.dp2px(16);
+ final ScrollView vScroll = new ScrollView(getContext());
+ vScroll.setFillViewport(true);
+ vScroll.setLayoutParams(new ViewGroup.LayoutParams(-1, -1));
+
+ final HorizontalScrollView scroll = new HorizontalScrollView(getContext());
+ scroll.setLayoutParams(new ViewGroup.LayoutParams(-1, -2));
+ scroll.setFillViewport(true);
+ scroll.setPadding(dp16, dp8, dp16, dp8);
+ scroll.setPaddingRelative(dp16, dp8, dp16, dp8);
+
+ final TextView text = new TextView(getContext());
+ text.setText(CHANGELOG == null ? CHANGELOG = changelogAsString() : CHANGELOG);
+ text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
+ text.setTextColor(ContextCompat.getColor(getContext(), R.color.primaryTextColor));
+ scroll.removeAllViews();
+ vScroll.removeAllViews();
+ scroll.addView(text, new ViewGroup.LayoutParams(-1, -1));
+ vScroll.addView(scroll);
+
+ final MaterialAlertDialogBuilder builder =
+ DialogUtils.newMaterialDialogBuilder(getContext());
+ builder.setTitle(R.string.pref_changelog);
+ builder.setView(vScroll);
+ builder.setPositiveButton(android.R.string.ok, null);
+ builder.create().show();
+ }
+
+ private String changelogAsString() {
+ try {
+ final SpannableStringBuilder sb = new SpannableStringBuilder();
+ JSONArray arr = new JSONArray(ResourceUtils.readAssets2String("changelog.json"));
+ for (int i = 0; i < arr.length(); i++) {
+ JSONObject version = arr.getJSONObject(i);
+ sb.append(
+ "v".concat(version.getString("version")),
+ new StyleSpan(Typeface.BOLD),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ JSONArray changes = version.getJSONArray("changes");
+ for (int j = 0; j < changes.length(); j++) {
+ sb.append("\n ");
+ sb.append("\u2022 ".concat(changes.getString(j)));
+ }
+ sb.append("\n\n");
+ }
+ return sb.toString();
+ } catch (Throwable th) {
+ return getString(R.string.msg_failed_get_changelog);
}
- return true;
- }
- };
- }
+ }
+
+ private Preference.OnPreferenceClickListener getListener() {
+ return new Preference.OnPreferenceClickListener() {
+
+ @Override
+ public boolean onPreferenceClick(Preference p1) {
+ final String key = p1.getKey();
+ if (key.equals(KEY_CHANGELOG)) {
+ showChangelog();
+ } else if (key.equals(KEY_TELEGRAM)) {
+ StudioApp.getInstance().openTelegramGroup();
+ } else if (key.equals(KEY_ISSUES)) {
+ StudioApp.getInstance().openGitHub();
+ } else if (key.equals(KEY_ABOUT)) {
+ startActivity(new Intent(getContext(), AboutActivity.class));
+ }
+ return true;
+ }
+ };
+ }
}
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 0cdc94b81..3dbe4f4d3 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
@@ -33,10 +33,12 @@ 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.appcompat.app.AlertDialog;
import androidx.transition.TransitionManager;
+
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.itsaky.androidide.R;
import com.itsaky.androidide.adapters.SimpleIconTextAdapter;
@@ -55,6 +57,9 @@ import com.itsaky.inflater.IAttribute;
import com.itsaky.inflater.IView;
import com.itsaky.inflater.impl.UiAttribute;
import com.itsaky.toaster.Toaster;
+
+import org.jetbrains.annotations.Contract;
+
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
@@ -63,318 +68,332 @@ import java.util.List;
import java.util.Objects;
import java.util.TreeSet;
import java.util.stream.Collectors;
-import org.jetbrains.annotations.Contract;
public class AttrEditorSheet extends BottomSheetDialogFragment
- implements SimpleIconTextAdapter.OnBindListener {
+ implements SimpleIconTextAdapter.OnBindListener {
- private static final List VIEW_ACTIONS = new ArrayList<>();
- private static final Logger LOG = Logger.instance("AttrBottomSheet");
- private AttributeListSheet mAttrListSheet;
- private IView selectedView;
- private File layout;
- private LayoutAttrEditorSheetBinding binding;
- private OnViewDeletionFailedListener mDeletionFailedListener;
+ private static final List VIEW_ACTIONS = new ArrayList<>();
+ private static final Logger LOG = Logger.instance("AttrBottomSheet");
+ private AttributeListSheet mAttrListSheet;
+ private IView selectedView;
+ private File layout;
+ private LayoutAttrEditorSheetBinding binding;
+ private OnViewDeletionFailedListener mDeletionFailedListener;
- @Nullable
- @Override
- public View onCreateView(
- @NonNull LayoutInflater inflater,
- @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- this.binding =
- LayoutAttrEditorSheetBinding.inflate(LayoutInflater.from(getContext()), container, false);
- return binding.getRoot();
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- if (VIEW_ACTIONS.isEmpty()) {
- Collections.addAll(
- VIEW_ACTIONS,
- IconTextListItem.create(getString(R.string.msg_viewaction_add_attr), R.drawable.ic_add),
- IconTextListItem.create(
- getString(R.string.title_viewaction_delete), R.drawable.ic_delete),
- IconTextListItem.create(
- getString(R.string.msg_viewaction_select_parent), R.drawable.ic_view_select_parent));
+ @Nullable
+ @Override
+ public View onCreateView(
+ @NonNull LayoutInflater inflater,
+ @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ this.binding =
+ LayoutAttrEditorSheetBinding.inflate(
+ LayoutInflater.from(getContext()), container, false);
+ return binding.getRoot();
}
- binding.valueEditorLayout.closeButton.setOnClickListener(v -> hideValueEditorLayout());
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
- setupViewData();
+ if (VIEW_ACTIONS.isEmpty()) {
+ Collections.addAll(
+ VIEW_ACTIONS,
+ IconTextListItem.create(
+ getString(R.string.msg_viewaction_add_attr), R.drawable.ic_add),
+ IconTextListItem.create(
+ getString(R.string.title_viewaction_delete), R.drawable.ic_delete),
+ IconTextListItem.create(
+ getString(R.string.msg_viewaction_select_parent),
+ R.drawable.ic_view_select_parent));
+ }
- AttributeDialogs.init(requireActivity());
- }
+ binding.valueEditorLayout.closeButton.setOnClickListener(v -> hideValueEditorLayout());
- @Override
- public void onDismiss(@NonNull DialogInterface dialog) {
- AttributeDialogs.release();
- super.onDismiss(dialog);
- }
-
- public AttrEditorSheet setDeletionFailedListener(OnViewDeletionFailedListener listener) {
- this.mDeletionFailedListener = listener;
- return this;
- }
-
- public AttrEditorSheet setLayout(File layout) {
- Objects.requireNonNull(layout);
-
- this.layout = layout;
- return this;
- }
-
- private void setupViewData() {
- binding.actionsList.setAdapter(new SimpleIconTextAdapter(VIEW_ACTIONS).setOnBindListener(this));
-
- if (this.selectedView == null) {
- LOG.error("Cannot edit attributes of a null view.");
- return;
- }
-
- binding.widgetName.setText(this.selectedView.getXmlTag());
- binding.attrList.setAdapter(
- new XMLAttributeListAdapter(
- this.selectedView.getAttributes().stream()
- .map(XMLAttribute::new)
- .collect(Collectors.toList()),
- this::onAttrClick));
- }
-
- public void setSelectedView(IView view) {
- this.selectedView = view;
- }
-
- private void onAttrClick(
- LayoutAttrEditorSheetItemBinding binding, @NonNull XMLAttribute attribute) {
-
- if (this.selectedView == null) {
- LOG.error("Cannot edit attributes of a null view.");
- return;
- }
-
- final var format = attribute.findFormat();
- if (format == -1) {
- StudioApp.getInstance().toast(getString(R.string.msg_no_attr_format), Toaster.Type.ERROR);
- LOG.error(getString(R.string.msg_no_attr_format), attribute);
- return;
- }
-
- showEditorDialog(attribute);
- }
-
- private void hideValueEditorLayout() {
- TransitionManager.beginDelayedTransition(binding.getRoot());
- binding.valueEditorLayout.getRoot().setVisibility(View.GONE);
- }
-
- private void showEditorDialog(@NonNull XMLAttribute attribute) {
- final var attr = attribute.getAttr();
- final var values = attr.possibleValues.toArray(new String[0]);
- final AttributeDialogs.OnClickListener onDone =
- (dialog, which, newValue) -> applyNewValue(attribute, newValue);
-
- final ColorPickerView.OnPickListener mPickListener =
- (color, hexCode) -> applyNewValue(attribute, hexCode);
-
- AlertDialog dialog = null;
- if (attribute.hasFormat(DIMENSION)) {
- dialog = AttributeDialogs.dimensionEditor(attribute.getValue(), onDone);
- } else if (attribute.hasFormat(STRING)) {
- dialog = AttributeDialogs.stringEditor(onDone);
- } else if (attribute.hasFormat(BOOLEAN)) {
- dialog = booleanEditor(onDone);
- } else if (attribute.hasFormat(ENUM)) {
- dialog = enumEditor(values, attribute.getValue(), onDone);
- } else if (attribute.hasFormat(FLAG)) {
- dialog = flagEditor(values, attribute.getValue(), onDone);
- } else if (attribute.hasFormat(COLOR)) {
- // TODO Implement something to allow values from colors.xml or ColorStateLists
- // Make this color picker optional
- dialog = colorPicker(mPickListener);
- }
-
- if (dialog != null) {
- dialog.show();
- }
- }
-
- private void applyNewValue(@NonNull IAttribute attribute, String newValue) {
- if (this.selectedView.hasAttribute(attribute.getNamespace(), attribute.getAttributeName())) {
- if (!this.selectedView.updateAttribute(
- attribute.getNamespace(), attribute.getAttributeName(), newValue)) {
- StudioApp.getInstance().toast(getString(R.string.msg_attr_not_updated), Toaster.Type.ERROR);
- } else {
- // Update the view data
- // This will make sure that the attributes list has been updated
setupViewData();
- }
- } else {
- addAttribute(
- new UiAttribute(attribute.getNamespace(), attribute.getAttributeName(), newValue));
- }
- }
- private void addAttribute(@NonNull IAttribute attribute) {
- this.selectedView.addAttribute(attribute);
- setupViewData();
- StudioApp.getInstance().toast(getString(R.string.msg_attr_added), Toaster.Type.SUCCESS);
- }
-
- @Override
- public void postBind(
- IconTextListItem item, @NonNull SimpleIconTextAdapter.VH holder, int position) {
- final var binding = holder.binding;
- binding.getRoot().setOnClickListener(v -> onViewActionClick(position));
- }
-
- private void onViewActionClick(int position) {
- if (this.selectedView == null) {
- return;
+ AttributeDialogs.init(requireActivity());
}
- if (position == 0) { // Add attribute
- var tag = this.selectedView.getXmlTag();
- if ("include".equals(tag) || "merge".equals(tag)) {
- tag = "View";
- }
+ @Override
+ public void onDismiss(@NonNull DialogInterface dialog) {
+ AttributeDialogs.release();
+ super.onDismiss(dialog);
+ }
- final var attrs = StudioApp.getInstance().attrInfo();
- final var style = attrs.getStyle(tag);
- if (style == null) {
- LOG.error("Unable to retrieve attributes for tag:", tag);
- return;
- }
+ public AttrEditorSheet setDeletionFailedListener(OnViewDeletionFailedListener listener) {
+ this.mDeletionFailedListener = listener;
+ return this;
+ }
- final var attributes = new TreeSet(Comparator.comparing(attr -> attr.name));
- attributes.addAll(style.attributes);
+ public AttrEditorSheet setLayout(File layout) {
+ Objects.requireNonNull(layout);
- final var widgetInfo = StudioApp.getInstance().widgetInfo();
- final var widget = widgetInfo.getWidgetBySimpleName(tag);
- if (widget != null) {
- for (var superclass : widget.superclasses) {
- if ("java.lang.Object".equals(superclass)) {
- break;
- }
+ this.layout = layout;
+ return this;
+ }
- final var simpleName = superclass.substring(superclass.lastIndexOf(".") + 1);
- final var superStyle = attrs.getStyle(simpleName);
- if (superStyle != null) {
- attributes.addAll(superStyle.attributes);
- }
+ private void setupViewData() {
+ binding.actionsList.setAdapter(
+ new SimpleIconTextAdapter(VIEW_ACTIONS).setOnBindListener(this));
+
+ if (this.selectedView == null) {
+ LOG.error("Cannot edit attributes of a null view.");
+ return;
}
- }
- final var parent = this.selectedView.getParent();
- if (parent != null) {
- final var parentTag = parent.getXmlTag();
- final var parentWidget = widgetInfo.getWidgetBySimpleName(parentTag);
- if (parentWidget != null) {
- final var parentLayoutParams = attrs.getStyle(parentTag + "_Layout");
- if (parentLayoutParams != null) {
- attributes.addAll(parentLayoutParams.attributes);
- }
+ binding.widgetName.setText(this.selectedView.getXmlTag());
+ binding.attrList.setAdapter(
+ new XMLAttributeListAdapter(
+ this.selectedView.getAttributes().stream()
+ .map(XMLAttribute::new)
+ .collect(Collectors.toList()),
+ this::onAttrClick));
+ }
- final var paramSuperclasses = widgetInfo.getLayoutParamSuperClasses(parentWidget.name);
- if (paramSuperclasses != null) {
- for (var superclass : paramSuperclasses) {
- if ("java.lang.Object".equals(superclass)) {
- continue;
- }
+ public void setSelectedView(IView view) {
+ this.selectedView = view;
+ }
- final var split = superclass.split("\\.");
- final var simpleClassName = split[split.length - 2];
- var paramName = split[split.length - 1];
- paramName = paramName.substring(0, paramName.length() - "Params".length());
- final var superParamEntry = attrs.getStyle(simpleClassName + "_" + paramName);
- if (superParamEntry != null) {
- attributes.addAll(superParamEntry.attributes);
- }
+ private void onAttrClick(
+ LayoutAttrEditorSheetItemBinding binding, @NonNull XMLAttribute attribute) {
+
+ if (this.selectedView == null) {
+ LOG.error("Cannot edit attributes of a null view.");
+ return;
+ }
+
+ final var format = attribute.findFormat();
+ if (format == -1) {
+ StudioApp.getInstance()
+ .toast(getString(R.string.msg_no_attr_format), Toaster.Type.ERROR);
+ LOG.error(getString(R.string.msg_no_attr_format), attribute);
+ return;
+ }
+
+ showEditorDialog(attribute);
+ }
+
+ private void hideValueEditorLayout() {
+ TransitionManager.beginDelayedTransition(binding.getRoot());
+ binding.valueEditorLayout.getRoot().setVisibility(View.GONE);
+ }
+
+ private void showEditorDialog(@NonNull XMLAttribute attribute) {
+ final var attr = attribute.getAttr();
+ final var values = attr.possibleValues.toArray(new String[0]);
+ final AttributeDialogs.OnClickListener onDone =
+ (dialog, which, newValue) -> applyNewValue(attribute, newValue);
+
+ final ColorPickerView.OnPickListener mPickListener =
+ (color, hexCode) -> applyNewValue(attribute, hexCode);
+
+ AlertDialog dialog = null;
+ if (attribute.hasFormat(DIMENSION)) {
+ dialog = AttributeDialogs.dimensionEditor(attribute.getValue(), onDone);
+ } else if (attribute.hasFormat(STRING)) {
+ dialog = AttributeDialogs.stringEditor(onDone);
+ } else if (attribute.hasFormat(BOOLEAN)) {
+ dialog = booleanEditor(onDone);
+ } else if (attribute.hasFormat(ENUM)) {
+ dialog = enumEditor(values, attribute.getValue(), onDone);
+ } else if (attribute.hasFormat(FLAG)) {
+ dialog = flagEditor(values, attribute.getValue(), onDone);
+ } else if (attribute.hasFormat(COLOR)) {
+ // TODO Implement something to allow values from colors.xml or ColorStateLists
+ // Make this color picker optional
+ dialog = colorPicker(mPickListener);
+ }
+
+ if (dialog != null) {
+ dialog.show();
+ }
+ }
+
+ private void applyNewValue(@NonNull IAttribute attribute, String newValue) {
+ if (this.selectedView.hasAttribute(
+ attribute.getNamespace(), attribute.getAttributeName())) {
+ if (!this.selectedView.updateAttribute(
+ attribute.getNamespace(), attribute.getAttributeName(), newValue)) {
+ StudioApp.getInstance()
+ .toast(getString(R.string.msg_attr_not_updated), Toaster.Type.ERROR);
+ } else {
+ // Update the view data
+ // This will make sure that the attributes list has been updated
+ setupViewData();
}
- }
+ } else {
+ addAttribute(
+ new UiAttribute(
+ attribute.getNamespace(), attribute.getAttributeName(), newValue));
}
- }
-
- final var sheet = getAttrListSheet();
- sheet.setItems(filterAppliedAttributes(attributes));
- sheet.show(getChildFragmentManager(), "attr_list_sheet");
-
- } else if (position == 1) { // Delete
- DialogUtils.newYesNoDialog(
- getContext(),
- (dialog, which) -> {
- var handled = selectedView.removeFromParent();
- if (!handled) {
- handled =
- mDeletionFailedListener != null
- && mDeletionFailedListener.onDeletionFailed(this.selectedView);
- }
- if (!handled) {
- StudioApp.getInstance()
- .toast(getString(R.string.msg_view_deletion_failed), Toaster.Type.ERROR);
- } else {
- dismiss();
- }
- },
- (dialog, which) -> dialog.dismiss())
- .show();
- } else if (position == 2) { // Select parent
- if (this.selectedView.getParent() == null) {
- StudioApp.getInstance().toast(getString(R.string.msg_no_view_parent), Toaster.Type.ERROR);
- return;
- }
-
- this.selectedView = this.selectedView.getParent();
-
- TransitionManager.beginDelayedTransition(binding.getRoot());
- setupViewData();
- }
- }
-
- @NonNull
- @Contract("_ -> new")
- private List filterAppliedAttributes(@NonNull TreeSet attributes) {
- attributes.removeIf(attr -> this.selectedView.hasAttribute(attr.namespace, attr.name));
- return new ArrayList<>(attributes);
- }
-
- @NonNull
- private AttributeListSheet getAttrListSheet() {
- if (mAttrListSheet == null) {
- mAttrListSheet = new AttributeListSheet();
- mAttrListSheet.onItemClick(this::addNewAttribute);
- mAttrListSheet.setCancelable(true);
}
- return mAttrListSheet;
- }
+ private void addAttribute(@NonNull IAttribute attribute) {
+ this.selectedView.addAttribute(attribute);
+ setupViewData();
+ StudioApp.getInstance().toast(getString(R.string.msg_attr_added), Toaster.Type.SUCCESS);
+ }
- private void addNewAttribute(@NonNull Attr attr) {
- final XMLAttribute attribute = new XMLAttribute(attr.namespace, attr.name, "", false);
- attribute.setAttr(attr);
- showEditorDialog(attribute);
- }
+ @Override
+ public void postBind(
+ IconTextListItem item, @NonNull SimpleIconTextAdapter.VH holder, int position) {
+ final var binding = holder.binding;
+ binding.getRoot().setOnClickListener(v -> onViewActionClick(position));
+ }
- /**
- * A listener can be used to get notified when we fail to remove a view from its parent. This is
- * used in {@link com.itsaky.androidide.DesignerActivity DesignerActivity}.
- *
- * When the user tries to remove the outermost view of the inflated layout, AttrEditorSheet
- * fails to remove the view from its parent ({@link IView#getParent()} is null for root XML
- * layout). In this case, DesignerActivity check if the view that we were trying to delete is the
- * root layout or not. If it is the root layout, then it deletes the layout from the layout
- * container.
- *
- * @author Akash Yadav
- */
- public interface OnViewDeletionFailedListener {
+ private void onViewActionClick(int position) {
+ if (this.selectedView == null) {
+ return;
+ }
+
+ if (position == 0) { // Add attribute
+ var tag = this.selectedView.getXmlTag();
+ if ("include".equals(tag) || "merge".equals(tag)) {
+ tag = "View";
+ }
+
+ final var attrs = StudioApp.getInstance().attrInfo();
+ final var style = attrs.getStyle(tag);
+ if (style == null) {
+ LOG.error("Unable to retrieve attributes for tag:", tag);
+ return;
+ }
+
+ final var attributes = new TreeSet(Comparator.comparing(attr -> attr.name));
+ attributes.addAll(style.attributes);
+
+ final var widgetInfo = StudioApp.getInstance().widgetInfo();
+ final var widget = widgetInfo.getWidgetBySimpleName(tag);
+ if (widget != null) {
+ for (var superclass : widget.superclasses) {
+ if ("java.lang.Object".equals(superclass)) {
+ break;
+ }
+
+ final var simpleName = superclass.substring(superclass.lastIndexOf(".") + 1);
+ final var superStyle = attrs.getStyle(simpleName);
+ if (superStyle != null) {
+ attributes.addAll(superStyle.attributes);
+ }
+ }
+ }
+
+ final var parent = this.selectedView.getParent();
+ if (parent != null) {
+ final var parentTag = parent.getXmlTag();
+ final var parentWidget = widgetInfo.getWidgetBySimpleName(parentTag);
+ if (parentWidget != null) {
+ final var parentLayoutParams = attrs.getStyle(parentTag + "_Layout");
+ if (parentLayoutParams != null) {
+ attributes.addAll(parentLayoutParams.attributes);
+ }
+
+ final var paramSuperclasses =
+ widgetInfo.getLayoutParamSuperClasses(parentWidget.name);
+ if (paramSuperclasses != null) {
+ for (var superclass : paramSuperclasses) {
+ if ("java.lang.Object".equals(superclass)) {
+ continue;
+ }
+
+ final var split = superclass.split("\\.");
+ final var simpleClassName = split[split.length - 2];
+ var paramName = split[split.length - 1];
+ paramName =
+ paramName.substring(0, paramName.length() - "Params".length());
+ final var superParamEntry =
+ attrs.getStyle(simpleClassName + "_" + paramName);
+ if (superParamEntry != null) {
+ attributes.addAll(superParamEntry.attributes);
+ }
+ }
+ }
+ }
+ }
+
+ final var sheet = getAttrListSheet();
+ sheet.setItems(filterAppliedAttributes(attributes));
+ sheet.show(getChildFragmentManager(), "attr_list_sheet");
+
+ } else if (position == 1) { // Delete
+ DialogUtils.newYesNoDialog(
+ getContext(),
+ (dialog, which) -> {
+ var handled = selectedView.removeFromParent();
+ if (!handled) {
+ handled =
+ mDeletionFailedListener != null
+ && mDeletionFailedListener.onDeletionFailed(
+ this.selectedView);
+ }
+ if (!handled) {
+ StudioApp.getInstance()
+ .toast(
+ getString(R.string.msg_view_deletion_failed),
+ Toaster.Type.ERROR);
+ } else {
+ dismiss();
+ }
+ },
+ (dialog, which) -> dialog.dismiss())
+ .show();
+ } else if (position == 2) { // Select parent
+ if (this.selectedView.getParent() == null) {
+ StudioApp.getInstance()
+ .toast(getString(R.string.msg_no_view_parent), Toaster.Type.ERROR);
+ return;
+ }
+
+ this.selectedView = this.selectedView.getParent();
+
+ TransitionManager.beginDelayedTransition(binding.getRoot());
+ setupViewData();
+ }
+ }
+
+ @NonNull
+ @Contract("_ -> new")
+ private List filterAppliedAttributes(@NonNull TreeSet attributes) {
+ attributes.removeIf(attr -> this.selectedView.hasAttribute(attr.namespace, attr.name));
+ return new ArrayList<>(attributes);
+ }
+
+ @NonNull
+ private AttributeListSheet getAttrListSheet() {
+ if (mAttrListSheet == null) {
+ mAttrListSheet = new AttributeListSheet();
+ mAttrListSheet.onItemClick(this::addNewAttribute);
+ mAttrListSheet.setCancelable(true);
+ }
+
+ return mAttrListSheet;
+ }
+
+ private void addNewAttribute(@NonNull Attr attr) {
+ final XMLAttribute attribute = new XMLAttribute(attr.namespace, attr.name, "", false);
+ attribute.setAttr(attr);
+ showEditorDialog(attribute);
+ }
/**
- * @param view The view that was not removed from its parent.
- * @return {@code true} if the listener handled the error. {@code false} otherwise.
+ * A listener can be used to get notified when we fail to remove a view from its parent. This is
+ * used in {@link com.itsaky.androidide.DesignerActivity DesignerActivity}.
+ *
+ * When the user tries to remove the outermost view of the inflated layout, AttrEditorSheet
+ * fails to remove the view from its parent ({@link IView#getParent()} is null for root XML
+ * layout). In this case, DesignerActivity check if the view that we were trying to delete is
+ * the root layout or not. If it is the root layout, then it deletes the layout from the layout
+ * container.
+ *
+ * @author Akash Yadav
*/
- boolean onDeletionFailed(IView view);
- }
+ public interface OnViewDeletionFailedListener {
+
+ /**
+ * @param view The view that was not removed from its parent.
+ * @return {@code true} if the listener handled the error. {@code false} otherwise.
+ */
+ boolean onDeletionFailed(IView view);
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttributeListSheet.java b/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttributeListSheet.java
index 4f2d4cfec..1cbe749ea 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttributeListSheet.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/sheets/AttributeListSheet.java
@@ -22,15 +22,18 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.SearchView;
import androidx.fragment.app.DialogFragment;
+
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.itsaky.androidide.R;
import com.itsaky.androidide.adapters.AttrListAdapter;
import com.itsaky.androidide.databinding.LayoutAddAttrSheetBinding;
import com.itsaky.attrinfo.models.Attr;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -45,81 +48,84 @@ import java.util.stream.Collectors;
*/
public class AttributeListSheet extends BottomSheetDialogFragment {
- private LayoutAddAttrSheetBinding binding;
- private Consumer clickConsumer;
- private List mItems;
+ private LayoutAddAttrSheetBinding binding;
+ private Consumer clickConsumer;
+ private List mItems;
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setStyle(DialogFragment.STYLE_NORMAL, R.style.AppTheme_SheetAboveKeyboard);
- }
-
- @Nullable
- @Override
- public View onCreateView(
- @NonNull LayoutInflater inflater,
- @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- this.binding = LayoutAddAttrSheetBinding.inflate(inflater, container, false);
- return binding.getRoot();
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- binding.searchBar.setOnQueryTextListener(
- new SearchView.OnQueryTextListener() {
-
- @Override
- public boolean onQueryTextSubmit(String query) {
- return filterAttributes(query.trim());
- }
-
- @Override
- public boolean onQueryTextChange(String newText) {
- return filterAttributes(newText.trim());
- }
- });
- binding.attrList.setAdapter(new AttrListAdapter(clickConsumer, mItems));
- }
-
- private boolean filterAttributes(@NonNull String query) {
- if (mItems == null || TextUtils.isEmpty(query)) {
- update();
- return true;
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setStyle(DialogFragment.STYLE_NORMAL, R.style.AppTheme_SheetAboveKeyboard);
}
- final var filtered =
- this.mItems.stream()
- .filter(
- attr -> attr.name.toLowerCase(Locale.ROOT).contains(query.toLowerCase(Locale.ROOT)))
- .collect(Collectors.toList());
-
- update(filtered);
- return true;
- }
-
- public void setItems(@Nullable List items) {
- if (items == null) {
- items = new ArrayList<>();
+ @Nullable
+ @Override
+ public View onCreateView(
+ @NonNull LayoutInflater inflater,
+ @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ this.binding = LayoutAddAttrSheetBinding.inflate(inflater, container, false);
+ return binding.getRoot();
}
- mItems = items;
- update();
- }
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ binding.searchBar.setOnQueryTextListener(
+ new SearchView.OnQueryTextListener() {
- public void onItemClick(Consumer onClick) {
- this.clickConsumer = onClick;
- }
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ return filterAttributes(query.trim());
+ }
- public void update() {
- update(mItems);
- }
-
- public void update(List items) {
- if (binding != null) {
- binding.attrList.setAdapter(new AttrListAdapter(clickConsumer, items));
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ return filterAttributes(newText.trim());
+ }
+ });
+ binding.attrList.setAdapter(new AttrListAdapter(clickConsumer, mItems));
+ }
+
+ private boolean filterAttributes(@NonNull String query) {
+ if (mItems == null || TextUtils.isEmpty(query)) {
+ update();
+ return true;
+ }
+
+ final var filtered =
+ this.mItems.stream()
+ .filter(
+ attr ->
+ attr.name
+ .toLowerCase(Locale.ROOT)
+ .contains(query.toLowerCase(Locale.ROOT)))
+ .collect(Collectors.toList());
+
+ update(filtered);
+ return true;
+ }
+
+ public void setItems(@Nullable List items) {
+ if (items == null) {
+ items = new ArrayList<>();
+ }
+
+ mItems = items;
+ update();
+ }
+
+ public void onItemClick(Consumer onClick) {
+ this.clickConsumer = onClick;
+ }
+
+ public void update() {
+ update(mItems);
+ }
+
+ public void update(List items) {
+ if (binding != null) {
+ binding.attrList.setAdapter(new AttrListAdapter(clickConsumer, items));
+ }
}
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sheets/BaseBottomSheetFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/sheets/BaseBottomSheetFragment.java
index 83fd82c13..ad19dc04a 100755
--- a/app/src/main/java/com/itsaky/androidide/fragments/sheets/BaseBottomSheetFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/sheets/BaseBottomSheetFragment.java
@@ -23,89 +23,91 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
+
import androidx.annotation.NonNull;
+
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.itsaky.androidide.databinding.LayoutSheetBinding;
public abstract class BaseBottomSheetFragment extends BottomSheetDialogFragment {
- protected Dialog mDialog;
- protected boolean shadowEnabled = true;
- protected boolean titleEnabled = true;
- private LayoutSheetBinding binding;
+ protected Dialog mDialog;
+ protected boolean shadowEnabled = true;
+ protected boolean titleEnabled = true;
+ private LayoutSheetBinding binding;
- @NonNull
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- mDialog = super.onCreateDialog(savedInstanceState);
- mDialog.setOnShowListener(p1 -> onShow());
- return mDialog;
- }
-
- @Override
- public View onCreateView(
- @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- binding = LayoutSheetBinding.inflate(inflater, container, false);
- return binding.getRoot();
- }
-
- @Override
- public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- bind(binding.container);
- binding.title.setText(getTitle());
- binding.title.setOnClickListener(v -> handleTitleClick());
-
- if (shouldHideTitle()) {
- binding.getRoot().removeView(binding.title);
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ mDialog = super.onCreateDialog(savedInstanceState);
+ mDialog.setOnShowListener(p1 -> onShow());
+ return mDialog;
}
- if (!shadowEnabled) {
- binding.shadow.setVisibility(View.GONE);
+ @Override
+ public View onCreateView(
+ @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ binding = LayoutSheetBinding.inflate(inflater, container, false);
+ return binding.getRoot();
}
- }
- private void handleTitleClick() {
- if (isCancelable()) {
- dismiss();
+ @Override
+ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ bind(binding.container);
+ binding.title.setText(getTitle());
+ binding.title.setOnClickListener(v -> handleTitleClick());
+
+ if (shouldHideTitle()) {
+ binding.getRoot().removeView(binding.title);
+ }
+
+ if (!shadowEnabled) {
+ binding.shadow.setVisibility(View.GONE);
+ }
}
- }
- public boolean isShowing() {
- return mDialog != null && mDialog.isShowing();
- }
-
- protected boolean shouldHideTitle() {
- return !titleEnabled;
- }
-
- protected String getTitle() {
- return "";
- }
-
- public BaseBottomSheetFragment setTitle(int title) {
- if (binding != null) {
- binding.title.setText(title);
+ private void handleTitleClick() {
+ if (isCancelable()) {
+ dismiss();
+ }
}
- return this;
- }
- public BaseBottomSheetFragment setTitle(String title) {
- if (binding != null) {
- binding.title.setText(title);
+ public boolean isShowing() {
+ return mDialog != null && mDialog.isShowing();
}
- return this;
- }
- public void setShowShadow(boolean enabled) {
- this.shadowEnabled = enabled;
- }
+ protected boolean shouldHideTitle() {
+ return !titleEnabled;
+ }
- public void setShowTitle(boolean enabled) {
- this.titleEnabled = enabled;
- }
+ protected String getTitle() {
+ return "";
+ }
- protected void onShow() {}
+ public BaseBottomSheetFragment setTitle(int title) {
+ if (binding != null) {
+ binding.title.setText(title);
+ }
+ return this;
+ }
- protected abstract void bind(LinearLayout container);
+ public BaseBottomSheetFragment setTitle(String title) {
+ if (binding != null) {
+ binding.title.setText(title);
+ }
+ return this;
+ }
+
+ public void setShowShadow(boolean enabled) {
+ this.shadowEnabled = enabled;
+ }
+
+ public void setShowTitle(boolean enabled) {
+ this.titleEnabled = enabled;
+ }
+
+ protected void onShow() {}
+
+ protected abstract void bind(LinearLayout container);
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sheets/OptionsListFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/sheets/OptionsListFragment.java
index 9d96e907b..a889bef26 100755
--- a/app/src/main/java/com/itsaky/androidide/fragments/sheets/OptionsListFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/sheets/OptionsListFragment.java
@@ -23,75 +23,78 @@ package com.itsaky.androidide.fragments.sheets;
import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;
+
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+
import com.itsaky.androidide.adapters.OptionsSheetAdapter;
import com.itsaky.androidide.models.SheetOption;
+
import java.util.ArrayList;
import java.util.List;
public class OptionsListFragment extends BaseBottomSheetFragment {
- private RecyclerView mList;
- private OnOptionsClickListener listener;
- private final List mOptions = new ArrayList<>();
+ private RecyclerView mList;
+ private OnOptionsClickListener listener;
+ private final List mOptions = new ArrayList<>();
- protected boolean dismissOnItemClick = true;
+ protected boolean dismissOnItemClick = true;
- @Override
- protected void bind(LinearLayout container) {
- mList = new RecyclerView(getContext());
- container.removeAllViews();
- container.addView(mList, new LinearLayout.LayoutParams(-1, -1));
- }
-
- @Override
- public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- mList.setLayoutManager(new LinearLayoutManager(getContext()));
- mList.setAdapter(
- new OptionsSheetAdapter(
- mOptions,
- __ -> {
- if (dismissOnItemClick) dismiss();
- if (listener != null) listener.onOptionsClick(__);
- }));
- }
-
- public OptionsListFragment setOnOptionsClickListener(OnOptionsClickListener listener) {
- this.listener = listener;
- return this;
- }
-
- public OptionsListFragment addOption(SheetOption option) {
- if (!mOptions.contains(option)) {
- mOptions.add(option);
+ @Override
+ protected void bind(LinearLayout container) {
+ mList = new RecyclerView(getContext());
+ container.removeAllViews();
+ container.addView(mList, new LinearLayout.LayoutParams(-1, -1));
}
- return this;
- }
+ @Override
+ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ mList.setLayoutManager(new LinearLayoutManager(getContext()));
+ mList.setAdapter(
+ new OptionsSheetAdapter(
+ mOptions,
+ __ -> {
+ if (dismissOnItemClick) dismiss();
+ if (listener != null) listener.onOptionsClick(__);
+ }));
+ }
- public OptionsListFragment removeOption(int optionIndex) {
- return removeOption(mOptions.get(optionIndex));
- }
+ public OptionsListFragment setOnOptionsClickListener(OnOptionsClickListener listener) {
+ this.listener = listener;
+ return this;
+ }
- public OptionsListFragment removeOption(SheetOption option) {
- mOptions.remove(option);
- return this;
- }
+ public OptionsListFragment addOption(SheetOption option) {
+ if (!mOptions.contains(option)) {
+ mOptions.add(option);
+ }
- public OptionsListFragment setDismissOnItemClick(boolean dissmiss) {
- this.dismissOnItemClick = dissmiss;
- return this;
- }
+ return this;
+ }
- @Override
- protected String getTitle() {
- return getString(com.itsaky.androidide.R.string.file_options);
- }
+ public OptionsListFragment removeOption(int optionIndex) {
+ return removeOption(mOptions.get(optionIndex));
+ }
- public static interface OnOptionsClickListener {
- void onOptionsClick(SheetOption option);
- }
+ public OptionsListFragment removeOption(SheetOption option) {
+ mOptions.remove(option);
+ return this;
+ }
+
+ public OptionsListFragment setDismissOnItemClick(boolean dissmiss) {
+ this.dismissOnItemClick = dissmiss;
+ return this;
+ }
+
+ @Override
+ protected String getTitle() {
+ return getString(com.itsaky.androidide.R.string.file_options);
+ }
+
+ public static interface OnOptionsClickListener {
+ void onOptionsClick(SheetOption option);
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sheets/ProgressSheet.java b/app/src/main/java/com/itsaky/androidide/fragments/sheets/ProgressSheet.java
index 61a823614..96adebc21 100755
--- a/app/src/main/java/com/itsaky/androidide/fragments/sheets/ProgressSheet.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/sheets/ProgressSheet.java
@@ -26,103 +26,105 @@ import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
+
import androidx.annotation.NonNull;
+
import com.itsaky.androidide.databinding.LayoutProgressSheetBinding;
import com.itsaky.androidide.utils.Logger;
public class ProgressSheet extends BaseBottomSheetFragment {
- private LayoutProgressSheetBinding binding;
- private Drawable drawable = null;
- private String message = "";
- private String subMessage = "";
- private boolean subMessageEnabled = false;
- private boolean welcomTextEnabled = false;
+ private LayoutProgressSheetBinding binding;
+ private Drawable drawable = null;
+ private String message = "";
+ private String subMessage = "";
+ private boolean subMessageEnabled = false;
+ private boolean welcomTextEnabled = false;
- private final Logger LOG = Logger.instance("ProgressSheet");
+ private final Logger LOG = Logger.instance("ProgressSheet");
- @Override
- protected void bind(LinearLayout container) {
- binding = LayoutProgressSheetBinding.inflate(LayoutInflater.from(getContext()));
- container.addView(binding.getRoot());
- }
-
- @Override
- public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- binding.message.setText(message);
- if (subMessageEnabled) {
- binding.subMessage.setText(subMessage);
- binding.subMessage.setVisibility(View.VISIBLE);
- RelativeLayout.LayoutParams p =
- (RelativeLayout.LayoutParams) binding.message.getLayoutParams();
- try {
- p.removeRule(RelativeLayout.CENTER_VERTICAL);
- } catch (Throwable th) {
- LOG.error("Unable to remove center_vertical rule.", th);
- }
- binding.message.setLayoutParams(p);
- } else {
- binding.subMessage.setVisibility(View.GONE);
- RelativeLayout.LayoutParams p =
- (RelativeLayout.LayoutParams) binding.message.getLayoutParams();
- try {
- p.addRule(RelativeLayout.CENTER_VERTICAL);
- } catch (Throwable th) {
- LOG.error("Unable to remove center_vertical rule.", th);
- }
- binding.message.setLayoutParams(p);
+ @Override
+ protected void bind(LinearLayout container) {
+ binding = LayoutProgressSheetBinding.inflate(LayoutInflater.from(getContext()));
+ container.addView(binding.getRoot());
}
- if (!welcomTextEnabled) {
- binding.welcomeText.setVisibility(View.GONE);
- }
- }
+ @Override
+ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
- @Override
- protected boolean shouldHideTitle() {
- return true;
- }
+ binding.message.setText(message);
+ if (subMessageEnabled) {
+ binding.subMessage.setText(subMessage);
+ binding.subMessage.setVisibility(View.VISIBLE);
+ RelativeLayout.LayoutParams p =
+ (RelativeLayout.LayoutParams) binding.message.getLayoutParams();
+ try {
+ p.removeRule(RelativeLayout.CENTER_VERTICAL);
+ } catch (Throwable th) {
+ LOG.error("Unable to remove center_vertical rule.", th);
+ }
+ binding.message.setLayoutParams(p);
+ } else {
+ binding.subMessage.setVisibility(View.GONE);
+ RelativeLayout.LayoutParams p =
+ (RelativeLayout.LayoutParams) binding.message.getLayoutParams();
+ try {
+ p.addRule(RelativeLayout.CENTER_VERTICAL);
+ } catch (Throwable th) {
+ LOG.error("Unable to remove center_vertical rule.", th);
+ }
+ binding.message.setLayoutParams(p);
+ }
- public ProgressSheet setWelcomeTextEnabled(boolean enabled) {
- this.welcomTextEnabled = enabled;
- return this;
- }
-
- public ProgressSheet setSubMessageEnabled(boolean enabled) {
- this.subMessageEnabled = enabled;
- return this;
- }
-
- public ProgressSheet setSubMessage(String msg) {
- this.subMessage = msg;
- if (isShowing()) {
- binding.subMessage.setText(msg);
- }
- return this;
- }
-
- public ProgressSheet setMessage(String message) {
- this.message = message;
- if (isShowing()) {
- binding.message.setText(message);
+ if (!welcomTextEnabled) {
+ binding.welcomeText.setVisibility(View.GONE);
+ }
}
- return this;
- }
-
- public ProgressSheet setProgressDrawable(Drawable drawable) {
- this.drawable = drawable;
- if (isShowing()) {
- binding.progress.setIndeterminateDrawable(drawable);
+ @Override
+ protected boolean shouldHideTitle() {
+ return true;
}
- return this;
- }
+ public ProgressSheet setWelcomeTextEnabled(boolean enabled) {
+ this.welcomTextEnabled = enabled;
+ return this;
+ }
- @Override
- public void dismiss() {
- if (isShowing()) super.dismiss();
- }
+ public ProgressSheet setSubMessageEnabled(boolean enabled) {
+ this.subMessageEnabled = enabled;
+ return this;
+ }
+
+ public ProgressSheet setSubMessage(String msg) {
+ this.subMessage = msg;
+ if (isShowing()) {
+ binding.subMessage.setText(msg);
+ }
+ return this;
+ }
+
+ public ProgressSheet setMessage(String message) {
+ this.message = message;
+ if (isShowing()) {
+ binding.message.setText(message);
+ }
+
+ return this;
+ }
+
+ public ProgressSheet setProgressDrawable(Drawable drawable) {
+ this.drawable = drawable;
+ if (isShowing()) {
+ binding.progress.setIndeterminateDrawable(drawable);
+ }
+
+ return this;
+ }
+
+ @Override
+ public void dismiss() {
+ if (isShowing()) super.dismiss();
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/sheets/TextSheetFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/sheets/TextSheetFragment.java
index b7e6a2da9..fcd82e62f 100755
--- a/app/src/main/java/com/itsaky/androidide/fragments/sheets/TextSheetFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/sheets/TextSheetFragment.java
@@ -22,112 +22,116 @@ import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.view.ViewGroup;
import android.widget.LinearLayout;
+
import androidx.annotation.NonNull;
+
import com.itsaky.androidide.R;
import com.itsaky.androidide.utils.TypefaceUtils;
import com.itsaky.androidide.views.editor.IDEEditor;
+
import io.github.rosemoe.sora.lang.EmptyLanguage;
public class TextSheetFragment extends BaseBottomSheetFragment {
- private SpannableStringBuilder outputBuilder;
- private IDEEditor editor;
- private boolean textSelectable;
+ private SpannableStringBuilder outputBuilder;
+ private IDEEditor editor;
+ private boolean textSelectable;
- public TextSheetFragment setTextSelectable(boolean textSelectable) {
- this.textSelectable = textSelectable;
- return this;
- }
-
- @Override
- protected void bind(@NonNull LinearLayout container) {
- final IDEEditor e = getEditor();
- container.setPadding(0, 0, 0, 0);
- container.setPaddingRelative(0, 0, 0, 0);
- LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(-1, 0);
- p.weight = 1.0f;
- if (e.getParent() != null && e.getParent() instanceof ViewGroup) {
- ((ViewGroup) e.getParent()).removeView(e);
+ public TextSheetFragment setTextSelectable(boolean textSelectable) {
+ this.textSelectable = textSelectable;
+ return this;
}
- container.addView(getEditor(), p);
- }
- @Override
- protected String getTitle() {
- return getString(R.string.build_output);
- }
-
- @Override
- protected void onShow() {
- super.onShow();
- editor.setText(getOutputBuilder());
- }
-
- private void scrollToBottom() {
- if (getActivity() != null) getActivity().runOnUiThread(() -> editor.goToEnd());
- }
-
- public TextSheetFragment setTitleText(int res) {
- super.setTitle(res);
- return this;
- }
-
- public TextSheetFragment setTitleText(String res) {
- super.setTitle(res);
- return this;
- }
-
- public void append(String text) {
- append(text, -1, true);
- }
-
- public void append(String text, int spanColor, boolean appendLine) {
- if (text != null && text.trim().length() > 0) {
- appendInternal(appendLine && !text.endsWith("\n") ? text.concat("\n") : text, spanColor);
+ @Override
+ protected void bind(@NonNull LinearLayout container) {
+ final IDEEditor e = getEditor();
+ container.setPadding(0, 0, 0, 0);
+ container.setPaddingRelative(0, 0, 0, 0);
+ LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(-1, 0);
+ p.weight = 1.0f;
+ if (e.getParent() != null && e.getParent() instanceof ViewGroup) {
+ ((ViewGroup) e.getParent()).removeView(e);
+ }
+ container.addView(getEditor(), p);
}
- }
- private void appendInternal(String text, int spanColor) {
- SpannableString str = new SpannableString(text);
- str.setSpan(
- new ForegroundColorSpan(spanColor == -1 ? Color.WHITE : spanColor),
- 0,
- text.length(),
- SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
- getOutputBuilder().append(str);
- if (mDialog != null && mDialog.isShowing()) {
- setToEditor(str);
+ @Override
+ protected String getTitle() {
+ return getString(R.string.build_output);
}
- }
- private void setToEditor(SpannableString line) {
- if (getActivity() != null)
- getActivity()
- .runOnUiThread(
- () -> {
- editor.append(line);
- scrollToBottom();
- });
- }
+ @Override
+ protected void onShow() {
+ super.onShow();
+ editor.setText(getOutputBuilder());
+ }
- private IDEEditor getEditor() {
- return editor == null ? createEditor() : editor;
- }
+ private void scrollToBottom() {
+ if (getActivity() != null) getActivity().runOnUiThread(() -> editor.goToEnd());
+ }
- private IDEEditor createEditor() {
- editor = new IDEEditor(getContext());
- editor.setEditable(false);
- editor.setDividerWidth(0);
- editor.setEditorLanguage(new EmptyLanguage());
- editor.setWordwrap(false);
- editor.setUndoEnabled(false);
- editor.setTypefaceLineNumber(TypefaceUtils.jetbrainsMono());
- editor.setTypefaceText(TypefaceUtils.jetbrainsMono());
- editor.setTextSize(12);
- return editor;
- }
+ public TextSheetFragment setTitleText(int res) {
+ super.setTitle(res);
+ return this;
+ }
- private SpannableStringBuilder getOutputBuilder() {
- return outputBuilder == null ? outputBuilder = new SpannableStringBuilder() : outputBuilder;
- }
+ public TextSheetFragment setTitleText(String res) {
+ super.setTitle(res);
+ return this;
+ }
+
+ public void append(String text) {
+ append(text, -1, true);
+ }
+
+ public void append(String text, int spanColor, boolean appendLine) {
+ if (text != null && text.trim().length() > 0) {
+ appendInternal(
+ appendLine && !text.endsWith("\n") ? text.concat("\n") : text, spanColor);
+ }
+ }
+
+ private void appendInternal(String text, int spanColor) {
+ SpannableString str = new SpannableString(text);
+ str.setSpan(
+ new ForegroundColorSpan(spanColor == -1 ? Color.WHITE : spanColor),
+ 0,
+ text.length(),
+ SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
+ getOutputBuilder().append(str);
+ if (mDialog != null && mDialog.isShowing()) {
+ setToEditor(str);
+ }
+ }
+
+ private void setToEditor(SpannableString line) {
+ if (getActivity() != null)
+ getActivity()
+ .runOnUiThread(
+ () -> {
+ editor.append(line);
+ scrollToBottom();
+ });
+ }
+
+ private IDEEditor getEditor() {
+ return editor == null ? createEditor() : editor;
+ }
+
+ private IDEEditor createEditor() {
+ editor = new IDEEditor(getContext());
+ editor.setEditable(false);
+ editor.setDividerWidth(0);
+ editor.setEditorLanguage(new EmptyLanguage());
+ editor.setWordwrap(false);
+ editor.setUndoEnabled(false);
+ editor.setTypefaceLineNumber(TypefaceUtils.jetbrainsMono());
+ editor.setTypefaceText(TypefaceUtils.jetbrainsMono());
+ editor.setTextSize(12);
+ return editor;
+ }
+
+ private SpannableStringBuilder getOutputBuilder() {
+ return outputBuilder == null ? outputBuilder = new SpannableStringBuilder() : outputBuilder;
+ }
}
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 c537b5c9b..ac8ee107a 100755
--- a/app/src/main/java/com/itsaky/androidide/handlers/BuildServiceHandler.java
+++ b/app/src/main/java/com/itsaky/androidide/handlers/BuildServiceHandler.java
@@ -18,6 +18,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;
@@ -30,6 +31,7 @@ import com.itsaky.androidide.tasks.GradleTask;
import com.itsaky.androidide.tasks.gradle.build.ApkGeneratingTask;
import com.itsaky.androidide.utils.DialogUtils;
import com.itsaky.androidide.utils.Environment;
+
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
@@ -40,210 +42,217 @@ import java.util.stream.Collectors;
public class BuildServiceHandler extends IDEHandler implements BuildListener {
- private IDEService service;
- private boolean shouldInstallApk = false;
+ private IDEService service;
+ private boolean shouldInstallApk = false;
- public BuildServiceHandler(Provider provider) {
- super(provider);
- }
-
- public IDEService getService() {
- return service;
- }
-
- @Override
- public void start() {
- if (this.activity() == null) {
- throwNPE();
+ public BuildServiceHandler(Provider provider) {
+ super(provider);
}
- AndroidProject project = provider.provideAndroidProject();
- if (project == null) {
- throwNPE();
+ public IDEService getService() {
+ return service;
}
- if (project.getProjectPath() == null) {
- throwNPE();
- }
-
- this.service = new IDEService(new File(project.getProjectPath()));
- this.service.setListener(this);
- }
-
- @Override
- public void stop() {
- this.service.stopAllDaemons();
- this.service.exit();
- }
-
- @Override
- public void onBuildModified() {
- // Does nothing currently
- }
-
- @Override
- public void onProjectLoaded(IDEProject project, Optional appModule) {
- project.iconPath = provider.provideAndroidProject().getIconPath();
- activity().setIDEProject(project);
-
- if (appModule.isPresent()) {
- IDEModule app = appModule.get();
- activity().setStatus(activity().getString(R.string.msg_starting_completion));
-
- final List paths =
- app.dependencies.stream().filter(this::isClasspathValid).collect(Collectors.toList());
-
- provider.provideAndroidProject().setClassPaths(paths);
-
- File androidJar =
- new File(
- Environment.ANDROID_HOME,
- String.format("platforms/%s/android.jar", app.compileSdkVersion));
- if (androidJar.exists()) {
- Environment.setBootClasspath(androidJar);
- provider.provideAndroidProject().addClasspath(androidJar.getAbsolutePath());
- activity().updateServices();
- } else activity().setStatus("android.jar not found!");
- } else activity().setStatus("Cannot get :app module...");
- }
-
- @Override
- public void onStartingGradleDaemon(GradleTask task) {
- activity().setStatus(activity().getString(R.string.msg_starting_daemon));
- }
-
- @Override
- public void onRunTask(GradleTask task, String name) {
- if (task != null && task.canOutput()) {
- activity().setStatus(service.typeString(task.getTaskID()) + " " + name);
- }
- }
-
- @Override
- public void onBuildSuccessful(GradleTask task, String msg) {
- appendOutput(task, msg);
- if (task == null || activity() == null) return;
- if (task.canOutput()) {
- activity().setStatus(msg);
- }
-
- // If this task generates APK, ask user if he/she wants to install them
- if (shouldInstallApk && task instanceof ApkGeneratingTask) {
- final IDEProject project = activity().provideIDEProject();
- final Optional app =
- project.getModuleByPath(":app"); // TODO Handle multiple application modules
- if (app.isPresent()) {
- final String path = app.get().projectDir;
- if (path != null) {
- final File buildDir = new File(path, "build");
- installApks(((ApkGeneratingTask) task).getApks(buildDir));
+ @Override
+ public void start() {
+ if (this.activity() == null) {
+ throwNPE();
}
- }
+
+ AndroidProject project = provider.provideAndroidProject();
+ if (project == null) {
+ throwNPE();
+ }
+
+ if (project.getProjectPath() == null) {
+ throwNPE();
+ }
+
+ this.service = new IDEService(new File(project.getProjectPath()));
+ this.service.setListener(this);
}
- activity()
- .getApp()
- .getPrefManager()
- .putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false);
- activity().invalidateOptionsMenu();
- activity().getBinding().buildProgressIndicator.setVisibility(View.GONE);
- }
-
- @Override
- public void onBuildFailed(GradleTask task, String msg) {
- appendOutput(task, msg);
- if (task == null) return;
- if (task.canOutput()) {
- activity().setStatus(msg);
+ @Override
+ public void stop() {
+ this.service.stopAllDaemons();
+ this.service.exit();
}
- activity()
- .getApp()
- .getPrefManager()
- .putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false);
- activity().invalidateOptionsMenu();
- activity().getBinding().buildProgressIndicator.setVisibility(View.GONE);
- }
-
- @Override
- public void saveFiles() {
- activity().saveAll(false, false);
- }
-
- @Override
- public void appendOutput(GradleTask task, CharSequence text) {
- if (text == null) return;
- if (task != null && !task.canOutput()) {
- return;
+ @Override
+ public void onBuildModified() {
+ // Does nothing currently
}
- activity().appendBuildOut(text.toString());
- }
+ @Override
+ public void onProjectLoaded(IDEProject project, Optional appModule) {
+ project.iconPath = provider.provideAndroidProject().getIconPath();
+ activity().setIDEProject(project);
+
+ if (appModule.isPresent()) {
+ IDEModule app = appModule.get();
+ activity().setStatus(activity().getString(R.string.msg_starting_completion));
+
+ final List paths =
+ app.dependencies.stream()
+ .filter(this::isClasspathValid)
+ .collect(Collectors.toList());
+
+ provider.provideAndroidProject().setClassPaths(paths);
+
+ File androidJar =
+ new File(
+ Environment.ANDROID_HOME,
+ String.format("platforms/%s/android.jar", app.compileSdkVersion));
+ if (androidJar.exists()) {
+ Environment.setBootClasspath(androidJar);
+ provider.provideAndroidProject().addClasspath(androidJar.getAbsolutePath());
+ activity().updateServices();
+ } else activity().setStatus("android.jar not found!");
+ } else activity().setStatus("Cannot get :app module...");
+ }
+
+ @Override
+ public void onStartingGradleDaemon(GradleTask task) {
+ activity().setStatus(activity().getString(R.string.msg_starting_daemon));
+ }
+
+ @Override
+ public void onRunTask(GradleTask task, String name) {
+ if (task != null && task.canOutput()) {
+ activity().setStatus(service.typeString(task.getTaskID()) + " " + name);
+ }
+ }
+
+ @Override
+ public void onBuildSuccessful(GradleTask task, String msg) {
+ appendOutput(task, msg);
+ if (task == null || activity() == null) return;
+ if (task.canOutput()) {
+ activity().setStatus(msg);
+ }
+
+ // If this task generates APK, ask user if he/she wants to install them
+ if (shouldInstallApk && task instanceof ApkGeneratingTask) {
+ final IDEProject project = activity().provideIDEProject();
+ final Optional app =
+ project.getModuleByPath(":app"); // TODO Handle multiple application modules
+ if (app.isPresent()) {
+ final String path = app.get().projectDir;
+ if (path != null) {
+ final File buildDir = new File(path, "build");
+ installApks(((ApkGeneratingTask) task).getApks(buildDir));
+ }
+ }
+ }
- @Override
- public void prepareBuild() {
- boolean isFirstBuild =
activity()
- .getApp()
- .getPrefManager()
- .getBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, true);
- activity()
- .setStatus(
- activity().getString(isFirstBuild ? R.string.preparing_first : R.string.preparing));
- if (isFirstBuild) {
- activity().showFirstBuildNotice();
- }
- activity().getBinding().buildProgressIndicator.setVisibility(View.VISIBLE);
- }
-
- public void setShouldInstallApk(boolean install) {
- this.shouldInstallApk = install;
- }
-
- public void assembleDebug(boolean shouldInstallApk) {
- setShouldInstallApk(shouldInstallApk);
- getService().assembleDebug(shouldInstallApk);
- }
-
- private boolean isClasspathValid(String path) {
- if (path == null || path.trim().length() <= 0 || path.trim().equals("/")) return false;
- File file = new File(path);
- return file.isFile()
- && file.getName().endsWith(".jar")
- /** Release R.jar shouldn't be included in classpath */
- && !file.getAbsolutePath().endsWith("/release/R.jar");
- }
-
- private void installApks(final Set apks) {
- if (activity() == null || apks == null || apks.isEmpty()) {
- return;
+ .getApp()
+ .getPrefManager()
+ .putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false);
+ activity().invalidateOptionsMenu();
+ activity().getBinding().buildProgressIndicator.setVisibility(View.GONE);
}
- if (apks.size() == 1) {
- activity().install(apks.iterator().next());
- } else {
- final List files = new ArrayList(apks);
- final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
- builder.setTitle(activity().getString(R.string.title_install_apks));
- builder.setItems(
- getNames(files),
- (d, w) -> {
- d.dismiss();
- activity().install(files.get(w));
- });
- builder.show();
- }
- }
+ @Override
+ public void onBuildFailed(GradleTask task, String msg) {
+ appendOutput(task, msg);
+ if (task == null) return;
+ if (task.canOutput()) {
+ activity().setStatus(msg);
+ }
- private CharSequence[] getNames(final Collection apks) {
- final String[] names = new String[apks.size()];
-
- int i = 0;
- for (File apk : apks) {
- names[i] = apk.getName();
- ++i;
+ activity()
+ .getApp()
+ .getPrefManager()
+ .putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false);
+ activity().invalidateOptionsMenu();
+ activity().getBinding().buildProgressIndicator.setVisibility(View.GONE);
}
- return names;
- }
+ @Override
+ public void saveFiles() {
+ activity().saveAll(false, false);
+ }
+
+ @Override
+ public void appendOutput(GradleTask task, CharSequence text) {
+ if (text == null) return;
+ if (task != null && !task.canOutput()) {
+ return;
+ }
+
+ activity().appendBuildOut(text.toString());
+ }
+
+ @Override
+ public void prepareBuild() {
+ boolean isFirstBuild =
+ activity()
+ .getApp()
+ .getPrefManager()
+ .getBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, true);
+ activity()
+ .setStatus(
+ activity()
+ .getString(
+ isFirstBuild
+ ? R.string.preparing_first
+ : R.string.preparing));
+ if (isFirstBuild) {
+ activity().showFirstBuildNotice();
+ }
+ activity().getBinding().buildProgressIndicator.setVisibility(View.VISIBLE);
+ }
+
+ public void setShouldInstallApk(boolean install) {
+ this.shouldInstallApk = install;
+ }
+
+ public void assembleDebug(boolean shouldInstallApk) {
+ setShouldInstallApk(shouldInstallApk);
+ getService().assembleDebug(shouldInstallApk);
+ }
+
+ private boolean isClasspathValid(String path) {
+ if (path == null || path.trim().length() <= 0 || path.trim().equals("/")) return false;
+ File file = new File(path);
+ return file.isFile()
+ && file.getName().endsWith(".jar")
+ /** Release R.jar shouldn't be included in classpath */
+ && !file.getAbsolutePath().endsWith("/release/R.jar");
+ }
+
+ private void installApks(final Set apks) {
+ if (activity() == null || apks == null || apks.isEmpty()) {
+ return;
+ }
+
+ if (apks.size() == 1) {
+ activity().install(apks.iterator().next());
+ } else {
+ final List files = new ArrayList(apks);
+ final MaterialAlertDialogBuilder builder =
+ DialogUtils.newMaterialDialogBuilder(activity());
+ builder.setTitle(activity().getString(R.string.title_install_apks));
+ builder.setItems(
+ getNames(files),
+ (d, w) -> {
+ d.dismiss();
+ activity().install(files.get(w));
+ });
+ builder.show();
+ }
+ }
+
+ private CharSequence[] getNames(final Collection apks) {
+ final String[] names = new String[apks.size()];
+
+ int i = 0;
+ for (File apk : apks) {
+ names[i] = apk.getName();
+ ++i;
+ }
+
+ return names;
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/handlers/FileOptionsHandler.java b/app/src/main/java/com/itsaky/androidide/handlers/FileOptionsHandler.java
index 2090c0b9c..64b685ab6 100755
--- a/app/src/main/java/com/itsaky/androidide/handlers/FileOptionsHandler.java
+++ b/app/src/main/java/com/itsaky/androidide/handlers/FileOptionsHandler.java
@@ -18,6 +18,7 @@
package com.itsaky.androidide.handlers;
import android.view.LayoutInflater;
+
import com.blankj.utilcode.util.ClipboardUtils;
import com.blankj.utilcode.util.FileIOUtils;
import com.blankj.utilcode.util.FileUtils;
@@ -33,364 +34,411 @@ import com.itsaky.androidide.utils.Environment;
import com.itsaky.androidide.utils.ProjectWriter;
import com.itsaky.toaster.Toaster;
import com.unnamed.b.atv.model.TreeNode;
+
import java.io.File;
import java.util.Objects;
import java.util.regex.Pattern;
public class FileOptionsHandler extends IDEHandler
- implements OptionsListFragment.OnOptionsClickListener {
+ implements OptionsListFragment.OnOptionsClickListener {
- public static final String RES_PATH_REGEX = "/.*/src/.*/res";
- public static final String LAYOUT_RES_PATH_REGEX = "/.*/src/.*/res/layout";
- public static final String MENU_RES_PATH_REGEX = "/.*/src/.*/res/menu";
- public static final String DRAWABLE_RES_PATH_REGEX = "/.*/src/.*/res/drawable";
- public static final String JAVA_PATH_REGEX = "/.*/src/.*/java";
+ public static final String RES_PATH_REGEX = "/.*/src/.*/res";
+ public static final String LAYOUT_RES_PATH_REGEX = "/.*/src/.*/res/layout";
+ public static final String MENU_RES_PATH_REGEX = "/.*/src/.*/res/menu";
+ public static final String DRAWABLE_RES_PATH_REGEX = "/.*/src/.*/res/drawable";
+ public static final String JAVA_PATH_REGEX = "/.*/src/.*/java";
- public FileOptionsHandler(Provider provider) {
- super(provider);
- }
-
- @Override
- public void start() {
- if (activity() == null) {
- throwNPE();
+ public FileOptionsHandler(Provider provider) {
+ super(provider);
}
- }
- @Override
- public void stop() {}
-
- @Override
- public void onOptionsClick(SheetOption option) {
- if (option.extra instanceof File) {
- final File f = (File) option.extra;
- switch (option.id) {
- case 0:
- ClipboardUtils.copyText("[AndroidIDE] Copied File Path", f.getAbsolutePath());
- activity().getApp().toast(R.string.copied, Toaster.Type.SUCCESS);
- break;
- case 1:
- renameFile(f);
- break;
- case 2:
- delete(f);
- break;
- case 3:
- createNewFile(f);
- break;
- case 4:
- createNewFolder(f);
- break;
- }
- }
- }
-
- private void createNewFile(File f) {
- createNewFile(f, false);
- }
-
- private void createNewFile(final File f, boolean forceUnknownType) {
- if (forceUnknownType) {
- createNewEmptyFile(f);
- } else {
- final boolean isJava =
- Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + JAVA_PATH_REGEX)
- .matcher(f.getAbsolutePath())
- .find();
- final boolean isRes =
- Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + RES_PATH_REGEX)
- .matcher(f.getAbsolutePath())
- .find();
- final boolean isLayoutRes =
- Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + LAYOUT_RES_PATH_REGEX)
- .matcher(f.getAbsolutePath())
- .find();
- final boolean isMenuRes =
- Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + MENU_RES_PATH_REGEX)
- .matcher(f.getAbsolutePath())
- .find();
- final boolean isDrawableRes =
- Pattern.compile(
- Pattern.quote(androidProject().getProjectPath()) + DRAWABLE_RES_PATH_REGEX)
- .matcher(f.getAbsolutePath())
- .find();
- if (isJava) {
- createJavaClass(f);
- } else if (isLayoutRes && f.getName().equals("layout")) {
- createLayoutRes(f);
- } else if (isMenuRes && f.getName().equals("menu")) {
- createMenuRes(f);
- } else if (isDrawableRes && f.getName().equals("drawable")) {
- createDrawableRes(f);
- } else if (isRes && f.getName().equals("res")) {
- createNewResource(f);
- } else {
- createNewEmptyFile(f);
- }
- }
- }
-
- private void createJavaClass(final File f) {
- final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
- final LayoutCreateFileJavaBinding binding =
- LayoutCreateFileJavaBinding.inflate(activity().getLayoutInflater());
- builder.setView(binding.getRoot());
- builder.setTitle(R.string.new_java_class);
- builder.setPositiveButton(
- R.string.text_create,
- (p1, p2) -> {
- p1.dismiss();
- final String name =
- Objects.requireNonNull(binding.name.getEditText()).getText().toString().trim();
- final String pkgName = ProjectWriter.getPackageName(f);
- if (pkgName == null || pkgName.trim().length() <= 0) {
- activity().getApp().toast(R.string.msg_get_package_failed, Toaster.Type.ERROR);
- } else {
- final int id = binding.typeGroup.getCheckedButtonId();
- final var javaName = name.endsWith(".java") ? name : name.concat(".java");
- final var className =
- !name.contains(".") ? name : name.substring(0, name.lastIndexOf("."));
- if (id == binding.typeClass.getId()) {
- createFile(f, javaName, ProjectWriter.createJavaClass(pkgName, className));
- } else if (id == binding.typeInterface.getId()) {
- createFile(f, javaName, ProjectWriter.createJavaInterface(pkgName, className));
- } else if (id == binding.typeEnum.getId()) {
- createFile(f, javaName, ProjectWriter.createJavaEnum(pkgName, className));
- } else if (id == binding.typeActivity.getId()) {
- createFile(f, javaName, ProjectWriter.createActivity(pkgName, className));
- } else {
- createFile(f, name, "");
- }
- }
- });
- builder.setNegativeButton(android.R.string.cancel, null);
- builder.setCancelable(false);
- builder.create().show();
- }
-
- private void createLayoutRes(File f) {
- createNewFileWithContent(Environment.mkdirIfNotExits(f), ProjectWriter.createLayout(), ".xml");
- }
-
- private void createMenuRes(File f) {
- createNewFileWithContent(Environment.mkdirIfNotExits(f), ProjectWriter.createMenu(), ".xml");
- }
-
- private void createDrawableRes(File f) {
- createNewFileWithContent(
- Environment.mkdirIfNotExits(f), ProjectWriter.createDrawable(), ".xml");
- }
-
- private void createNewResource(File f) {
- final String[] labels = {
- activity().getString(R.string.restype_drawable),
- activity().getString(R.string.restype_layout),
- activity().getString(R.string.restype_menu),
- activity().getString(R.string.restype_other)
- };
- final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
- builder.setTitle(R.string.new_xml_resource);
- builder.setItems(
- labels,
- (dialogInterface, position) -> {
- if (position == 0) {
- createDrawableRes(new File(f, "drawable"));
- } else if (position == 1) {
- createLayoutRes(new File(f, "layout"));
- } else if (position == 2) {
- createMenuRes(new File(f, "menu"));
- } else if (position == 3) {
- createNewFile(f, true);
- }
- });
- builder.create().show();
- }
-
- private void createNewEmptyFile(File f) {
- createNewFileWithContent(f, "");
- }
-
- private void createNewFileWithContent(File f, String content) {
- createNewFileWithContent(f, content, null);
- }
-
- private void createNewFileWithContent(File folder, String content, String extension) {
- final LayoutDialogTextInputBinding binding =
- LayoutDialogTextInputBinding.inflate(activity().getLayoutInflater());
- final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
-
- Objects.requireNonNull(binding.name.getEditText()).setHint(R.string.file_name);
-
- builder.setTitle(R.string.new_file);
- builder.setMessage(
- activity()
- .getString(R.string.msg_can_contain_slashes)
- .concat("\n\n")
- .concat(activity().getString(R.string.msg_newfile_dest, folder.getAbsolutePath())));
- builder.setView(binding.getRoot());
- builder.setCancelable(false);
- builder.setPositiveButton(
- R.string.text_create,
- (p1, p2) -> {
- p1.dismiss();
- String name = binding.name.getEditText().getText().toString().trim();
- if (extension != null && extension.trim().length() > 0) {
- name = name.endsWith(extension) ? name : name.concat(extension);
- }
- createFile(folder, name, content);
- });
- builder.setNegativeButton(android.R.string.cancel, null);
- builder.create().show();
- }
-
- private void createFile(File f, String name, String content) {
- if (name.length() > 0 && name.length() <= 40 && !name.startsWith("/")) {
- final File file = new File(f, name);
- if (file.exists()) {
- activity().getApp().toast(R.string.msg_file_exists, Toaster.Type.ERROR);
- } else {
- if (FileIOUtils.writeFileFromString(file, content)) {
- // TODO Notify language servers about file created event
- activity().getApp().toast(R.string.msg_file_created, Toaster.Type.SUCCESS);
- if (activity().getLastHoldTreeNode() != null) {
- TreeNode node = new TreeNode(file);
- node.setViewHolder(new FileTreeViewHolder(activity()));
- activity().getLastHoldTreeNode().addChild(node);
- activity().getFileTreeFragment().expandNode(activity().getLastHoldTreeNode());
- } else {
- activity().getFileTreeFragment().listProjectFiles();
- }
- } else {
- activity().getApp().toast(R.string.msg_file_creation_failed, Toaster.Type.ERROR);
+ @Override
+ public void start() {
+ if (activity() == null) {
+ throwNPE();
}
- }
- } else {
- activity().getApp().toast(R.string.msg_invalid_name, Toaster.Type.ERROR);
}
- }
- private void createNewFolder(File f) {
- final LayoutDialogTextInputBinding binding =
- LayoutDialogTextInputBinding.inflate(activity().getLayoutInflater());
- final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
+ @Override
+ public void stop() {}
- Objects.requireNonNull(binding.name.getEditText()).setHint(R.string.folder_name);
+ @Override
+ public void onOptionsClick(SheetOption option) {
+ if (option.extra instanceof File) {
+ final File f = (File) option.extra;
+ switch (option.id) {
+ case 0:
+ ClipboardUtils.copyText("[AndroidIDE] Copied File Path", f.getAbsolutePath());
+ activity().getApp().toast(R.string.copied, Toaster.Type.SUCCESS);
+ break;
+ case 1:
+ renameFile(f);
+ break;
+ case 2:
+ delete(f);
+ break;
+ case 3:
+ createNewFile(f);
+ break;
+ case 4:
+ createNewFolder(f);
+ break;
+ }
+ }
+ }
- builder.setTitle(R.string.new_folder);
- builder.setMessage(R.string.msg_can_contain_slashes);
- builder.setView(binding.getRoot());
- builder.setCancelable(false);
- builder.setPositiveButton(
- R.string.text_create,
- (p1, p2) -> {
- p1.dismiss();
- final String name = binding.name.getEditText().getText().toString().trim();
- if (name.length() > 0 && name.length() <= 40 && !name.startsWith("/")) {
+ private void createNewFile(File f) {
+ createNewFile(f, false);
+ }
+
+ private void createNewFile(final File f, boolean forceUnknownType) {
+ if (forceUnknownType) {
+ createNewEmptyFile(f);
+ } else {
+ final boolean isJava =
+ Pattern.compile(
+ Pattern.quote(androidProject().getProjectPath())
+ + JAVA_PATH_REGEX)
+ .matcher(f.getAbsolutePath())
+ .find();
+ final boolean isRes =
+ Pattern.compile(
+ Pattern.quote(androidProject().getProjectPath())
+ + RES_PATH_REGEX)
+ .matcher(f.getAbsolutePath())
+ .find();
+ final boolean isLayoutRes =
+ Pattern.compile(
+ Pattern.quote(androidProject().getProjectPath())
+ + LAYOUT_RES_PATH_REGEX)
+ .matcher(f.getAbsolutePath())
+ .find();
+ final boolean isMenuRes =
+ Pattern.compile(
+ Pattern.quote(androidProject().getProjectPath())
+ + MENU_RES_PATH_REGEX)
+ .matcher(f.getAbsolutePath())
+ .find();
+ final boolean isDrawableRes =
+ Pattern.compile(
+ Pattern.quote(androidProject().getProjectPath())
+ + DRAWABLE_RES_PATH_REGEX)
+ .matcher(f.getAbsolutePath())
+ .find();
+ if (isJava) {
+ createJavaClass(f);
+ } else if (isLayoutRes && f.getName().equals("layout")) {
+ createLayoutRes(f);
+ } else if (isMenuRes && f.getName().equals("menu")) {
+ createMenuRes(f);
+ } else if (isDrawableRes && f.getName().equals("drawable")) {
+ createDrawableRes(f);
+ } else if (isRes && f.getName().equals("res")) {
+ createNewResource(f);
+ } else {
+ createNewEmptyFile(f);
+ }
+ }
+ }
+
+ private void createJavaClass(final File f) {
+ final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
+ final LayoutCreateFileJavaBinding binding =
+ LayoutCreateFileJavaBinding.inflate(activity().getLayoutInflater());
+ builder.setView(binding.getRoot());
+ builder.setTitle(R.string.new_java_class);
+ builder.setPositiveButton(
+ R.string.text_create,
+ (p1, p2) -> {
+ p1.dismiss();
+ final String name =
+ Objects.requireNonNull(binding.name.getEditText())
+ .getText()
+ .toString()
+ .trim();
+ final String pkgName = ProjectWriter.getPackageName(f);
+ if (pkgName == null || pkgName.trim().length() <= 0) {
+ activity()
+ .getApp()
+ .toast(R.string.msg_get_package_failed, Toaster.Type.ERROR);
+ } else {
+ final int id = binding.typeGroup.getCheckedButtonId();
+ final var javaName = name.endsWith(".java") ? name : name.concat(".java");
+ final var className =
+ !name.contains(".")
+ ? name
+ : name.substring(0, name.lastIndexOf("."));
+ if (id == binding.typeClass.getId()) {
+ createFile(
+ f, javaName, ProjectWriter.createJavaClass(pkgName, className));
+ } else if (id == binding.typeInterface.getId()) {
+ createFile(
+ f,
+ javaName,
+ ProjectWriter.createJavaInterface(pkgName, className));
+ } else if (id == binding.typeEnum.getId()) {
+ createFile(
+ f, javaName, ProjectWriter.createJavaEnum(pkgName, className));
+ } else if (id == binding.typeActivity.getId()) {
+ createFile(
+ f, javaName, ProjectWriter.createActivity(pkgName, className));
+ } else {
+ createFile(f, name, "");
+ }
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.setCancelable(false);
+ builder.create().show();
+ }
+
+ private void createLayoutRes(File f) {
+ createNewFileWithContent(
+ Environment.mkdirIfNotExits(f), ProjectWriter.createLayout(), ".xml");
+ }
+
+ private void createMenuRes(File f) {
+ createNewFileWithContent(
+ Environment.mkdirIfNotExits(f), ProjectWriter.createMenu(), ".xml");
+ }
+
+ private void createDrawableRes(File f) {
+ createNewFileWithContent(
+ Environment.mkdirIfNotExits(f), ProjectWriter.createDrawable(), ".xml");
+ }
+
+ private void createNewResource(File f) {
+ final String[] labels = {
+ activity().getString(R.string.restype_drawable),
+ activity().getString(R.string.restype_layout),
+ activity().getString(R.string.restype_menu),
+ activity().getString(R.string.restype_other)
+ };
+ final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
+ builder.setTitle(R.string.new_xml_resource);
+ builder.setItems(
+ labels,
+ (dialogInterface, position) -> {
+ if (position == 0) {
+ createDrawableRes(new File(f, "drawable"));
+ } else if (position == 1) {
+ createLayoutRes(new File(f, "layout"));
+ } else if (position == 2) {
+ createMenuRes(new File(f, "menu"));
+ } else if (position == 3) {
+ createNewFile(f, true);
+ }
+ });
+ builder.create().show();
+ }
+
+ private void createNewEmptyFile(File f) {
+ createNewFileWithContent(f, "");
+ }
+
+ private void createNewFileWithContent(File f, String content) {
+ createNewFileWithContent(f, content, null);
+ }
+
+ private void createNewFileWithContent(File folder, String content, String extension) {
+ final LayoutDialogTextInputBinding binding =
+ LayoutDialogTextInputBinding.inflate(activity().getLayoutInflater());
+ final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
+
+ Objects.requireNonNull(binding.name.getEditText()).setHint(R.string.file_name);
+
+ builder.setTitle(R.string.new_file);
+ builder.setMessage(
+ activity()
+ .getString(R.string.msg_can_contain_slashes)
+ .concat("\n\n")
+ .concat(
+ activity()
+ .getString(
+ R.string.msg_newfile_dest,
+ folder.getAbsolutePath())));
+ builder.setView(binding.getRoot());
+ builder.setCancelable(false);
+ builder.setPositiveButton(
+ R.string.text_create,
+ (p1, p2) -> {
+ p1.dismiss();
+ String name = binding.name.getEditText().getText().toString().trim();
+ if (extension != null && extension.trim().length() > 0) {
+ name = name.endsWith(extension) ? name : name.concat(extension);
+ }
+ createFile(folder, name, content);
+ });
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.create().show();
+ }
+
+ private void createFile(File f, String name, String content) {
+ if (name.length() > 0 && name.length() <= 40 && !name.startsWith("/")) {
final File file = new File(f, name);
if (file.exists()) {
- activity().getApp().toast(R.string.msg_folder_exists, Toaster.Type.ERROR);
+ activity().getApp().toast(R.string.msg_file_exists, Toaster.Type.ERROR);
} else {
- if (file.mkdirs()) {
- activity().getApp().toast(R.string.msg_folder_created, Toaster.Type.SUCCESS);
- if (activity().getLastHoldTreeNode() != null) {
- TreeNode node = new TreeNode(file);
- node.setViewHolder(new FileTreeViewHolder(activity()));
- activity().getLastHoldTreeNode().addChild(node);
- activity().getFileTreeFragment().expandNode(activity().getLastHoldTreeNode());
+ if (FileIOUtils.writeFileFromString(file, content)) {
+ // TODO Notify language servers about file created event
+ activity().getApp().toast(R.string.msg_file_created, Toaster.Type.SUCCESS);
+ if (activity().getLastHoldTreeNode() != null) {
+ TreeNode node = new TreeNode(file);
+ node.setViewHolder(new FileTreeViewHolder(activity()));
+ activity().getLastHoldTreeNode().addChild(node);
+ activity()
+ .getFileTreeFragment()
+ .expandNode(activity().getLastHoldTreeNode());
+ } else {
+ activity().getFileTreeFragment().listProjectFiles();
+ }
} else {
- activity().getFileTreeFragment().listProjectFiles();
+ activity()
+ .getApp()
+ .toast(R.string.msg_file_creation_failed, Toaster.Type.ERROR);
}
- } else {
- activity().getApp().toast(R.string.msg_folder_creation_failed, Toaster.Type.ERROR);
- }
}
- } else {
+ } else {
activity().getApp().toast(R.string.msg_invalid_name, Toaster.Type.ERROR);
- }
- });
- builder.setNegativeButton(android.R.string.cancel, null);
- builder.create().show();
- }
+ }
+ }
- private void delete(final File f) {
- MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
- builder
- .setNegativeButton(R.string.no, null)
- .setPositiveButton(
- R.string.yes,
- (p1, p2) -> {
- p1.dismiss();
- final boolean deleted = FileUtils.delete(f);
- activity()
- .getApp()
- .toast(
- deleted ? R.string.deleted : R.string.delete_failed,
- deleted ? Toaster.Type.SUCCESS : Toaster.Type.ERROR);
- if (deleted) {
- // TODO Notify language servers about file delete event
- if (activity().getLastHoldTreeNode() != null) {
- TreeNode parent = activity().getLastHoldTreeNode().getParent();
- parent.deleteChild(activity().getLastHoldTreeNode());
- activity().getFileTreeFragment().expandNode(parent);
- } else {
- activity().getFileTreeFragment().listProjectFiles();
- }
+ private void createNewFolder(File f) {
+ final LayoutDialogTextInputBinding binding =
+ LayoutDialogTextInputBinding.inflate(activity().getLayoutInflater());
+ final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
- final var frag = activity().getEditorForFile(f);
- if (frag != null) {
- activity().closeFile(activity().findIndexOfEditorByFile(frag.getFile()));
- }
- }
- })
- .setTitle(R.string.title_confirm_delete)
- .setMessage(
- activity()
- .getString(
- R.string.msg_confirm_delete,
- String.format("%s [%s]", f.getName(), f.getAbsolutePath())))
- .setCancelable(false)
- .create()
- .show();
- }
+ Objects.requireNonNull(binding.name.getEditText()).setHint(R.string.folder_name);
- private void renameFile(File f) {
- final LayoutDialogTextInputBinding binding =
- LayoutDialogTextInputBinding.inflate(LayoutInflater.from(activity()));
- MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
+ builder.setTitle(R.string.new_folder);
+ builder.setMessage(R.string.msg_can_contain_slashes);
+ builder.setView(binding.getRoot());
+ builder.setCancelable(false);
+ builder.setPositiveButton(
+ R.string.text_create,
+ (p1, p2) -> {
+ p1.dismiss();
+ final String name = binding.name.getEditText().getText().toString().trim();
+ if (name.length() > 0 && name.length() <= 40 && !name.startsWith("/")) {
+ final File file = new File(f, name);
+ if (file.exists()) {
+ activity()
+ .getApp()
+ .toast(R.string.msg_folder_exists, Toaster.Type.ERROR);
+ } else {
+ if (file.mkdirs()) {
+ activity()
+ .getApp()
+ .toast(R.string.msg_folder_created, Toaster.Type.SUCCESS);
+ if (activity().getLastHoldTreeNode() != null) {
+ TreeNode node = new TreeNode(file);
+ node.setViewHolder(new FileTreeViewHolder(activity()));
+ activity().getLastHoldTreeNode().addChild(node);
+ activity()
+ .getFileTreeFragment()
+ .expandNode(activity().getLastHoldTreeNode());
+ } else {
+ activity().getFileTreeFragment().listProjectFiles();
+ }
+ } else {
+ activity()
+ .getApp()
+ .toast(
+ R.string.msg_folder_creation_failed,
+ Toaster.Type.ERROR);
+ }
+ }
+ } else {
+ activity().getApp().toast(R.string.msg_invalid_name, Toaster.Type.ERROR);
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.create().show();
+ }
- Objects.requireNonNull(binding.name.getEditText())
- .setHint(activity().getString(R.string.new_name));
- binding.name.getEditText().setText(f.getName());
+ private void delete(final File f) {
+ MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
+ builder.setNegativeButton(R.string.no, null)
+ .setPositiveButton(
+ R.string.yes,
+ (p1, p2) -> {
+ p1.dismiss();
+ final boolean deleted = FileUtils.delete(f);
+ activity()
+ .getApp()
+ .toast(
+ deleted ? R.string.deleted : R.string.delete_failed,
+ deleted ? Toaster.Type.SUCCESS : Toaster.Type.ERROR);
+ if (deleted) {
+ // TODO Notify language servers about file delete event
+ if (activity().getLastHoldTreeNode() != null) {
+ TreeNode parent = activity().getLastHoldTreeNode().getParent();
+ parent.deleteChild(activity().getLastHoldTreeNode());
+ activity().getFileTreeFragment().expandNode(parent);
+ } else {
+ activity().getFileTreeFragment().listProjectFiles();
+ }
- builder.setTitle(R.string.rename_file);
- builder.setMessage(R.string.msg_rename_file);
- builder.setView(binding.getRoot());
- builder.setNegativeButton(android.R.string.cancel, null);
- builder.setPositiveButton(
- R.string.rename_file,
- (p1, p2) -> {
- p1.dismiss();
- String name = binding.name.getEditText().getText().toString().trim();
- boolean renamed = name.length() > 0 && name.length() <= 40 && FileUtils.rename(f, name);
- activity()
- .getApp()
- .toast(
- renamed ? R.string.renamed : R.string.rename_failed,
- renamed ? Toaster.Type.SUCCESS : Toaster.Type.ERROR);
- if (renamed) {
- // TODO Notify language servers about file rename event
- if (activity().getLastHoldTreeNode() != null) {
- TreeNode parent = activity().getLastHoldTreeNode().getParent();
- parent.deleteChild(activity().getLastHoldTreeNode());
- TreeNode node = new TreeNode(new File(f.getParentFile(), name));
- node.setViewHolder(new FileTreeViewHolder(activity()));
- parent.addChild(node);
- activity().getFileTreeFragment().expandNode(parent);
- } else {
- activity().getFileTreeFragment().listProjectFiles();
- }
- }
- });
- builder.create().show();
- }
+ final var frag = activity().getEditorForFile(f);
+ if (frag != null) {
+ activity()
+ .closeFile(
+ activity()
+ .findIndexOfEditorByFile(
+ frag.getFile()));
+ }
+ }
+ })
+ .setTitle(R.string.title_confirm_delete)
+ .setMessage(
+ activity()
+ .getString(
+ R.string.msg_confirm_delete,
+ String.format("%s [%s]", f.getName(), f.getAbsolutePath())))
+ .setCancelable(false)
+ .create()
+ .show();
+ }
+
+ private void renameFile(File f) {
+ final LayoutDialogTextInputBinding binding =
+ LayoutDialogTextInputBinding.inflate(LayoutInflater.from(activity()));
+ MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
+
+ Objects.requireNonNull(binding.name.getEditText())
+ .setHint(activity().getString(R.string.new_name));
+ binding.name.getEditText().setText(f.getName());
+
+ builder.setTitle(R.string.rename_file);
+ builder.setMessage(R.string.msg_rename_file);
+ builder.setView(binding.getRoot());
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.setPositiveButton(
+ R.string.rename_file,
+ (p1, p2) -> {
+ p1.dismiss();
+ String name = binding.name.getEditText().getText().toString().trim();
+ boolean renamed =
+ name.length() > 0 && name.length() <= 40 && FileUtils.rename(f, name);
+ activity()
+ .getApp()
+ .toast(
+ renamed ? R.string.renamed : R.string.rename_failed,
+ renamed ? Toaster.Type.SUCCESS : Toaster.Type.ERROR);
+ if (renamed) {
+ // TODO Notify language servers about file rename event
+ if (activity().getLastHoldTreeNode() != null) {
+ TreeNode parent = activity().getLastHoldTreeNode().getParent();
+ parent.deleteChild(activity().getLastHoldTreeNode());
+ TreeNode node = new TreeNode(new File(f.getParentFile(), name));
+ node.setViewHolder(new FileTreeViewHolder(activity()));
+ parent.addChild(node);
+ activity().getFileTreeFragment().expandNode(parent);
+ } else {
+ activity().getFileTreeFragment().listProjectFiles();
+ }
+ }
+ });
+ builder.create().show();
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/handlers/IDEHandler.java b/app/src/main/java/com/itsaky/androidide/handlers/IDEHandler.java
index 5b2589cf6..2f1bb80f2 100755
--- a/app/src/main/java/com/itsaky/androidide/handlers/IDEHandler.java
+++ b/app/src/main/java/com/itsaky/androidide/handlers/IDEHandler.java
@@ -28,56 +28,56 @@ import com.itsaky.androidide.utils.Logger;
*/
public abstract class IDEHandler {
- protected static final Logger LOG = Logger.instance();
+ protected static final Logger LOG = Logger.instance();
- protected Provider provider;
+ protected Provider provider;
- public IDEHandler(Provider provider) {
- this.provider = provider;
- }
+ public IDEHandler(Provider provider) {
+ this.provider = provider;
+ }
- protected void throwNPE() {
- throw new NullPointerException();
- }
+ protected void throwNPE() {
+ throw new NullPointerException();
+ }
- protected EditorActivity activity() {
- return provider.provideEditorActivity();
- }
+ protected EditorActivity activity() {
+ return provider.provideEditorActivity();
+ }
- protected AndroidProject androidProject() {
- return provider.provideAndroidProject();
- }
+ protected AndroidProject androidProject() {
+ return provider.provideAndroidProject();
+ }
- protected IDEProject ideProject() {
- return provider.provideIDEProject();
- }
+ protected IDEProject ideProject() {
+ return provider.provideIDEProject();
+ }
- public abstract void start();
+ public abstract void start();
- public abstract void stop();
+ public abstract void stop();
- /** An interface to communicate between a handler and its client */
- public interface Provider {
+ /** An interface to communicate between a handler and its client */
+ public interface Provider {
- /**
- * Called by handler to get a reference to {@link EditorActivity}
- *
- * @throws NullPointerException is this is required
- */
- EditorActivity provideEditorActivity();
+ /**
+ * Called by handler to get a reference to {@link EditorActivity}
+ *
+ * @throws NullPointerException is this is required
+ */
+ EditorActivity provideEditorActivity();
- /**
- * Called by handler to get a reference of the current {@link AndroidProject}
- *
- * @throws NullPointerException is this is required
- */
- AndroidProject provideAndroidProject();
+ /**
+ * Called by handler to get a reference of the current {@link AndroidProject}
+ *
+ * @throws NullPointerException is this is required
+ */
+ AndroidProject provideAndroidProject();
- /**
- * Called by handler to get a reference of the current {@link IDEProject}
- *
- * @throws NullPointerException is this is required
- */
- IDEProject provideIDEProject();
- }
+ /**
+ * Called by handler to get a reference of the current {@link IDEProject}
+ *
+ * @throws NullPointerException is this is required
+ */
+ IDEProject provideIDEProject();
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/interfaces/DiagnosticClickListener.java b/app/src/main/java/com/itsaky/androidide/interfaces/DiagnosticClickListener.java
index 629f27b53..6d347055d 100755
--- a/app/src/main/java/com/itsaky/androidide/interfaces/DiagnosticClickListener.java
+++ b/app/src/main/java/com/itsaky/androidide/interfaces/DiagnosticClickListener.java
@@ -19,10 +19,11 @@ package com.itsaky.androidide.interfaces;
import com.itsaky.androidide.models.DiagnosticGroup;
import com.itsaky.lsp.models.DiagnosticItem;
+
import java.io.File;
public interface DiagnosticClickListener {
- void onGroupClick(DiagnosticGroup group);
+ void onGroupClick(DiagnosticGroup group);
- void onDiagnosticClick(File file, DiagnosticItem diagnostic);
+ void onDiagnosticClick(File file, DiagnosticItem diagnostic);
}
diff --git a/app/src/main/java/com/itsaky/androidide/interfaces/EditorActivityProvider.java b/app/src/main/java/com/itsaky/androidide/interfaces/EditorActivityProvider.java
index fa4ef4a48..e39ffa78d 100755
--- a/app/src/main/java/com/itsaky/androidide/interfaces/EditorActivityProvider.java
+++ b/app/src/main/java/com/itsaky/androidide/interfaces/EditorActivityProvider.java
@@ -21,9 +21,10 @@ import com.itsaky.androidide.EditorActivity;
public interface EditorActivityProvider {
- /**
- * Provides a reference to {@link com.itsaky.androidide.EditorActivity EditorActivity}. This is
- * used by LanguageClients to get reference to EditorActivity without keeping a strong reference.
- */
- EditorActivity provide();
+ /**
+ * Provides a reference to {@link com.itsaky.androidide.EditorActivity EditorActivity}. This is
+ * used by LanguageClients to get reference to EditorActivity without keeping a strong
+ * reference.
+ */
+ EditorActivity provide();
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/CommonCompletionProvider.java b/app/src/main/java/com/itsaky/androidide/language/CommonCompletionProvider.java
index e574687db..ab2869fba 100644
--- a/app/src/main/java/com/itsaky/androidide/language/CommonCompletionProvider.java
+++ b/app/src/main/java/com/itsaky/androidide/language/CommonCompletionProvider.java
@@ -18,6 +18,7 @@
package com.itsaky.androidide.language;
import androidx.annotation.NonNull;
+
import com.itsaky.androidide.utils.Logger;
import com.itsaky.lsp.api.ICompletionProvider;
import com.itsaky.lsp.api.ILanguageServer;
@@ -25,10 +26,14 @@ import com.itsaky.lsp.models.CompletionItem;
import com.itsaky.lsp.models.CompletionParams;
import com.itsaky.lsp.models.CompletionResult;
import com.itsaky.lsp.models.Position;
+
import io.github.rosemoe.sora.lang.completion.CompletionHelper;
import io.github.rosemoe.sora.text.CharPosition;
import io.github.rosemoe.sora.text.ContentReference;
import io.github.rosemoe.sora.util.MyCharacter;
+
+import org.jetbrains.annotations.Contract;
+
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
@@ -37,7 +42,6 @@ import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
-import org.jetbrains.annotations.Contract;
/**
* Common implementation of completion provider which requests completions to provided language
@@ -47,92 +51,95 @@ import org.jetbrains.annotations.Contract;
*/
public class CommonCompletionProvider {
- private CompletableFuture future;
+ private CompletableFuture future;
- private final ILanguageServer server;
+ private final ILanguageServer server;
- public CommonCompletionProvider(ILanguageServer server) {
- this.server = server;
- }
-
- /**
- * Computes completion items using the provided language server instance.
- *
- * @param content The reference to the content of the editor.
- * @param file The file to compute completions for.
- * @param position The position of the cursor in the content.
- * @return The computed completion items. May return an empty list if the there was an error
- * computing the completion items.
- */
- public List complete(
- ContentReference content,
- Path file,
- CharPosition position,
- Predicate prefixMatcher) {
- if (this.future != null && !this.future.isDone()) {
- try {
- this.future.cancel(true);
- } catch (CancellationException e) {
- return new ArrayList<>();
- }
+ public CommonCompletionProvider(ILanguageServer server) {
+ this.server = server;
}
- this.future =
- CompletableFuture.supplyAsync(
- () -> {
- final var prefix =
- CompletionHelper.computePrefix(content, position, prefixMatcher::test);
- final var completer = server.getCompletionProvider();
-
- if (!completer.canComplete(file)) {
- return ICompletionProvider.EMPTY;
- }
-
- final var params =
- new CompletionParams(
- new Position(position.line, position.column, position.index), file);
- params.setContent(content);
- params.setPrefix(prefix);
- return completer.complete(params);
- });
-
- try {
- return finalizeResults(future.get().getItems());
- } catch (Throwable e) {
- LOG.error("Unable to compute completions", e);
- return Collections.emptyList();
- }
- }
-
- public static boolean checkJavaCompletionChar(char c) {
- return MyCharacter.isJavaIdentifierPart(c) || c == '.';
- }
-
- public static boolean checkXMLCompletionChar(char c) {
- return MyCharacter.isJavaIdentifierPart(c) || c == '<' || c == '/';
- }
-
- @NonNull
- @Contract("_ -> param1")
- private List finalizeResults(@NonNull List items) {
- items.sort(RESULT_SORTER);
- return items;
- }
-
- private static final Comparator RESULT_SORTER =
- (p1, p2) -> {
- if (p1 == null && p2 == null) {
- return 0;
- } else if (p1 == null) {
- return -1;
- } else if (p2 == null) {
- return 1;
+ /**
+ * Computes completion items using the provided language server instance.
+ *
+ * @param content The reference to the content of the editor.
+ * @param file The file to compute completions for.
+ * @param position The position of the cursor in the content.
+ * @return The computed completion items. May return an empty list if the there was an error
+ * computing the completion items.
+ */
+ public List complete(
+ ContentReference content,
+ Path file,
+ CharPosition position,
+ Predicate prefixMatcher) {
+ if (this.future != null && !this.future.isDone()) {
+ try {
+ this.future.cancel(true);
+ } catch (CancellationException e) {
+ return new ArrayList<>();
+ }
}
- String s1 = p1.getSortText() == null ? p1.getLabel() : p1.getSortText();
- String s2 = p2.getSortText() == null ? p2.getLabel() : p2.getSortText();
- return s1.compareTo(s2);
- };
+ this.future =
+ CompletableFuture.supplyAsync(
+ () -> {
+ final var prefix =
+ CompletionHelper.computePrefix(
+ content, position, prefixMatcher::test);
+ final var completer = server.getCompletionProvider();
- private static final Logger LOG = Logger.instance("CommonCompletionProvider");
+ if (!completer.canComplete(file)) {
+ return ICompletionProvider.EMPTY;
+ }
+
+ final var params =
+ new CompletionParams(
+ new Position(
+ position.line, position.column, position.index),
+ file);
+ params.setContent(content);
+ params.setPrefix(prefix);
+ return completer.complete(params);
+ });
+
+ try {
+ return finalizeResults(future.get().getItems());
+ } catch (Throwable e) {
+ LOG.error("Unable to compute completions", e);
+ return Collections.emptyList();
+ }
+ }
+
+ public static boolean checkJavaCompletionChar(char c) {
+ return MyCharacter.isJavaIdentifierPart(c) || c == '.';
+ }
+
+ public static boolean checkXMLCompletionChar(char c) {
+ return MyCharacter.isJavaIdentifierPart(c) || c == '<' || c == '/';
+ }
+
+ @NonNull
+ @Contract("_ -> param1")
+ private List finalizeResults(@NonNull List items) {
+ items.sort(RESULT_SORTER);
+ return items;
+ }
+
+ private static final Comparator RESULT_SORTER =
+ (p1, p2) -> {
+ if (p1 == null && p2 == null) {
+ return 0;
+ } else if (p1 == null) {
+ return -1;
+ } else if (p2 == null) {
+ return 1;
+ }
+
+ String s1 = p1.getSortText() == null ? p1.getLabel() : p1.getSortText();
+ String s2 = p2.getSortText() == null ? p2.getLabel() : p2.getSortText();
+ return s1.compareTo(s2);
+ };
+
+ private static final Logger LOG = Logger.instance("CommonCompletionProvider");
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java b/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java
index 23e9b2b6b..99460286a 100644
--- a/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java
+++ b/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java
@@ -17,9 +17,12 @@
package com.itsaky.androidide.language;
import androidx.annotation.NonNull;
+
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.lsp.models.DiagnosticItem;
+
import io.github.rosemoe.sora.lang.Language;
+
import java.util.Collections;
import java.util.List;
@@ -30,22 +33,22 @@ import java.util.List;
*/
public abstract class IDELanguage implements Language {
- @Override
- public boolean useTab() {
- return false;
- }
+ @Override
+ public boolean useTab() {
+ return false;
+ }
- public int getTabSize() {
- return StudioApp.getInstance().getPrefManager().getEditorTabSize();
- }
+ public int getTabSize() {
+ return StudioApp.getInstance().getPrefManager().getEditorTabSize();
+ }
- /**
- * Get the diagnostics from the last analyze.
- *
- * @return The diagnostics. Must not be null.
- */
- @NonNull
- public List getDiagnostics() {
- return Collections.emptyList();
- }
+ /**
+ * Get the diagnostics from the last analyze.
+ *
+ * @return The diagnostics. Must not be null.
+ */
+ @NonNull
+ public List getDiagnostics() {
+ return Collections.emptyList();
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.java b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.java
index eb301fdb7..46a46fc56 100755
--- a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.java
+++ b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.java
@@ -31,231 +31,234 @@ import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.get;
import com.itsaky.androidide.lexers.groovy.GroovyLexer;
import com.itsaky.androidide.utils.CharSequenceReader;
import com.itsaky.androidide.utils.Logger;
+
import io.github.rosemoe.sora.lang.analysis.SimpleAnalyzeManager;
import io.github.rosemoe.sora.lang.styling.CodeBlock;
import io.github.rosemoe.sora.lang.styling.MappedSpans;
import io.github.rosemoe.sora.lang.styling.Styles;
-import java.io.IOException;
-import java.util.Stack;
+
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CodePointCharStream;
import org.antlr.v4.runtime.Token;
+import java.io.IOException;
+import java.util.Stack;
+
public class GroovyAnalyzer extends SimpleAnalyzeManager {
- private static final Logger LOG = Logger.instance("GroovyAnalyzer");
+ private static final Logger LOG = Logger.instance("GroovyAnalyzer");
- @Override
- protected Styles analyze(StringBuilder text, Delegate delegate) {
- final var styles = new Styles();
- final var colors = new MappedSpans.Builder();
- final CodePointCharStream stream;
- try {
- stream = CharStreams.fromReader(new CharSequenceReader(text));
- } catch (IOException e) {
- LOG.error("Unable to read text to analyze", e);
- return styles;
+ @Override
+ protected Styles analyze(StringBuilder text, Delegate delegate) {
+ final var styles = new Styles();
+ final var colors = new MappedSpans.Builder();
+ final CodePointCharStream stream;
+ try {
+ stream = CharStreams.fromReader(new CharSequenceReader(text));
+ } catch (IOException e) {
+ LOG.error("Unable to read text to analyze", e);
+ return styles;
+ }
+
+ final var lexer = new GroovyLexer(stream);
+ final var stack = new Stack();
+
+ Token token;
+ int line, column, lastLine = 1, currSwitch = 0, maxSwitch = 0, type, previous = 0;
+ boolean isFirst = true;
+
+ while (!delegate.isCancelled()) {
+ token = lexer.nextToken();
+
+ if (token == null) {
+ break;
+ }
+
+ line = token.getLine() - 1;
+ column = token.getCharPositionInLine();
+ type = token.getType();
+
+ if (type == GroovyLexer.EOF) {
+ lastLine = line;
+ break;
+ }
+
+ switch (type) {
+ case GroovyLexer.WS:
+ if (isFirst) {
+ colors.addNormalIfNull();
+ }
+ break;
+ case GroovyLexer.ABSTRACT:
+ case GroovyLexer.ASSERT:
+ case GroovyLexer.BREAK:
+ case GroovyLexer.CASE:
+ case GroovyLexer.CATCH:
+ case GroovyLexer.CLASS:
+ case GroovyLexer.CONST:
+ case GroovyLexer.CONTINUE:
+ case GroovyLexer.DEFAULT:
+ case GroovyLexer.DO:
+ case GroovyLexer.ELSE:
+ case GroovyLexer.EXTENDS:
+ case GroovyLexer.FINAL:
+ case GroovyLexer.FINALLY:
+ case GroovyLexer.FOR:
+ case GroovyLexer.IF:
+ case GroovyLexer.GOTO:
+ case GroovyLexer.IMPLEMENTS:
+ case GroovyLexer.IMPORT:
+ case GroovyLexer.INSTANCEOF:
+ case GroovyLexer.INTERFACE:
+ case GroovyLexer.NATIVE:
+ case GroovyLexer.NEW:
+ case GroovyLexer.PACKAGE:
+ case GroovyLexer.PRIVATE:
+ case GroovyLexer.PROTECTED:
+ case GroovyLexer.PUBLIC:
+ case GroovyLexer.RETURN:
+ case GroovyLexer.STATIC:
+ case GroovyLexer.STRICTFP:
+ case GroovyLexer.SUPER:
+ case GroovyLexer.SWITCH:
+ case GroovyLexer.SYNCHRONIZED:
+ case GroovyLexer.THIS:
+ case GroovyLexer.THROW:
+ case GroovyLexer.THROWS:
+ case GroovyLexer.TRANSIENT:
+ case GroovyLexer.TRY:
+ case GroovyLexer.VOID:
+ case GroovyLexer.VOLATILE:
+ case GroovyLexer.WHILE:
+ colors.addIfNeeded(line, column, forKeyword());
+ break;
+ case GroovyLexer.DECIMAL_LITERAL:
+ case GroovyLexer.HEX_LITERAL:
+ case GroovyLexer.OCT_LITERAL:
+ case GroovyLexer.BINARY_LITERAL:
+ case GroovyLexer.FLOAT_LITERAL:
+ case GroovyLexer.HEX_FLOAT_LITERAL:
+ case GroovyLexer.BOOL_LITERAL:
+ case GroovyLexer.CHAR_LITERAL:
+ case GroovyLexer.NULL_LITERAL:
+ colors.addIfNeeded(line, column, get(LITERAL));
+ break;
+ case GroovyLexer.STRING_LITERAL:
+ case GroovyLexer.SINGLE_QUOTE_STRING:
+ colors.addIfNeeded(line, column, forString());
+ break;
+ case GroovyLexer.LPAREN:
+ case GroovyLexer.RPAREN:
+ case GroovyLexer.LBRACK:
+ case GroovyLexer.RBRACK:
+ case GroovyLexer.SEMI:
+ case GroovyLexer.COMMA:
+ case GroovyLexer.ASSIGN:
+ case GroovyLexer.GT:
+ case GroovyLexer.LT:
+ case GroovyLexer.BANG:
+ case GroovyLexer.TILDE:
+ case GroovyLexer.QUESTION:
+ case GroovyLexer.COLON:
+ case GroovyLexer.EQUAL:
+ case GroovyLexer.GE:
+ case GroovyLexer.LE:
+ case GroovyLexer.NOTEQUAL:
+ case GroovyLexer.AND:
+ case GroovyLexer.OR:
+ case GroovyLexer.INC:
+ case GroovyLexer.DEC:
+ case GroovyLexer.ADD:
+ case GroovyLexer.SUB:
+ case GroovyLexer.MUL:
+ case GroovyLexer.DIV:
+ case GroovyLexer.BITAND:
+ case GroovyLexer.BITOR:
+ case GroovyLexer.CARET:
+ case GroovyLexer.MOD:
+ case GroovyLexer.ADD_ASSIGN:
+ case GroovyLexer.SUB_ASSIGN:
+ case GroovyLexer.MUL_ASSIGN:
+ case GroovyLexer.DIV_ASSIGN:
+ case GroovyLexer.AND_ASSIGN:
+ case GroovyLexer.OR_ASSIGN:
+ case GroovyLexer.XOR_ASSIGN:
+ case GroovyLexer.MOD_ASSIGN:
+ case GroovyLexer.LSHIFT_ASSIGN:
+ case GroovyLexer.RSHIFT_ASSIGN:
+ case GroovyLexer.URSHIFT_ASSIGN:
+ case GroovyLexer.ARROW:
+ case GroovyLexer.COLONCOLON:
+ case GroovyLexer.ELLIPSIS:
+ case GroovyLexer.DOT:
+ colors.addIfNeeded(line, column, get(OPERATOR));
+ break;
+ case GroovyLexer.BOOLEAN:
+ case GroovyLexer.BYTE:
+ case GroovyLexer.CHAR:
+ case GroovyLexer.DOUBLE:
+ case GroovyLexer.ENUM:
+ case GroovyLexer.FLOAT:
+ case GroovyLexer.INT:
+ case GroovyLexer.LONG:
+ case GroovyLexer.SHORT:
+ colors.addIfNeeded(line, column, get(TYPE_NAME));
+ break;
+ case GroovyLexer.COMMENT:
+ case GroovyLexer.LINE_COMMENT:
+ colors.addIfNeeded(line, column, forComment());
+ break;
+ case GroovyLexer.AT:
+ colors.addIfNeeded(line, column, get(ANNOTATION));
+ break;
+ case GroovyLexer.IDENTIFIER:
+ if (previous == GroovyLexer.AT) {
+ colors.addIfNeeded(line, column, get(ANNOTATION));
+ break;
+ }
+
+ colors.addIfNeeded(line, column, get(TEXT_NORMAL));
+ break;
+ case GroovyLexer.LBRACE:
+ colors.addIfNeeded(line, column, get(OPERATOR));
+ if (stack.isEmpty()) {
+ if (currSwitch > maxSwitch) maxSwitch = currSwitch;
+ currSwitch = 0;
+ }
+ currSwitch++;
+ var block = styles.obtainNewBlock();
+ block.startLine = line;
+ block.startColumn = column;
+ stack.push(block);
+ break;
+ case GroovyLexer.RBRACE:
+ colors.addIfNeeded(line, column, get(OPERATOR));
+ if (!stack.isEmpty()) {
+ var block2 = stack.pop();
+ block2.endLine = line;
+ block2.endColumn = column;
+ if (block2.startLine != block2.endLine) styles.addCodeBlock(block2);
+ }
+ break;
+ default:
+ colors.addIfNeeded(line, column, get(TEXT_NORMAL));
+ break;
+ }
+
+ isFirst = false;
+ if (type != GroovyLexer.WS) {
+ previous = type;
+ }
+ }
+
+ if (stack.isEmpty() && currSwitch > maxSwitch) {
+ maxSwitch = currSwitch;
+ }
+
+ colors.determine(lastLine);
+ styles.setSuppressSwitch(maxSwitch + 10);
+ styles.spans = colors.build();
+
+ return styles;
}
-
- final var lexer = new GroovyLexer(stream);
- final var stack = new Stack();
-
- Token token;
- int line, column, lastLine = 1, currSwitch = 0, maxSwitch = 0, type, previous = 0;
- boolean isFirst = true;
-
- while (!delegate.isCancelled()) {
- token = lexer.nextToken();
-
- if (token == null) {
- break;
- }
-
- line = token.getLine() - 1;
- column = token.getCharPositionInLine();
- type = token.getType();
-
- if (type == GroovyLexer.EOF) {
- lastLine = line;
- break;
- }
-
- switch (type) {
- case GroovyLexer.WS:
- if (isFirst) {
- colors.addNormalIfNull();
- }
- break;
- case GroovyLexer.ABSTRACT:
- case GroovyLexer.ASSERT:
- case GroovyLexer.BREAK:
- case GroovyLexer.CASE:
- case GroovyLexer.CATCH:
- case GroovyLexer.CLASS:
- case GroovyLexer.CONST:
- case GroovyLexer.CONTINUE:
- case GroovyLexer.DEFAULT:
- case GroovyLexer.DO:
- case GroovyLexer.ELSE:
- case GroovyLexer.EXTENDS:
- case GroovyLexer.FINAL:
- case GroovyLexer.FINALLY:
- case GroovyLexer.FOR:
- case GroovyLexer.IF:
- case GroovyLexer.GOTO:
- case GroovyLexer.IMPLEMENTS:
- case GroovyLexer.IMPORT:
- case GroovyLexer.INSTANCEOF:
- case GroovyLexer.INTERFACE:
- case GroovyLexer.NATIVE:
- case GroovyLexer.NEW:
- case GroovyLexer.PACKAGE:
- case GroovyLexer.PRIVATE:
- case GroovyLexer.PROTECTED:
- case GroovyLexer.PUBLIC:
- case GroovyLexer.RETURN:
- case GroovyLexer.STATIC:
- case GroovyLexer.STRICTFP:
- case GroovyLexer.SUPER:
- case GroovyLexer.SWITCH:
- case GroovyLexer.SYNCHRONIZED:
- case GroovyLexer.THIS:
- case GroovyLexer.THROW:
- case GroovyLexer.THROWS:
- case GroovyLexer.TRANSIENT:
- case GroovyLexer.TRY:
- case GroovyLexer.VOID:
- case GroovyLexer.VOLATILE:
- case GroovyLexer.WHILE:
- colors.addIfNeeded(line, column, forKeyword());
- break;
- case GroovyLexer.DECIMAL_LITERAL:
- case GroovyLexer.HEX_LITERAL:
- case GroovyLexer.OCT_LITERAL:
- case GroovyLexer.BINARY_LITERAL:
- case GroovyLexer.FLOAT_LITERAL:
- case GroovyLexer.HEX_FLOAT_LITERAL:
- case GroovyLexer.BOOL_LITERAL:
- case GroovyLexer.CHAR_LITERAL:
- case GroovyLexer.NULL_LITERAL:
- colors.addIfNeeded(line, column, get(LITERAL));
- break;
- case GroovyLexer.STRING_LITERAL:
- case GroovyLexer.SINGLE_QUOTE_STRING:
- colors.addIfNeeded(line, column, forString());
- break;
- case GroovyLexer.LPAREN:
- case GroovyLexer.RPAREN:
- case GroovyLexer.LBRACK:
- case GroovyLexer.RBRACK:
- case GroovyLexer.SEMI:
- case GroovyLexer.COMMA:
- case GroovyLexer.ASSIGN:
- case GroovyLexer.GT:
- case GroovyLexer.LT:
- case GroovyLexer.BANG:
- case GroovyLexer.TILDE:
- case GroovyLexer.QUESTION:
- case GroovyLexer.COLON:
- case GroovyLexer.EQUAL:
- case GroovyLexer.GE:
- case GroovyLexer.LE:
- case GroovyLexer.NOTEQUAL:
- case GroovyLexer.AND:
- case GroovyLexer.OR:
- case GroovyLexer.INC:
- case GroovyLexer.DEC:
- case GroovyLexer.ADD:
- case GroovyLexer.SUB:
- case GroovyLexer.MUL:
- case GroovyLexer.DIV:
- case GroovyLexer.BITAND:
- case GroovyLexer.BITOR:
- case GroovyLexer.CARET:
- case GroovyLexer.MOD:
- case GroovyLexer.ADD_ASSIGN:
- case GroovyLexer.SUB_ASSIGN:
- case GroovyLexer.MUL_ASSIGN:
- case GroovyLexer.DIV_ASSIGN:
- case GroovyLexer.AND_ASSIGN:
- case GroovyLexer.OR_ASSIGN:
- case GroovyLexer.XOR_ASSIGN:
- case GroovyLexer.MOD_ASSIGN:
- case GroovyLexer.LSHIFT_ASSIGN:
- case GroovyLexer.RSHIFT_ASSIGN:
- case GroovyLexer.URSHIFT_ASSIGN:
- case GroovyLexer.ARROW:
- case GroovyLexer.COLONCOLON:
- case GroovyLexer.ELLIPSIS:
- case GroovyLexer.DOT:
- colors.addIfNeeded(line, column, get(OPERATOR));
- break;
- case GroovyLexer.BOOLEAN:
- case GroovyLexer.BYTE:
- case GroovyLexer.CHAR:
- case GroovyLexer.DOUBLE:
- case GroovyLexer.ENUM:
- case GroovyLexer.FLOAT:
- case GroovyLexer.INT:
- case GroovyLexer.LONG:
- case GroovyLexer.SHORT:
- colors.addIfNeeded(line, column, get(TYPE_NAME));
- break;
- case GroovyLexer.COMMENT:
- case GroovyLexer.LINE_COMMENT:
- colors.addIfNeeded(line, column, forComment());
- break;
- case GroovyLexer.AT:
- colors.addIfNeeded(line, column, get(ANNOTATION));
- break;
- case GroovyLexer.IDENTIFIER:
- if (previous == GroovyLexer.AT) {
- colors.addIfNeeded(line, column, get(ANNOTATION));
- break;
- }
-
- colors.addIfNeeded(line, column, get(TEXT_NORMAL));
- break;
- case GroovyLexer.LBRACE:
- colors.addIfNeeded(line, column, get(OPERATOR));
- if (stack.isEmpty()) {
- if (currSwitch > maxSwitch) maxSwitch = currSwitch;
- currSwitch = 0;
- }
- currSwitch++;
- var block = styles.obtainNewBlock();
- block.startLine = line;
- block.startColumn = column;
- stack.push(block);
- break;
- case GroovyLexer.RBRACE:
- colors.addIfNeeded(line, column, get(OPERATOR));
- if (!stack.isEmpty()) {
- var block2 = stack.pop();
- block2.endLine = line;
- block2.endColumn = column;
- if (block2.startLine != block2.endLine) styles.addCodeBlock(block2);
- }
- break;
- default:
- colors.addIfNeeded(line, column, get(TEXT_NORMAL));
- break;
- }
-
- isFirst = false;
- if (type != GroovyLexer.WS) {
- previous = type;
- }
- }
-
- if (stack.isEmpty() && currSwitch > maxSwitch) {
- maxSwitch = currSwitch;
- }
-
- colors.determine(lastLine);
- styles.setSuppressSwitch(maxSwitch + 10);
- styles.spans = colors.build();
-
- return styles;
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAutoComplete.java b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAutoComplete.java
index 95f65b84e..c150281c0 100755
--- a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAutoComplete.java
+++ b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAutoComplete.java
@@ -20,361 +20,368 @@
package com.itsaky.androidide.language.groovy;
import android.os.Bundle;
+
import com.itsaky.lsp.models.CompletionItem;
import com.itsaky.lsp.models.CompletionItemKind;
import com.itsaky.lsp.models.InsertTextFormat;
+
import io.github.rosemoe.sora.lang.completion.CompletionHelper;
import io.github.rosemoe.sora.lang.completion.CompletionPublisher;
import io.github.rosemoe.sora.text.CharPosition;
import io.github.rosemoe.sora.text.ContentReference;
import io.github.rosemoe.sora.util.MyCharacter;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class GroovyAutoComplete {
- private static final List ANDROIDX_ARTIFACTS = createArtifacts();
- private static final List CONFIGURATIONS = createConfigs();
- private static final List OTHERS = createOtherCompletions();
+ private static final List ANDROIDX_ARTIFACTS = createArtifacts();
+ private static final List CONFIGURATIONS = createConfigs();
+ private static final List OTHERS = createOtherCompletions();
- public void complete(
- ContentReference content,
- CharPosition position,
- CompletionPublisher publisher,
- Bundle extraArguments) {
- publisher.setUpdateThreshold(0);
- final var prefix =
- CompletionHelper.computePrefix(content, position, MyCharacter::isJavaIdentifierPart);
- for (String artifact : ANDROIDX_ARTIFACTS) {
- if (!artifact.toLowerCase(Locale.ROOT).startsWith(prefix.toLowerCase(Locale.ROOT))) continue;
- publisher.addItem(createCompletionItem(artifact));
+ public void complete(
+ ContentReference content,
+ CharPosition position,
+ CompletionPublisher publisher,
+ Bundle extraArguments) {
+ publisher.setUpdateThreshold(0);
+ final var prefix =
+ CompletionHelper.computePrefix(
+ content, position, MyCharacter::isJavaIdentifierPart);
+ for (String artifact : ANDROIDX_ARTIFACTS) {
+ if (!artifact.toLowerCase(Locale.ROOT).startsWith(prefix.toLowerCase(Locale.ROOT)))
+ continue;
+ publisher.addItem(createCompletionItem(artifact));
+ }
+
+ for (String config : CONFIGURATIONS) {
+ if (!config.toLowerCase(Locale.ROOT).startsWith(prefix.toLowerCase(Locale.ROOT)))
+ continue;
+ publisher.addItem(createCompletionItem(config));
+ }
+
+ for (String other : OTHERS) {
+ if (!other.toLowerCase(Locale.ROOT).startsWith(prefix.toLowerCase(Locale.ROOT)))
+ continue;
+ publisher.addItem(createCompletionItem(other));
+ }
}
- for (String config : CONFIGURATIONS) {
- if (!config.toLowerCase(Locale.ROOT).startsWith(prefix.toLowerCase(Locale.ROOT))) continue;
- publisher.addItem(createCompletionItem(config));
+ private CompletionItem createCompletionItem(String itemLabel) {
+ CompletionItem item = new CompletionItem();
+ item.setLabel(itemLabel);
+ item.setDetail("");
+ item.setInsertText(itemLabel);
+ item.setSortText("0" + itemLabel);
+ item.setInsertTextFormat(InsertTextFormat.PLAIN_TEXT);
+ item.setKind(CompletionItemKind.SNIPPET);
+ return item;
}
- for (String other : OTHERS) {
- if (!other.toLowerCase(Locale.ROOT).startsWith(prefix.toLowerCase(Locale.ROOT))) continue;
- publisher.addItem(createCompletionItem(other));
+ private static List createArtifacts() {
+ var artifacts = new ArrayList();
+ artifacts.add("androidx.arch.core:core-common");
+ artifacts.add("androidx.arch.core:core");
+ artifacts.add("androidx.arch.core:core-testing");
+ artifacts.add("androidx.arch.core:core-runtime");
+ artifacts.add("androidx.lifecycle:lifecycle-common");
+ artifacts.add("androidx.lifecycle:lifecycle-common-java8");
+ artifacts.add("androidx.lifecycle:lifecycle-compiler");
+ artifacts.add("androidx.lifecycle:lifecycle-extensions");
+ artifacts.add("androidx.lifecycle:lifecycle-livedata");
+ artifacts.add("androidx.lifecycle:lifecycle-livedata-core");
+ artifacts.add("androidx.lifecycle:lifecycle-reactivestreams");
+ artifacts.add("androidx.lifecycle:lifecycle-runtime");
+ artifacts.add("androidx.lifecycle:lifecycle-viewmodel");
+ artifacts.add("androidx.paging:paging-common");
+ artifacts.add("androidx.paging:paging-runtime");
+ artifacts.add("androidx.paging:paging-rxjava2");
+ artifacts.add("androidx.room:room-common");
+ artifacts.add("androidx.room:room-compiler");
+ artifacts.add("androidx.room:room-guava");
+ artifacts.add("androidx.room:room-migration");
+ artifacts.add("androidx.room:room-runtime");
+ artifacts.add("androidx.room:room-rxjava2");
+ artifacts.add("androidx.room:room-testing");
+ artifacts.add("androidx.sqlite:sqlite");
+ artifacts.add("androidx.sqlite:sqlite-framework");
+ artifacts.add("androidx.constraintlayout:constraintlayout");
+ artifacts.add("androidx.constraintlayout:constraintlayout-solver");
+ artifacts.add("androidx.test.espresso.idling:idling-concurrent");
+ artifacts.add("androidx.test.espresso.idling:idling-net");
+ artifacts.add("androidx.test.espresso:espresso-accessibility");
+ artifacts.add("androidx.test.espresso:espresso-contrib");
+ artifacts.add("androidx.test.espresso:espresso-core");
+ artifacts.add("androidx.test.espresso:espresso-idling-resource");
+ artifacts.add("androidx.test.espresso:espresso-intents");
+ artifacts.add("androidx.test.espresso:espresso-remote");
+ artifacts.add("androidx.test.espresso:espresso-web");
+ artifacts.add("androidx.test.jank:janktesthelper");
+ artifacts.add("androidx.test:test-services");
+ artifacts.add("androidx.test.uiautomator:uiautomator");
+ artifacts.add("androidx.test:monitor");
+ artifacts.add("androidx.test:orchestrator");
+ artifacts.add("androidx.test:rules");
+ artifacts.add("androidx.test:runner");
+ artifacts.add("androidx.vectordrawable:vectordrawable-animated");
+ artifacts.add("androidx.appcompat:appcompat");
+ artifacts.add("androidx.asynclayoutinflater:asynclayoutinflater");
+ artifacts.add("androidx.car:car");
+ artifacts.add("androidx.cardview:cardview");
+ artifacts.add("androidx.collection:collection");
+ artifacts.add("androidx.coordinatorlayout:coordinatorlayout");
+ artifacts.add("androidx.cursoradapter:cursoradapter");
+ artifacts.add("androidx.browser:browser");
+ artifacts.add("androidx.customview:customview");
+ artifacts.add("com.google.android.material:material");
+ artifacts.add("androidx.documentfile:documentfile");
+ artifacts.add("androidx.drawerlayout:drawerlayout");
+ artifacts.add("androidx.exifinterface:exifinterface");
+ artifacts.add("androidx.gridlayout:gridlayout");
+ artifacts.add("androidx.heifwriter:heifwriter");
+ artifacts.add("androidx.interpolator:interpolator");
+ artifacts.add("androidx.leanback:leanback");
+ artifacts.add("androidx.loader:loader");
+ artifacts.add("androidx.localbroadcastmanager:localbroadcastmanager");
+ artifacts.add("androidx.media2:media2");
+ artifacts.add("androidx.media2:media2-exoplayer");
+ artifacts.add("androidx.mediarouter:mediarouter");
+ artifacts.add("androidx.multidex:multidex");
+ artifacts.add("androidx.multidex:multidex-instrumentation");
+ artifacts.add("androidx.palette:palette");
+ artifacts.add("androidx.percentlayout:percentlayout");
+ artifacts.add("androidx.leanback:leanback-preference");
+ artifacts.add("androidx.legacy:legacy-preference-v14");
+ artifacts.add("androidx.preference:preference");
+ artifacts.add("androidx.print:print");
+ artifacts.add("androidx.recommendation:recommendation");
+ artifacts.add("androidx.recyclerview:recyclerview-selection");
+ artifacts.add("androidx.recyclerview:recyclerview");
+ artifacts.add("androidx.slice:slice-builders");
+ artifacts.add("androidx.slice:slice-core");
+ artifacts.add("androidx.slice:slice-view");
+ artifacts.add("androidx.slidingpanelayout:slidingpanelayout");
+ artifacts.add("androidx.annotation:annotation");
+ artifacts.add("androidx.core:core");
+ artifacts.add("androidx.contentpager:contentpager");
+ artifacts.add("androidx.legacy:legacy-support-core-ui");
+ artifacts.add("androidx.legacy:legacy-support-core-utils");
+ artifacts.add("androidx.dynamicanimation:dynamicanimation");
+ artifacts.add("androidx.emoji:emoji");
+ artifacts.add("androidx.emoji:emoji-appcompat");
+ artifacts.add("androidx.emoji:emoji-bundled");
+ artifacts.add("androidx.fragment:fragment");
+ artifacts.add("androidx.media:media");
+ artifacts.add("androidx.tvprovider:tvprovider");
+ artifacts.add("androidx.legacy:legacy-support-v13");
+ artifacts.add("androidx.legacy:legacy-support-v4");
+ artifacts.add("androidx.vectordrawable:vectordrawable");
+ artifacts.add("androidx.swiperefreshlayout:swiperefreshlayout");
+ artifacts.add("androidx.textclassifier:textclassifier");
+ artifacts.add("androidx.transition:transition");
+ artifacts.add("androidx.versionedparcelable:versionedparcelable");
+ artifacts.add("androidx.viewpager:viewpager");
+ artifacts.add("androidx.wear:wear");
+ artifacts.add("androidx.webkit:webkit");
+
+ artifacts.add("fileTree(dir: 'libs', include: ['*.jar'])");
+ return artifacts;
}
- }
- private CompletionItem createCompletionItem(String itemLabel) {
- CompletionItem item = new CompletionItem();
- item.setLabel(itemLabel);
- item.setDetail("");
- item.setInsertText(itemLabel);
- item.setSortText("0" + itemLabel);
- item.setInsertTextFormat(InsertTextFormat.PLAIN_TEXT);
- item.setKind(CompletionItemKind.SNIPPET);
- return item;
- }
+ private static List createConfigs() {
+ var configs = new ArrayList();
+ configs.add("androidApis");
+ configs.add("androidTestAnnotationProcessor");
+ configs.add("androidTestApi");
+ configs.add("androidTestApk");
+ configs.add("androidTestCompile");
+ configs.add("androidTestCompileOnly");
+ configs.add("androidTestDebugAnnotationProcessor");
+ configs.add("androidTestDebugApi");
+ configs.add("androidTestDebugApk");
+ configs.add("androidTestDebugCompile");
+ configs.add("androidTestDebugCompileOnly");
+ configs.add("androidTestDebugImplementation");
+ configs.add("androidTestDebugProvided");
+ configs.add("androidTestDebugRuntimeOnly");
+ configs.add("androidTestDebugWearApp");
+ configs.add("androidTestImplementation");
+ configs.add("androidTestProvided");
+ configs.add("androidTestRuntimeOnly");
+ configs.add("androidTestUtil");
+ configs.add("androidTestWearApp");
+ configs.add("annotationProcessor");
+ configs.add("api");
+ configs.add("apk");
+ configs.add("archives");
+ configs.add("compile");
+ configs.add("compileOnly");
+ configs.add("coreLibraryDesugaring");
+ configs.add("debugAnnotationProcessor");
+ configs.add("debugApi");
+ configs.add("debugApk");
+ configs.add("debugCompile");
+ configs.add("debugCompileOnly");
+ configs.add("debugImplementation");
+ configs.add("debugProvided");
+ configs.add("debugRuntimeOnly");
+ configs.add("debugWearApp");
+ configs.add("default");
+ configs.add("implementation");
+ configs.add("implementation platform()");
+ configs.add("lintChecks");
+ configs.add("lintClassPath");
+ configs.add("lintPublish");
+ configs.add("provided");
+ configs.add("releaseAnnotationProcessor");
+ configs.add("releaseApi");
+ configs.add("releaseApk");
+ configs.add("releaseCompile");
+ configs.add("releaseCompileOnly");
+ configs.add("releaseImplementation");
+ configs.add("releaseProvided");
+ configs.add("releaseRuntimeOnly");
+ configs.add("releaseWearApp");
+ configs.add("runtimeOnly");
+ configs.add("testAnnotationProcessor");
+ configs.add("testApi");
+ configs.add("testApk");
+ configs.add("testCompile");
+ configs.add("testCompileOnly");
+ configs.add("testDebugAnnotationProcessor");
+ configs.add("testDebugApi");
+ configs.add("testDebugApk");
+ configs.add("testDebugCompile");
+ configs.add("testDebugCompileOnly");
+ configs.add("testDebugImplementation");
+ configs.add("testDebugProvided");
+ configs.add("testDebugRuntimeOnly");
+ configs.add("testDebugWearApp");
+ configs.add("testImplementation");
+ configs.add("testProvided");
+ configs.add("testReleaseAnnotationProcessor");
+ configs.add("testReleaseApi");
+ configs.add("testReleaseApk");
+ configs.add("testReleaseCompile");
+ configs.add("testReleaseCompileOnly");
+ configs.add("testReleaseImplementation");
+ configs.add("testReleaseProvided");
+ configs.add("testReleaseRuntimeOnly");
+ configs.add("testReleaseWearApp");
+ configs.add("testRuntimeOnly");
+ configs.add("testWearApp");
+ configs.add("wearApp");
+ return configs;
+ }
- private static List createArtifacts() {
- var artifacts = new ArrayList();
- artifacts.add("androidx.arch.core:core-common");
- artifacts.add("androidx.arch.core:core");
- artifacts.add("androidx.arch.core:core-testing");
- artifacts.add("androidx.arch.core:core-runtime");
- artifacts.add("androidx.lifecycle:lifecycle-common");
- artifacts.add("androidx.lifecycle:lifecycle-common-java8");
- artifacts.add("androidx.lifecycle:lifecycle-compiler");
- artifacts.add("androidx.lifecycle:lifecycle-extensions");
- artifacts.add("androidx.lifecycle:lifecycle-livedata");
- artifacts.add("androidx.lifecycle:lifecycle-livedata-core");
- artifacts.add("androidx.lifecycle:lifecycle-reactivestreams");
- artifacts.add("androidx.lifecycle:lifecycle-runtime");
- artifacts.add("androidx.lifecycle:lifecycle-viewmodel");
- artifacts.add("androidx.paging:paging-common");
- artifacts.add("androidx.paging:paging-runtime");
- artifacts.add("androidx.paging:paging-rxjava2");
- artifacts.add("androidx.room:room-common");
- artifacts.add("androidx.room:room-compiler");
- artifacts.add("androidx.room:room-guava");
- artifacts.add("androidx.room:room-migration");
- artifacts.add("androidx.room:room-runtime");
- artifacts.add("androidx.room:room-rxjava2");
- artifacts.add("androidx.room:room-testing");
- artifacts.add("androidx.sqlite:sqlite");
- artifacts.add("androidx.sqlite:sqlite-framework");
- artifacts.add("androidx.constraintlayout:constraintlayout");
- artifacts.add("androidx.constraintlayout:constraintlayout-solver");
- artifacts.add("androidx.test.espresso.idling:idling-concurrent");
- artifacts.add("androidx.test.espresso.idling:idling-net");
- artifacts.add("androidx.test.espresso:espresso-accessibility");
- artifacts.add("androidx.test.espresso:espresso-contrib");
- artifacts.add("androidx.test.espresso:espresso-core");
- artifacts.add("androidx.test.espresso:espresso-idling-resource");
- artifacts.add("androidx.test.espresso:espresso-intents");
- artifacts.add("androidx.test.espresso:espresso-remote");
- artifacts.add("androidx.test.espresso:espresso-web");
- artifacts.add("androidx.test.jank:janktesthelper");
- artifacts.add("androidx.test:test-services");
- artifacts.add("androidx.test.uiautomator:uiautomator");
- artifacts.add("androidx.test:monitor");
- artifacts.add("androidx.test:orchestrator");
- artifacts.add("androidx.test:rules");
- artifacts.add("androidx.test:runner");
- artifacts.add("androidx.vectordrawable:vectordrawable-animated");
- artifacts.add("androidx.appcompat:appcompat");
- artifacts.add("androidx.asynclayoutinflater:asynclayoutinflater");
- artifacts.add("androidx.car:car");
- artifacts.add("androidx.cardview:cardview");
- artifacts.add("androidx.collection:collection");
- artifacts.add("androidx.coordinatorlayout:coordinatorlayout");
- artifacts.add("androidx.cursoradapter:cursoradapter");
- artifacts.add("androidx.browser:browser");
- artifacts.add("androidx.customview:customview");
- artifacts.add("com.google.android.material:material");
- artifacts.add("androidx.documentfile:documentfile");
- artifacts.add("androidx.drawerlayout:drawerlayout");
- artifacts.add("androidx.exifinterface:exifinterface");
- artifacts.add("androidx.gridlayout:gridlayout");
- artifacts.add("androidx.heifwriter:heifwriter");
- artifacts.add("androidx.interpolator:interpolator");
- artifacts.add("androidx.leanback:leanback");
- artifacts.add("androidx.loader:loader");
- artifacts.add("androidx.localbroadcastmanager:localbroadcastmanager");
- artifacts.add("androidx.media2:media2");
- artifacts.add("androidx.media2:media2-exoplayer");
- artifacts.add("androidx.mediarouter:mediarouter");
- artifacts.add("androidx.multidex:multidex");
- artifacts.add("androidx.multidex:multidex-instrumentation");
- artifacts.add("androidx.palette:palette");
- artifacts.add("androidx.percentlayout:percentlayout");
- artifacts.add("androidx.leanback:leanback-preference");
- artifacts.add("androidx.legacy:legacy-preference-v14");
- artifacts.add("androidx.preference:preference");
- artifacts.add("androidx.print:print");
- artifacts.add("androidx.recommendation:recommendation");
- artifacts.add("androidx.recyclerview:recyclerview-selection");
- artifacts.add("androidx.recyclerview:recyclerview");
- artifacts.add("androidx.slice:slice-builders");
- artifacts.add("androidx.slice:slice-core");
- artifacts.add("androidx.slice:slice-view");
- artifacts.add("androidx.slidingpanelayout:slidingpanelayout");
- artifacts.add("androidx.annotation:annotation");
- artifacts.add("androidx.core:core");
- artifacts.add("androidx.contentpager:contentpager");
- artifacts.add("androidx.legacy:legacy-support-core-ui");
- artifacts.add("androidx.legacy:legacy-support-core-utils");
- artifacts.add("androidx.dynamicanimation:dynamicanimation");
- artifacts.add("androidx.emoji:emoji");
- artifacts.add("androidx.emoji:emoji-appcompat");
- artifacts.add("androidx.emoji:emoji-bundled");
- artifacts.add("androidx.fragment:fragment");
- artifacts.add("androidx.media:media");
- artifacts.add("androidx.tvprovider:tvprovider");
- artifacts.add("androidx.legacy:legacy-support-v13");
- artifacts.add("androidx.legacy:legacy-support-v4");
- artifacts.add("androidx.vectordrawable:vectordrawable");
- artifacts.add("androidx.swiperefreshlayout:swiperefreshlayout");
- artifacts.add("androidx.textclassifier:textclassifier");
- artifacts.add("androidx.transition:transition");
- artifacts.add("androidx.versionedparcelable:versionedparcelable");
- artifacts.add("androidx.viewpager:viewpager");
- artifacts.add("androidx.wear:wear");
- artifacts.add("androidx.webkit:webkit");
+ private static List createOtherCompletions() {
+ var others = new ArrayList();
- artifacts.add("fileTree(dir: 'libs', include: ['*.jar'])");
- return artifacts;
- }
+ // Common words that user may use
+ others.add("android");
+ others.add("applicationVariants");
+ others.add("compileSdkVersion");
+ others.add("buildToolsVersion");
+ others.add("defaultConfig");
+ others.add("applicationId");
+ others.add("minSdkVersion");
+ others.add("targetSdkVersion");
+ others.add("versionCode");
+ others.add("versionName");
+ others.add("multiDexEnabled");
+ others.add("vectorDrawables.useSupportLibrary");
+ others.add("compileOptions");
+ others.add("coreLibraryDesugaringEnabled");
+ others.add("sourceCompatibility");
+ others.add("targetCompatibility");
+ others.add("JavaVersion.VERSION_1_8");
+ others.add("JavaVersion.VERSION_1_7");
- private static List createConfigs() {
- var configs = new ArrayList();
- configs.add("androidApis");
- configs.add("androidTestAnnotationProcessor");
- configs.add("androidTestApi");
- configs.add("androidTestApk");
- configs.add("androidTestCompile");
- configs.add("androidTestCompileOnly");
- configs.add("androidTestDebugAnnotationProcessor");
- configs.add("androidTestDebugApi");
- configs.add("androidTestDebugApk");
- configs.add("androidTestDebugCompile");
- configs.add("androidTestDebugCompileOnly");
- configs.add("androidTestDebugImplementation");
- configs.add("androidTestDebugProvided");
- configs.add("androidTestDebugRuntimeOnly");
- configs.add("androidTestDebugWearApp");
- configs.add("androidTestImplementation");
- configs.add("androidTestProvided");
- configs.add("androidTestRuntimeOnly");
- configs.add("androidTestUtil");
- configs.add("androidTestWearApp");
- configs.add("annotationProcessor");
- configs.add("api");
- configs.add("apk");
- configs.add("archives");
- configs.add("compile");
- configs.add("compileOnly");
- configs.add("coreLibraryDesugaring");
- configs.add("debugAnnotationProcessor");
- configs.add("debugApi");
- configs.add("debugApk");
- configs.add("debugCompile");
- configs.add("debugCompileOnly");
- configs.add("debugImplementation");
- configs.add("debugProvided");
- configs.add("debugRuntimeOnly");
- configs.add("debugWearApp");
- configs.add("default");
- configs.add("implementation");
- configs.add("implementation platform()");
- configs.add("lintChecks");
- configs.add("lintClassPath");
- configs.add("lintPublish");
- configs.add("provided");
- configs.add("releaseAnnotationProcessor");
- configs.add("releaseApi");
- configs.add("releaseApk");
- configs.add("releaseCompile");
- configs.add("releaseCompileOnly");
- configs.add("releaseImplementation");
- configs.add("releaseProvided");
- configs.add("releaseRuntimeOnly");
- configs.add("releaseWearApp");
- configs.add("runtimeOnly");
- configs.add("testAnnotationProcessor");
- configs.add("testApi");
- configs.add("testApk");
- configs.add("testCompile");
- configs.add("testCompileOnly");
- configs.add("testDebugAnnotationProcessor");
- configs.add("testDebugApi");
- configs.add("testDebugApk");
- configs.add("testDebugCompile");
- configs.add("testDebugCompileOnly");
- configs.add("testDebugImplementation");
- configs.add("testDebugProvided");
- configs.add("testDebugRuntimeOnly");
- configs.add("testDebugWearApp");
- configs.add("testImplementation");
- configs.add("testProvided");
- configs.add("testReleaseAnnotationProcessor");
- configs.add("testReleaseApi");
- configs.add("testReleaseApk");
- configs.add("testReleaseCompile");
- configs.add("testReleaseCompileOnly");
- configs.add("testReleaseImplementation");
- configs.add("testReleaseProvided");
- configs.add("testReleaseRuntimeOnly");
- configs.add("testReleaseWearApp");
- configs.add("testRuntimeOnly");
- configs.add("testWearApp");
- configs.add("wearApp");
- return configs;
- }
+ // Lint options
+ others.add("lintOptions");
+ others.add("quiet");
+ others.add("abortOnError");
+ others.add("checkReleaseBuilds");
+ others.add("ignoreWarnings");
+ others.add("checkAllWarnings");
+ others.add("warningsAsErrors");
+ others.add("disable");
+ others.add("enable");
+ others.add("check");
+ others.add("noLines");
+ others.add("showAll");
+ others.add("explainIssues");
+ others.add("lintConfig");
+ others.add("textReport");
+ others.add("textOutput");
+ others.add("xmlReport");
+ others.add("xmlOutput");
+ others.add("htmlReport");
+ others.add("htmlOutput");
+ others.add("fatal");
+ others.add("error");
+ others.add("warning");
+ others.add("ignore");
+ others.add("informational");
+ others.add("baseline");
+ others.add("checkTestSources");
+ others.add("ignoreTestSources");
+ others.add("checkGeneratedSources");
+ others.add("checkDependencies");
- private static List createOtherCompletions() {
- var others = new ArrayList();
+ // buildFeatures options
+ others.add("buildFeatures");
+ others.add("aidl");
+ others.add("buildConfig");
+ others.add("compose");
+ others.add("prefab");
+ others.add("renderScript");
+ others.add("resValues");
+ others.add("shaders");
+ others.add("viewBinding");
- // Common words that user may use
- others.add("android");
- others.add("applicationVariants");
- others.add("compileSdkVersion");
- others.add("buildToolsVersion");
- others.add("defaultConfig");
- others.add("applicationId");
- others.add("minSdkVersion");
- others.add("targetSdkVersion");
- others.add("versionCode");
- others.add("versionName");
- others.add("multiDexEnabled");
- others.add("vectorDrawables.useSupportLibrary");
- others.add("compileOptions");
- others.add("coreLibraryDesugaringEnabled");
- others.add("sourceCompatibility");
- others.add("targetCompatibility");
- others.add("JavaVersion.VERSION_1_8");
- others.add("JavaVersion.VERSION_1_7");
+ // Signing configs and all
+ others.add("signingConfigs");
+ others.add("debug");
+ others.add("storeFile");
+ others.add("storePassword");
+ others.add("keyAlias");
+ others.add("keyPassword");
+ others.add("release");
+ others.add("storeFile");
+ others.add("storePassword");
+ others.add("keyAlias");
+ others.add("keyPassword");
+ others.add("buildTypes");
+ others.add("debug");
+ others.add("minifyEnabled");
+ others.add("proguardFiles");
+ others.add("signingConfig");
+ others.add("stringfog.enable");
+ others.add("release");
+ others.add("minifyEnabled");
+ others.add("proguardFiles");
+ others.add("signingConfig");
+ others.add("stringfog.enable");
+ others.add("packagingOptions");
+ others.add("exclude");
- // Lint options
- others.add("lintOptions");
- others.add("quiet");
- others.add("abortOnError");
- others.add("checkReleaseBuilds");
- others.add("ignoreWarnings");
- others.add("checkAllWarnings");
- others.add("warningsAsErrors");
- others.add("disable");
- others.add("enable");
- others.add("check");
- others.add("noLines");
- others.add("showAll");
- others.add("explainIssues");
- others.add("lintConfig");
- others.add("textReport");
- others.add("textOutput");
- others.add("xmlReport");
- others.add("xmlOutput");
- others.add("htmlReport");
- others.add("htmlOutput");
- others.add("fatal");
- others.add("error");
- others.add("warning");
- others.add("ignore");
- others.add("informational");
- others.add("baseline");
- others.add("checkTestSources");
- others.add("ignoreTestSources");
- others.add("checkGeneratedSources");
- others.add("checkDependencies");
-
- // buildFeatures options
- others.add("buildFeatures");
- others.add("aidl");
- others.add("buildConfig");
- others.add("compose");
- others.add("prefab");
- others.add("renderScript");
- others.add("resValues");
- others.add("shaders");
- others.add("viewBinding");
-
- // Signing configs and all
- others.add("signingConfigs");
- others.add("debug");
- others.add("storeFile");
- others.add("storePassword");
- others.add("keyAlias");
- others.add("keyPassword");
- others.add("release");
- others.add("storeFile");
- others.add("storePassword");
- others.add("keyAlias");
- others.add("keyPassword");
- others.add("buildTypes");
- others.add("debug");
- others.add("minifyEnabled");
- others.add("proguardFiles");
- others.add("signingConfig");
- others.add("stringfog.enable");
- others.add("release");
- others.add("minifyEnabled");
- others.add("proguardFiles");
- others.add("signingConfig");
- others.add("stringfog.enable");
- others.add("packagingOptions");
- others.add("exclude");
-
- // Maybe used in root project's build.gradle
- others.add("buildscript");
- others.add("ext");
- others.add("project.ext");
- others.add("repositories");
- others.add("maven");
- others.add("url");
- others.add("google()");
- others.add("mavenLocal()");
- others.add("mavenCentral()");
- others.add("jcenter()");
- others.add("classpath");
- others.add("allprojects");
- others.add("subprojects");
- return others;
- }
+ // Maybe used in root project's build.gradle
+ others.add("buildscript");
+ others.add("ext");
+ others.add("project.ext");
+ others.add("repositories");
+ others.add("maven");
+ others.add("url");
+ others.add("google()");
+ others.add("mavenLocal()");
+ others.add("mavenCentral()");
+ others.add("jcenter()");
+ others.add("classpath");
+ others.add("allprojects");
+ others.add("subprojects");
+ return others;
+ }
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyLanguage.java b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyLanguage.java
index 7aa08713c..94ed4e613 100755
--- a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyLanguage.java
+++ b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyLanguage.java
@@ -18,11 +18,14 @@
package com.itsaky.androidide.language.groovy;
import android.os.Bundle;
+
import androidx.annotation.NonNull;
+
import com.itsaky.androidide.language.IDELanguage;
import com.itsaky.androidide.lexers.groovy.GroovyLexer;
import com.itsaky.androidide.utils.CharSequenceReader;
import com.itsaky.androidide.utils.Logger;
+
import io.github.rosemoe.sora.lang.analysis.AnalyzeManager;
import io.github.rosemoe.sora.lang.completion.CompletionCancelledException;
import io.github.rosemoe.sora.lang.completion.CompletionPublisher;
@@ -32,133 +35,141 @@ import io.github.rosemoe.sora.text.CharPosition;
import io.github.rosemoe.sora.text.ContentReference;
import io.github.rosemoe.sora.text.TextUtils;
import io.github.rosemoe.sora.widget.SymbolPairMatch;
-import java.io.StringReader;
+
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.Token;
+import java.io.StringReader;
+
public class GroovyLanguage extends IDELanguage {
- private final GroovyAnalyzer analyzer;
- private final GroovyAutoComplete completer;
+ private final GroovyAnalyzer analyzer;
+ private final GroovyAutoComplete completer;
- private static final Logger LOG = Logger.instance("GroovyLanguage");
+ private static final Logger LOG = Logger.instance("GroovyLanguage");
- public GroovyLanguage() {
- analyzer = new GroovyAnalyzer();
- completer = new GroovyAutoComplete();
- }
-
- public int getIndentAdvance(String p1) {
- try {
- GroovyLexer lexer = new GroovyLexer(CharStreams.fromReader(new StringReader(p1)));
- Token token = null;
- int advance = 0;
- while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) {
- switch (token.getType()) {
- case GroovyLexer.LBRACE:
- advance++;
- break;
- case GroovyLexer.RBRACE:
- advance--;
- break;
- }
- }
- advance = Math.max(0, advance);
- return advance * getTabSize();
- } catch (Throwable e) {
- LOG.error("Failed to calculate indent advance", e);
+ public GroovyLanguage() {
+ analyzer = new GroovyAnalyzer();
+ completer = new GroovyAutoComplete();
}
- return 0;
- }
- @Override
- public SymbolPairMatch getSymbolPairs() {
- return new SymbolPairMatch.DefaultSymbolPairs();
- }
-
- @NonNull
- @Override
- public AnalyzeManager getAnalyzeManager() {
- return analyzer;
- }
-
- @Override
- public int getInterruptionLevel() {
- return INTERRUPTION_LEVEL_STRONG;
- }
-
- @Override
- public void requireAutoComplete(
- @NonNull ContentReference content,
- @NonNull CharPosition position,
- @NonNull CompletionPublisher publisher,
- @NonNull Bundle extraArguments)
- throws CompletionCancelledException {
-
- completer.complete(content, position, publisher, extraArguments);
- }
-
- @Override
- public int getIndentAdvance(@NonNull ContentReference content, int line, int column) {
- try {
- GroovyLexer lexer = new GroovyLexer(CharStreams.fromReader(new CharSequenceReader(content)));
- Token token;
- int advance = 0;
- while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) {
- switch (token.getType()) {
- case GroovyLexer.LBRACE:
- advance++;
- break;
- case GroovyLexer.RBRACE:
- advance--;
- break;
+ public int getIndentAdvance(String p1) {
+ try {
+ GroovyLexer lexer = new GroovyLexer(CharStreams.fromReader(new StringReader(p1)));
+ Token token = null;
+ int advance = 0;
+ while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) {
+ switch (token.getType()) {
+ case GroovyLexer.LBRACE:
+ advance++;
+ break;
+ case GroovyLexer.RBRACE:
+ advance--;
+ break;
+ }
+ }
+ advance = Math.max(0, advance);
+ return advance * getTabSize();
+ } catch (Throwable e) {
+ LOG.error("Failed to calculate indent advance", e);
}
- }
- advance = Math.max(0, advance);
- return advance * getTabSize();
- } catch (Throwable e) {
- LOG.error("Failed to calculate indent advance", e);
- }
- return 0;
- }
-
- @Override
- public CharSequence format(CharSequence content) {
- return content;
- }
-
- private final NewlineHandler[] newlineHandlers = new NewlineHandler[] {new BraceHandler()};
-
- @Override
- public NewlineHandler[] getNewlineHandlers() {
- return newlineHandlers;
- }
-
- @Override
- public void destroy() {}
-
- class BraceHandler implements NewlineHandler {
-
- @Override
- public boolean matchesRequirement(String beforeText, String afterText) {
- beforeText = beforeText.trim();
- afterText = afterText.trim();
- return beforeText.endsWith("{") && afterText.startsWith("}");
+ return 0;
}
@Override
- public NewlineHandleResult handleNewline(String beforeText, String afterText, int tabSize) {
- int count = TextUtils.countLeadingSpaceCount(beforeText, tabSize);
- int advanceBefore = getIndentAdvance(beforeText);
- int advanceAfter = getIndentAdvance(afterText);
- String text;
- StringBuilder sb =
- new StringBuilder("\n")
- .append(TextUtils.createIndent(count + advanceBefore, tabSize, useTab()))
- .append('\n')
- .append(text = TextUtils.createIndent(count + advanceAfter, tabSize, useTab()));
- int shiftLeft = text.length() + 1;
- return new NewlineHandleResult(sb, shiftLeft);
+ public SymbolPairMatch getSymbolPairs() {
+ return new SymbolPairMatch.DefaultSymbolPairs();
+ }
+
+ @NonNull
+ @Override
+ public AnalyzeManager getAnalyzeManager() {
+ return analyzer;
+ }
+
+ @Override
+ public int getInterruptionLevel() {
+ return INTERRUPTION_LEVEL_STRONG;
+ }
+
+ @Override
+ public void requireAutoComplete(
+ @NonNull ContentReference content,
+ @NonNull CharPosition position,
+ @NonNull CompletionPublisher publisher,
+ @NonNull Bundle extraArguments)
+ throws CompletionCancelledException {
+
+ completer.complete(content, position, publisher, extraArguments);
+ }
+
+ @Override
+ public int getIndentAdvance(@NonNull ContentReference content, int line, int column) {
+ try {
+ GroovyLexer lexer =
+ new GroovyLexer(CharStreams.fromReader(new CharSequenceReader(content)));
+ Token token;
+ int advance = 0;
+ while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) {
+ switch (token.getType()) {
+ case GroovyLexer.LBRACE:
+ advance++;
+ break;
+ case GroovyLexer.RBRACE:
+ advance--;
+ break;
+ }
+ }
+ advance = Math.max(0, advance);
+ return advance * getTabSize();
+ } catch (Throwable e) {
+ LOG.error("Failed to calculate indent advance", e);
+ }
+ return 0;
+ }
+
+ @Override
+ public CharSequence format(CharSequence content) {
+ return content;
+ }
+
+ private final NewlineHandler[] newlineHandlers = new NewlineHandler[] {new BraceHandler()};
+
+ @Override
+ public NewlineHandler[] getNewlineHandlers() {
+ return newlineHandlers;
+ }
+
+ @Override
+ public void destroy() {}
+
+ class BraceHandler implements NewlineHandler {
+
+ @Override
+ public boolean matchesRequirement(String beforeText, String afterText) {
+ beforeText = beforeText.trim();
+ afterText = afterText.trim();
+ return beforeText.endsWith("{") && afterText.startsWith("}");
+ }
+
+ @Override
+ public NewlineHandleResult handleNewline(String beforeText, String afterText, int tabSize) {
+ int count = TextUtils.countLeadingSpaceCount(beforeText, tabSize);
+ int advanceBefore = getIndentAdvance(beforeText);
+ int advanceAfter = getIndentAdvance(afterText);
+ String text;
+ StringBuilder sb =
+ new StringBuilder("\n")
+ .append(
+ TextUtils.createIndent(
+ count + advanceBefore, tabSize, useTab()))
+ .append('\n')
+ .append(
+ text =
+ TextUtils.createIndent(
+ count + advanceAfter, tabSize, useTab()));
+ int shiftLeft = text.length() + 1;
+ return new NewlineHandleResult(sb, shiftLeft);
+ }
}
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/JavaAnalyzer.java b/app/src/main/java/com/itsaky/androidide/language/java/JavaAnalyzer.java
index 60ad2ef8a..4b7899002 100644
--- a/app/src/main/java/com/itsaky/androidide/language/java/JavaAnalyzer.java
+++ b/app/src/main/java/com/itsaky/androidide/language/java/JavaAnalyzer.java
@@ -28,6 +28,7 @@ import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.get;
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.withoutCompletion;
import androidx.annotation.NonNull;
+
import com.itsaky.androidide.lexers.java.JavaLexer;
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
import com.itsaky.androidide.utils.CharSequenceReader;
@@ -39,21 +40,24 @@ import com.itsaky.lsp.models.DiagnosticResult;
import com.itsaky.lsp.models.DiagnosticSeverity;
import com.itsaky.lsp.models.Position;
import com.itsaky.lsp.models.Range;
+
import io.github.rosemoe.sora.lang.analysis.SimpleAnalyzeManager;
import io.github.rosemoe.sora.lang.styling.CodeBlock;
import io.github.rosemoe.sora.lang.styling.MappedSpans;
import io.github.rosemoe.sora.lang.styling.Span;
import io.github.rosemoe.sora.lang.styling.Styles;
+
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CodePointCharStream;
+import org.antlr.v4.runtime.Token;
+import org.jetbrains.annotations.Contract;
+
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.concurrent.CompletableFuture;
-import org.antlr.v4.runtime.CharStreams;
-import org.antlr.v4.runtime.CodePointCharStream;
-import org.antlr.v4.runtime.Token;
-import org.jetbrains.annotations.Contract;
/**
* Code analyzer for the Java Language
@@ -62,335 +66,338 @@ import org.jetbrains.annotations.Contract;
*/
public class JavaAnalyzer extends SimpleAnalyzeManager {
- private List diagnostics = new ArrayList<>();
- private final List ideDiagnostics = new ArrayList<>();
- private final ILanguageServer languageServer;
+ private List diagnostics = new ArrayList<>();
+ private final List ideDiagnostics = new ArrayList<>();
+ private final ILanguageServer languageServer;
- public JavaAnalyzer(ILanguageServer languageServer) {
- this.languageServer = languageServer;
- }
-
- @NonNull
- public List getDiagnostics() {
- final var result = new ArrayList<>(ideDiagnostics);
- if (diagnostics != null && !diagnostics.isEmpty()) {
- result.addAll(diagnostics);
- }
- result.sort(DiagnosticItem.START_COMPARATOR);
- return result;
- }
-
- @Override
- protected Styles analyze(StringBuilder text, Delegate delegate) {
- final var args = getExtraArguments();
- if (args.containsKey(IDEEditor.KEY_FILE)) {
- final var file = new File(args.getString(IDEEditor.KEY_FILE));
- if (languageServer != null && file.exists()) {
- CompletableFuture.runAsync(
- () -> {
- final var analyzer = languageServer.getCodeAnalyzer();
-
- diagnostics = analyzer.analyze(file.toPath());
-
- if (languageServer.getClient() != null) {
- languageServer
- .getClient()
- .publishDiagnostics(new DiagnosticResult(file.toPath(), diagnostics));
- }
- });
- }
+ public JavaAnalyzer(ILanguageServer languageServer) {
+ this.languageServer = languageServer;
}
- final CodePointCharStream stream;
- final var styles = new Styles();
- final var colors = new MappedSpans.Builder();
- try {
- stream = CharStreams.fromReader(new CharSequenceReader(text));
- } catch (IOException e) {
- LOG.error("Unable to create char stream for text", e);
- return styles;
+ @NonNull
+ public List getDiagnostics() {
+ final var result = new ArrayList<>(ideDiagnostics);
+ if (diagnostics != null && !diagnostics.isEmpty()) {
+ result.addAll(diagnostics);
+ }
+ result.sort(DiagnosticItem.START_COMPARATOR);
+ return result;
}
- final var lexer = new JavaLexer(stream);
- final var stack = new Stack();
- Token token;
- int line, column, lastLine = 1, type, currSwitch = 0, maxSwitch = 0, previous = -1;
- boolean isFirst = true;
+ @Override
+ protected Styles analyze(StringBuilder text, Delegate delegate) {
+ final var args = getExtraArguments();
+ if (args.containsKey(IDEEditor.KEY_FILE)) {
+ final var file = new File(args.getString(IDEEditor.KEY_FILE));
+ if (languageServer != null && file.exists()) {
+ CompletableFuture.runAsync(
+ () -> {
+ final var analyzer = languageServer.getCodeAnalyzer();
- ideDiagnostics.clear();
+ diagnostics = analyzer.analyze(file.toPath());
- while (!delegate.isCancelled()) {
- token = lexer.nextToken();
+ if (languageServer.getClient() != null) {
+ languageServer
+ .getClient()
+ .publishDiagnostics(
+ new DiagnosticResult(file.toPath(), diagnostics));
+ }
+ });
+ }
+ }
- if (token == null) {
- break;
- }
+ final CodePointCharStream stream;
+ final var styles = new Styles();
+ final var colors = new MappedSpans.Builder();
+ try {
+ stream = CharStreams.fromReader(new CharSequenceReader(text));
+ } catch (IOException e) {
+ LOG.error("Unable to create char stream for text", e);
+ return styles;
+ }
+ final var lexer = new JavaLexer(stream);
+ final var stack = new Stack();
- type = token.getType();
- line = token.getLine() - 1;
- column = token.getCharPositionInLine();
+ Token token;
+ int line, column, lastLine = 1, type, currSwitch = 0, maxSwitch = 0, previous = -1;
+ boolean isFirst = true;
- if (type == JavaLexer.EOF) {
- lastLine = line;
- break;
- }
+ ideDiagnostics.clear();
- final var tokenLength = token.getText().length();
- switch (type) {
- case JavaLexer.WS:
- if (isFirst) {
- colors.addNormalIfNull();
- }
- break;
- case JavaLexer.ABSTRACT:
- case JavaLexer.ASSERT:
- case JavaLexer.BREAK:
- case JavaLexer.CASE:
- case JavaLexer.CATCH:
- case JavaLexer.CLASS:
- case JavaLexer.CONST:
- case JavaLexer.CONTINUE:
- case JavaLexer.DEFAULT:
- case JavaLexer.DO:
- case JavaLexer.ELSE:
- case JavaLexer.EXTENDS:
- case JavaLexer.FINAL:
- case JavaLexer.FINALLY:
- case JavaLexer.FOR:
- case JavaLexer.IF:
- case JavaLexer.GOTO:
- case JavaLexer.IMPLEMENTS:
- case JavaLexer.IMPORT:
- case JavaLexer.INSTANCEOF:
- case JavaLexer.INTERFACE:
- case JavaLexer.NATIVE:
- case JavaLexer.NEW:
- case JavaLexer.PACKAGE:
- case JavaLexer.PRIVATE:
- case JavaLexer.PROTECTED:
- case JavaLexer.PUBLIC:
- case JavaLexer.RETURN:
- case JavaLexer.STATIC:
- case JavaLexer.STRICTFP:
- case JavaLexer.SUPER:
- case JavaLexer.SWITCH:
- case JavaLexer.SYNCHRONIZED:
- case JavaLexer.THIS:
- case JavaLexer.THROW:
- case JavaLexer.THROWS:
- case JavaLexer.TRANSIENT:
- case JavaLexer.TRY:
- case JavaLexer.VOID:
- case JavaLexer.VOLATILE:
- case JavaLexer.WHILE:
- case JavaLexer.VAR:
- colors.addIfNeeded(line, column, forKeyword());
- break;
- case JavaLexer.DECIMAL_LITERAL:
- case JavaLexer.HEX_LITERAL:
- case JavaLexer.OCT_LITERAL:
- case JavaLexer.BINARY_LITERAL:
- case JavaLexer.FLOAT_LITERAL:
- case JavaLexer.HEX_FLOAT_LITERAL:
- case JavaLexer.BOOL_LITERAL:
- case JavaLexer.CHAR_LITERAL:
- case JavaLexer.NULL_LITERAL:
- colors.addIfNeeded(line, column, get(LITERAL));
- break;
- case JavaLexer.STRING_LITERAL:
- colors.addIfNeeded(line, column, forString());
- break;
- case JavaLexer.LPAREN:
- case JavaLexer.RPAREN:
- case JavaLexer.LBRACK:
- case JavaLexer.RBRACK:
- case JavaLexer.SEMI:
- case JavaLexer.COMMA:
- case JavaLexer.ASSIGN:
- case JavaLexer.GT:
- case JavaLexer.LT:
- case JavaLexer.BANG:
- case JavaLexer.TILDE:
- case JavaLexer.QUESTION:
- case JavaLexer.COLON:
- case JavaLexer.EQUAL:
- case JavaLexer.GE:
- case JavaLexer.LE:
- case JavaLexer.NOTEQUAL:
- case JavaLexer.AND:
- case JavaLexer.OR:
- case JavaLexer.INC:
- case JavaLexer.DEC:
- case JavaLexer.ADD:
- case JavaLexer.SUB:
- case JavaLexer.MUL:
- case JavaLexer.DIV:
- case JavaLexer.BITAND:
- case JavaLexer.BITOR:
- case JavaLexer.CARET:
- case JavaLexer.MOD:
- case JavaLexer.ADD_ASSIGN:
- case JavaLexer.SUB_ASSIGN:
- case JavaLexer.MUL_ASSIGN:
- case JavaLexer.DIV_ASSIGN:
- case JavaLexer.AND_ASSIGN:
- case JavaLexer.OR_ASSIGN:
- case JavaLexer.XOR_ASSIGN:
- case JavaLexer.MOD_ASSIGN:
- case JavaLexer.LSHIFT_ASSIGN:
- case JavaLexer.RSHIFT_ASSIGN:
- case JavaLexer.URSHIFT_ASSIGN:
- case JavaLexer.ARROW:
- case JavaLexer.COLONCOLON:
- case JavaLexer.ELLIPSIS:
- case JavaLexer.DOT:
- colors.addIfNeeded(line, column, get(OPERATOR));
- break;
- case JavaLexer.BOOLEAN:
- case JavaLexer.BYTE:
- case JavaLexer.CHAR:
- case JavaLexer.DOUBLE:
- case JavaLexer.ENUM:
- case JavaLexer.FLOAT:
- case JavaLexer.INT:
- case JavaLexer.LONG:
- case JavaLexer.SHORT:
- colors.addIfNeeded(line, column, get(TYPE_NAME));
- break;
- case JavaLexer.BLOCK_COMMENT:
- colors.addIfNeeded(line, column, forComment());
- break;
- case JavaLexer.LINE_COMMENT:
- var commentType = SchemeAndroidIDE.COMMENT;
+ while (!delegate.isCancelled()) {
+ token = lexer.nextToken();
- // highlight special line comments
- var commentText = token.getText();
- if (commentText.length() > 2) {
- commentText = commentText.substring(2);
- commentText = commentText.trim();
-
- var mark = true;
- if ("todo".equalsIgnoreCase(commentText.substring(0, 4))) {
- commentType = SchemeAndroidIDE.TODO_COMMENT;
- } else if ("fixme".equalsIgnoreCase(commentText.substring(0, 5))) {
- commentType = SchemeAndroidIDE.FIXME_COMMENT;
- } else {
- mark = false;
+ if (token == null) {
+ break;
}
- if (mark) {
- if (diagnostics == null) {
- diagnostics = new ArrayList<>();
- }
- final var diagnostic = new DiagnosticItem();
- diagnostic.setSeverity(DiagnosticSeverity.WARNING);
- diagnostic.setMessage(commentText);
- diagnostic.setCode("special.comment");
- diagnostic.setRange(
- new Range(new Position(line, column), new Position(line, column + tokenLength)));
- diagnostic.setSource(commentText);
- ideDiagnostics.add(diagnostic);
+ type = token.getType();
+ line = token.getLine() - 1;
+ column = token.getCharPositionInLine();
+
+ if (type == JavaLexer.EOF) {
+ lastLine = line;
+ break;
}
- }
- colors.addIfNeeded(line, column, withoutCompletion(commentType));
+ final var tokenLength = token.getText().length();
+ switch (type) {
+ case JavaLexer.WS:
+ if (isFirst) {
+ colors.addNormalIfNull();
+ }
+ break;
+ case JavaLexer.ABSTRACT:
+ case JavaLexer.ASSERT:
+ case JavaLexer.BREAK:
+ case JavaLexer.CASE:
+ case JavaLexer.CATCH:
+ case JavaLexer.CLASS:
+ case JavaLexer.CONST:
+ case JavaLexer.CONTINUE:
+ case JavaLexer.DEFAULT:
+ case JavaLexer.DO:
+ case JavaLexer.ELSE:
+ case JavaLexer.EXTENDS:
+ case JavaLexer.FINAL:
+ case JavaLexer.FINALLY:
+ case JavaLexer.FOR:
+ case JavaLexer.IF:
+ case JavaLexer.GOTO:
+ case JavaLexer.IMPLEMENTS:
+ case JavaLexer.IMPORT:
+ case JavaLexer.INSTANCEOF:
+ case JavaLexer.INTERFACE:
+ case JavaLexer.NATIVE:
+ case JavaLexer.NEW:
+ case JavaLexer.PACKAGE:
+ case JavaLexer.PRIVATE:
+ case JavaLexer.PROTECTED:
+ case JavaLexer.PUBLIC:
+ case JavaLexer.RETURN:
+ case JavaLexer.STATIC:
+ case JavaLexer.STRICTFP:
+ case JavaLexer.SUPER:
+ case JavaLexer.SWITCH:
+ case JavaLexer.SYNCHRONIZED:
+ case JavaLexer.THIS:
+ case JavaLexer.THROW:
+ case JavaLexer.THROWS:
+ case JavaLexer.TRANSIENT:
+ case JavaLexer.TRY:
+ case JavaLexer.VOID:
+ case JavaLexer.VOLATILE:
+ case JavaLexer.WHILE:
+ case JavaLexer.VAR:
+ colors.addIfNeeded(line, column, forKeyword());
+ break;
+ case JavaLexer.DECIMAL_LITERAL:
+ case JavaLexer.HEX_LITERAL:
+ case JavaLexer.OCT_LITERAL:
+ case JavaLexer.BINARY_LITERAL:
+ case JavaLexer.FLOAT_LITERAL:
+ case JavaLexer.HEX_FLOAT_LITERAL:
+ case JavaLexer.BOOL_LITERAL:
+ case JavaLexer.CHAR_LITERAL:
+ case JavaLexer.NULL_LITERAL:
+ colors.addIfNeeded(line, column, get(LITERAL));
+ break;
+ case JavaLexer.STRING_LITERAL:
+ colors.addIfNeeded(line, column, forString());
+ break;
+ case JavaLexer.LPAREN:
+ case JavaLexer.RPAREN:
+ case JavaLexer.LBRACK:
+ case JavaLexer.RBRACK:
+ case JavaLexer.SEMI:
+ case JavaLexer.COMMA:
+ case JavaLexer.ASSIGN:
+ case JavaLexer.GT:
+ case JavaLexer.LT:
+ case JavaLexer.BANG:
+ case JavaLexer.TILDE:
+ case JavaLexer.QUESTION:
+ case JavaLexer.COLON:
+ case JavaLexer.EQUAL:
+ case JavaLexer.GE:
+ case JavaLexer.LE:
+ case JavaLexer.NOTEQUAL:
+ case JavaLexer.AND:
+ case JavaLexer.OR:
+ case JavaLexer.INC:
+ case JavaLexer.DEC:
+ case JavaLexer.ADD:
+ case JavaLexer.SUB:
+ case JavaLexer.MUL:
+ case JavaLexer.DIV:
+ case JavaLexer.BITAND:
+ case JavaLexer.BITOR:
+ case JavaLexer.CARET:
+ case JavaLexer.MOD:
+ case JavaLexer.ADD_ASSIGN:
+ case JavaLexer.SUB_ASSIGN:
+ case JavaLexer.MUL_ASSIGN:
+ case JavaLexer.DIV_ASSIGN:
+ case JavaLexer.AND_ASSIGN:
+ case JavaLexer.OR_ASSIGN:
+ case JavaLexer.XOR_ASSIGN:
+ case JavaLexer.MOD_ASSIGN:
+ case JavaLexer.LSHIFT_ASSIGN:
+ case JavaLexer.RSHIFT_ASSIGN:
+ case JavaLexer.URSHIFT_ASSIGN:
+ case JavaLexer.ARROW:
+ case JavaLexer.COLONCOLON:
+ case JavaLexer.ELLIPSIS:
+ case JavaLexer.DOT:
+ colors.addIfNeeded(line, column, get(OPERATOR));
+ break;
+ case JavaLexer.BOOLEAN:
+ case JavaLexer.BYTE:
+ case JavaLexer.CHAR:
+ case JavaLexer.DOUBLE:
+ case JavaLexer.ENUM:
+ case JavaLexer.FLOAT:
+ case JavaLexer.INT:
+ case JavaLexer.LONG:
+ case JavaLexer.SHORT:
+ colors.addIfNeeded(line, column, get(TYPE_NAME));
+ break;
+ case JavaLexer.BLOCK_COMMENT:
+ colors.addIfNeeded(line, column, forComment());
+ break;
+ case JavaLexer.LINE_COMMENT:
+ var commentType = SchemeAndroidIDE.COMMENT;
- break;
- case JavaLexer.AT:
- colors.addIfNeeded(line, column, get(ANNOTATION));
- break;
- case JavaLexer.IDENTIFIER:
- int colorId = SchemeAndroidIDE.TEXT_NORMAL;
- if (previous == JavaLexer.AT) {
- colorId = SchemeAndroidIDE.ANNOTATION;
- }
- colors.addIfNeeded(line, column, get(colorId));
- break;
- case JavaLexer.LBRACE:
- colors.addIfNeeded(line, column, get(OPERATOR));
- if (stack.isEmpty()) {
- if (currSwitch > maxSwitch) {
- maxSwitch = currSwitch;
+ // highlight special line comments
+ var commentText = token.getText();
+ if (commentText.length() > 2) {
+ commentText = commentText.substring(2);
+ commentText = commentText.trim();
+
+ var mark = true;
+ if ("todo".equalsIgnoreCase(commentText.substring(0, 4))) {
+ commentType = SchemeAndroidIDE.TODO_COMMENT;
+ } else if ("fixme".equalsIgnoreCase(commentText.substring(0, 5))) {
+ commentType = SchemeAndroidIDE.FIXME_COMMENT;
+ } else {
+ mark = false;
+ }
+
+ if (mark) {
+ if (diagnostics == null) {
+ diagnostics = new ArrayList<>();
+ }
+ final var diagnostic = new DiagnosticItem();
+ diagnostic.setSeverity(DiagnosticSeverity.WARNING);
+ diagnostic.setMessage(commentText);
+ diagnostic.setCode("special.comment");
+ diagnostic.setRange(
+ new Range(
+ new Position(line, column),
+ new Position(line, column + tokenLength)));
+ diagnostic.setSource(commentText);
+ ideDiagnostics.add(diagnostic);
+ }
+ }
+
+ colors.addIfNeeded(line, column, withoutCompletion(commentType));
+
+ break;
+ case JavaLexer.AT:
+ colors.addIfNeeded(line, column, get(ANNOTATION));
+ break;
+ case JavaLexer.IDENTIFIER:
+ int colorId = SchemeAndroidIDE.TEXT_NORMAL;
+ if (previous == JavaLexer.AT) {
+ colorId = SchemeAndroidIDE.ANNOTATION;
+ }
+ colors.addIfNeeded(line, column, get(colorId));
+ break;
+ case JavaLexer.LBRACE:
+ colors.addIfNeeded(line, column, get(OPERATOR));
+ if (stack.isEmpty()) {
+ if (currSwitch > maxSwitch) {
+ maxSwitch = currSwitch;
+ }
+ currSwitch = 0;
+ }
+ currSwitch++;
+ var block = styles.obtainNewBlock();
+ block.startLine = line;
+ block.startColumn = column;
+ stack.push(block);
+ break;
+ case JavaLexer.RBRACE:
+ colors.addIfNeeded(line, column, get(OPERATOR));
+ if (!stack.isEmpty()) {
+ var block2 = stack.pop();
+ block2.endLine = line;
+ block2.endColumn = column;
+ if (block2.startLine != block2.endLine) {
+ styles.addCodeBlock(block2);
+ }
+ }
+ break;
+ default:
+ colors.addIfNeeded(line, column, get(TEXT_NORMAL));
+ break;
}
- currSwitch = 0;
- }
- currSwitch++;
- var block = styles.obtainNewBlock();
- block.startLine = line;
- block.startColumn = column;
- stack.push(block);
- break;
- case JavaLexer.RBRACE:
- colors.addIfNeeded(line, column, get(OPERATOR));
- if (!stack.isEmpty()) {
- var block2 = stack.pop();
- block2.endLine = line;
- block2.endColumn = column;
- if (block2.startLine != block2.endLine) {
- styles.addCodeBlock(block2);
+
+ isFirst = false;
+
+ if (type != JavaLexer.WS) {
+ previous = type;
}
- }
- break;
- default:
- colors.addIfNeeded(line, column, get(TEXT_NORMAL));
- break;
- }
+ }
- isFirst = false;
+ if (stack.isEmpty() && currSwitch > maxSwitch) {
+ maxSwitch = currSwitch;
+ }
- if (type != JavaLexer.WS) {
- previous = type;
- }
+ colors.determine(lastLine);
+ styles.setSuppressSwitch(maxSwitch + 10);
+
+ if (diagnostics != null && !diagnostics.isEmpty()) {
+ markDiagnostics(colors, diagnostics);
+ }
+
+ if (!ideDiagnostics.isEmpty()) {
+ markDiagnostics(colors, ideDiagnostics);
+ }
+
+ styles.spans = colors.build();
+
+ return styles;
}
- if (stack.isEmpty() && currSwitch > maxSwitch) {
- maxSwitch = currSwitch;
+ private void markDiagnostics(MappedSpans.Builder colors, List diagnostics) {
+ diagnostics.sort(DiagnosticItem.START_COMPARATOR);
+ for (var d : diagnostics) {
+ var start = d.getRange().getStart();
+ var end = d.getRange().getEnd();
+
+ colors.markProblemRegion(
+ convertToSpanFlag(d.getSeverity()),
+ start.getLine(),
+ start.getColumn(),
+ end.getLine(),
+ end.getColumn());
+ }
}
- colors.determine(lastLine);
- styles.setSuppressSwitch(maxSwitch + 10);
-
- if (diagnostics != null && !diagnostics.isEmpty()) {
- markDiagnostics(colors, diagnostics);
+ @Contract(pure = true)
+ private int convertToSpanFlag(@NonNull DiagnosticSeverity severity) {
+ switch (severity) {
+ case WARNING:
+ return Span.FLAG_WARNING;
+ case ERROR:
+ return Span.FLAG_ERROR;
+ case HINT:
+ case INFO:
+ default:
+ return Span.FLAG_TYPO;
+ }
}
- if (!ideDiagnostics.isEmpty()) {
- markDiagnostics(colors, ideDiagnostics);
- }
-
- styles.spans = colors.build();
-
- return styles;
- }
-
- private void markDiagnostics(MappedSpans.Builder colors, List diagnostics) {
- diagnostics.sort(DiagnosticItem.START_COMPARATOR);
- for (var d : diagnostics) {
- var start = d.getRange().getStart();
- var end = d.getRange().getEnd();
-
- colors.markProblemRegion(
- convertToSpanFlag(d.getSeverity()),
- start.getLine(),
- start.getColumn(),
- end.getLine(),
- end.getColumn());
- }
- }
-
- @Contract(pure = true)
- private int convertToSpanFlag(@NonNull DiagnosticSeverity severity) {
- switch (severity) {
- case WARNING:
- return Span.FLAG_WARNING;
- case ERROR:
- return Span.FLAG_ERROR;
- case HINT:
- case INFO:
- default:
- return Span.FLAG_TYPO;
- }
- }
-
- private static final Logger LOG = Logger.instance("JavaAnalyzer");
+ private static final Logger LOG = Logger.instance("JavaAnalyzer");
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java b/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java
index e293efcd6..ca8e019eb 100755
--- a/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java
+++ b/app/src/main/java/com/itsaky/androidide/language/java/JavaLanguage.java
@@ -18,7 +18,9 @@
package com.itsaky.androidide.language.java;
import android.os.Bundle;
+
import androidx.annotation.NonNull;
+
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.language.CommonCompletionProvider;
import com.itsaky.androidide.language.IDELanguage;
@@ -27,6 +29,7 @@ import com.itsaky.androidide.lexers.java.JavaParser;
import com.itsaky.androidide.utils.Logger;
import com.itsaky.androidide.views.editor.IDEEditor;
import com.itsaky.lsp.models.DiagnosticItem;
+
import io.github.rosemoe.sora.lang.analysis.AnalyzeManager;
import io.github.rosemoe.sora.lang.completion.CompletionCancelledException;
import io.github.rosemoe.sora.lang.completion.CompletionPublisher;
@@ -36,153 +39,163 @@ import io.github.rosemoe.sora.text.CharPosition;
import io.github.rosemoe.sora.text.ContentReference;
import io.github.rosemoe.sora.text.TextUtils;
import io.github.rosemoe.sora.widget.SymbolPairMatch;
+
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.Token;
+
import java.io.StringReader;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import org.antlr.v4.runtime.CharStreams;
-import org.antlr.v4.runtime.Token;
public class JavaLanguage extends IDELanguage {
- private JavaAnalyzer analyzer;
- private CommonCompletionProvider completer;
+ private JavaAnalyzer analyzer;
+ private CommonCompletionProvider completer;
- private final NewlineHandler[] newlineHandlers;
+ private final NewlineHandler[] newlineHandlers;
- private static final Logger LOG = Logger.instance("JavaLanguage");
+ private static final Logger LOG = Logger.instance("JavaLanguage");
- public JavaLanguage() {
- final var server = StudioApp.getInstance().getJavaLanguageServer();
- this.analyzer = new JavaAnalyzer(server);
- this.completer = new CommonCompletionProvider(server);
+ public JavaLanguage() {
+ final var server = StudioApp.getInstance().getJavaLanguageServer();
+ this.analyzer = new JavaAnalyzer(server);
+ this.completer = new CommonCompletionProvider(server);
- this.newlineHandlers = new NewlineHandler[1];
- this.newlineHandlers[0] = new BraceHandler();
- }
+ this.newlineHandlers = new NewlineHandler[1];
+ this.newlineHandlers[0] = new BraceHandler();
+ }
- @NonNull
- @Override
- public List getDiagnostics() {
- return analyzer == null ? Collections.emptyList() : analyzer.getDiagnostics();
- }
+ @NonNull
+ @Override
+ public List getDiagnostics() {
+ return analyzer == null ? Collections.emptyList() : analyzer.getDiagnostics();
+ }
- public int getIndentAdvance(String p1) {
- try {
- JavaLexer lexer = new JavaLexer(CharStreams.fromReader(new StringReader(p1)));
- Token token;
- int advance = 0;
- while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) {
- switch (token.getType()) {
- case JavaLexer.LBRACE:
- advance++;
- break;
- case JavaParser.RBRACE:
- advance--;
- break;
+ public int getIndentAdvance(String p1) {
+ try {
+ JavaLexer lexer = new JavaLexer(CharStreams.fromReader(new StringReader(p1)));
+ Token token;
+ int advance = 0;
+ while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) {
+ switch (token.getType()) {
+ case JavaLexer.LBRACE:
+ advance++;
+ break;
+ case JavaParser.RBRACE:
+ advance--;
+ break;
+ }
+ }
+ advance = Math.max(0, advance);
+ return advance * getTabSize();
+ } catch (Throwable e) {
+ LOG.error("Error calculating indent advance", e);
}
- }
- advance = Math.max(0, advance);
- return advance * getTabSize();
- } catch (Throwable e) {
- LOG.error("Error calculating indent advance", e);
- }
- return 0;
- }
-
- @Override
- public SymbolPairMatch getSymbolPairs() {
- return new JavaSymbolPairs();
- }
-
- @NonNull
- @Override
- public AnalyzeManager getAnalyzeManager() {
- return analyzer;
- }
-
- @Override
- public int getInterruptionLevel() {
- return 0;
- }
-
- @Override
- public void requireAutoComplete(
- @NonNull ContentReference content,
- @NonNull CharPosition position,
- @NonNull CompletionPublisher publisher,
- @NonNull Bundle extraArguments)
- throws CompletionCancelledException {
- if (!extraArguments.containsKey(IDEEditor.KEY_FILE)) {
- return;
- }
- final var file = Paths.get(extraArguments.getString(IDEEditor.KEY_FILE));
- publisher.setUpdateThreshold(0);
- publisher.addItems(
- new ArrayList<>(
- completer.complete(
- content, file, position, CommonCompletionProvider::checkJavaCompletionChar)));
- }
-
- @Override
- public int getIndentAdvance(@NonNull ContentReference content, int line, int column) {
- return getIndentAdvance(content.getLine(line).substring(0, column));
- }
-
- @Override
- public boolean useTab() {
- return false;
- }
-
- @Override
- public CharSequence format(CharSequence content) {
- return content;
- }
-
- @Override
- public NewlineHandler[] getNewlineHandlers() {
- return newlineHandlers;
- }
-
- @Override
- public void destroy() {
- analyzer = null;
- completer = null;
- }
-
- class BraceHandler implements NewlineHandler {
-
- @Override
- public boolean matchesRequirement(String beforeText, String afterText) {
- return beforeText.endsWith("{") && afterText.startsWith("}");
+ return 0;
}
@Override
- public NewlineHandleResult handleNewline(String beforeText, String afterText, int tabSize) {
- int count = TextUtils.countLeadingSpaceCount(beforeText, tabSize);
- int advanceBefore = getIndentAdvance(beforeText);
- int advanceAfter = getIndentAdvance(afterText);
- String text;
- StringBuilder sb =
- new StringBuilder("\n")
- .append(TextUtils.createIndent(count + advanceBefore, tabSize, useTab()))
- .append('\n')
- .append(text = TextUtils.createIndent(count + advanceAfter, tabSize, useTab()));
- int shiftLeft = text.length() + 1;
- return new NewlineHandleResult(sb, shiftLeft);
+ public SymbolPairMatch getSymbolPairs() {
+ return new JavaSymbolPairs();
}
- }
- private static class JavaSymbolPairs extends SymbolPairMatch {
- public JavaSymbolPairs() {
- super.putPair('{', new Replacement("{}", 1));
- super.putPair('(', new Replacement("()", 1));
- super.putPair('[', new Replacement("[]", 1));
- super.putPair('"', new Replacement("\"\"", 1));
- super.putPair('\'', new Replacement("''", 1));
- super.putPair('<', new Replacement("<>", 1));
+ @NonNull
+ @Override
+ public AnalyzeManager getAnalyzeManager() {
+ return analyzer;
+ }
+
+ @Override
+ public int getInterruptionLevel() {
+ return 0;
+ }
+
+ @Override
+ public void requireAutoComplete(
+ @NonNull ContentReference content,
+ @NonNull CharPosition position,
+ @NonNull CompletionPublisher publisher,
+ @NonNull Bundle extraArguments)
+ throws CompletionCancelledException {
+ if (!extraArguments.containsKey(IDEEditor.KEY_FILE)) {
+ return;
+ }
+ final var file = Paths.get(extraArguments.getString(IDEEditor.KEY_FILE));
+ publisher.setUpdateThreshold(0);
+ publisher.addItems(
+ new ArrayList<>(
+ completer.complete(
+ content,
+ file,
+ position,
+ CommonCompletionProvider::checkJavaCompletionChar)));
+ }
+
+ @Override
+ public int getIndentAdvance(@NonNull ContentReference content, int line, int column) {
+ return getIndentAdvance(content.getLine(line).substring(0, column));
+ }
+
+ @Override
+ public boolean useTab() {
+ return false;
+ }
+
+ @Override
+ public CharSequence format(CharSequence content) {
+ return content;
+ }
+
+ @Override
+ public NewlineHandler[] getNewlineHandlers() {
+ return newlineHandlers;
+ }
+
+ @Override
+ public void destroy() {
+ analyzer = null;
+ completer = null;
+ }
+
+ class BraceHandler implements NewlineHandler {
+
+ @Override
+ public boolean matchesRequirement(String beforeText, String afterText) {
+ return beforeText.endsWith("{") && afterText.startsWith("}");
+ }
+
+ @Override
+ public NewlineHandleResult handleNewline(String beforeText, String afterText, int tabSize) {
+ int count = TextUtils.countLeadingSpaceCount(beforeText, tabSize);
+ int advanceBefore = getIndentAdvance(beforeText);
+ int advanceAfter = getIndentAdvance(afterText);
+ String text;
+ StringBuilder sb =
+ new StringBuilder("\n")
+ .append(
+ TextUtils.createIndent(
+ count + advanceBefore, tabSize, useTab()))
+ .append('\n')
+ .append(
+ text =
+ TextUtils.createIndent(
+ count + advanceAfter, tabSize, useTab()));
+ int shiftLeft = text.length() + 1;
+ return new NewlineHandleResult(sb, shiftLeft);
+ }
+ }
+
+ private static class JavaSymbolPairs extends SymbolPairMatch {
+ public JavaSymbolPairs() {
+ super.putPair('{', new Replacement("{}", 1));
+ super.putPair('(', new Replacement("()", 1));
+ super.putPair('[', new Replacement("[]", 1));
+ super.putPair('"', new Replacement("\"\"", 1));
+ super.putPair('\'', new Replacement("''", 1));
+ super.putPair('<', new Replacement("<>", 1));
+ }
}
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/logs/LogLanguageImpl.java b/app/src/main/java/com/itsaky/androidide/language/logs/LogLanguageImpl.java
index 9cf5d2c1a..3067cc108 100755
--- a/app/src/main/java/com/itsaky/androidide/language/logs/LogLanguageImpl.java
+++ b/app/src/main/java/com/itsaky/androidide/language/logs/LogLanguageImpl.java
@@ -18,10 +18,13 @@
package com.itsaky.androidide.language.logs;
import android.os.Bundle;
+
import androidx.annotation.NonNull;
+
import com.itsaky.androidide.language.IDELanguage;
import com.itsaky.androidide.models.LogLine;
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
+
import io.github.rosemoe.sora.lang.analysis.AnalyzeManager;
import io.github.rosemoe.sora.lang.analysis.SimpleAnalyzeManager;
import io.github.rosemoe.sora.lang.completion.CompletionCancelledException;
@@ -32,90 +35,91 @@ import io.github.rosemoe.sora.lang.styling.Styles;
import io.github.rosemoe.sora.text.CharPosition;
import io.github.rosemoe.sora.text.ContentReference;
import io.github.rosemoe.sora.widget.SymbolPairMatch;
+
import java.util.ArrayList;
import java.util.List;
public class LogLanguageImpl extends IDELanguage {
- private static final LogAnalyzer analyzer = new LogAnalyzer();
+ private static final LogAnalyzer analyzer = new LogAnalyzer();
- public void addLine(LogLine line) {
- analyzer.addLine(line);
- }
+ public void addLine(LogLine line) {
+ analyzer.addLine(line);
+ }
- @NonNull
- @Override
- public AnalyzeManager getAnalyzeManager() {
- return analyzer;
- }
-
- @Override
- public int getInterruptionLevel() {
- return 0;
- }
-
- @Override
- public void requireAutoComplete(
- @NonNull ContentReference content,
- @NonNull CharPosition position,
- @NonNull CompletionPublisher publisher,
- @NonNull Bundle extraArguments)
- throws CompletionCancelledException {}
-
- @Override
- public int getIndentAdvance(@NonNull ContentReference content, int line, int column) {
- return 0;
- }
-
- @Override
- public boolean useTab() {
- return false;
- }
-
- @Override
- public CharSequence format(CharSequence text) {
- return text;
- }
-
- @Override
- public SymbolPairMatch getSymbolPairs() {
- return new SymbolPairMatch.DefaultSymbolPairs();
- }
-
- @Override
- public NewlineHandler[] getNewlineHandlers() {
- return new NewlineHandler[0];
- }
-
- @Override
- public void destroy() {}
-
- private static class LogAnalyzer extends SimpleAnalyzeManager {
-
- private static final List lines = new ArrayList<>();
-
- public LogAnalyzer addLine(LogLine line) {
- if (line != null) lines.add(line);
-
- return this;
+ @NonNull
+ @Override
+ public AnalyzeManager getAnalyzeManager() {
+ return analyzer;
}
@Override
- protected Styles analyze(StringBuilder text, Delegate delegate) {
- final var styles = new Styles();
- final var colors = new MappedSpans.Builder();
- int lastLine = 0;
- for (int i = 0; i < lines.size() && !delegate.isCancelled(); i++) {
- if (i == 0) colors.addNormalIfNull();
- LogLine line = lines.get(i);
- colors.addIfNeeded(i, 0, SchemeAndroidIDE.get(line.priority));
- lastLine = i;
- }
- colors.determine(lastLine);
-
- styles.spans = colors.build();
-
- return styles;
+ public int getInterruptionLevel() {
+ return 0;
+ }
+
+ @Override
+ public void requireAutoComplete(
+ @NonNull ContentReference content,
+ @NonNull CharPosition position,
+ @NonNull CompletionPublisher publisher,
+ @NonNull Bundle extraArguments)
+ throws CompletionCancelledException {}
+
+ @Override
+ public int getIndentAdvance(@NonNull ContentReference content, int line, int column) {
+ return 0;
+ }
+
+ @Override
+ public boolean useTab() {
+ return false;
+ }
+
+ @Override
+ public CharSequence format(CharSequence text) {
+ return text;
+ }
+
+ @Override
+ public SymbolPairMatch getSymbolPairs() {
+ return new SymbolPairMatch.DefaultSymbolPairs();
+ }
+
+ @Override
+ public NewlineHandler[] getNewlineHandlers() {
+ return new NewlineHandler[0];
+ }
+
+ @Override
+ public void destroy() {}
+
+ private static class LogAnalyzer extends SimpleAnalyzeManager {
+
+ private static final List lines = new ArrayList<>();
+
+ public LogAnalyzer addLine(LogLine line) {
+ if (line != null) lines.add(line);
+
+ return this;
+ }
+
+ @Override
+ protected Styles analyze(StringBuilder text, Delegate delegate) {
+ final var styles = new Styles();
+ final var colors = new MappedSpans.Builder();
+ int lastLine = 0;
+ for (int i = 0; i < lines.size() && !delegate.isCancelled(); i++) {
+ if (i == 0) colors.addNormalIfNull();
+ LogLine line = lines.get(i);
+ colors.addIfNeeded(i, 0, SchemeAndroidIDE.get(line.priority));
+ lastLine = i;
+ }
+ colors.determine(lastLine);
+
+ styles.spans = colors.build();
+
+ return styles;
+ }
}
- }
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/xml/XMLAnalyzer.java b/app/src/main/java/com/itsaky/androidide/language/xml/XMLAnalyzer.java
index 00f21573f..2ca58de72 100755
--- a/app/src/main/java/com/itsaky/androidide/language/xml/XMLAnalyzer.java
+++ b/app/src/main/java/com/itsaky/androidide/language/xml/XMLAnalyzer.java
@@ -21,22 +21,26 @@ import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.OPERATO
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.TEXT_SELECTED;
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forComment;
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.get;
+
import static io.github.rosemoe.sora.widget.schemes.EditorColorScheme.LITERAL;
import com.itsaky.androidide.lexers.xml.XMLLexer;
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
import com.itsaky.androidide.utils.CharSequenceReader;
import com.itsaky.androidide.utils.Logger;
+
import io.github.rosemoe.sora.lang.analysis.SimpleAnalyzeManager;
import io.github.rosemoe.sora.lang.styling.CodeBlock;
import io.github.rosemoe.sora.lang.styling.MappedSpans;
import io.github.rosemoe.sora.lang.styling.Styles;
-import java.io.IOException;
-import java.util.Stack;
+
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CodePointCharStream;
import org.antlr.v4.runtime.Token;
+import java.io.IOException;
+import java.util.Stack;
+
/**
* Simple analyzer implementation for the XML files.
*
@@ -44,109 +48,109 @@ import org.antlr.v4.runtime.Token;
*/
public class XMLAnalyzer extends SimpleAnalyzeManager {
- private static final Logger LOG = Logger.instance("XMLAnalyzer");
+ private static final Logger LOG = Logger.instance("XMLAnalyzer");
- @Override
- protected Styles analyze(StringBuilder text, Delegate