diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md index 53cb77e34..426c3f4bf 100644 --- a/.github/ISSUE_TEMPLATE/bug.md +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -1,9 +1,6 @@ --- -name: Bug -about: Report a bug in the IDE. -title: "" -labels: bug -assignees: '' +name: Bug about: Report a bug in the IDE. title: "" +labels: bug assignees: '' --- **Describe the bug** @@ -20,9 +17,10 @@ Steps to reproduce the behavior: **Details (please complete the following information):** - - Device: [e.g. realme 3 Pro] - - OS: [e.g. Android 11] - - AndroidIDE Version [e.g. v1.0.3-alpha] + +- Device: [e.g. realme 3 Pro] +- OS: [e.g. Android 11] +- AndroidIDE Version [e.g. v1.0.3-alpha] **Additional context** diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index e107cb083..d26ce3aa0 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,9 +1,6 @@ --- -name: Feature request -about: Suggest an idea for this project -title: "[FEATURE]: " -labels: feature -assignees: '' +name: Feature request about: Suggest an idea for this project title: "[FEATURE]: " +labels: feature assignees: '' --- diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index aa9a491fd..0e742cde6 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -2,124 +2,110 @@ ## Our Pledge -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. +We as members, contributors, and leaders pledge to make participation in our community a +harassment-free experience for everyone, regardless of age, body size, visible or invisible +disability, ethnicity, sex characteristics, gender identity and expression, level of experience, +education, socio-economic status, nationality, personal appearance, race, religion, or sexual +identity and orientation. -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and +healthy community. ## Our Standards -Examples of behavior that contributes to a positive environment for our -community include: +Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community +* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the + experience +* Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or - advances of any kind +* The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting +* Publishing others' private information, such as a physical or email address, without their + explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. +Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior +and will take appropriate and fair corrective action in response to any behavior that they deem +inappropriate, threatening, offensive, or harmful. -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. +Community leaders have the right and responsibility to remove, edit, or reject comments, commits, +code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and +will communicate reasons for moderation decisions when appropriate. ## Scope -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. +This Code of Conduct applies within all community spaces, and also applies when an individual is +officially representing the community in public spaces. Examples of representing our community +include using an official e-mail address, posting via an official social media account, or acting as +an appointed representative at an online or offline event. ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement either by opening -an issue or writing me an email at [itsaky01@gmail.com](mailto:itsaky01@gmail.com). -All complaints will be reviewed and investigated promptly and fairly. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community +leaders responsible for enforcement either by opening an issue or writing me an email +at [itsaky01@gmail.com](mailto:itsaky01@gmail.com). All complaints will be reviewed and investigated +promptly and fairly. -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. +All community leaders are obligated to respect the privacy and security of the reporter of any +incident. ## Enforcement Guidelines -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: +Community leaders will follow these Community Impact Guidelines in determining the consequences for +any action they deem in violation of this Code of Conduct: ### 1. Correction -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. +**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or +unwelcome in the community. -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. +**Consequence**: A private, written warning from community leaders, providing clarity around the +nature of the violation and an explanation of why the behavior was inappropriate. A public apology +may be requested. ### 2. Warning -**Community Impact**: A violation through a single incident or series -of actions. +**Community Impact**: A violation through a single incident or series of actions. -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. +**Consequence**: A warning with consequences for continued behavior. No interaction with the people +involved, including unsolicited interaction with those enforcing the Code of Conduct, for a +specified period of time. This includes avoiding interactions in community spaces as well as +external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. +**Community Impact**: A serious violation of community standards, including sustained inappropriate +behavior. -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. +**Consequence**: A temporary ban from any sort of interaction or public communication with the +community for a specified period of time. No public or private interaction with the people involved, +including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this +period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. +**Community Impact**: Demonstrating a pattern of violation of community standards, including +sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement +of classes of individuals. -**Consequence**: A permanent ban from any sort of public interaction within -the community. +**Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). +Community Impact Guidelines were inspired +by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index caf287553..3be699346 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,8 +1,12 @@ # Contributing -This document outlines how to propose a change to AndrodIDE. By contributing to this project, you agree to abide the terms specified in [CODE OF CONDUCT](./CODE_OF_CONDUCT.md). + +This document outlines how to propose a change to AndrodIDE. By contributing to this project, you +agree to abide the terms specified in [CODE OF CONDUCT](./CODE_OF_CONDUCT.md). ## Propose a change + To contribute to this project, + - Fork the repo. - Clone the forked repo to your local machine. - Open the project in Android Studio. @@ -10,19 +14,29 @@ To contribute to this project, - Create a pull request. ## Note -`Java 11` is required to build the project. -Building AndroidIDE in AndroidIDE itself is also possible. But that might require some modifications. +`Java 11` is required to build the project. -If you face `AAPT2` issue while building AndroidIDE in the IDE itself, specify the `android.aapt2FromMavenOverride` property in `gradle.properties` with location of `aapt2` binary from Android SDK. However, this is an **experimental** property. +Building AndroidIDE in AndroidIDE itself is also possible. But that might require some +modifications. + +If you face `AAPT2` issue while building AndroidIDE in the IDE itself, specify +the `android.aapt2FromMavenOverride` property in `gradle.properties` with location of `aapt2` binary +from Android SDK. However, this is an **experimental** property. ## Source code format -The source code must have `2-space` indentation (soft tab). -Java source code is formatted using `google-java-format` with `GOOGLE` style formatting. -You can execute `./gradlew formatJavaSources` in terminal to format all Java source files using Google Java Format. + +The source code must have `2-space` indentation (soft tab). Java source code is formatted +using `google-java-format` with `GOOGLE` style formatting. You can +execute `./gradlew formatJavaSources` in terminal to format all Java source files using Google Java +Format. **This will commit the changes as well**. ## Issues & Pull requests -Use the issue/PR templates when possible. Provide a proper title and a clear description for the issue/PR. For bug reports, provide a step-by-step procedure to reproduce the issue. Always put crash logs, build outputs, etc in `code blocks`. -If you want to discuss anything about the project, you can discuss it in the [Telegram group](https://t.me/androidide_discussions). \ No newline at end of file +Use the issue/PR templates when possible. Provide a proper title and a clear description for the +issue/PR. For bug reports, provide a step-by-step procedure to reproduce the issue. Always put crash +logs, build outputs, etc in `code blocks`. + +If you want to discuss anything about the project, you can discuss it in +the [Telegram group](https://t.me/androidide_discussions). \ No newline at end of file diff --git a/README.md b/README.md index c7e87554a..260b73968 100755 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@


## Features + - [x] Gradle support. - [x] `JDK 11` and `JDK 17` available for use. - [x] Terminal with necessary packages. @@ -17,49 +18,65 @@ - [x] SDK Manager (Available via terminal). - [x] API information for classes and their members (since, removed, deprecated). - [ ] Language servers - - [x] Java - - [x] XML (*) - - [ ] Kotlin + - [x] Java + - [x] XML (*) + - [ ] Kotlin - [ ] UI Designer - - [x] Layout inflater - - [x] Resolve resource references - - [x] Auto-complete resource values when user edits attributes using the attribute editor - - [x] Drag & Drop (*) - - [x] Visual attribute editor (*) - - [x] Android Widgets (*) + - [x] Layout inflater + - [x] Resolve resource references + - [x] Auto-complete resource values when user edits attributes using the attribute editor + - [x] Drag & Drop (*) + - [x] Visual attribute editor (*) + - [x] Android Widgets (*) - [ ] String Translator - [ ] Asset Studio (Drawable & Icon Maker) - [x] Git (*) Items marked with `(*)` are partially implemented. -`XML` language server supports only code completion at the moment. Git is currently available only in the terminal i.e. no GUI interface for `git`. +`XML` language server supports only code completion at the moment. Git is currently available only +in the terminal i.e. no GUI interface for `git`. ## Installation -- Download the AndroidIDE APK from [releases](https://github.com/itsaky/AndroidIDE/releases). You can also download APKs from [GitHub actions](https://github.com/itsaky/AndroidIDE/actions). -- Follow the instructions [here](https://github.com/itsaky/androidide-build-tools#installing-in-androidide) to install the build tools. + +- Download the AndroidIDE APK from [releases](https://github.com/itsaky/AndroidIDE/releases). You + can also download APKs from [GitHub actions](https://github.com/itsaky/AndroidIDE/actions). +- Follow the + instructions [here](https://github.com/itsaky/androidide-build-tools#installing-in-androidide) to + install the build tools. ## Limitations -- SDK Manager is already included in Android SDK and is accessible in AndroidIDE via its Terminal. But, you cannot use it to install some tools (like NDK) because those tools are not built for Android. + +- SDK Manager is already included in Android SDK and is accessible in AndroidIDE via its Terminal. + But, you cannot use it to install some tools (like NDK) because those tools are not built for + Android. - No official NDK support because we haven't built the NDK for Android. -The app is still being developed actively. It's in beta stage and may not be stable. if you have any issues using the app, please let us know. +The app is still being developed actively. It's in beta stage and may not be stable. if you have any +issues using the app, please let us know. ## Contributing + See the [contributing guide](./CONTRIBUTING.md). ## Thanks to -- [Rosemoe](https://github.com/Rosemoe) for the awesome [CodeEditor](https://github.com/Rosemoe/sora-editor) + +- [Rosemoe](https://github.com/Rosemoe) for the + awesome [CodeEditor](https://github.com/Rosemoe/sora-editor) - [Termux](https://github.com/termux) for [Terminal Emulator](https://github.com/termux/termux-app) -- [Bogdan Melnychuk](https://github.com/bmelnychuk) for [AndroidTreeView](https://github.com/bmelnychuk/AndroidTreeView) -- [George Fraser](https://github.com/georgewfraser) for the [Java Language Server](https://github.com/georgewfraser/java-language-server) +- [Bogdan Melnychuk](https://github.com/bmelnychuk) + for [AndroidTreeView](https://github.com/bmelnychuk/AndroidTreeView) +- [George Fraser](https://github.com/georgewfraser) for + the [Java Language Server](https://github.com/georgewfraser/java-language-server) And all the developers who have contributed to this project. ## Contact Us + - [Website](https://androidide.com) - [Telegram](https://t.me/androidide_discussions) ## License + ``` AndroidIDE is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -75,4 +92,5 @@ You should have received a copy of the GNU General Public License along with AndroidIDE. If not, see . ``` -Any violations to the license can be reported either by opening an issue or writing a mail to us directly. +Any violations to the license can be reported either by opening an issue or writing a mail to us +directly. diff --git a/actions/build.gradle b/actions/build.gradle index c1f471947..ab4d2288a 100644 --- a/actions/build.gradle +++ b/actions/build.gradle @@ -4,14 +4,14 @@ apply plugin: 'kotlin-android' android { compileSdkVersion rootProject.ext.compileSdk buildToolsVersion rootProject.ext.buildTools - + defaultConfig { minSdkVersion rootProject.ext.minSdk targetSdkVersion rootProject.ext.targetSdk } - + buildFeatures.viewBinding true - + compileOptions { sourceCompatibility rootProject.ext.javaSourceVersion targetCompatibility rootProject.ext.javaTargetVersion @@ -20,7 +20,7 @@ android { kotlinOptions { jvmTarget = "11" } - + buildTypes { release { minifyEnabled true @@ -29,14 +29,14 @@ android { } dependencies { - implementation project(':common') + implementation project (':common') implementation common.editor implementation common.utilcode - + implementation androidx.ktx implementation common.kotlin } repositories { - mavenCentral() + mavenCentral () } \ No newline at end of file diff --git a/actions/src/main/java/com/itsaky/androidide/actions/ActionItem.kt b/actions/src/main/java/com/itsaky/androidide/actions/ActionItem.kt index 4351acc07..16c0c5a60 100644 --- a/actions/src/main/java/com/itsaky/androidide/actions/ActionItem.kt +++ b/actions/src/main/java/com/itsaky/androidide/actions/ActionItem.kt @@ -26,88 +26,88 @@ import android.graphics.drawable.Drawable */ interface ActionItem { - val id: String - var label: String - var visible: Boolean - var enabled: Boolean - var icon: Drawable? - var requiresUIThread: Boolean - var location: Location + val id: String + var label: String + var visible: Boolean + var enabled: Boolean + var icon: Drawable? + var requiresUIThread: Boolean + var location: Location - /** - * Prepare the action. Subclasses can modify the visual properties of this action here. - * @param data The data containing various information about the event. - */ - fun prepare(data: ActionData) + /** + * Prepare the action. Subclasses can modify the visual properties of this action here. + * @param data The data containing various information about the event. + */ + fun prepare(data: ActionData) - /** - * Execute the action. The action executed in a background thread by default. - * - * @param data The data containing various information about the event. - * @return `true` if this action was executed successfully, `false` otherwise. - */ - fun execAction(data: ActionData): Any + /** + * Execute the action. The action executed in a background thread by default. + * + * @param data The data containing various information about the event. + * @return `true` if this action was executed successfully, `false` otherwise. + */ + fun execAction(data: ActionData): Any - /** - * Called just after the [execAction] method executes **successfully** (i.e. returns `true`). - * Subclasses are free to do UI related work here as this method is called on UI thread. - * - * @param data The data containing various information about the event. - */ - fun postExec(data: ActionData, result: Any) = Unit + /** + * Called just after the [execAction] method executes **successfully** (i.e. returns `true`). + * Subclasses are free to do UI related work here as this method is called on UI thread. + * + * @param data The data containing various information about the event. + */ + fun postExec(data: ActionData, result: Any) = Unit - /** - * Return the show as action flags for the menu item. - * - * @return The show as action flags. - */ - fun getShowAsActionFlags(data: ActionData): Int = -1 + /** + * Return the show as action flags for the menu item. + * + * @return The show as action flags. + */ + fun getShowAsActionFlags(data: ActionData): Int = -1 - /** - * Checks if the given [ActionData] has instances of the given [types]. - * - * @param data The data to check. - * @param types The type of objects to look for. - * @return `true` if the [data] has the given [types], `false` otherwise. - */ - fun hasRequiredData(data: ActionData, vararg types: Class<*>): Boolean { - for (type in types) { - data.get(type) ?: return false - } - - return true + /** + * Checks if the given [ActionData] has instances of the given [types]. + * + * @param data The data to check. + * @param types The type of objects to look for. + * @return `true` if the [data] has the given [types], `false` otherwise. + */ + fun hasRequiredData(data: ActionData, vararg types: Class<*>): Boolean { + for (type in types) { + data.get(type) ?: return false } - /** Marks this action item as invisible. */ - fun markInvisible() { - visible = false - enabled = false + return true + } + + /** Marks this action item as invisible. */ + fun markInvisible() { + visible = false + enabled = false + } + + /** Location where an action item will be shown. */ + enum class Location(val id: String) { + + /** Location marker for action items shown in editor activity's toolbar. */ + EDITOR_TOOLBAR("editor.activity.toolbar"), + + /** Location marker for action items shown in editor's text action menu. */ + EDITOR_TEXT_ACTIONS("editor.textActions"), + + /** + * Location marker for action items shown in 'Code actions' submenu in editor's text action + * menu. + */ + EDITOR_CODE_ACTIONS("editor.codeActions"), + + /** Location marker for action items show in 'Find & Replace' action mode menu in editor. */ + EDITOR_SEARCH_ACTION_MODE("editor.searchActionMode"); + + override fun toString(): String { + return id } - /** Location where an action item will be shown. */ - enum class Location(val id: String) { - - /** Location marker for action items shown in editor activity's toolbar. */ - EDITOR_TOOLBAR("editor.activity.toolbar"), - - /** Location marker for action items shown in editor's text action menu. */ - EDITOR_TEXT_ACTIONS("editor.textActions"), - - /** - * Location marker for action items shown in 'Code actions' submenu in editor's text action - * menu. - */ - EDITOR_CODE_ACTIONS("editor.codeActions"), - - /** Location marker for action items show in 'Find & Replace' action mode menu in editor. */ - EDITOR_SEARCH_ACTION_MODE("editor.searchActionMode"); - - override fun toString(): String { - return id - } - - fun forId(id: String): Location { - return values().first { it.id == id } - } + fun forId(id: String): Location { + return values().first { it.id == id } } + } } diff --git a/actions/src/main/java/com/itsaky/androidide/actions/ActionMenu.kt b/actions/src/main/java/com/itsaky/androidide/actions/ActionMenu.kt index 9d8e092df..b758775de 100644 --- a/actions/src/main/java/com/itsaky/androidide/actions/ActionMenu.kt +++ b/actions/src/main/java/com/itsaky/androidide/actions/ActionMenu.kt @@ -23,14 +23,14 @@ package com.itsaky.androidide.actions */ interface ActionMenu : ActionItem { - val children: MutableSet + val children: MutableSet - fun addAction(action: ActionItem) = children.add(action) + fun addAction(action: ActionItem) = children.add(action) - fun removeAction(action: ActionItem) = children.remove(action) + fun removeAction(action: ActionItem) = children.remove(action) - /** Action menus are not supposed to perform any action */ - override fun execAction(data: ActionData): Boolean { - return false - } + /** Action menus are not supposed to perform any action */ + override fun execAction(data: ActionData): Boolean { + return false + } } diff --git a/actions/src/main/java/com/itsaky/androidide/actions/ActionsRegistry.kt b/actions/src/main/java/com/itsaky/androidide/actions/ActionsRegistry.kt index ce6aaa938..7f020ef8d 100644 --- a/actions/src/main/java/com/itsaky/androidide/actions/ActionsRegistry.kt +++ b/actions/src/main/java/com/itsaky/androidide/actions/ActionsRegistry.kt @@ -22,87 +22,87 @@ import com.itsaky.androidide.actions.internal.DefaultActionsRegistry /** @author Akash Yadav */ abstract class ActionsRegistry { - companion object { + companion object { - private var instance: ActionsRegistry? = null + private var instance: ActionsRegistry? = null - @JvmStatic - fun getInstance(): ActionsRegistry { - if (instance == null) { - instance = DefaultActionsRegistry() - } + @JvmStatic + fun getInstance(): ActionsRegistry { + if (instance == null) { + instance = DefaultActionsRegistry() + } - return instance as ActionsRegistry - } + return instance as ActionsRegistry } + } + + /** + * Register the given action. + * + * @param action The action to register. Must not be `null`. + * @return `true` if the action was registered successfully. `false` otherwise. + */ + abstract fun registerAction(action: ActionItem): Boolean + + /** + * Unregister the given action. + * + * @param action The action to unregister. Must not be `null`. + * @return `true` if the action was unregistered successfully. `false` otherwise. + */ + abstract fun unregisterAction(action: ActionItem): Boolean + + /** + * Unregister the action with the given ID. + * + * @param id The ID of action to unregister. Must not be `null`. + * @return `true` if the action was unregistered successfully. `false` otherwise. + */ + abstract fun unregisterAction(id: String): Boolean + + /** + * Find the action with the given ID. + * + * @param id The ID of the action to find. + * @return The found action or `null`. + */ + abstract fun findAction(location: ActionItem.Location, id: String): ActionItem? + + /** + * Fill the given menu with the registered actions. + * + * Subclasses must first call [ActionItem.prepare] to update the action. + */ + abstract fun fillMenu(data: ActionData, location: ActionItem.Location, menu: Menu) + + /** Get all the registered actions at the given location. */ + abstract fun getActions(location: ActionItem.Location): Map + /** Clear all the registered actions. */ + abstract fun clearActions(location: ActionItem.Location) + + /** + * Register a listener that will be called when any action is executed. The callbacks are called + * irrespective of the action's execution result. + * + * @param listener The listener to register. + */ + abstract fun registerActionExecListener(listener: ActionExecListener) + + /** + * Unregister the given listener. + * + * @param listener The listener to unregister. + */ + abstract fun unregisterActionExecListener(listener: ActionExecListener) + + /** Listener for listening to callbacks for action execution. */ + interface ActionExecListener { /** - * Register the given action. - * - * @param action The action to register. Must not be `null`. - * @return `true` if the action was registered successfully. `false` otherwise. + * The given action was executed. + * @param action The action that was executed. + * @param result The result of the action execution. */ - abstract fun registerAction(action: ActionItem): Boolean - - /** - * Unregister the given action. - * - * @param action The action to unregister. Must not be `null`. - * @return `true` if the action was unregistered successfully. `false` otherwise. - */ - abstract fun unregisterAction(action: ActionItem): Boolean - - /** - * Unregister the action with the given ID. - * - * @param id The ID of action to unregister. Must not be `null`. - * @return `true` if the action was unregistered successfully. `false` otherwise. - */ - abstract fun unregisterAction(id: String): Boolean - - /** - * Find the action with the given ID. - * - * @param id The ID of the action to find. - * @return The found action or `null`. - */ - abstract fun findAction(location: ActionItem.Location, id: String): ActionItem? - - /** - * Fill the given menu with the registered actions. - * - * Subclasses must first call [ActionItem.prepare] to update the action. - */ - abstract fun fillMenu(data: ActionData, location: ActionItem.Location, menu: Menu) - - /** Get all the registered actions at the given location. */ - abstract fun getActions(location: ActionItem.Location): Map - /** Clear all the registered actions. */ - abstract fun clearActions(location: ActionItem.Location) - - /** - * Register a listener that will be called when any action is executed. The callbacks are called - * irrespective of the action's execution result. - * - * @param listener The listener to register. - */ - abstract fun registerActionExecListener(listener: ActionExecListener) - - /** - * Unregister the given listener. - * - * @param listener The listener to unregister. - */ - abstract fun unregisterActionExecListener(listener: ActionExecListener) - - /** Listener for listening to callbacks for action execution. */ - interface ActionExecListener { - - /** - * The given action was executed. - * @param action The action that was executed. - * @param result The result of the action execution. - */ - fun onExec(action: ActionItem, result: Any) - } + fun onExec(action: ActionItem, result: Any) + } } diff --git a/actions/src/main/java/com/itsaky/androidide/actions/internal/DefaultActionsRegistry.kt b/actions/src/main/java/com/itsaky/androidide/actions/internal/DefaultActionsRegistry.kt index b93158919..07b668ac5 100644 --- a/actions/src/main/java/com/itsaky/androidide/actions/internal/DefaultActionsRegistry.kt +++ b/actions/src/main/java/com/itsaky/androidide/actions/internal/DefaultActionsRegistry.kt @@ -33,142 +33,142 @@ import java.util.concurrent.* */ class DefaultActionsRegistry : ActionsRegistry() { - private val log = ILogger.newInstance("DefaultActionsRegistry") - private val actions: MutableMap> = mutableMapOf() - private val listeners = mutableSetOf() + private val log = ILogger.newInstance("DefaultActionsRegistry") + private val actions: MutableMap> = mutableMapOf() + private val listeners = mutableSetOf() - override fun getActions(location: ActionItem.Location): MutableMap { - if (actions[location.id] == null) { - actions[location.id] = mutableMapOf() - } - - return actions[location.id]!! + override fun getActions(location: ActionItem.Location): MutableMap { + if (actions[location.id] == null) { + actions[location.id] = mutableMapOf() } - override fun registerAction(action: ActionItem): Boolean { - val actionsAtLocation = getActions(action.location) - actionsAtLocation[action.id] = action + return actions[location.id]!! + } + + override fun registerAction(action: ActionItem): Boolean { + val actionsAtLocation = getActions(action.location) + actionsAtLocation[action.id] = action + return true + } + + override fun unregisterAction(action: ActionItem): Boolean { + val actions = getActions(action.location) + val older = actions.remove(action.id) + if (older != null) { + return true + } + + return false + } + + override fun unregisterAction(id: String): Boolean { + for (locations in this.actions.values) { + val older = locations.remove(id) + if (older != null) { return true + } } - override fun unregisterAction(action: ActionItem): Boolean { - val actions = getActions(action.location) - val older = actions.remove(action.id) - if (older != null) { - return true + return false + } + + override fun findAction(location: ActionItem.Location, id: String): ActionItem? = + getActions(location)[id] + + override fun clearActions(location: ActionItem.Location) = getActions(location).clear() + + override fun registerActionExecListener(listener: ActionExecListener) { + listeners.add(listener) + } + + override fun unregisterActionExecListener(listener: ActionExecListener) { + listeners.remove(listener) + } + + override fun fillMenu(data: ActionData, location: ActionItem.Location, menu: Menu) { + val actions = getActions(location) + + for (action in actions.values) { + action.prepare(data) + + if (!action.visible) { + continue + } + addActionToMenu(menu, action, data) + } + } + + private fun addActionToMenu(menu: Menu, action: ActionItem, data: ActionData) { + val item: MenuItem = + if (action is ActionMenu) { + val sub = menu.addSubMenu(action.label) + + for (subItem in action.children) { + subItem.prepare(data) + if (subItem.visible) { + addActionToMenu(sub, subItem, data) + } } - return false + sub.item + } else { + menu.add(action.label) + } + + item.isEnabled = action.enabled + item.icon = action.icon + + if (item.icon != null) { + item.icon.alpha = if (action.enabled) 255 else 76 + item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) + } else { + item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) } - override fun unregisterAction(id: String): Boolean { - for (locations in this.actions.values) { - val older = locations.remove(id) - if (older != null) { - return true - } - } - - return false + if (action.getShowAsActionFlags(data) != -1) { + item.setShowAsAction(action.getShowAsActionFlags(data)) } - override fun findAction(location: ActionItem.Location, id: String): ActionItem? = - getActions(location)[id] - - override fun clearActions(location: ActionItem.Location) = getActions(location).clear() - - override fun registerActionExecListener(listener: ActionExecListener) { - listeners.add(listener) - } - - override fun unregisterActionExecListener(listener: ActionExecListener) { - listeners.remove(listener) - } - - override fun fillMenu(data: ActionData, location: ActionItem.Location, menu: Menu) { - val actions = getActions(location) - - for (action in actions.values) { - action.prepare(data) - - if (!action.visible) { - continue - } - addActionToMenu(menu, action, data) - } - } - - private fun addActionToMenu(menu: Menu, action: ActionItem, data: ActionData) { - val item: MenuItem = - if (action is ActionMenu) { - val sub = menu.addSubMenu(action.label) - - for (subItem in action.children) { - subItem.prepare(data) - if (subItem.visible) { - addActionToMenu(sub, subItem, data) - } - } - - sub.item - } else { - menu.add(action.label) - } - - item.isEnabled = action.enabled - item.icon = action.icon - - if (item.icon != null) { - item.icon.alpha = if (action.enabled) 255 else 76 - item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) + if (action !is ActionMenu) { + item.setOnMenuItemClickListener { + if (action.requiresUIThread) { + ThreadUtils.runOnUiThread { + val result = action.execAction(data) + action.postExec(data, result) + notifyActionExec(action, result) + } } else { - item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) + execInBackground(action, data, it) } - if (action.getShowAsActionFlags(data) != -1) { - item.setShowAsAction(action.getShowAsActionFlags(data)) - } - - if (action !is ActionMenu) { - item.setOnMenuItemClickListener { - if (action.requiresUIThread) { - ThreadUtils.runOnUiThread { - val result = action.execAction(data) - action.postExec(data, result) - notifyActionExec(action, result) - } - } else { - execInBackground(action, data, it) - } - - true - } - } + true + } } + } - private fun execInBackground(action: ActionItem, data: ActionData, it: MenuItem) { - val start = System.currentTimeMillis() - CompletableFuture.supplyAsync { action.execAction(data) } - .whenComplete { result, error -> - if (result == null || (result is Boolean && !result) || error != null) { - log.error( - "An error occurred when performing action '${it.title}'. Action failed in ${System.currentTimeMillis() - start}ms", - error) - } else { - log.info( - "Action '${it.title}' completed in ${System.currentTimeMillis() - start}ms") - } - - ThreadUtils.runOnUiThread { - action.postExec(data, result ?: false) - notifyActionExec(action, result ?: false) - } - } - } - - private fun notifyActionExec(action: ActionItem, result: Any) { - for (listener in listeners) { - listener.onExec(action, result) + private fun execInBackground(action: ActionItem, data: ActionData, it: MenuItem) { + val start = System.currentTimeMillis() + CompletableFuture.supplyAsync { action.execAction(data) } + .whenComplete { result, error -> + if (result == null || (result is Boolean && !result) || error != null) { + log.error( + "An error occurred when performing action '${it.title}'. Action failed in ${System.currentTimeMillis() - start}ms", + error + ) + } else { + log.info("Action '${it.title}' completed in ${System.currentTimeMillis() - start}ms") } + + ThreadUtils.runOnUiThread { + action.postExec(data, result ?: false) + notifyActionExec(action, result ?: false) + } + } + } + + private fun notifyActionExec(action: ActionItem, result: Any) { + for (listener in listeners) { + listener.onExec(action, result) } + } } diff --git a/app/src/main/assets/templates/xml/drawable.xml b/app/src/main/assets/templates/xml/drawable.xml index 684fed7fc..dc52b23ed 100644 --- a/app/src/main/assets/templates/xml/drawable.xml +++ b/app/src/main/assets/templates/xml/drawable.xml @@ -1,8 +1,5 @@ - diff --git a/app/src/main/assets/templates/xml/layout.xml b/app/src/main/assets/templates/xml/layout.xml index afb27a645..b2bfb7242 100644 --- a/app/src/main/assets/templates/xml/layout.xml +++ b/app/src/main/assets/templates/xml/layout.xml @@ -1,8 +1,6 @@ - @@ -19,7 +17,7 @@ android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@color/colorPrimary" - app:popupOverlay="@style/ThemeOverlay.MaterialComponents.Light"/> + app:popupOverlay="@style/ThemeOverlay.MaterialComponents.Light" /> @@ -28,10 +26,10 @@ android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> - + - - + + \ No newline at end of file diff --git a/app/src/main/assets/templates/xml/menu.xml b/app/src/main/assets/templates/xml/menu.xml index 121fbf1c6..de612fead 100644 --- a/app/src/main/assets/templates/xml/menu.xml +++ b/app/src/main/assets/templates/xml/menu.xml @@ -1,11 +1,8 @@ - + + android:name="Menu Item" /> \ No newline at end of file diff --git a/app/src/main/java/com/itsaky/androidide/AboutActivity.java b/app/src/main/java/com/itsaky/androidide/AboutActivity.java index 484e4ca1d..5980e08b7 100755 --- a/app/src/main/java/com/itsaky/androidide/AboutActivity.java +++ b/app/src/main/java/com/itsaky/androidide/AboutActivity.java @@ -38,12 +38,6 @@ public class AboutActivity extends StudioActivity { public static final String JDK_SOURCE = "https://github.com/itsaky/OpenJDK-Android"; private ActivityAboutBinding binding; - @Override - protected View bindLayout() { - binding = ActivityAboutBinding.inflate(getLayoutInflater()); - return binding.getRoot(); - } - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -64,6 +58,12 @@ public class AboutActivity extends StudioActivity { items.website.setOnClickListener(v -> getApp().openWebsite()); } + @Override + protected View bindLayout() { + binding = ActivityAboutBinding.inflate(getLayoutInflater()); + return binding.getRoot(); + } + private void setupTranslations() { final var translations = createTranslationsList(); final var sb = new StringBuilder(); diff --git a/app/src/main/java/com/itsaky/androidide/CrashHandlerActivity.java b/app/src/main/java/com/itsaky/androidide/CrashHandlerActivity.java index ed9d3e8dd..00dde1fc3 100644 --- a/app/src/main/java/com/itsaky/androidide/CrashHandlerActivity.java +++ b/app/src/main/java/com/itsaky/androidide/CrashHandlerActivity.java @@ -26,16 +26,9 @@ import com.itsaky.androidide.fragments.CrashReportFragment; public class CrashHandlerActivity extends StudioActivity { - private ActivityCrashHandlerBinding binding; - 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(); - } + private ActivityCrashHandlerBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { @@ -57,6 +50,12 @@ public class CrashHandlerActivity extends StudioActivity { .commit(); } + @Override + protected View bindLayout() { + binding = ActivityCrashHandlerBinding.inflate(getLayoutInflater()); + return binding.getRoot(); + } + @Override protected void onDestroy() { super.onDestroy(); diff --git a/app/src/main/java/com/itsaky/androidide/DesignerActivity.java b/app/src/main/java/com/itsaky/androidide/DesignerActivity.java index 3be897079..8564c4160 100755 --- a/app/src/main/java/com/itsaky/androidide/DesignerActivity.java +++ b/app/src/main/java/com/itsaky/androidide/DesignerActivity.java @@ -189,6 +189,12 @@ public class DesignerActivity extends StudioActivity .show(); } + private void finishWithError() { + final var intent = new Intent(); + setResult(-123, intent); + finish(); + } + private void finishWithResult(String result) { final var intent = new Intent(); intent.putExtra(KEY_GENERATED_CODE, result); @@ -196,12 +202,6 @@ public class DesignerActivity extends StudioActivity finish(); } - private void finishWithError() { - final var intent = new Intent(); - setResult(-123, intent); - finish(); - } - @Override public void onDragStarted(View view) { mBinding.getRoot().closeDrawer(GravityCompat.START); diff --git a/app/src/main/java/com/itsaky/androidide/EditorActivity.java b/app/src/main/java/com/itsaky/androidide/EditorActivity.java index 286beafa5..62c3efe80 100755 --- a/app/src/main/java/com/itsaky/androidide/EditorActivity.java +++ b/app/src/main/java/com/itsaky/androidide/EditorActivity.java @@ -429,12 +429,6 @@ public class EditorActivity extends StudioActivity return openFile(file, null); } - public void hideViewOptions() { - if (mEditorBottomSheet.getState() != BottomSheetBehavior.STATE_COLLAPSED) { - mEditorBottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED); - } - } - public CodeEditorView openFile(File file, com.itsaky.lsp.models.Range selection) { if (selection == null) { selection = com.itsaky.lsp.models.Range.NONE; @@ -536,6 +530,12 @@ public class EditorActivity extends StudioActivity return mFileOptionsFragment; } + public void hideViewOptions() { + if (mEditorBottomSheet.getState() != BottomSheetBehavior.STATE_COLLAPSED) { + mEditorBottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED); + } + } + public ActivityEditorBinding getBinding() { return mBinding; } @@ -790,17 +790,6 @@ public class EditorActivity extends StudioActivity return saveAll(notify, false); } - @NonNull - private CompletableFuture> getResourceDirPaths() { - return getResourceDirs() - .thenApply( - files -> - files.stream() - .filter(Objects::nonNull) - .map(File::getAbsolutePath) - .collect(Collectors.toCollection(ArrayList::new))); - } - public boolean saveAll(boolean notify, boolean canProcessResources) { SaveResult result = saveAllResult(); @@ -819,10 +808,6 @@ public class EditorActivity extends StudioActivity return result.gradleSaved; } - private CompletableFuture> getResourceDirs() { - return ProjectManager.INSTANCE.getApplicationResDirectories(); - } - public SaveResult saveAllResult() { SaveResult result = new SaveResult(); for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { @@ -832,15 +817,6 @@ public class EditorActivity extends StudioActivity return result; } - public void notifySyncNeeded() { - if (mBuildService != null && !mBuildService.isBuildInProgress()) { - getSyncBanner() - .setNegative(android.R.string.cancel, null) - .setPositive(android.R.string.ok, v -> initializeProject()) - .show(); - } - } - public void saveResult(int index, SaveResult result) { if (index >= 0 && index < mViewModel.getOpenedFileCount()) { var frag = getEditorAtIndex(index); @@ -879,6 +855,15 @@ public class EditorActivity extends StudioActivity ThreadUtils.runOnUiThread(() -> mViewModel.setFilesModified(finalModified)); } + public void notifySyncNeeded() { + if (mBuildService != null && !mBuildService.isBuildInProgress()) { + getSyncBanner() + .setNegative(android.R.string.cancel, null) + .setPositive(android.R.string.ok, v -> initializeProject()) + .show(); + } + } + public MaterialBanner getSyncBanner() { return mBinding .syncBanner @@ -925,6 +910,18 @@ public class EditorActivity extends StudioActivity setStatus(text, Gravity.CENTER); } + public void setStatus(final CharSequence text, @GravityInt int gravity) { + try { + runOnUiThread( + () -> { + mBinding.bottomSheet.statusText.setGravity(gravity); + mBinding.bottomSheet.statusText.setText(text); + }); + } catch (Throwable th) { + LOG.error("Failed to update status text", th); + } + } + protected void onProjectInitialized() { ProjectManager.INSTANCE.notifyProjectUpdate(); ThreadUtils.runOnUiThread( @@ -941,16 +938,47 @@ public class EditorActivity extends StudioActivity }); } - public void setStatus(final CharSequence text, @GravityInt int gravity) { + private void initialSetup() { + getApp() + .getPrefManager() + .setOpenedProject(Objects.requireNonNull(ProjectManager.INSTANCE.getProjectDirPath())); + try { - runOnUiThread( - () -> { - mBinding.bottomSheet.statusText.setGravity(gravity); - mBinding.bottomSheet.statusText.setText(text); - }); + //noinspection ConstantConditions + final var rootProject = ProjectManager.INSTANCE.getRootProject(); + + var projectName = rootProject.getName().get(); + if (projectName.isEmpty()) { + projectName = new File(ProjectManager.INSTANCE.getProjectDirPath()).getName(); + getSupportActionBar().setSubtitle(projectName); + } else { + getSupportActionBar().setSubtitle(projectName); + } } catch (Throwable th) { - LOG.error("Failed to update status text", th); + // ignored } + + getResourceDirs() + .thenAccept( + dirs -> { + dirs.removeIf(Objects::isNull); + ValuesTableFactory.setupWithResDirectories(dirs.toArray(new File[0])); + }); + } + + private CompletableFuture> getResourceDirs() { + return ProjectManager.INSTANCE.getApplicationResDirectories(); + } + + @NonNull + private CompletableFuture> getResourceDirPaths() { + return getResourceDirs() + .thenApply( + files -> + files.stream() + .filter(Objects::nonNull) + .map(File::getAbsolutePath) + .collect(Collectors.toCollection(ArrayList::new))); } public void assembleDebug(boolean installApk) { @@ -977,6 +1005,14 @@ public class EditorActivity extends StudioActivity })); } + public void appendBuildOut(final String str) { + final var frag = bottomSheetTabAdapter.getBuildOutputFragment(); + + if (frag != null) { + frag.appendOutput(str); + } + } + public Consumer installableTaskResultConsumer(@NonNull String variantName) { return result -> { if (result != null && result.isSuccessful()) { @@ -1026,14 +1062,6 @@ public class EditorActivity extends StudioActivity }; } - public void appendBuildOut(final String str) { - final var frag = bottomSheetTabAdapter.getBuildOutputFragment(); - - if (frag != null) { - frag.appendOutput(str); - } - } - public void install(@NonNull File apk) { runOnUiThread( () -> { @@ -1079,14 +1107,14 @@ public class EditorActivity extends StudioActivity execTasks(null, "lintRelease"); } - public void cleanAndRebuild() { - execTasks(null, "clean", "build"); - } - ///////////////////////////////////////////////// ////////////// PRIVATE APIS ///////////////////// ///////////////////////////////////////////////// + public void cleanAndRebuild() { + execTasks(null, "clean", "build"); + } + public AlertDialog getFindInProjectDialog() { return mFindInProjectDialog == null ? createFindInProjectDialog() : mFindInProjectDialog; } @@ -1232,18 +1260,6 @@ public class EditorActivity extends StudioActivity } } - private void dispatchOnResumeToEditors() { - CompletableFuture.runAsync( - () -> { - for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { - final var editor = getEditorAtIndex(i); - if (editor != null) { - editor.onResume(); - } - } - }); - } - private void showCompilerModuleInstallError(Throwable error) { final var stacktrace = ThrowableUtils.getFullStackTrace(error); final var builder = DialogUtils.newMaterialDialogBuilder(this); @@ -1260,6 +1276,18 @@ public class EditorActivity extends StudioActivity builder.show(); } + private void dispatchOnResumeToEditors() { + CompletableFuture.runAsync( + () -> { + for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { + final var editor = getEditorAtIndex(i); + if (editor != null) { + editor.onResume(); + } + } + }); + } + private void dispatchOnPauseToEditors() { CompletableFuture.runAsync( () -> { @@ -1544,34 +1572,6 @@ public class EditorActivity extends StudioActivity startActivity(intent); } - private void initialSetup() { - getApp() - .getPrefManager() - .setOpenedProject(Objects.requireNonNull(ProjectManager.INSTANCE.getProjectDirPath())); - - try { - //noinspection ConstantConditions - final var rootProject = ProjectManager.INSTANCE.getRootProject(); - - var projectName = rootProject.getName().get(); - if (projectName.isEmpty()) { - projectName = new File(ProjectManager.INSTANCE.getProjectDirPath()).getName(); - getSupportActionBar().setSubtitle(projectName); - } else { - getSupportActionBar().setSubtitle(projectName); - } - } catch (Throwable th) { - // ignored - } - - getResourceDirs() - .thenAccept( - dirs -> { - dirs.removeIf(Objects::isNull); - ValuesTableFactory.setupWithResDirectories(dirs.toArray(new File[0])); - }); - } - private void setupDiagnosticInfo() { GradientDrawable gd = new GradientDrawable(); gd.setShape(GradientDrawable.RECTANGLE); diff --git a/app/src/main/java/com/itsaky/androidide/MainActivity.java b/app/src/main/java/com/itsaky/androidide/MainActivity.java index 02adba101..2e55960ab 100755 --- a/app/src/main/java/com/itsaky/androidide/MainActivity.java +++ b/app/src/main/java/com/itsaky/androidide/MainActivity.java @@ -27,7 +27,6 @@ import android.view.View; import androidx.annotation.NonNull; import com.google.android.material.dialog.MaterialAlertDialogBuilder; - import com.itsaky.androidide.app.StudioActivity; import com.itsaky.androidide.databinding.ActivityMainBinding; import com.itsaky.androidide.fragments.MainFragment; @@ -42,19 +41,12 @@ import java.io.File; public class MainActivity extends StudioActivity { private ActivityMainBinding binding; - public void openProject(@NonNull File root) { - ProjectManager.INSTANCE.setProjectPath(root.getAbsolutePath()); - startActivity(new Intent(this, EditorActivity.class)); - } - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); openLastProject(); } - private void showMoveRequired() {} - private void openLastProject() { binding .getRoot() @@ -90,12 +82,6 @@ public class MainActivity extends StudioActivity { .commit(); } - @Override - protected View bindLayout() { - binding = ActivityMainBinding.inflate(getLayoutInflater()); - return binding.getRoot(); - } - private void askProjectOpenPermission(File root) { final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this); builder.setTitle(R.string.title_confirm_open_project); @@ -106,6 +92,11 @@ public class MainActivity extends StudioActivity { builder.show(); } + public void openProject(@NonNull File root) { + ProjectManager.INSTANCE.setProjectPath(root.getAbsolutePath()); + startActivity(new Intent(this, EditorActivity.class)); + } + @Override protected void onStorageGranted() {} @@ -115,9 +106,17 @@ public class MainActivity extends StudioActivity { finishAffinity(); } + @Override + protected View bindLayout() { + binding = ActivityMainBinding.inflate(getLayoutInflater()); + return binding.getRoot(); + } + @Override protected void onDestroy() { super.onDestroy(); binding = null; } + + private void showMoveRequired() {} } diff --git a/app/src/main/java/com/itsaky/androidide/PreferencesActivity.java b/app/src/main/java/com/itsaky/androidide/PreferencesActivity.java index 51b9ae1a7..449cc7780 100755 --- a/app/src/main/java/com/itsaky/androidide/PreferencesActivity.java +++ b/app/src/main/java/com/itsaky/androidide/PreferencesActivity.java @@ -34,12 +34,6 @@ public class PreferencesActivity extends StudioActivity { private ActivityPreferencesBinding binding; private IDEPreferences mPref; - @Override - protected View bindLayout() { - binding = ActivityPreferencesBinding.inflate(getLayoutInflater()); - return binding.getRoot(); - } - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -53,6 +47,12 @@ public class PreferencesActivity extends StudioActivity { loadFragment(getPrefsFragment()); } + @Override + protected View bindLayout() { + binding = ActivityPreferencesBinding.inflate(getLayoutInflater()); + return binding.getRoot(); + } + private IDEPreferences getPrefsFragment() { return mPref == null ? mPref = new IDEPreferences() : mPref; } diff --git a/app/src/main/java/com/itsaky/androidide/SplashActivity.java b/app/src/main/java/com/itsaky/androidide/SplashActivity.java index a7bf0bbee..bf832bd06 100755 --- a/app/src/main/java/com/itsaky/androidide/SplashActivity.java +++ b/app/src/main/java/com/itsaky/androidide/SplashActivity.java @@ -49,14 +49,14 @@ public class SplashActivity extends StudioActivity { } @Override - protected void onStorageAlreadyGranted() { - super.onStorageAlreadyGranted(); + protected void onStorageGranted() { + super.onStorageGranted(); proceed(); } @Override - protected void onStorageGranted() { - super.onStorageGranted(); + protected void onStorageAlreadyGranted() { + super.onStorageAlreadyGranted(); proceed(); } diff --git a/app/src/main/java/com/itsaky/androidide/TerminalActivity.java b/app/src/main/java/com/itsaky/androidide/TerminalActivity.java index 03386147e..5b6f4a56f 100755 --- a/app/src/main/java/com/itsaky/androidide/TerminalActivity.java +++ b/app/src/main/java/com/itsaky/androidide/TerminalActivity.java @@ -70,21 +70,196 @@ public class TerminalActivity extends StudioActivity private static final ILogger LOG = ILogger.newInstance("TerminalActivity"); private static final byte[] SOURCES_LIST_CONTENT = "deb https://androidide.com/packages/apt/termux-main/ stable main".getBytes(); + private static final String KEY_FONT_SIZE = "terminal_fontSize"; private ActivityTerminalBinding binding; private TerminalView terminal; private TerminalSession session; private boolean isVisible = false; private KeyListener listener; - private int MIN_FONT_SIZE; private int MAX_FONT_SIZE; private int DEFAULT_FONT_SIZE; - private static final String KEY_FONT_SIZE = "terminal_fontSize"; + + public boolean isVisible() { + return isVisible; + } @Override - protected View bindLayout() { - binding = ActivityTerminalBinding.inflate(getLayoutInflater()); - return binding.getRoot(); + public boolean onKeyUp(int keyCode, KeyEvent e) { + 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) { + if (scale < 0.9f || scale > 1.1f) { + boolean increase = scale > 1.f; + changeFontSize(increase); + return 1.0f; + } + return scale; + } + + private void changeFontSize(final boolean increase) { + int fontSize = getFontSize(); + fontSize += (increase ? 1 : -1) * 2; + fontSize = Math.max(MIN_FONT_SIZE, Math.min(fontSize, MAX_FONT_SIZE)); + setFontSize(fontSize, true); + } + + @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 onKeyDown(int keyCode, KeyEvent e, TerminalSession session) { + if (keyCode == KeyEvent.KEYCODE_ENTER && !session.isRunning()) { + finish(); + return true; + } + return false; + } + + @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 readShiftKey() { + return false; + } + + @Override + public boolean readFnKey() { + return false; + } + + @Override + public boolean onCodePoint(int codePoint, boolean ctrlDown, TerminalSession session) { + 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) { + LOG.error(tag + ":", message); + } + + @Override + public void logWarn(String tag, String message) { + LOG.warn(tag + ":", message); + } + + @Override + public void logInfo(String tag, String message) { + LOG.info(tag + ":", message); + } + + @Override + public void logDebug(String tag, String message) { + LOG.debug(tag + ":", message); + } + + @Override + public void logVerbose(String tag, String message) { + LOG.verbose(tag + ":", message); + } + + @Override + public void logStackTraceWithMessage(String tag, String message, Exception e) { + LOG.error(tag + ":", message, e); + } + + @Override + public void logStackTrace(String tag, Exception e) { + LOG.error(tag + ":", e); } @Override @@ -140,6 +315,12 @@ public class TerminalActivity extends StudioActivity } } + @Override + protected View bindLayout() { + binding = ActivityTerminalBinding.inflate(getLayoutInflater()); + return binding.getRoot(); + } + private void showInstallationError(Throwable throwable) { binding.virtualKeyTable.setVisibility(View.GONE); @@ -198,41 +379,6 @@ public class TerminalActivity extends StudioActivity 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); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - binding = null; - } - - 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 environment = getEnvironment(); final String[] env = new String[environment.size()]; @@ -322,6 +468,60 @@ public class TerminalActivity extends StudioActivity return sizes; } + public void setFontSize(int value, boolean apply) { + getApp().getPrefManager().putString(KEY_FONT_SIZE, String.valueOf(value)); + + if (apply) { + terminal.setTextSize(getFontSize()); + } + } + + public int getFontSize() { + int fontSize; + try { + fontSize = + Integer.parseInt( + getApp() + .getPrefManager() + .getString(KEY_FONT_SIZE, String.valueOf(DEFAULT_FONT_SIZE))); + } catch (NumberFormatException | ClassCastException e) { + fontSize = DEFAULT_FONT_SIZE; + } + + return Math.min(Math.max(fontSize, MIN_FONT_SIZE), MAX_FONT_SIZE); + } + + @Override + protected void onResume() { + super.onResume(); + setTerminalCursorBlinkingState(true); + } + + private void setTerminalCursorBlinkingState(boolean start) { + if (terminal != null && terminal.mEmulator != null) { + terminal.setTerminalCursorBlinkerState(start, true); + } + } + + @Override + protected void onStart() { + super.onStart(); + isVisible = true; + } + + @Override + protected void onStop() { + super.onStop(); + isVisible = false; + setTerminalCursorBlinkingState(false); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + binding = null; + } + private static final class KeyListener implements VirtualKeysView.IVirtualKeysView { private final TerminalView terminal; @@ -404,205 +604,4 @@ public class TerminalActivity extends StudioActivity return false; } } - - @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) { - if (scale < 0.9f || scale > 1.1f) { - boolean increase = scale > 1.f; - changeFontSize(increase); - return 1.0f; - } - return scale; - } - - private void changeFontSize(final boolean increase) { - int fontSize = getFontSize(); - fontSize += (increase ? 1 : -1) * 2; - fontSize = Math.max(MIN_FONT_SIZE, Math.min(fontSize, MAX_FONT_SIZE)); - setFontSize(fontSize, true); - } - - public int getFontSize() { - int fontSize; - try { - fontSize = - Integer.parseInt( - getApp() - .getPrefManager() - .getString(KEY_FONT_SIZE, String.valueOf(DEFAULT_FONT_SIZE))); - } catch (NumberFormatException | ClassCastException e) { - fontSize = DEFAULT_FONT_SIZE; - } - - return Math.min(Math.max(fontSize, MIN_FONT_SIZE), MAX_FONT_SIZE); - } - - public void setFontSize(int value, boolean apply) { - getApp().getPrefManager().putString(KEY_FONT_SIZE, String.valueOf(value)); - - if (apply) { - terminal.setTextSize(getFontSize()); - } - } - - @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) { - LOG.error(tag + ":", message); - } - - @Override - public void logWarn(String tag, String message) { - LOG.warn(tag + ":", message); - } - - @Override - public void logInfo(String tag, String message) { - LOG.info(tag + ":", message); - } - - @Override - public void logDebug(String tag, String message) { - LOG.debug(tag + ":", message); - } - - @Override - public void logVerbose(String tag, String message) { - LOG.verbose(tag + ":", message); - } - - @Override - public void logStackTraceWithMessage(String tag, String message, Exception e) { - LOG.error(tag + ":", message, e); - } - - @Override - public void logStackTrace(String tag, Exception e) { - LOG.error(tag + ":", e); - } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/BaseBuildAction.kt b/app/src/main/java/com/itsaky/androidide/actions/BaseBuildAction.kt index 4cf2e418c..c738bd670 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/BaseBuildAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/BaseBuildAction.kt @@ -26,30 +26,30 @@ import com.itsaky.androidide.EditorActivity */ abstract class BaseBuildAction : EditorActivityAction() { - override fun prepare(data: ActionData) { - val context = getActivity(data) - if (context == null) { - visible = false - return - } else { - visible = true - } - - if (isBuildInProgress(context)) { - enabled = false - return - } else { - enabled = true - } + override fun prepare(data: ActionData) { + val context = getActivity(data) + if (context == null) { + visible = false + return + } else { + visible = true } - override fun postExec(data: ActionData, result: Any) { - val context = getActivity(data) ?: return - context.invalidateOptionsMenu() + if (isBuildInProgress(context)) { + enabled = false + return + } else { + enabled = true } + } - fun shouldPrepare() = visible && enabled + override fun postExec(data: ActionData, result: Any) { + val context = getActivity(data) ?: return + context.invalidateOptionsMenu() + } - private fun isBuildInProgress(activity: EditorActivity): Boolean = - activity.buildService == null || activity.buildService.isBuildInProgress + fun shouldPrepare() = visible && enabled + + private fun isBuildInProgress(activity: EditorActivity): Boolean = + activity.buildService == null || activity.buildService.isBuildInProgress } diff --git a/app/src/main/java/com/itsaky/androidide/actions/BaseEditorAction.kt b/app/src/main/java/com/itsaky/androidide/actions/BaseEditorAction.kt index 1cafae6d2..041bd5edd 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/BaseEditorAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/BaseEditorAction.kt @@ -25,38 +25,38 @@ import com.itsaky.androidide.views.editor.IDEEditor /** @author Akash Yadav */ abstract class BaseEditorAction : ActionItem { - override var label: String = "" - override var visible: Boolean = true - override var enabled: Boolean = true - override var icon: Drawable? = null - override var requiresUIThread: Boolean = - true // all editor actions must be executed on UI thread - override var location: ActionItem.Location = ActionItem.Location.EDITOR_TEXT_ACTIONS + override var label: String = "" + override var visible: Boolean = true + override var enabled: Boolean = true + override var icon: Drawable? = null + override var requiresUIThread: Boolean = true // all editor actions must be executed on UI thread + override var location: ActionItem.Location = ActionItem.Location.EDITOR_TEXT_ACTIONS - override fun prepare(data: ActionData) { - getEditor(data) - ?: kotlin.run { - visible = false - enabled = false - return - } + override fun prepare(data: ActionData) { + getEditor(data) + ?: kotlin.run { + visible = false + enabled = false + return + } - visible = true - enabled = true - } + visible = true + enabled = true + } - fun getEditor(data: ActionData): IDEEditor? { - return data.get(IDEEditor::class.java) - } + fun getEditor(data: ActionData): IDEEditor? { + return data.get(IDEEditor::class.java) + } - fun getContext(data: ActionData): Context? { - val editor = getEditor(data) ?: return null - return editor.context - } + fun getContext(data: ActionData): Context? { + val editor = getEditor(data) ?: return null + return editor.context + } - fun tintDrawable(context: Context, drawable: Drawable): Drawable { - drawable.setTint( - ContextCompat.getColor(context, com.itsaky.androidide.R.color.primaryIconColor)) - return drawable - } + fun tintDrawable(context: Context, drawable: Drawable): Drawable { + drawable.setTint( + ContextCompat.getColor(context, com.itsaky.androidide.R.color.primaryIconColor) + ) + return drawable + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/EditorActivityAction.kt b/app/src/main/java/com/itsaky/androidide/actions/EditorActivityAction.kt index 7fc122ea9..10e95af99 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/EditorActivityAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/EditorActivityAction.kt @@ -24,22 +24,22 @@ import com.itsaky.androidide.EditorActivity /** @author Akash Yadav */ abstract class EditorActivityAction : ActionItem { - override var enabled: Boolean = true - override var visible: Boolean = true - override var icon: Drawable? = null - override var label: String = "" - override var location: ActionItem.Location = ActionItem.Location.EDITOR_TOOLBAR + override var enabled: Boolean = true + override var visible: Boolean = true + override var icon: Drawable? = null + override var label: String = "" + override var location: ActionItem.Location = ActionItem.Location.EDITOR_TOOLBAR - override var requiresUIThread: Boolean = false + override var requiresUIThread: Boolean = false - override fun prepare(data: ActionData) { - if (!hasRequiredData(data, Context::class.java)) { - markInvisible() - } + override fun prepare(data: ActionData) { + if (!hasRequiredData(data, Context::class.java)) { + markInvisible() } + } - fun getActivity(data: ActionData): EditorActivity? { - val context = data.get(Context::class.java) ?: return null - return context as EditorActivity - } + fun getActivity(data: ActionData): EditorActivity? { + val context = data.get(Context::class.java) ?: return null + return context as EditorActivity + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/EditorActivityActionMenu.kt b/app/src/main/java/com/itsaky/androidide/actions/EditorActivityActionMenu.kt index 51ed638c7..98945d5b0 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/EditorActivityActionMenu.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/EditorActivityActionMenu.kt @@ -19,5 +19,5 @@ package com.itsaky.androidide.actions /** @author Akash Yadav */ abstract class EditorActivityActionMenu : EditorActivityAction(), ActionMenu { - override val children: MutableSet = mutableSetOf() + override val children: MutableSet = mutableSetOf() } diff --git a/app/src/main/java/com/itsaky/androidide/actions/EditorRelatedAction.kt b/app/src/main/java/com/itsaky/androidide/actions/EditorRelatedAction.kt index 60800ff91..8520a2535 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/EditorRelatedAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/EditorRelatedAction.kt @@ -23,24 +23,24 @@ import com.itsaky.androidide.views.editor.IDEEditor /** @author Akash Yadav */ abstract class EditorRelatedAction : EditorActivityAction() { - override var requiresUIThread: Boolean = true + override var requiresUIThread: Boolean = true - override fun prepare(data: ActionData) { - val editor = - getEditor(data) - ?: run { - visible = false - enabled = false - return - } + override fun prepare(data: ActionData) { + val editor = + getEditor(data) + ?: run { + visible = false + enabled = false + return + } - val file = editor.file + val file = editor.file - visible = file != null - enabled = visible - } + visible = file != null + enabled = visible + } - fun getEditor(data: ActionData): IDEEditor? = data.get(IDEEditor::class.java) + fun getEditor(data: ActionData): IDEEditor? = data.get(IDEEditor::class.java) - fun getEditorView(data: ActionData): CodeEditorView? = data[CodeEditorView::class.java] + fun getEditorView(data: ActionData): CodeEditorView? = data[CodeEditorView::class.java] } diff --git a/app/src/main/java/com/itsaky/androidide/actions/build/AssembleDebugAction.kt b/app/src/main/java/com/itsaky/androidide/actions/build/AssembleDebugAction.kt index 6a8c877f3..fd7c8593e 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/build/AssembleDebugAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/build/AssembleDebugAction.kt @@ -26,17 +26,17 @@ import com.itsaky.androidide.actions.BaseBuildAction /** @author Akash Yadav */ class AssembleDebugAction() : BaseBuildAction() { - constructor(context: Context) : this() { - label = context.getString(R.string.build_debug) - icon = ContextCompat.getDrawable(context, R.drawable.ic_run_debug) - } + constructor(context: Context) : this() { + label = context.getString(R.string.build_debug) + icon = ContextCompat.getDrawable(context, R.drawable.ic_run_debug) + } - override val id: String = "editor_assembleDebug" + override val id: String = "editor_assembleDebug" - override fun execAction(data: ActionData): Boolean { - val activity = getActivity(data) ?: return false + override fun execAction(data: ActionData): Boolean { + val activity = getActivity(data) ?: return false - activity.assembleDebug(true) - return true - } + activity.assembleDebug(true) + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/build/AssembleReleaseAction.kt b/app/src/main/java/com/itsaky/androidide/actions/build/AssembleReleaseAction.kt index 41fde4ecb..743198773 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/build/AssembleReleaseAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/build/AssembleReleaseAction.kt @@ -26,17 +26,17 @@ import com.itsaky.androidide.actions.BaseBuildAction /** @author Akash Yadav */ class AssembleReleaseAction() : BaseBuildAction() { - constructor(context: Context) : this() { - label = context.getString(R.string.build_release) - icon = ContextCompat.getDrawable(context, R.drawable.ic_run_release) - } + constructor(context: Context) : this() { + label = context.getString(R.string.build_release) + icon = ContextCompat.getDrawable(context, R.drawable.ic_run_release) + } - override val id: String = "editor_assembleRelease" + override val id: String = "editor_assembleRelease" - override fun execAction(data: ActionData): Boolean { - val activity = getActivity(data) ?: return false + override fun execAction(data: ActionData): Boolean { + val activity = getActivity(data) ?: return false - activity.assembleRelease() - return true - } + activity.assembleRelease() + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/build/BuildAction.kt b/app/src/main/java/com/itsaky/androidide/actions/build/BuildAction.kt index 01244e3f0..8a750c592 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/build/BuildAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/build/BuildAction.kt @@ -26,17 +26,17 @@ import com.itsaky.androidide.actions.BaseBuildAction /** @author Akash Yadav */ class BuildAction() : BaseBuildAction() { - constructor(context: Context) : this() { - label = context.getString(R.string.build) - icon = ContextCompat.getDrawable(context, R.drawable.ic_run) - } + constructor(context: Context) : this() { + label = context.getString(R.string.build) + icon = ContextCompat.getDrawable(context, R.drawable.ic_run) + } - override val id: String = "editor_build" + override val id: String = "editor_build" - override fun execAction(data: ActionData): Boolean { - val activity = getActivity(data) ?: return false + override fun execAction(data: ActionData): Boolean { + val activity = getActivity(data) ?: return false - activity.build() - return true - } + activity.build() + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/build/BuildActionMenu.kt b/app/src/main/java/com/itsaky/androidide/actions/build/BuildActionMenu.kt index ed52406fc..e8d77a412 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/build/BuildActionMenu.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/build/BuildActionMenu.kt @@ -27,18 +27,18 @@ import com.itsaky.androidide.actions.BaseBuildAction /** @author Akash Yadav */ class BuildActionMenu() : BaseBuildAction(), ActionMenu { - constructor(context: Context) : this() { - label = context.getString(R.string.build_tasks) - icon = ContextCompat.getDrawable(context, R.drawable.ic_run) + constructor(context: Context) : this() { + label = context.getString(R.string.build_tasks) + icon = ContextCompat.getDrawable(context, R.drawable.ic_run) - addAction(BuildAction(context)) - addAction(AssembleDebugAction(context)) - addAction(AssembleReleaseAction(context)) - addAction(CreateAABAction(context)) - addAction(CleanAction(context)) - addAction(RebuildAction(context)) - } + addAction(BuildAction(context)) + addAction(AssembleDebugAction(context)) + addAction(AssembleReleaseAction(context)) + addAction(CreateAABAction(context)) + addAction(CleanAction(context)) + addAction(RebuildAction(context)) + } - override val children: MutableSet = mutableSetOf() - override val id: String = "editor_buildTasks" + override val children: MutableSet = mutableSetOf() + override val id: String = "editor_buildTasks" } diff --git a/app/src/main/java/com/itsaky/androidide/actions/build/CancelBuildAction.kt b/app/src/main/java/com/itsaky/androidide/actions/build/CancelBuildAction.kt index 6c7cfba71..3c960e704 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/build/CancelBuildAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/build/CancelBuildAction.kt @@ -27,40 +27,41 @@ import com.itsaky.androidide.utils.ILogger /** @author Akash Yadav */ class CancelBuildAction() : EditorActivityAction() { - private val log = ILogger.newInstance(javaClass.simpleName) + private val log = ILogger.newInstance(javaClass.simpleName) - constructor(context: Context) : this() { - label = context.getString(R.string.title_cancel_build) - icon = ContextCompat.getDrawable(context, R.drawable.ic_stop_daemons) - } - - override val id: String = "editor_stopGradleDaemons" - - override fun prepare(data: ActionData) { - visible = true - enabled = true - } - - override fun execAction(data: ActionData): Boolean { - val context = getActivity(data) ?: return false - - log.info("Sending build cancellation request...") - context.buildService.cancelCurrentBuild().whenComplete { result, error -> - if (error != null) { - log.error("Failed to send build cancellation request", error) - return@whenComplete - } - - if (!result.wasEnqueued) { - log.warn( - "Unable to enqueue cancellation request", - result.failureReason, - result.failureReason!!.message) - return@whenComplete - } - - log.info("Build cancellation request was successfully enqueued...") - } - return true + constructor(context: Context) : this() { + label = context.getString(R.string.title_cancel_build) + icon = ContextCompat.getDrawable(context, R.drawable.ic_stop_daemons) + } + + override val id: String = "editor_stopGradleDaemons" + + override fun prepare(data: ActionData) { + visible = true + enabled = true + } + + override fun execAction(data: ActionData): Boolean { + val context = getActivity(data) ?: return false + + log.info("Sending build cancellation request...") + context.buildService.cancelCurrentBuild().whenComplete { result, error -> + if (error != null) { + log.error("Failed to send build cancellation request", error) + return@whenComplete + } + + if (!result.wasEnqueued) { + log.warn( + "Unable to enqueue cancellation request", + result.failureReason, + result.failureReason!!.message + ) + return@whenComplete + } + + log.info("Build cancellation request was successfully enqueued...") } + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/build/CleanAction.kt b/app/src/main/java/com/itsaky/androidide/actions/build/CleanAction.kt index 10379bbb2..8e37a6a20 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/build/CleanAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/build/CleanAction.kt @@ -26,16 +26,16 @@ import com.itsaky.androidide.actions.BaseBuildAction /** @author Akash Yadav */ class CleanAction() : BaseBuildAction() { - constructor(context: Context) : this() { - label = context.getString(R.string.clean_project) - icon = ContextCompat.getDrawable(context, R.drawable.ic_clean) - } + constructor(context: Context) : this() { + label = context.getString(R.string.clean_project) + icon = ContextCompat.getDrawable(context, R.drawable.ic_clean) + } - override val id: String = "editor_clean" - override fun execAction(data: ActionData): Boolean { - val context = getActivity(data) ?: return false + override val id: String = "editor_clean" + override fun execAction(data: ActionData): Boolean { + val context = getActivity(data) ?: return false - context.clean() - return true - } + context.clean() + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/build/CreateAABAction.kt b/app/src/main/java/com/itsaky/androidide/actions/build/CreateAABAction.kt index cd2d32a91..2964f3c88 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/build/CreateAABAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/build/CreateAABAction.kt @@ -26,16 +26,16 @@ import com.itsaky.androidide.actions.BaseBuildAction /** @author Akash Yadav */ class CreateAABAction() : BaseBuildAction() { - constructor(context: Context) : this() { - label = context.getString(R.string.create_aab) - icon = ContextCompat.getDrawable(context, R.drawable.ic_bundle) - } + constructor(context: Context) : this() { + label = context.getString(R.string.create_aab) + icon = ContextCompat.getDrawable(context, R.drawable.ic_bundle) + } - override val id: String = "editor_createAAB" - override fun execAction(data: ActionData): Boolean { - val context = getActivity(data) ?: return false + override val id: String = "editor_createAAB" + override fun execAction(data: ActionData): Boolean { + val context = getActivity(data) ?: return false - context.bundle() - return true - } + context.bundle() + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/build/ProjectSyncAction.kt b/app/src/main/java/com/itsaky/androidide/actions/build/ProjectSyncAction.kt index a6b33bd98..1ff9c785f 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/build/ProjectSyncAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/build/ProjectSyncAction.kt @@ -23,7 +23,6 @@ import com.itsaky.androidide.R import com.itsaky.androidide.R.string import com.itsaky.androidide.actions.ActionData import com.itsaky.androidide.actions.BaseBuildAction -import com.itsaky.androidide.actions.EditorActivityAction /** * Triggers a project sync request. @@ -32,15 +31,15 @@ import com.itsaky.androidide.actions.EditorActivityAction */ class ProjectSyncAction() : BaseBuildAction() { - override val id: String = "action_editor_syncProject" - override var requiresUIThread = true + override val id: String = "action_editor_syncProject" + override var requiresUIThread = true - constructor(context: Context) : this() { - label = context.getString(string.title_sync_project) - icon = ContextCompat.getDrawable(context, R.drawable.ic_sync) - } + constructor(context: Context) : this() { + label = context.getString(string.title_sync_project) + icon = ContextCompat.getDrawable(context, R.drawable.ic_sync) + } - override fun execAction(data: ActionData): Any { - return getActivity(data)!!.initializeProject() - } + override fun execAction(data: ActionData): Any { + return getActivity(data)!!.initializeProject() + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/build/QuickRunAction.kt b/app/src/main/java/com/itsaky/androidide/actions/build/QuickRunAction.kt index 2665d4d7e..c4a22e441 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/build/QuickRunAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/build/QuickRunAction.kt @@ -30,16 +30,16 @@ import com.itsaky.androidide.actions.BaseBuildAction */ class QuickRunAction() : BaseBuildAction() { - constructor(context: Context) : this() { - label = context.getString(R.string.quick_run_debug) - icon = ContextCompat.getDrawable(context, R.drawable.ic_run_outline) - } + constructor(context: Context) : this() { + label = context.getString(R.string.quick_run_debug) + icon = ContextCompat.getDrawable(context, R.drawable.ic_run_outline) + } - override val id: String = "editor_quick_run" + override val id: String = "editor_quick_run" - override fun execAction(data: ActionData): Boolean { - val context = getActivity(data) ?: return false - context.assembleDebug(true) - return true - } + override fun execAction(data: ActionData): Boolean { + val context = getActivity(data) ?: return false + context.assembleDebug(true) + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/build/RebuildAction.kt b/app/src/main/java/com/itsaky/androidide/actions/build/RebuildAction.kt index 8b72ea0c9..e0b55113f 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/build/RebuildAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/build/RebuildAction.kt @@ -26,16 +26,16 @@ import com.itsaky.androidide.actions.BaseBuildAction /** @author Akash Yadav */ class RebuildAction() : BaseBuildAction() { - constructor(context: Context) : this() { - label = context.getString(R.string.clean_amp_build) - icon = ContextCompat.getDrawable(context, R.drawable.ic_rebuild) - } + constructor(context: Context) : this() { + label = context.getString(R.string.clean_amp_build) + icon = ContextCompat.getDrawable(context, R.drawable.ic_rebuild) + } - override val id: String = "editor_cleanAndBuild" - override fun execAction(data: ActionData): Boolean { - val context = getActivity(data) ?: return false + override val id: String = "editor_cleanAndBuild" + override fun execAction(data: ActionData): Boolean { + val context = getActivity(data) ?: return false - context.cleanAndRebuild() - return true - } + context.cleanAndRebuild() + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/build/RunActionMenu.kt b/app/src/main/java/com/itsaky/androidide/actions/build/RunActionMenu.kt index 958c370d2..055dcf044 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/build/RunActionMenu.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/build/RunActionMenu.kt @@ -28,15 +28,15 @@ import com.itsaky.androidide.actions.lint.LintTaskMenu /** @author Akash Yadav */ class RunActionMenu() : BaseBuildAction(), ActionMenu { - override val children: MutableSet = mutableSetOf() + override val children: MutableSet = mutableSetOf() - constructor(context: Context) : this() { - label = context.getString(R.string.run) - icon = ContextCompat.getDrawable(context, R.drawable.ic_run) + constructor(context: Context) : this() { + label = context.getString(R.string.run) + icon = ContextCompat.getDrawable(context, R.drawable.ic_run) - addAction(BuildActionMenu(context)) - addAction(LintTaskMenu(context)) - } + addAction(BuildActionMenu(context)) + addAction(LintTaskMenu(context)) + } - override val id: String = "editor_runMenu" + override val id: String = "editor_runMenu" } diff --git a/app/src/main/java/com/itsaky/androidide/actions/editor/CopyAction.kt b/app/src/main/java/com/itsaky/androidide/actions/editor/CopyAction.kt index ef310d077..3c7753218 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/editor/CopyAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/editor/CopyAction.kt @@ -25,18 +25,18 @@ import com.itsaky.androidide.actions.BaseEditorAction /** @author Akash Yadav */ class CopyAction() : BaseEditorAction() { - constructor(context: Context) : this() { - label = context.getString(R.string.copy) + constructor(context: Context) : this() { + label = context.getString(R.string.copy) - val arr = context.obtainStyledAttributes(intArrayOf(android.R.attr.actionModeCopyDrawable)) - icon = arr.getDrawable(0)?.let { tintDrawable(context, it) } - arr.recycle() - } + val arr = context.obtainStyledAttributes(intArrayOf(android.R.attr.actionModeCopyDrawable)) + icon = arr.getDrawable(0)?.let { tintDrawable(context, it) } + arr.recycle() + } - override val id: String = "ideEditor_copy" - override fun execAction(data: ActionData): Boolean { - val editor = getEditor(data) ?: return false - editor.copyText() - return true - } + override val id: String = "ideEditor_copy" + override fun execAction(data: ActionData): Boolean { + val editor = getEditor(data) ?: return false + editor.copyText() + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/editor/CutAction.kt b/app/src/main/java/com/itsaky/androidide/actions/editor/CutAction.kt index caecfbffa..1513476c0 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/editor/CutAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/editor/CutAction.kt @@ -18,36 +18,35 @@ package com.itsaky.androidide.actions.editor import android.content.Context -import com.itsaky.androidide.R import com.itsaky.androidide.actions.ActionData import com.itsaky.androidide.actions.BaseEditorAction /** @author Akash Yadav */ class CutAction() : BaseEditorAction() { - constructor(context: Context) : this() { - label = context.getString(android.R.string.cut) + constructor(context: Context) : this() { + label = context.getString(android.R.string.cut) - val arr = context.obtainStyledAttributes(intArrayOf(android.R.attr.actionModeCutDrawable)) - icon = arr.getDrawable(0)?.let { tintDrawable(context, it) } - arr.recycle() + val arr = context.obtainStyledAttributes(intArrayOf(android.R.attr.actionModeCutDrawable)) + icon = arr.getDrawable(0)?.let { tintDrawable(context, it) } + arr.recycle() + } + + override fun prepare(data: ActionData) { + super.prepare(data) + + if (!visible) { + return } - override fun prepare(data: ActionData) { - super.prepare(data) + visible = getEditor(data)?.isEditable ?: false + enabled = visible + } - if (!visible) { - return - } - - visible = getEditor(data)?.isEditable ?: false - enabled = visible - } - - override val id: String = "ideEditor_cut" - override fun execAction(data: ActionData): Boolean { - val editor = getEditor(data) ?: return false - editor.cutText() - return true - } + override val id: String = "ideEditor_cut" + override fun execAction(data: ActionData): Boolean { + val editor = getEditor(data) ?: return false + editor.cutText() + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/editor/ExpandSelectionAction.kt b/app/src/main/java/com/itsaky/androidide/actions/editor/ExpandSelectionAction.kt index fc094feb3..2ac50579c 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/editor/ExpandSelectionAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/editor/ExpandSelectionAction.kt @@ -30,15 +30,15 @@ import com.itsaky.androidide.actions.EditorRelatedAction * @author Akash Yadav */ class ExpandSelectionAction() : EditorRelatedAction() { - override val id: String = "editor_expandSelection" - override var location: ActionItem.Location = ActionItem.Location.EDITOR_TEXT_ACTIONS + override val id: String = "editor_expandSelection" + override var location: ActionItem.Location = ActionItem.Location.EDITOR_TEXT_ACTIONS - constructor(context: Context) : this() { - label = context.getString(R.string.action_expand_selection) - icon = ContextCompat.getDrawable(context, R.drawable.ic_expand_selection) - } + constructor(context: Context) : this() { + label = context.getString(R.string.action_expand_selection) + icon = ContextCompat.getDrawable(context, R.drawable.ic_expand_selection) + } - override fun execAction(data: ActionData): Any { - return getEditor(data)!!.expandSelection() - } + override fun execAction(data: ActionData): Any { + return getEditor(data)!!.expandSelection() + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/editor/PasteAction.kt b/app/src/main/java/com/itsaky/androidide/actions/editor/PasteAction.kt index c344f0b1a..387189d41 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/editor/PasteAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/editor/PasteAction.kt @@ -18,37 +18,36 @@ package com.itsaky.androidide.actions.editor import android.content.Context -import com.itsaky.androidide.R import com.itsaky.androidide.actions.ActionData import com.itsaky.androidide.actions.BaseEditorAction /** @author Akash Yadav */ class PasteAction() : BaseEditorAction() { - constructor(context: Context) : this() { - label = context.getString(android.R.string.paste) + constructor(context: Context) : this() { + label = context.getString(android.R.string.paste) - val arr = context.obtainStyledAttributes(intArrayOf(android.R.attr.actionModePasteDrawable)) - icon = arr.getDrawable(0)?.let { tintDrawable(context, it) } - arr.recycle() + val arr = context.obtainStyledAttributes(intArrayOf(android.R.attr.actionModePasteDrawable)) + icon = arr.getDrawable(0)?.let { tintDrawable(context, it) } + arr.recycle() + } + + override val id: String = "ideEditor_paste" + + override fun prepare(data: ActionData) { + super.prepare(data) + + if (!visible) { + return } - override val id: String = "ideEditor_paste" - - override fun prepare(data: ActionData) { - super.prepare(data) - - if (!visible) { - return - } - - visible = getEditor(data)?.isEditable ?: false - enabled = visible - } - - override fun execAction(data: ActionData): Boolean { - val editor = getEditor(data) ?: return false - editor.pasteText() - return true - } + visible = getEditor(data)?.isEditable ?: false + enabled = visible + } + + override fun execAction(data: ActionData): Boolean { + val editor = getEditor(data) ?: return false + editor.pasteText() + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/editor/SelectAllAction.kt b/app/src/main/java/com/itsaky/androidide/actions/editor/SelectAllAction.kt index 92f900703..a59973809 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/editor/SelectAllAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/editor/SelectAllAction.kt @@ -18,26 +18,25 @@ package com.itsaky.androidide.actions.editor import android.content.Context -import com.itsaky.androidide.R import com.itsaky.androidide.actions.ActionData import com.itsaky.androidide.actions.BaseEditorAction /** @author Akash Yadav */ class SelectAllAction() : BaseEditorAction() { - - constructor(context: Context) : this() { - label = context.getString(android.R.string.selectAll) - - val arr = context.obtainStyledAttributes(intArrayOf(android.R.attr.actionModeSelectAllDrawable)) - icon = arr.getDrawable(0)?.let { tintDrawable(context, it) } - arr.recycle() - } - - override val id: String = "ideEditor_selectAll" - override fun execAction(data: ActionData): Boolean { - val editor = getEditor(data) ?: return false - editor.selectAll() - return true - } + constructor(context: Context) : this() { + label = context.getString(android.R.string.selectAll) + + val arr = context.obtainStyledAttributes(intArrayOf(android.R.attr.actionModeSelectAllDrawable)) + icon = arr.getDrawable(0)?.let { tintDrawable(context, it) } + arr.recycle() + } + + override val id: String = "ideEditor_selectAll" + + override fun execAction(data: ActionData): Boolean { + val editor = getEditor(data) ?: return false + editor.selectAll() + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/editor/find/GoToNextAction.kt b/app/src/main/java/com/itsaky/androidide/actions/editor/find/GoToNextAction.kt index d2de92acd..7a420f548 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/editor/find/GoToNextAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/editor/find/GoToNextAction.kt @@ -27,19 +27,19 @@ import com.itsaky.androidide.actions.ActionData * @author Akash Yadav */ class GoToNextAction() : SearchActionModeAction() { - override val id: String = "editor.find.next" + override val id: String = "editor.find.next" - constructor(context: Context) : this() { - label = context.getString(R.string.next) - icon = ContextCompat.getDrawable(context, R.drawable.ic_search_next) + constructor(context: Context) : this() { + label = context.getString(R.string.next) + icon = ContextCompat.getDrawable(context, R.drawable.ic_search_next) + } + + override fun execAction(data: ActionData): Any { + val editor = getEditor(data)!! + if (!editor.searcher.hasQuery()) { + return false } - override fun execAction(data: ActionData): Any { - val editor = getEditor(data)!! - if (!editor.searcher.hasQuery()) { - return false - } - - return editor.searcher.gotoNext() - } + return editor.searcher.gotoNext() + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/editor/find/GoToPreviousAction.kt b/app/src/main/java/com/itsaky/androidide/actions/editor/find/GoToPreviousAction.kt index eef634816..cf6d795b4 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/editor/find/GoToPreviousAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/editor/find/GoToPreviousAction.kt @@ -28,19 +28,19 @@ import com.itsaky.androidide.actions.ActionData * @author Akash Yadav */ class GoToPreviousAction() : SearchActionModeAction() { - override val id: String = "editor.find.previous" + override val id: String = "editor.find.previous" - constructor(context: Context) : this() { - label = context.getString(string.previous) - icon = ContextCompat.getDrawable(context, R.drawable.ic_search_previous) + constructor(context: Context) : this() { + label = context.getString(string.previous) + icon = ContextCompat.getDrawable(context, R.drawable.ic_search_previous) + } + + override fun execAction(data: ActionData): Any { + val editor = getEditor(data)!! + if (!editor.searcher.hasQuery()) { + return false } - override fun execAction(data: ActionData): Any { - val editor = getEditor(data)!! - if (!editor.searcher.hasQuery()) { - return false - } - - return editor.searcher.gotoPrevious() - } + return editor.searcher.gotoPrevious() + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/editor/find/ReplaceAction.kt b/app/src/main/java/com/itsaky/androidide/actions/editor/find/ReplaceAction.kt index d16b6749c..8317d16c4 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/editor/find/ReplaceAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/editor/find/ReplaceAction.kt @@ -33,55 +33,55 @@ import com.itsaky.androidide.utils.ILogger */ class ReplaceAction() : SearchActionModeAction() { - override val id: String = "editor.search.replace" - private val log = ILogger.newInstance(javaClass.simpleName) + override val id: String = "editor.search.replace" + private val log = ILogger.newInstance(javaClass.simpleName) - constructor(context: Context) : this() { - label = context.getString(R.string.replace) - icon = ContextCompat.getDrawable(context, R.drawable.ic_search_replace) + constructor(context: Context) : this() { + label = context.getString(R.string.replace) + icon = ContextCompat.getDrawable(context, R.drawable.ic_search_replace) + } + + override fun execAction(data: ActionData): Any { + val context = data[Context::class.java]!! + val editor = getEditor(data)!! + + if (!editor.searcher.hasQuery()) { + return false } - override fun execAction(data: ActionData): Any { - val context = data[Context::class.java]!! - val editor = getEditor(data)!! + val binding = LayoutEditorFindReplaceBinding.inflate(LayoutInflater.from(context)) + val builder = DialogUtils.newMaterialDialogBuilder(context) + builder.setTitle(R.string.replace) + builder.setView(binding.root) + builder.setNegativeButton(android.R.string.cancel, null) + builder.setPositiveButton(R.string.replace) { dialog, _ -> + dialog.dismiss() + val input = binding.replacementInput.editText + if (input == null) { + log.error("Unable to perform replace action. Input field is null") + return@setPositiveButton + } - if (!editor.searcher.hasQuery()) { - return false - } - - val binding = LayoutEditorFindReplaceBinding.inflate(LayoutInflater.from(context)) - val builder = DialogUtils.newMaterialDialogBuilder(context) - builder.setTitle(R.string.replace) - builder.setView(binding.root) - builder.setNegativeButton(android.R.string.cancel, null) - builder.setPositiveButton(R.string.replace) { dialog, _ -> - dialog.dismiss() - val input = binding.replacementInput.editText - if (input == null) { - log.error("Unable to perform replace action. Input field is null") - return@setPositiveButton - } - - editor.searcher.replaceThis(input.text.toString()) - } - builder.setNeutralButton(R.string.replaceAll) { dialog, _ -> - dialog.dismiss() - val input = binding.replacementInput.editText - if (input == null) { - log.error("Unable to perform replace action. Input field is null") - return@setNeutralButton - } - - editor.searcher.replaceAllAsync(input.text.toString()).whenComplete { _, error -> - if (error != null) { - log.error("Unable to replace all matched text", error) - } else { - editor.post { data[ActionMode::class.java]?.finish() } - } - } - } - builder.show() - - return true + editor.searcher.replaceThis(input.text.toString()) } + builder.setNeutralButton(R.string.replaceAll) { dialog, _ -> + dialog.dismiss() + val input = binding.replacementInput.editText + if (input == null) { + log.error("Unable to perform replace action. Input field is null") + return@setNeutralButton + } + + editor.searcher.replaceAllAsync(input.text.toString()).whenComplete { _, error -> + if (error != null) { + log.error("Unable to replace all matched text", error) + } else { + editor.post { data[ActionMode::class.java]?.finish() } + } + } + } + builder.show() + + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/editor/find/SearchActionModeAction.kt b/app/src/main/java/com/itsaky/androidide/actions/editor/find/SearchActionModeAction.kt index 28dac7ccc..a7a73ac70 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/editor/find/SearchActionModeAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/editor/find/SearchActionModeAction.kt @@ -23,5 +23,5 @@ import com.itsaky.androidide.actions.EditorRelatedAction /** @author Akash Yadav */ abstract class SearchActionModeAction : EditorRelatedAction() { - override var location: Location = ActionItem.Location.EDITOR_SEARCH_ACTION_MODE + override var location: Location = ActionItem.Location.EDITOR_SEARCH_ACTION_MODE } diff --git a/app/src/main/java/com/itsaky/androidide/actions/etc/DaemonStatusAction.kt b/app/src/main/java/com/itsaky/androidide/actions/etc/DaemonStatusAction.kt index 79e9f8b28..c033cb63f 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/etc/DaemonStatusAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/etc/DaemonStatusAction.kt @@ -25,22 +25,22 @@ import com.itsaky.androidide.actions.EditorActivityAction /** @author Akash Yadav */ class DaemonStatusAction() : EditorActivityAction() { - override val id: String = "editor_daemonStatus" - override var requiresUIThread: Boolean = true + override val id: String = "editor_daemonStatus" + override var requiresUIThread: Boolean = true - constructor(context: Context) : this() { - label = context.getString(R.string.gradle_daemon_status) - icon = ContextCompat.getDrawable(context, R.drawable.ic_info) - } + constructor(context: Context) : this() { + label = context.getString(R.string.gradle_daemon_status) + icon = ContextCompat.getDrawable(context, R.drawable.ic_info) + } - override fun prepare(data: ActionData) { - visible = true - enabled = true - } + override fun prepare(data: ActionData) { + visible = true + enabled = true + } - override fun execAction(data: ActionData): Boolean { - val context = getActivity(data) ?: return false - context.showDaemonStatus() - return true - } + override fun execAction(data: ActionData): Boolean { + val context = getActivity(data) ?: return false + context.showDaemonStatus() + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/etc/FileTreeAction.kt b/app/src/main/java/com/itsaky/androidide/actions/etc/FileTreeAction.kt index 2016442f1..6b9a81788 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/etc/FileTreeAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/etc/FileTreeAction.kt @@ -27,26 +27,26 @@ import com.itsaky.androidide.actions.EditorActivityAction /** @author Akash Yadav */ class FileTreeAction() : EditorActivityAction() { - override val id: String = "editor_fileTree" + override val id: String = "editor_fileTree" - constructor(context: Context) : this() { - label = context.getString(R.string.msg_file_tree) - icon = ContextCompat.getDrawable(context, R.drawable.ic_folder) + constructor(context: Context) : this() { + label = context.getString(R.string.msg_file_tree) + icon = ContextCompat.getDrawable(context, R.drawable.ic_folder) + } + + override fun prepare(data: ActionData) { + visible = true + enabled = true + } + + override fun execAction(data: ActionData): Boolean { + val context = getActivity(data) ?: return false + + if (!context.binding.root.isDrawerOpen(GravityCompat.END)) { + context.binding.root.openDrawer(GravityCompat.END) + return true } - override fun prepare(data: ActionData) { - visible = true - enabled = true - } - - override fun execAction(data: ActionData): Boolean { - val context = getActivity(data) ?: return false - - if (!context.binding.root.isDrawerOpen(GravityCompat.END)) { - context.binding.root.openDrawer(GravityCompat.END) - return true - } - - return false - } + return false + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/etc/FindActionMenu.kt b/app/src/main/java/com/itsaky/androidide/actions/etc/FindActionMenu.kt index 9909c8888..ee551f713 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/etc/FindActionMenu.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/etc/FindActionMenu.kt @@ -28,19 +28,19 @@ import com.itsaky.androidide.actions.EditorActivityAction /** @author Akash Yadav */ class FindActionMenu() : EditorActivityAction(), ActionMenu { - constructor(context: Context) : this() { - label = context.getString(R.string.menu_find) - icon = ContextCompat.getDrawable(context, R.drawable.ic_search) + constructor(context: Context) : this() { + label = context.getString(R.string.menu_find) + icon = ContextCompat.getDrawable(context, R.drawable.ic_search) - addAction(FindInFileAction(context)) - addAction(FindInProjectAction(context)) - } + addAction(FindInFileAction(context)) + addAction(FindInProjectAction(context)) + } - override val children: MutableSet = mutableSetOf() - override val id: String = "editor_findActions" + override val children: MutableSet = mutableSetOf() + override val id: String = "editor_findActions" - override fun prepare(data: ActionData) { - visible = true - enabled = children.size > 0 - } + override fun prepare(data: ActionData) { + visible = true + enabled = children.size > 0 + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/etc/FindInFileAction.kt b/app/src/main/java/com/itsaky/androidide/actions/etc/FindInFileAction.kt index 674c2001a..9865d8a23 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/etc/FindInFileAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/etc/FindInFileAction.kt @@ -26,17 +26,17 @@ import com.itsaky.androidide.actions.EditorRelatedAction /** @author Akash Yadav */ class FindInFileAction() : EditorRelatedAction() { - override val id: String = "editor_findInFile" - override var requiresUIThread: Boolean = true + override val id: String = "editor_findInFile" + override var requiresUIThread: Boolean = true - constructor(context: Context) : this() { - label = context.getString(R.string.menu_find_file) - icon = ContextCompat.getDrawable(context, R.drawable.ic_search_file) - } + constructor(context: Context) : this() { + label = context.getString(R.string.menu_find_file) + icon = ContextCompat.getDrawable(context, R.drawable.ic_search_file) + } - override fun execAction(data: ActionData): Boolean { - val editor = getEditorView(data) ?: return false - editor.beginSearch() - return true - } + override fun execAction(data: ActionData): Boolean { + val editor = getEditorView(data) ?: return false + editor.beginSearch() + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/etc/FindInProjectAction.kt b/app/src/main/java/com/itsaky/androidide/actions/etc/FindInProjectAction.kt index 7c78713bd..b251e4894 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/etc/FindInProjectAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/etc/FindInProjectAction.kt @@ -26,36 +26,36 @@ import com.itsaky.androidide.actions.EditorActivityAction /** @author Akash Yadav */ class FindInProjectAction() : EditorActivityAction() { - override var requiresUIThread: Boolean = true + override var requiresUIThread: Boolean = true - constructor(context: Context) : this() { - label = context.getString(R.string.menu_find_project) - icon = ContextCompat.getDrawable(context, R.drawable.ic_search_project) - } - - override val id: String = "editor_findInProject" - - override fun prepare(data: ActionData) { - getActivity(data) - ?: run { - visible = false - enabled = false - return - } - - visible = true - enabled = true - } - - override fun execAction(data: ActionData): Boolean { - val context = getActivity(data) ?: return false - val dialog = context.findInProjectDialog - - return if (dialog != null) { - dialog.show() - true - } else { - false - } + constructor(context: Context) : this() { + label = context.getString(R.string.menu_find_project) + icon = ContextCompat.getDrawable(context, R.drawable.ic_search_project) + } + + override val id: String = "editor_findInProject" + + override fun prepare(data: ActionData) { + getActivity(data) + ?: run { + visible = false + enabled = false + return + } + + visible = true + enabled = true + } + + override fun execAction(data: ActionData): Boolean { + val context = getActivity(data) ?: return false + val dialog = context.findInProjectDialog + + return if (dialog != null) { + dialog.show() + true + } else { + false } + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/etc/PreviewLayoutAction.kt b/app/src/main/java/com/itsaky/androidide/actions/etc/PreviewLayoutAction.kt index ce52e991a..ee343f597 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/etc/PreviewLayoutAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/etc/PreviewLayoutAction.kt @@ -27,36 +27,35 @@ import com.itsaky.androidide.handlers.FileOptionsHandler /** @author Akash Yadav */ class PreviewLayoutAction() : EditorRelatedAction() { - override val id: String = "editor_previewLayout" + override val id: String = "editor_previewLayout" - constructor(context: Context) : this() { - label = context.getString(R.string.title_preview_layout) - icon = ContextCompat.getDrawable(context, R.drawable.ic_preview_layout) + constructor(context: Context) : this() { + label = context.getString(R.string.title_preview_layout) + icon = ContextCompat.getDrawable(context, R.drawable.ic_preview_layout) + } + + override fun prepare(data: ActionData) { + super.prepare(data) + + if (!visible) { + return } - override fun prepare(data: ActionData) { - super.prepare(data) + val editor = getEditor(data)!! + val file = editor.file!! - if (!visible) { - return - } + val isXml = file.name.endsWith(".xml") - val editor = getEditor(data)!! - val file = editor.file!! + visible = + isXml && + file.parentFile != null && + Regex(FileOptionsHandler.LAYOUT_RES_PATH_REGEX).matches(file.parentFile!!.absolutePath) + enabled = visible + } - val isXml = file.name.endsWith(".xml") - - visible = - isXml && - file.parentFile != null && - Regex(FileOptionsHandler.LAYOUT_RES_PATH_REGEX) - .matches(file.parentFile!!.absolutePath) - enabled = visible - } - - override fun execAction(data: ActionData): Any { - val activity = getActivity(data)!! - activity.previewLayout() - return true - } + override fun execAction(data: ActionData): Any { + val activity = getActivity(data)!! + activity.previewLayout() + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/file/FormatCodeAction.kt b/app/src/main/java/com/itsaky/androidide/actions/file/FormatCodeAction.kt index c537d613b..7a81130c3 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/file/FormatCodeAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/file/FormatCodeAction.kt @@ -30,17 +30,17 @@ import com.itsaky.androidide.actions.EditorRelatedAction * @author Akash Yadav */ class FormatCodeAction() : EditorRelatedAction() { - override val id: String = "editor_file_formatCode" - override var location: ActionItem.Location = ActionItem.Location.EDITOR_TEXT_ACTIONS + override val id: String = "editor_file_formatCode" + override var location: ActionItem.Location = ActionItem.Location.EDITOR_TEXT_ACTIONS - constructor(context: Context) : this() { - label = context.getString(R.string.title_format_code) - icon = ContextCompat.getDrawable(context, R.drawable.ic_format_code) - } + constructor(context: Context) : this() { + label = context.getString(R.string.title_format_code) + icon = ContextCompat.getDrawable(context, R.drawable.ic_format_code) + } - override fun execAction(data: ActionData): Any { - val editor = getEditor(data)!! - editor.formatCodeAsync() - return true - } + override fun execAction(data: ActionData): Any { + val editor = getEditor(data)!! + editor.formatCodeAsync() + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/file/SaveFileAction.kt b/app/src/main/java/com/itsaky/androidide/actions/file/SaveFileAction.kt index bc8007372..dd4b4d238 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/file/SaveFileAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/file/SaveFileAction.kt @@ -30,57 +30,57 @@ import com.itsaky.toaster.Toaster /** @author Akash Yadav */ class SaveFileAction() : EditorRelatedAction() { - private val log = ILogger.newInstance("SaveFileAction") + private val log = ILogger.newInstance("SaveFileAction") - override val id: String = "editor_saveFile" + override val id: String = "editor_saveFile" - constructor(context: Context) : this() { - label = context.getString(R.string.save) - icon = ContextCompat.getDrawable(context, R.drawable.ic_save) - } + constructor(context: Context) : this() { + label = context.getString(R.string.save) + icon = ContextCompat.getDrawable(context, R.drawable.ic_save) + } - override fun prepare(data: ActionData) { + override fun prepare(data: ActionData) { - val context = - getActivity(data) - ?: run { - visible = false - enabled = false - return - } - - visible = context.viewModel.openedFiles.isNotEmpty() - enabled = context.areFilesModified() - } - - override fun execAction(data: ActionData): Boolean { - val context = getActivity(data) ?: return false - - return try { - // Cannot use context.saveAll() because this.execAction is called on non-UI thread - // and saveAll call will result in UI actions - val result = context.saveAllResult() - - if (result.xmlSaved) { - ProjectManager.generateSources(context.buildService) - } - - if (result.gradleSaved) { - context.notifySyncNeeded() - } - true - } catch (error: Throwable) { - log.error("Failed to save file", error) - false + val context = + getActivity(data) + ?: run { + visible = false + enabled = false + return } - } - override fun postExec(data: ActionData, result: Any) { - if (result is Boolean && result) { - StudioApp.getInstance().toast(R.string.all_saved, Toaster.Type.SUCCESS) - } else { - log.error("Failed to save file") - TODO("Create message in strings.xml") - } + visible = context.viewModel.openedFiles.isNotEmpty() + enabled = context.areFilesModified() + } + + override fun execAction(data: ActionData): Boolean { + val context = getActivity(data) ?: return false + + return try { + // Cannot use context.saveAll() because this.execAction is called on non-UI thread + // and saveAll call will result in UI actions + val result = context.saveAllResult() + + if (result.xmlSaved) { + ProjectManager.generateSources(context.buildService) + } + + if (result.gradleSaved) { + context.notifySyncNeeded() + } + true + } catch (error: Throwable) { + log.error("Failed to save file", error) + false } + } + + override fun postExec(data: ActionData, result: Any) { + if (result is Boolean && result) { + StudioApp.getInstance().toast(R.string.all_saved, Toaster.Type.SUCCESS) + } else { + log.error("Failed to save file") + TODO("Create message in strings.xml") + } + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/lint/LintAction.kt b/app/src/main/java/com/itsaky/androidide/actions/lint/LintAction.kt index 998cfaa6d..366197859 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/lint/LintAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/lint/LintAction.kt @@ -26,16 +26,16 @@ import com.itsaky.androidide.actions.BaseBuildAction /** @author Akash Yadav */ class LintAction() : BaseBuildAction() { - constructor(context: Context) : this() { - label = context.getString(R.string.lint) - icon = ContextCompat.getDrawable(context, R.drawable.ic_lint) - } + constructor(context: Context) : this() { + label = context.getString(R.string.lint) + icon = ContextCompat.getDrawable(context, R.drawable.ic_lint) + } - override val id: String = "editor_lint" - override fun execAction(data: ActionData): Boolean { - val context = getActivity(data) ?: return false + override val id: String = "editor_lint" + override fun execAction(data: ActionData): Boolean { + val context = getActivity(data) ?: return false - context.lint() - return true - } + context.lint() + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/lint/LintDebugAction.kt b/app/src/main/java/com/itsaky/androidide/actions/lint/LintDebugAction.kt index f30da1c95..555af4aa0 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/lint/LintDebugAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/lint/LintDebugAction.kt @@ -26,16 +26,16 @@ import com.itsaky.androidide.actions.BaseBuildAction /** @author Akash Yadav */ class LintDebugAction() : BaseBuildAction() { - constructor(context: Context) : this() { - label = context.getString(R.string.lint_debug) - icon = ContextCompat.getDrawable(context, R.drawable.ic_run_debug) - } + constructor(context: Context) : this() { + label = context.getString(R.string.lint_debug) + icon = ContextCompat.getDrawable(context, R.drawable.ic_run_debug) + } - override val id: String = "editor_lintDebug" - override fun execAction(data: ActionData): Boolean { - val context = getActivity(data) ?: return false + override val id: String = "editor_lintDebug" + override fun execAction(data: ActionData): Boolean { + val context = getActivity(data) ?: return false - context.lintDebug() - return true - } + context.lintDebug() + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/lint/LintReleaseAction.kt b/app/src/main/java/com/itsaky/androidide/actions/lint/LintReleaseAction.kt index 5004d3b9b..b4c9540a3 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/lint/LintReleaseAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/lint/LintReleaseAction.kt @@ -26,16 +26,16 @@ import com.itsaky.androidide.actions.BaseBuildAction /** @author Akash Yadav */ class LintReleaseAction() : BaseBuildAction() { - constructor(context: Context) : this() { - label = context.getString(R.string.lint_release) - icon = ContextCompat.getDrawable(context, R.drawable.ic_run_release) - } + constructor(context: Context) : this() { + label = context.getString(R.string.lint_release) + icon = ContextCompat.getDrawable(context, R.drawable.ic_run_release) + } - override val id: String = "editor_lintRelease" - override fun execAction(data: ActionData): Boolean { - val context = getActivity(data) ?: return false + override val id: String = "editor_lintRelease" + override fun execAction(data: ActionData): Boolean { + val context = getActivity(data) ?: return false - context.lintRelease() - return true - } + context.lintRelease() + return true + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/lint/LintTaskMenu.kt b/app/src/main/java/com/itsaky/androidide/actions/lint/LintTaskMenu.kt index 791948464..eaadc4dc6 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/lint/LintTaskMenu.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/lint/LintTaskMenu.kt @@ -27,15 +27,15 @@ import com.itsaky.androidide.actions.BaseBuildAction /** @author Akash Yadav */ class LintTaskMenu() : BaseBuildAction(), ActionMenu { - constructor(context: Context) : this() { - label = context.getString(R.string.lint_tasks) - icon = ContextCompat.getDrawable(context, R.drawable.ic_lint) + constructor(context: Context) : this() { + label = context.getString(R.string.lint_tasks) + icon = ContextCompat.getDrawable(context, R.drawable.ic_lint) - addAction(LintAction(context)) - addAction(LintDebugAction(context)) - addAction(LintReleaseAction(context)) - } + addAction(LintAction(context)) + addAction(LintDebugAction(context)) + addAction(LintReleaseAction(context)) + } - override val children: MutableSet = mutableSetOf() - override val id: String = "editor_lintTasks" + override val children: MutableSet = mutableSetOf() + override val id: String = "editor_lintTasks" } diff --git a/app/src/main/java/com/itsaky/androidide/actions/text/RedoAction.kt b/app/src/main/java/com/itsaky/androidide/actions/text/RedoAction.kt index f11cc9a5f..10511df06 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/text/RedoAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/text/RedoAction.kt @@ -29,35 +29,35 @@ import com.itsaky.androidide.actions.EditorRelatedAction /** @author Akash Yadav */ class RedoAction() : EditorRelatedAction() { - constructor(context: Context) : this() { - label = context.getString(R.string.redo) - icon = ContextCompat.getDrawable(context, R.drawable.ic_redo) + constructor(context: Context) : this() { + label = context.getString(R.string.redo) + icon = ContextCompat.getDrawable(context, R.drawable.ic_redo) + } + + override val id: String = "editor_redo" + + override fun prepare(data: ActionData) { + super.prepare(data) + + if (!visible) { + return } - override val id: String = "editor_redo" + val editor = getEditor(data)!! + enabled = editor.canRedo() + } - override fun prepare(data: ActionData) { - super.prepare(data) + override fun execAction(data: ActionData): Boolean { + val editor = getEditor(data)!! + editor.redo() + return true + } - if (!visible) { - return - } - - val editor = getEditor(data)!! - enabled = editor.canRedo() - } - - override fun execAction(data: ActionData): Boolean { - val editor = getEditor(data)!! - editor.redo() - return true - } - - override fun getShowAsActionFlags(data: ActionData): Int { - return if (KeyboardUtils.isSoftInputVisible(data.get(Context::class.java) as Activity)) { - MenuItem.SHOW_AS_ACTION_IF_ROOM - } else { - MenuItem.SHOW_AS_ACTION_NEVER - } + override fun getShowAsActionFlags(data: ActionData): Int { + return if (KeyboardUtils.isSoftInputVisible(data.get(Context::class.java) as Activity)) { + MenuItem.SHOW_AS_ACTION_IF_ROOM + } else { + MenuItem.SHOW_AS_ACTION_NEVER } + } } diff --git a/app/src/main/java/com/itsaky/androidide/actions/text/UndoAction.kt b/app/src/main/java/com/itsaky/androidide/actions/text/UndoAction.kt index 7e535e2c9..db873e8d8 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/text/UndoAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/text/UndoAction.kt @@ -26,31 +26,31 @@ import com.itsaky.androidide.actions.EditorRelatedAction /** @author Akash Yadav */ class UndoAction() : EditorRelatedAction() { - override val id: String = "editor_undo" + override val id: String = "editor_undo" - constructor(context: Context) : this() { - label = context.getString(R.string.undo) - icon = ContextCompat.getDrawable(context, R.drawable.ic_undo) + constructor(context: Context) : this() { + label = context.getString(R.string.undo) + icon = ContextCompat.getDrawable(context, R.drawable.ic_undo) + } + + override fun prepare(data: ActionData) { + super.prepare(data) + + if (!visible) { + return } - override fun prepare(data: ActionData) { - super.prepare(data) + val editor = getEditor(data)!! + enabled = editor.canUndo() + } - if (!visible) { - return - } - - val editor = getEditor(data)!! - enabled = editor.canUndo() - } - - override fun execAction(data: ActionData): Any { - val editor = getEditor(data) - return if (editor != null) { - editor.undo() - true - } else { - false - } + override fun execAction(data: ActionData): Any { + val editor = getEditor(data) + return if (editor != null) { + editor.undo() + true + } else { + false } + } } 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 222d9f8f4..43b6fc3da 100644 --- a/app/src/main/java/com/itsaky/androidide/adapters/AttrListAdapter.java +++ b/app/src/main/java/com/itsaky/androidide/adapters/AttrListAdapter.java @@ -46,6 +46,20 @@ public class AttrListAdapter extends IconTextAdapter { this.attrs = attrs; } + @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) { @@ -62,18 +76,4 @@ public class AttrListAdapter extends IconTextAdapter { 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/EditorBottomSheetTabAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/EditorBottomSheetTabAdapter.java index 0109324ba..1a4b77ccc 100644 --- a/app/src/main/java/com/itsaky/androidide/adapters/EditorBottomSheetTabAdapter.java +++ b/app/src/main/java/com/itsaky/androidide/adapters/EditorBottomSheetTabAdapter.java @@ -70,6 +70,29 @@ public class EditorBottomSheetTabAdapter extends FragmentStateAdapter { return getFragmentById(getItemId(index)); } + @Nullable + public Fragment getFragmentById(long itemId) { + final var fragments = getFragments(); + if (fragments != null) { + return fragments.get(itemId); + } + + return null; + } + + @Nullable + private LongSparseArray getFragments() { + try { + final var field = FragmentStateAdapter.class.getDeclaredField("mFragments"); + field.setAccessible(true); + return (LongSparseArray) field.get(this); + } catch (Throwable th) { + LOG.error("Unable to reflect fragment list from adapter."); + } + + return null; + } + @NonNull @Override public Fragment createFragment(int position) { @@ -98,21 +121,6 @@ public class EditorBottomSheetTabAdapter extends FragmentStateAdapter { return findFragmentByClass(SimpleOutputFragment.class); } - @Nullable - public AppLogFragment getLogFragment() { - return findFragmentByClass(AppLogFragment.class); - } - - @Nullable - public DiagnosticsListFragment getDiagnosticsFragment() { - return findFragmentByClass(DiagnosticsListFragment.class); - } - - @Nullable - public SearchResultFragment getSearchResultFragment() { - return findFragmentByClass(SearchResultFragment.class); - } - @Nullable private T findFragmentByClass(Class clazz) { final var name = clazz.getName(); @@ -126,26 +134,18 @@ public class EditorBottomSheetTabAdapter extends FragmentStateAdapter { } @Nullable - public Fragment getFragmentById(long itemId) { - final var fragments = getFragments(); - if (fragments != null) { - return fragments.get(itemId); - } - - return null; + public AppLogFragment getLogFragment() { + return findFragmentByClass(AppLogFragment.class); } @Nullable - private LongSparseArray getFragments() { - try { - final var field = FragmentStateAdapter.class.getDeclaredField("mFragments"); - field.setAccessible(true); - return (LongSparseArray) field.get(this); - } catch (Throwable th) { - LOG.error("Unable to reflect fragment list from adapter."); - } + public DiagnosticsListFragment getDiagnosticsFragment() { + return findFragmentByClass(DiagnosticsListFragment.class); + } - return null; + @Nullable + public SearchResultFragment getSearchResultFragment() { + return findFragmentByClass(SearchResultFragment.class); } public int findIndexOfFragmentByClass(@NonNull Class tClass) { 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 8fc7b4bda..5d9035caf 100644 --- a/app/src/main/java/com/itsaky/androidide/adapters/IconTextAdapter.java +++ b/app/src/main/java/com/itsaky/androidide/adapters/IconTextAdapter.java @@ -98,14 +98,6 @@ public abstract class IconTextAdapter extends RecyclerView.Adapter { - 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; @@ -115,4 +107,12 @@ public abstract class IconTextAdapter extends RecyclerView.Adapter { + default boolean onBind(T item, VH holder, int position) { + return false; + } + + default void postBind(T item, VH holder, int position) {} + } } diff --git a/app/src/main/java/com/itsaky/androidide/adapters/LogLinesAdapter.kt b/app/src/main/java/com/itsaky/androidide/adapters/LogLinesAdapter.kt index b8394d258..8de02078c 100644 --- a/app/src/main/java/com/itsaky/androidide/adapters/LogLinesAdapter.kt +++ b/app/src/main/java/com/itsaky/androidide/adapters/LogLinesAdapter.kt @@ -41,118 +41,122 @@ import com.itsaky.androidide.utils.TypefaceUtils */ class LogLinesAdapter : RecyclerView.Adapter() { - private val log = ILogger.newInstance(javaClass.simpleName) - private val lines: MutableList = mutableListOf() - var simpleFormatting = true + private val log = ILogger.newInstance(javaClass.simpleName) + private val lines: MutableList = mutableListOf() + var simpleFormatting = true - companion object { - const val MAX_LINES = 10 * 1000 + companion object { + const val MAX_LINES = 10 * 1000 + } + + class VH(val text: TextView) : RecyclerView.ViewHolder(text) + + @SuppressLint("NotifyDataSetChanged") + fun clear() { + this.lines.clear() + this.notifyDataSetChanged() + } + + fun add(line: LogLine) { + val wrapped = wrap(line) + this.lines.add(wrapped) + notifyItemInserted(this.lines.lastIndex) + + if (this.lines.size > MAX_LINES) { + this.lines.removeAt(0) + this.notifyItemRemoved(0) } + } - class VH(val text: TextView) : RecyclerView.ViewHolder(text) + fun getLine(index: Int) = this.lines[index].log - @SuppressLint("NotifyDataSetChanged") - fun clear() { - this.lines.clear() - this.notifyDataSetChanged() - } - - fun add(line: LogLine) { - val wrapped = wrap(line) - this.lines.add(wrapped) - notifyItemInserted(this.lines.lastIndex) - - if (this.lines.size > MAX_LINES) { - this.lines.removeAt(0) - this.notifyItemRemoved(0) + fun allAsString() = + StringBuilder() + .apply { + for (line in lines) { + append(lineString(line)) + append("\n") } + } + .toString() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH { + val dp1 = SizeUtils.dp2px(1f) + val text = TextView(parent.context) + text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11f) + text.setPaddingRelative(dp1, dp1 / 2, dp1, dp1 / 2) + text.setTextIsSelectable(true) + text.maxLines = 1 + text.typeface = TypefaceUtils.jetbrainsMono() + return VH(text) + } + + override fun onBindViewHolder(holder: VH, position: Int) { + val line = lines[position] + if (!line.visible) { + holder.itemView.visibility = View.GONE + return + } else { + holder.itemView.visibility = View.VISIBLE } - fun getLine(index: Int) = this.lines[index].log + val spanned = line.spannable + holder.text.text = spanned + } - fun allAsString() = - StringBuilder() - .apply { - for (line in lines) { - append(lineString(line)) - append("\n") - } - } - .toString() + override fun getItemCount() = this.lines.size - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH { - val dp1 = SizeUtils.dp2px(1f) - val text = TextView(parent.context) - text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11f) - text.setPaddingRelative(dp1, dp1 / 2, dp1, dp1 / 2) - text.setTextIsSelectable(true) - text.maxLines = 1 - text.typeface = TypefaceUtils.jetbrainsMono() - return VH(text) + private fun wrap(line: LogLine): LogWrapper { + val spannable = SpannableString(lineString(line)) + val color = + when (line.priority) { + ERROR -> -0xbbcca + WARNING -> -0x14c5 + INFO -> -0xb350b0 + else -> -0xa0a0b + } + spannable.setSpan( + ForegroundColorSpan(color), + 0, + spannable.length, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) + return LogWrapper(line, spannable).apply { visible = true } + } + + private fun lineString(logLine: LogLine) = + if (simpleFormatting) { + logLine.toSimpleString() + } else { + logLine.toString() } - override fun onBindViewHolder(holder: VH, position: Int) { - val line = lines[position] - if (!line.visible) { - holder.itemView.visibility = View.GONE - return - } else { - holder.itemView.visibility = View.VISIBLE - } + class LogWrapper(val log: LogLine, val spannable: Spannable) : LogLine() { - val spanned = line.spannable - holder.text.text = spanned + var visible = true + + override fun toString() = this.log.toString() + override fun toSimpleString(): String = this.log.toSimpleString() + override fun formattedTagAndMessage(): String = this.log.formattedTagAndMessage() + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as LogWrapper + + if (log != other.log) return false + if (spannable != other.spannable) return false + if (visible != other.visible) return false + + return true } - override fun getItemCount() = this.lines.size - - private fun wrap(line: LogLine): LogWrapper { - val spannable = SpannableString(lineString(line)) - val color = - when (line.priority) { - ERROR -> -0xbbcca - WARNING -> -0x14c5 - INFO -> -0xb350b0 - else -> -0xa0a0b - } - spannable.setSpan( - ForegroundColorSpan(color), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - return LogWrapper(line, spannable).apply { visible = true } - } - - private fun lineString(logLine: LogLine) = - if (simpleFormatting) { - logLine.toSimpleString() - } else { - logLine.toString() - } - - class LogWrapper(val log: LogLine, val spannable: Spannable) : LogLine() { - - var visible = true - - override fun toString() = this.log.toString() - override fun toSimpleString(): String = this.log.toSimpleString() - override fun formattedTagAndMessage(): String = this.log.formattedTagAndMessage() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as LogWrapper - - if (log != other.log) return false - if (spannable != other.spannable) return false - if (visible != other.visible) return false - - return true - } - - override fun hashCode(): Int { - var result = log.hashCode() - result = 31 * result + spannable.hashCode() - result = 31 * result + visible.hashCode() - return result - } + override fun hashCode(): Int { + var result = log.hashCode() + result = 31 * result + spannable.hashCode() + result = 31 * result + visible.hashCode() + return result } + } } 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 a6daf4f45..0b1a67d6f 100755 --- a/app/src/main/java/com/itsaky/androidide/adapters/SimpleIconTextAdapter.java +++ b/app/src/main/java/com/itsaky/androidide/adapters/SimpleIconTextAdapter.java @@ -36,7 +36,10 @@ public class SimpleIconTextAdapter extends IconTextAdapter { this.items = items; } - @NonNull + @Override + public int getItemCount() { + return items.size(); + } @NonNull @Override public IconTextListItem getItemAt(int index) { return items.get(index); @@ -53,8 +56,5 @@ public class SimpleIconTextAdapter extends IconTextAdapter { return getItemAt(index).getText(); } - @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 efba874d5..0ee79b2b3 100755 --- a/app/src/main/java/com/itsaky/androidide/adapters/SymbolInputAdapter.java +++ b/app/src/main/java/com/itsaky/androidide/adapters/SymbolInputAdapter.java @@ -33,6 +33,18 @@ import java.util.List; public class SymbolInputAdapter extends RecyclerView.Adapter { + private static final List pairs; + + static { + pairs = new ArrayList<>(); + pairs.add('}'); + pairs.add(')'); + pairs.add(']'); + pairs.add('"'); + pairs.add('\''); + pairs.add('>'); + } + private final IDEEditor editor; private Symbol[] symbols; @@ -73,15 +85,6 @@ public class SymbolInputAdapter extends RecyclerView.Adapter text.length()) { return; @@ -108,15 +111,12 @@ public class SymbolInputAdapter extends RecyclerView.Adapter pairs; + public static class VH extends RecyclerView.ViewHolder { + LayoutSymbolItemBinding binding; - static { - pairs = new ArrayList<>(); - pairs.add('}'); - pairs.add(')'); - pairs.add(']'); - pairs.add('"'); - pairs.add('\''); - pairs.add('>'); + public VH(LayoutSymbolItemBinding binding) { + super(binding.getRoot()); + this.binding = binding; + } } } diff --git a/app/src/main/java/com/itsaky/androidide/adapters/WizardTemplateAdapter.java b/app/src/main/java/com/itsaky/androidide/adapters/WizardTemplateAdapter.java index b6fffbf1e..0268a8afd 100644 --- a/app/src/main/java/com/itsaky/androidide/adapters/WizardTemplateAdapter.java +++ b/app/src/main/java/com/itsaky/androidide/adapters/WizardTemplateAdapter.java @@ -1,8 +1,6 @@ package com.itsaky.androidide.adapters; -import android.net.Uri; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; import android.widget.TextView; @@ -13,23 +11,17 @@ import androidx.recyclerview.widget.RecyclerView; import com.blankj.utilcode.util.ConvertUtils; import com.google.android.material.imageview.ShapeableImageView; import com.google.android.material.shape.CornerFamily; - import com.itsaky.androidide.databinding.WizardTemplateItemBinding; import com.itsaky.androidide.models.ProjectTemplate; -import java.io.File; + import java.util.ArrayList; import java.util.List; import java.util.Objects; public class WizardTemplateAdapter extends RecyclerView.Adapter { - public interface OnItemClickListener { - void onItemClick(ProjectTemplate item, int position); - } - private final List mItems = new ArrayList<>(); private OnItemClickListener mListener; - public WizardTemplateAdapter() {} public void setOnItemClickListener(OnItemClickListener listener) { @@ -117,4 +109,8 @@ public class WizardTemplateAdapter extends RecyclerView.AdapterThe reverse of @{link #getFileForDocId}. - */ - private static String getDocIdForFile(File file) { - return file.getAbsolutePath(); - } - - /** Get the file given a document id (the reverse of {@link #getDocIdForFile(File)}). */ - private static File getFileForDocId(String docId) throws FileNotFoundException { - final File f = new File(docId); - if (!f.exists()) throw new FileNotFoundException(f.getAbsolutePath() + " not found"); - return f; - } - - private static String getMimeType(File file) { - if (file.isDirectory()) { - return Document.MIME_TYPE_DIR; - } else { - final String name = file.getName(); - final int lastDot = name.lastIndexOf('.'); - if (lastDot >= 0) { - final String extension = name.substring(lastDot + 1).toLowerCase(Locale.ROOT); - final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); - if (mime != null) return mime; - } - return "application/octet-stream"; - } - } - - @Override - public Cursor queryRoots(String[] projection) { - final MatrixCursor result = - new MatrixCursor(projection != null ? projection : DEFAULT_ROOT_PROJECTION); - final String applicationName = getContext().getString(R.string.app_name); - - final MatrixCursor.RowBuilder row = result.newRow(); - LOG.debug("queryRoots() before all add"); - row.add(Root.COLUMN_ROOT_ID, getDocIdForFile(BASE_DIR)); - LOG.debug("queryRoots() before all add, 1"); - row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(BASE_DIR)); - LOG.debug("queryRoots() before all add, 2"); - row.add(Root.COLUMN_SUMMARY, null); - LOG.debug("queryRoots() before all add, 3"); - row.add( - Root.COLUMN_FLAGS, - Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD); - LOG.debug("queryRoots() before all add, 4"); - row.add(Root.COLUMN_TITLE, applicationName); - LOG.debug("queryRoots() before all add, 5"); - row.add(Root.COLUMN_MIME_TYPES, ALL_MIME_TYPES); - LOG.debug("queryRoots() before all add, 6"); - row.add(Root.COLUMN_AVAILABLE_BYTES, BASE_DIR.getFreeSpace()); - LOG.debug("queryRoots() before all add, 7"); - row.add(Root.COLUMN_ICON, R.mipmap.ic_launcher); - LOG.debug("queryRoots() before all add"); - return result; - } - - @Override - public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException { - final MatrixCursor result = - new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION); - includeFile(result, documentId, null); - return result; - } - - @Override - public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder) - throws FileNotFoundException { - final MatrixCursor result = - new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION); - final File parent = getFileForDocId(parentDocumentId); - for (File file : parent.listFiles()) { - includeFile(result, null, file); - } - return result; - } - - @Override - public ParcelFileDescriptor openDocument( - final String documentId, String mode, CancellationSignal signal) - throws FileNotFoundException { - final File file = getFileForDocId(documentId); - final int accessMode = ParcelFileDescriptor.parseMode(mode); - return ParcelFileDescriptor.open(file, accessMode); - } - - @Override - public AssetFileDescriptor openDocumentThumbnail( - String documentId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException { - final File file = getFileForDocId(documentId); - final ParcelFileDescriptor pfd = - ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); - return new AssetFileDescriptor(pfd, 0, file.length()); - } - @Override public boolean onCreate() { return true; } + @Override + public boolean isChildDocument(String parentDocumentId, String documentId) { + return documentId.startsWith(parentDocumentId); + } + @Override public String createDocument(String parentDocumentId, String mimeType, String displayName) throws FileNotFoundException { @@ -231,9 +137,62 @@ public class IDEDocumentsProvider extends DocumentsProvider { } @Override - public String getDocumentType(String documentId) throws FileNotFoundException { - File file = getFileForDocId(documentId); - return getMimeType(file); + public Cursor queryRoots(String[] projection) { + final MatrixCursor result = + new MatrixCursor(projection != null ? projection : DEFAULT_ROOT_PROJECTION); + final String applicationName = getContext().getString(R.string.app_name); + + final MatrixCursor.RowBuilder row = result.newRow(); + LOG.debug("queryRoots() before all add"); + row.add(Root.COLUMN_ROOT_ID, getDocIdForFile(BASE_DIR)); + LOG.debug("queryRoots() before all add, 1"); + row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(BASE_DIR)); + LOG.debug("queryRoots() before all add, 2"); + row.add(Root.COLUMN_SUMMARY, null); + LOG.debug("queryRoots() before all add, 3"); + row.add( + Root.COLUMN_FLAGS, + Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD); + LOG.debug("queryRoots() before all add, 4"); + row.add(Root.COLUMN_TITLE, applicationName); + LOG.debug("queryRoots() before all add, 5"); + row.add(Root.COLUMN_MIME_TYPES, ALL_MIME_TYPES); + LOG.debug("queryRoots() before all add, 6"); + row.add(Root.COLUMN_AVAILABLE_BYTES, BASE_DIR.getFreeSpace()); + LOG.debug("queryRoots() before all add, 7"); + row.add(Root.COLUMN_ICON, R.mipmap.ic_launcher); + LOG.debug("queryRoots() before all add"); + return result; + } + + /** + * Get the document id given a file. This document id must be consistent across time as other + * applications may save the ID and use it to reference documents later. + * + *

The reverse of @{link #getFileForDocId}. + */ + private static String getDocIdForFile(File file) { + return file.getAbsolutePath(); + } + + @Override + public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException { + final MatrixCursor result = + new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION); + includeFile(result, documentId, null); + return result; + } + + @Override + public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder) + throws FileNotFoundException { + final MatrixCursor result = + new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION); + final File parent = getFileForDocId(parentDocumentId); + for (File file : parent.listFiles()) { + includeFile(result, null, file); + } + return result; } @Override @@ -277,8 +236,27 @@ public class IDEDocumentsProvider extends DocumentsProvider { } @Override - public boolean isChildDocument(String parentDocumentId, String documentId) { - return documentId.startsWith(parentDocumentId); + public String getDocumentType(String documentId) throws FileNotFoundException { + File file = getFileForDocId(documentId); + return getMimeType(file); + } + + @Override + public ParcelFileDescriptor openDocument( + final String documentId, String mode, CancellationSignal signal) + throws FileNotFoundException { + final File file = getFileForDocId(documentId); + final int accessMode = ParcelFileDescriptor.parseMode(mode); + return ParcelFileDescriptor.open(file, accessMode); + } + + @Override + public AssetFileDescriptor openDocumentThumbnail( + String documentId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException { + final File file = getFileForDocId(documentId); + final ParcelFileDescriptor pfd = + ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); + return new AssetFileDescriptor(pfd, 0, file.length()); } /** @@ -317,4 +295,26 @@ public class IDEDocumentsProvider extends DocumentsProvider { row.add(Document.COLUMN_FLAGS, flags); row.add(Document.COLUMN_ICON, R.mipmap.ic_launcher); } + + /** Get the file given a document id (the reverse of {@link #getDocIdForFile(File)}). */ + private static File getFileForDocId(String docId) throws FileNotFoundException { + final File f = new File(docId); + if (!f.exists()) throw new FileNotFoundException(f.getAbsolutePath() + " not found"); + return f; + } + + private static String getMimeType(File file) { + if (file.isDirectory()) { + return Document.MIME_TYPE_DIR; + } else { + final String name = file.getName(); + final int lastDot = name.lastIndexOf('.'); + if (lastDot >= 0) { + final String extension = name.substring(lastDot + 1).toLowerCase(Locale.ROOT); + final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + if (mime != null) return mime; + } + return "application/octet-stream"; + } + } } 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 a4b757f11..debf39a82 100755 --- a/app/src/main/java/com/itsaky/androidide/fragments/BaseFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/BaseFragment.java @@ -20,9 +20,6 @@ package com.itsaky.androidide.fragments; -import abhishekti7.unicorn.filepicker.UnicornFilePicker; -import abhishekti7.unicorn.filepicker.ui.FilePickerActivity; - import android.app.Activity; import android.content.Intent; import android.os.Environment; @@ -41,31 +38,12 @@ import com.itsaky.toaster.Toaster; import java.io.File; import java.util.ArrayList; +import abhishekti7.unicorn.filepicker.UnicornFilePicker; +import abhishekti7.unicorn.filepicker.ui.FilePickerActivity; + public class BaseFragment extends Fragment { private FileChoserCallback dirCallback; - - protected StudioActivity getStudioActivity() { - return (StudioActivity) getActivity(); - } - - protected void pickDirectory(FileChoserCallback dirCallback) { - this.dirCallback = dirCallback; - UnicornFilePicker.from(this) - .addConfigBuilder() - .addItemDivider(false) - .selectMultipleFiles(false) - .setRootDirectory(Environment.getExternalStorageDirectory().getAbsolutePath()) - .showHiddenFiles(true) - .showOnlyDirectory(true) - .theme(R.style.AppTheme_FilePicker) - .build(); - Intent intent = new Intent(requireActivity(), FilePickerActivity.class); - - // .forResult(abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE); - startForResult.launch(intent); - } - ActivityResultLauncher startForResult = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), @@ -95,6 +73,27 @@ public class BaseFragment extends Fragment { } }); + protected StudioActivity getStudioActivity() { + return (StudioActivity) getActivity(); + } + + protected void pickDirectory(FileChoserCallback dirCallback) { + this.dirCallback = dirCallback; + UnicornFilePicker.from(this) + .addConfigBuilder() + .addItemDivider(false) + .selectMultipleFiles(false) + .setRootDirectory(Environment.getExternalStorageDirectory().getAbsolutePath()) + .showHiddenFiles(true) + .showOnlyDirectory(true) + .theme(R.style.AppTheme_FilePicker) + .build(); + Intent intent = new Intent(requireActivity(), FilePickerActivity.class); + + // .forResult(abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE); + startForResult.launch(intent); + } + interface FileChoserCallback { void picketDictionary(File file); } 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 bb5b2a912..970560321 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/DiagnosticsListFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/DiagnosticsListFragment.java @@ -70,6 +70,14 @@ public class DiagnosticsListFragment extends Fragment { unsavedAdapter = null; } + @Override + public void onDestroyView() { + super.onDestroyView(); + unsavedAdapter = null; + list = null; + emptyView = null; + } + public void setAdapter(@NonNull DiagnosticsAdapter adapter) { if (list != null) { list.setAdapter(adapter); @@ -84,12 +92,4 @@ public class DiagnosticsListFragment extends Fragment { list.setVisibility(error ? View.GONE : View.VISIBLE); } } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unsavedAdapter = null; - list = null; - emptyView = null; - } } 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 d11fe6506..59d7aac06 100755 --- a/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java @@ -95,6 +95,14 @@ public class FileTreeFragment extends BottomSheetDialogFragment } } + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; + mFileTreeView = null; + mFileActionListener = null; + } + @Override public View onCreateView( @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -201,12 +209,6 @@ public class FileTreeFragment extends BottomSheetDialogFragment __ -> finalWhenDone.run()); } - private void updateChevron(@NonNull TreeNode node) { - if (node.getViewHolder() instanceof FileTreeViewHolder) { - ((FileTreeViewHolder) node.getViewHolder()).updateChevron(!node.isExpanded()); - } - } - private void getNodeFromFiles(File[] files, TreeNode parent) { Arrays.sort(files, new FileTreeCallable.SortFileName()); Arrays.sort(files, new FileTreeCallable.SortFolder()); @@ -217,6 +219,12 @@ public class FileTreeFragment extends BottomSheetDialogFragment } } + 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) { @@ -332,14 +340,6 @@ public class FileTreeFragment extends BottomSheetDialogFragment } } - @Override - public void onDestroyView() { - super.onDestroyView(); - binding = null; - mFileTreeView = null; - mFileActionListener = null; - } - public interface FileActionListener { CodeEditorView openFile(File file); diff --git a/app/src/main/java/com/itsaky/androidide/fragments/LogFragments.kt b/app/src/main/java/com/itsaky/androidide/fragments/LogFragments.kt index 946e758f0..67972e73d 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/LogFragments.kt +++ b/app/src/main/java/com/itsaky/androidide/fragments/LogFragments.kt @@ -24,30 +24,30 @@ import com.itsaky.androidide.utils.ILogger * @author Akash Yadav */ class IDELogFragment : LogViewFragment() { - private var logListener: ILogger.LogListener? = - ILogger.LogListener { priority, tag, message -> - if (message.contains("\n")) { - val split = message.split("\n").toTypedArray() - for (line in split) { - logLine(priority, tag, line) - } - } else { - logLine(priority, tag, message) - } + private var logListener: ILogger.LogListener? = + ILogger.LogListener { priority, tag, message -> + if (message.contains("\n")) { + val split = message.split("\n").toTypedArray() + for (line in split) { + logLine(priority, tag, line) } - - init { - ILogger.addLogListener(this.logListener) + } else { + logLine(priority, tag, message) + } } - override fun isSimpleFormattingEnabled() = true - override fun getLogType() = "ide_logs" + init { + ILogger.addLogListener(this.logListener) + } - override fun onDestroy() { - super.onDestroy() - ILogger.removeLogListener(logListener) - logListener = null - } + override fun isSimpleFormattingEnabled() = true + override fun getLogType() = "ide_logs" + + override fun onDestroy() { + super.onDestroy() + ILogger.removeLogListener(logListener) + logListener = null + } } /** @@ -55,6 +55,6 @@ class IDELogFragment : LogViewFragment() { * @author Akash Yadav */ class AppLogFragment : LogViewFragment() { - override fun isSimpleFormattingEnabled() = false - override fun getLogType() = "app_logs" + override fun isSimpleFormattingEnabled() = false + override fun getLogType() = "app_logs" } diff --git a/app/src/main/java/com/itsaky/androidide/fragments/LogViewFragment.kt b/app/src/main/java/com/itsaky/androidide/fragments/LogViewFragment.kt index ea9eaf60b..debd47151 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/LogViewFragment.kt +++ b/app/src/main/java/com/itsaky/androidide/fragments/LogViewFragment.kt @@ -55,7 +55,7 @@ abstract class LogViewFragment : Fragment() { if (!lineString.endsWith("\n")) { lineString += "\n" } - + ThreadUtils.runOnUiThread { this.binding!!.editor.append(lineString) } } diff --git a/app/src/main/java/com/itsaky/androidide/fragments/MainFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/MainFragment.java index b28e4e499..7262ba93b 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/MainFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/MainFragment.java @@ -1,34 +1,22 @@ package com.itsaky.androidide.fragments; -import abhishekti7.unicorn.filepicker.UnicornFilePicker; -import abhishekti7.unicorn.filepicker.ui.FilePickerActivity; - -import android.app.Activity; import android.content.Intent; import android.os.Bundle; -import android.os.Environment; 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.google.android.material.dialog.MaterialAlertDialogBuilder; import com.itsaky.androidide.EditorActivity; import com.itsaky.androidide.PreferencesActivity; import com.itsaky.androidide.R; import com.itsaky.androidide.TerminalActivity; -import com.itsaky.androidide.app.StudioApp; import com.itsaky.androidide.databinding.FragmentMainBinding; import com.itsaky.androidide.projects.ProjectManager; -import com.itsaky.androidide.utils.DialogUtils; -import com.itsaky.toaster.Toaster; import java.io.File; -import java.util.ArrayList; public class MainFragment extends BaseFragment implements WizardFragment.OnProjectCreatedListener, View.OnClickListener { @@ -97,13 +85,13 @@ public class MainFragment extends BaseFragment .commit(); } - private void gotoSettings() { - startActivity(new Intent(requireActivity(), PreferencesActivity.class)); - } - @Override public void openProject(@NonNull File root) { ProjectManager.INSTANCE.setProjectPath(root.getAbsolutePath()); startActivity(new Intent(requireActivity(), EditorActivity.class)); } + + private void gotoSettings() { + startActivity(new Intent(requireActivity(), PreferencesActivity.class)); + } } 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 284f9735c..016748e95 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/NonEditableEditorFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/NonEditableEditorFragment.java @@ -64,6 +64,12 @@ public abstract class NonEditableEditorFragment extends Fragment { editor.setColorScheme(new SchemeAndroidIDE()); } + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; + } + @Nullable public IDEEditor getEditor() { if (binding == null) { @@ -71,10 +77,4 @@ public abstract class NonEditableEditorFragment extends Fragment { } return binding.editor; } - - @Override - public void onDestroyView() { - super.onDestroyView(); - binding = 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 a76486540..8bbb0e514 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/SearchResultFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/SearchResultFragment.java @@ -68,6 +68,14 @@ public class SearchResultFragment extends Fragment { unsavedAdapter = null; } + @Override + public void onDestroyView() { + super.onDestroyView(); + unsavedAdapter = null; + list = null; + emptyView = null; + } + public void setAdapter(@NonNull final SearchListAdapter adapter) { if (list == null) { unsavedAdapter = adapter; @@ -82,12 +90,4 @@ public class SearchResultFragment extends Fragment { list.setVisibility(error ? View.GONE : View.VISIBLE); } } - - @Override - public void onDestroyView() { - super.onDestroyView(); - unsavedAdapter = null; - list = null; - emptyView = null; - } } diff --git a/app/src/main/java/com/itsaky/androidide/fragments/WizardFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/WizardFragment.java index ed98d3d5c..e27088431 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/WizardFragment.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/WizardFragment.java @@ -19,7 +19,6 @@ import androidx.transition.TransitionManager; import com.google.android.material.transition.MaterialFadeThrough; import com.google.android.material.transition.MaterialSharedAxis; - import com.itsaky.androidide.R; import com.itsaky.androidide.adapters.WizardTemplateAdapter; import com.itsaky.androidide.app.StudioApp; @@ -68,9 +67,6 @@ public class WizardFragment extends BaseFragment { private OnProjectCreatedListener mListener; private boolean mLast; - private int minSdkIndex; - private int targetSdkIndex; - private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) { @Override @@ -78,6 +74,8 @@ public class WizardFragment extends BaseFragment { onNavigateBack(); } }; + private int minSdkIndex; + private int targetSdkIndex; @Nullable @Override @@ -171,12 +169,6 @@ public class WizardFragment extends BaseFragment { initDetailsView(); } - @Override - public void onDestroy() { - super.onDestroy(); - onBackPressedCallback.setEnabled(false); - } - @Override public void onDestroyView() { super.onDestroyView(); @@ -190,6 +182,28 @@ public class WizardFragment extends BaseFragment { mProgressSheet = null; } + @Override + public void onDestroy() { + super.onDestroy(); + onBackPressedCallback.setEnabled(false); + } + + public void onSuccess(File root) { + if (mProgressSheet != null && mProgressSheet.isShowing()) { + mProgressSheet.dismiss(); + } + StudioApp.getInstance().toast(R.string.project_created_successfully, Toaster.Type.SUCCESS); + + if (mListener != null) { + getParentFragmentManager().popBackStack(); + mListener.openProject(root); + } + } + + public void setOnProjectCreatedListener(OnProjectCreatedListener listener) { + mListener = listener; + } + private String getSelectedItem(int pos, AutoCompleteTextView view) { return view.getAdapter().getItem(pos).toString(); } @@ -516,18 +530,6 @@ public class WizardFragment extends BaseFragment { setSaveLocation(); } - public void onSuccess(File root) { - if (mProgressSheet != null && mProgressSheet.isShowing()) { - mProgressSheet.dismiss(); - } - StudioApp.getInstance().toast(R.string.project_created_successfully, Toaster.Type.SUCCESS); - - if (mListener != null) { - getParentFragmentManager().popBackStack(); - mListener.openProject(root); - } - } - private void createProgressSheet() { mProgressSheet = new ProgressSheet(); mProgressSheet.setShowShadow(false); @@ -554,8 +556,4 @@ public class WizardFragment extends BaseFragment { public interface OnProjectCreatedListener { void openProject(File project); } - - public void setOnProjectCreatedListener(OnProjectCreatedListener listener) { - mListener = listener; - } } diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/AbstractReferenceEditor.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/AbstractReferenceEditor.java index cda03ea3e..cb52a09bf 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/AbstractReferenceEditor.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/attr/AbstractReferenceEditor.java @@ -41,15 +41,6 @@ public abstract class AbstractReferenceEditor extends BaseValueEditorFragment { private static final ILogger LOG = ILogger.newInstance("ReferenceEditor"); public TextWatcherAdapter resInputWatcher; - @Override - protected void notifyValueChanged(@NonNull String newValue) { - try { - super.notifyValueChanged(newValue); - } catch (Throwable e) { - LOG.error("Unable to update resource value to '" + newValue + "'", e); - } - } - protected void setupReferenceInput(MaterialAutoCompleteTextView referenceInput) { Objects.requireNonNull(referenceInput); @@ -78,6 +69,15 @@ public abstract class AbstractReferenceEditor extends BaseValueEditorFragment { .whenComplete((list, error) -> handleAutoCompleteResult(list, error, referenceInput)); } + @Override + protected void notifyValueChanged(@NonNull String newValue) { + try { + super.notifyValueChanged(newValue); + } catch (Throwable e) { + LOG.error("Unable to update resource value to '" + newValue + "'", e); + } + } + protected void handleAutoCompleteResult( List list, Throwable error, AutoCompleteTextView referenceInput) { if (list == null || list.isEmpty()) { diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/ColorEditor.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/ColorEditor.java index b4cc6379f..a4d5bf513 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/ColorEditor.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/attr/ColorEditor.java @@ -18,7 +18,6 @@ package com.itsaky.androidide.fragments.attr; import android.app.Dialog; -import android.graphics.Color; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -77,28 +76,6 @@ public class ColorEditor extends AbstractReferenceEditor { } } - @Override - protected List computeReferenceItems() { - final var list = new ArrayList(); - final var tables = ValuesTableFactory.getAllTables(); - for (var entry : tables.entrySet()) { - final var dimens = entry.getValue().getTable("color"); - if (dimens != null) { - list.addAll(dimens.keySet().stream().map("@color/"::concat).collect(Collectors.toSet())); - } - } - - list.addAll( - FrameworkValues.listColors().stream() - .map("@android:color/"::concat) - .collect(Collectors.toList())); - - final var resTable = StudioApp.getInstance().getResourceTable(); - list.addAll(resTable.listResourceNames("color")); - - return list; - } - private Dialog getColorPickerDialog() { if (colorPickerDialog == null) { @@ -122,4 +99,26 @@ public class ColorEditor extends AbstractReferenceEditor { return colorPickerDialog; } + + @Override + protected List computeReferenceItems() { + final var list = new ArrayList(); + final var tables = ValuesTableFactory.getAllTables(); + for (var entry : tables.entrySet()) { + final var dimens = entry.getValue().getTable("color"); + if (dimens != null) { + list.addAll(dimens.keySet().stream().map("@color/"::concat).collect(Collectors.toSet())); + } + } + + list.addAll( + FrameworkValues.listColors().stream() + .map("@android:color/"::concat) + .collect(Collectors.toList())); + + final var resTable = StudioApp.getInstance().getResourceTable(); + list.addAll(resTable.listResourceNames("color")); + + return list; + } } diff --git a/app/src/main/java/com/itsaky/androidide/fragments/attr/FlagEditor.java b/app/src/main/java/com/itsaky/androidide/fragments/attr/FlagEditor.java index e20a42c46..fa9088ab5 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/attr/FlagEditor.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/attr/FlagEditor.java @@ -63,6 +63,13 @@ public class FlagEditor extends FixedValueEditor { // Does noting... } + @Override + protected Chip newChip(String title, boolean checked) { + final var chip = super.newChip(title, checked); + chip.setOnCheckedChangeListener(this::onCheckChangedMultiple); + return chip; + } + protected void onCheckChangedMultiple(CompoundButton compoundButton, boolean b) { final var items = new ArrayList(); for (var id : this.chipGroup.getCheckedChipIds()) { @@ -78,11 +85,4 @@ public class FlagEditor extends FixedValueEditor { notifyValueChanged(TextUtils.join("|", items)); } - - @Override - protected Chip newChip(String title, boolean checked) { - final var chip = super.newChip(title, checked); - chip.setOnCheckedChangeListener(this::onCheckChangedMultiple); - return chip; - } } 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 1a54bff9a..71892a537 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 @@ -31,6 +31,11 @@ import com.itsaky.androidide.managers.PreferenceManager; public abstract class BasePreferenceFragment extends PreferenceFragmentCompat { + @Override + public void onCreatePreferences(Bundle p1, String p2) { + setPreferencesFromResource(com.itsaky.androidide.R.xml.ide_prefs, p2); + } + @Override public View onCreateView( @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -43,11 +48,6 @@ public abstract class BasePreferenceFragment extends PreferenceFragmentCompat { return v; } - @Override - public void onCreatePreferences(Bundle p1, String p2) { - setPreferencesFromResource(com.itsaky.androidide.R.xml.ide_prefs, p2); - } - protected PreferenceManager getPrefManager() { return StudioApp.getInstance().getPrefManager(); } 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 466a76bc9..f4961b57f 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 @@ -120,15 +120,6 @@ public class BuildPreferences extends BasePreferenceFragment return true; } - @Override - public boolean onPreferenceChange(final Preference preference, final Object newValue) { - final var key = preference.getKey(); - if (KEY_TP_FIX.equals(key)) { - getPrefManager().putBoolean(KEY_TP_FIX, ((boolean) newValue)); - } - return true; - } - private void changeGradleDist() { final var builder = DialogUtils.newMaterialDialogBuilder(getContext()); final var binding = LayoutDialogTextInputBinding.inflate(getLayoutInflater()); @@ -228,4 +219,13 @@ public class BuildPreferences extends BasePreferenceFragment ? progressSheet = new ProgressSheet().setMessage(getString(R.string.please_wait)) : progressSheet; } + + @Override + public boolean onPreferenceChange(final Preference preference, final Object newValue) { + final var key = preference.getKey(); + if (KEY_TP_FIX.equals(key)) { + getPrefManager().putBoolean(KEY_TP_FIX, ((boolean) newValue)); + } + 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 15dcba6d9..f4e3cee23 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 @@ -123,20 +123,6 @@ public class IDEPreferences extends BasePreferenceFragment : mBuildPreferences; } - private void showChangelog() { - final var intent = new Intent(Intent.ACTION_VIEW); - intent.setData( - Uri.parse( - BaseApplication.GITHUB_URL.concat("/releases/tag/v").concat(BuildConfig.VERSION_NAME))); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - try { - startActivity(intent); - } catch (Throwable th) { - LOG.error("Unable to start activity to show changelog", th); - StudioApp.getInstance().toast("Unable to start activity", Toaster.Type.ERROR); - } - } - @Override public boolean onPreferenceClick(@NonNull Preference preference) { final String key = preference.getKey(); @@ -156,4 +142,18 @@ public class IDEPreferences extends BasePreferenceFragment } return true; } + + private void showChangelog() { + final var intent = new Intent(Intent.ACTION_VIEW); + intent.setData( + Uri.parse( + BaseApplication.GITHUB_URL.concat("/releases/tag/v").concat(BuildConfig.VERSION_NAME))); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + startActivity(intent); + } catch (Throwable th) { + LOG.error("Unable to start activity to show changelog", th); + StudioApp.getInstance().toast("Unable to start activity", Toaster.Type.ERROR); + } + } } 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 0a01e99d1..6ce903dc2 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 @@ -102,19 +102,6 @@ public class AttrEditorSheet extends BottomSheetDialogFragment setupViewData(); } - @Override - public void onAttach(@NonNull Context context) { - super.onAttach(context); - mDeletionFailedListener = (OnViewDeletionFailedListener) context; - } - - 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)); @@ -132,10 +119,6 @@ public class AttrEditorSheet extends BottomSheetDialogFragment this::onAttrClick)); } - public void setSelectedView(IView view) { - this.selectedView = view; - } - private void onAttrClick( LayoutAttrEditorSheetItemBinding binding, @NonNull XMLAttribute attribute) { @@ -158,6 +141,42 @@ public class AttrEditorSheet extends BottomSheetDialogFragment getValueEditorSheet(attribute).show(getChildFragmentManager(), "attr_value_editor_sheet"); } + @NonNull + public AttrValueEditorSheet getValueEditorSheet(@NonNull XMLAttribute attribute) { + + if (mValueEditorSheet == null) { + mValueEditorSheet = AttrValueEditorSheet.newInstance(attribute); + } + + mValueEditorSheet.setAttribute(attribute); + return mValueEditorSheet; + } + + private boolean dismissIfSelectedNull() { + if (selectedView == null) { + dismiss(); + return true; + } + return false; + } + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + mDeletionFailedListener = (OnViewDeletionFailedListener) context; + } + + public AttrEditorSheet setLayout(File layout) { + Objects.requireNonNull(layout); + + this.layout = layout; + return this; + } + + public void setSelectedView(IView view) { + this.selectedView = view; + } + @Override public void postBind( IconTextListItem item, @NonNull SimpleIconTextAdapter.VH holder, int position) { @@ -276,11 +295,6 @@ public class AttrEditorSheet extends BottomSheetDialogFragment return new ArrayList<>(attributes); } - @Override - public void accept(Attr attr) { - addNewAttribute(attr); - } - @NonNull private AttributeListSheet getAttrListSheet() { if (mAttrListSheet == null) { @@ -291,15 +305,15 @@ public class AttrEditorSheet extends BottomSheetDialogFragment return mAttrListSheet; } - @NonNull - public AttrValueEditorSheet getValueEditorSheet(@NonNull XMLAttribute attribute) { + @Override + public void accept(Attr attr) { + addNewAttribute(attr); + } - if (mValueEditorSheet == null) { - mValueEditorSheet = AttrValueEditorSheet.newInstance(attribute); - } - - mValueEditorSheet.setAttribute(attribute); - return mValueEditorSheet; + private void addNewAttribute(@NonNull Attr attr) { + final XMLAttribute attribute = new XMLAttribute(attr.namespace, attr.name, "", false); + attribute.setAttr(attr); + showValueEditorSheet(attribute); } @Override @@ -320,20 +334,6 @@ public class AttrEditorSheet extends BottomSheetDialogFragment } } - private void addNewAttribute(@NonNull Attr attr) { - final XMLAttribute attribute = new XMLAttribute(attr.namespace, attr.name, "", false); - attribute.setAttr(attr); - showValueEditorSheet(attribute); - } - - private boolean dismissIfSelectedNull() { - if (selectedView == null) { - dismiss(); - return true; - } - return false; - } - /** * 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}. 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 0a01a1981..a4a5ea473 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 @@ -53,12 +53,6 @@ public class AttributeListSheet extends BottomSheetDialogFragment { 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( @@ -104,6 +98,16 @@ public class AttributeListSheet extends BottomSheetDialogFragment { return true; } + public void update() { + update(mItems); + } + + public void update(List items) { + if (binding != null) { + binding.attrList.setAdapter(new AttrListAdapter(clickConsumer, items)); + } + } + public void setItems(@Nullable List items) { if (items == null) { items = new ArrayList<>(); @@ -120,13 +124,9 @@ public class AttributeListSheet extends BottomSheetDialogFragment { this.clickConsumer = (Consumer) getParentFragment(); } - public void update() { - update(mItems); - } - - public void update(List items) { - if (binding != null) { - binding.attrList.setAdapter(new AttrListAdapter(clickConsumer, items)); - } + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setStyle(DialogFragment.STYLE_NORMAL, R.style.AppTheme_SheetAboveKeyboard); } } 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 31b5be043..e2a5396cd 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 @@ -47,6 +47,8 @@ public abstract class BaseBottomSheetFragment extends BottomSheetDialogFragment return mDialog; } + protected void onShow() {} + @Override public View onCreateView( @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -97,10 +99,6 @@ public abstract class BaseBottomSheetFragment extends BottomSheetDialogFragment } } - public boolean isShowing() { - return mDialog != null && mDialog.isShowing(); - } - protected boolean shouldHideTitle() { return !titleEnabled; } @@ -123,6 +121,12 @@ public abstract class BaseBottomSheetFragment extends BottomSheetDialogFragment return this; } + protected abstract void bind(LinearLayout container); + + public boolean isShowing() { + return mDialog != null && mDialog.isShowing(); + } + public void setShowShadow(boolean enabled) { this.shadowEnabled = enabled; } @@ -131,10 +135,6 @@ public abstract class BaseBottomSheetFragment extends BottomSheetDialogFragment this.titleEnabled = enabled; } - protected void onShow() {} - - protected abstract void bind(LinearLayout container); - @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); 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 0de6aa74c..3893a2608 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 @@ -42,13 +42,6 @@ public class OptionsListFragment extends BaseBottomSheetFragment { private RecyclerView mList; private OnOptionsClickListener listener; - @Override - protected void bind(@NonNull LinearLayout container) { - mList = new RecyclerView(requireContext()); - container.removeAllViews(); - container.addView(mList, new LinearLayout.LayoutParams(-1, -1)); - } - @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); @@ -64,6 +57,18 @@ public class OptionsListFragment extends BaseBottomSheetFragment { })); } + @Override + protected String getTitle() { + return getString(com.itsaky.androidide.R.string.file_options); + } + + @Override + protected void bind(@NonNull LinearLayout container) { + mList = new RecyclerView(requireContext()); + container.removeAllViews(); + container.addView(mList, new LinearLayout.LayoutParams(-1, -1)); + } + @Override public void onAttach(@NonNull Context context) { super.onAttach(context); @@ -90,11 +95,6 @@ public class OptionsListFragment extends BaseBottomSheetFragment { 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 c0deb4d9a..917b4f017 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 @@ -41,12 +41,6 @@ public class ProgressSheet extends BaseBottomSheetFragment { private boolean subMessageEnabled = false; private boolean welcomeTextEnabled = false; - @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); @@ -85,6 +79,12 @@ public class ProgressSheet extends BaseBottomSheetFragment { return true; } + @Override + protected void bind(LinearLayout container) { + binding = LayoutProgressSheetBinding.inflate(LayoutInflater.from(getContext())); + container.addView(binding.getRoot()); + } + public ProgressSheet setWelcomeTextEnabled(boolean enabled) { this.welcomeTextEnabled = enabled; return this; 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 dffd7ccd8..1144a8d88 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 @@ -40,34 +40,6 @@ public class TextSheetFragment extends BaseBottomSheetFragment { 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); - } - 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; @@ -111,6 +83,34 @@ public class TextSheetFragment extends BaseBottomSheetFragment { }); } + private void scrollToBottom() { + if (getActivity() != null) getActivity().runOnUiThread(() -> editor.goToEnd()); + } + + @Override + protected String getTitle() { + return getString(R.string.build_output); + } + + @Override + protected void onShow() { + super.onShow(); + editor.setText(getOutputBuilder()); + } + + @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 IDEEditor getEditor() { return editor == null ? createEditor() : editor; } 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 15d318aaa..3c407446c 100644 --- a/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java +++ b/app/src/main/java/com/itsaky/androidide/language/IDELanguage.java @@ -20,11 +20,6 @@ import androidx.annotation.NonNull; import com.itsaky.androidide.app.StudioApp; import com.itsaky.lsp.api.ILanguageServer; -import com.itsaky.lsp.models.DiagnosticItem; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import io.github.rosemoe.sora.lang.Language; @@ -40,10 +35,6 @@ public abstract class IDELanguage implements Language { return false; } - public int getTabSize() { - return StudioApp.getInstance().getPrefManager().getEditorTabSize(); - } - @Override public CharSequence format(CharSequence text) { final var server = getLanguageServer(); @@ -54,11 +45,15 @@ public abstract class IDELanguage implements Language { return text; } - public int getIndentAdvance(@NonNull String line) { - return 0; - } - protected ILanguageServer getLanguageServer() { return null; } + + public int getTabSize() { + return StudioApp.getInstance().getPrefManager().getEditorTabSize(); + } + + public int getIndentAdvance(@NonNull String line) { + return 0; + } } diff --git a/app/src/main/java/com/itsaky/androidide/language/cpp/CppAnalyzer.java b/app/src/main/java/com/itsaky/androidide/language/cpp/CppAnalyzer.java index 2a7505be1..836265993 100644 --- a/app/src/main/java/com/itsaky/androidide/language/cpp/CppAnalyzer.java +++ b/app/src/main/java/com/itsaky/androidide/language/cpp/CppAnalyzer.java @@ -26,13 +26,9 @@ import com.itsaky.androidide.utils.ILogger; import java.lang.Override; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import io.github.rosemoe.sora.lang.analysis.AsyncIncrementalAnalyzeManager; -import io.github.rosemoe.sora.lang.styling.CodeBlock; import io.github.rosemoe.sora.lang.styling.Span; -import io.github.rosemoe.sora.text.Content; public class CppAnalyzer extends BaseIncrementalAnalyzeManager { @@ -43,8 +39,10 @@ public class CppAnalyzer extends BaseIncrementalAnalyzeManager { } @Override - protected int[] getCodeBlockTokens() { - return new int[] {LeftBrace, RightBrace}; + protected int[][] getMultilineTokenStartEndTypes() { + final var start = new int[] {Div, Star}; + final var end = new int[] {Star, Div}; + return new int[][] {start, end}; } @Override @@ -215,15 +213,13 @@ public class CppAnalyzer extends BaseIncrementalAnalyzeManager { return spans; } - @Override - protected int[][] getMultilineTokenStartEndTypes() { - final var start = new int[] {Div, Star}; - final var end = new int[] {Star, Div}; - return new int[][] {start, end}; - } - @Override protected void handleIncompleteToken(final IncrementalToken token) { token.type = BlockComment; } + + @Override + protected int[] getCodeBlockTokens() { + return new int[] {LeftBrace, RightBrace}; + } } diff --git a/app/src/main/java/com/itsaky/androidide/language/cpp/CppLanguage.java b/app/src/main/java/com/itsaky/androidide/language/cpp/CppLanguage.java index a51801cb1..7809b3002 100644 --- a/app/src/main/java/com/itsaky/androidide/language/cpp/CppLanguage.java +++ b/app/src/main/java/com/itsaky/androidide/language/cpp/CppLanguage.java @@ -43,42 +43,13 @@ import io.github.rosemoe.sora.widget.SymbolPairMatch; public class CppLanguage extends IDELanguage { private static final ILogger LOG = ILogger.newInstance("CppLanguage"); - private CppAnalyzer analyzer; private final NewlineHandler[] newlineHandlers = new NewlineHandler[] {new BraceHandler()}; + private CppAnalyzer analyzer; public CppLanguage() { analyzer = new CppAnalyzer(); } - @Override - public int getIndentAdvance(@NonNull String line) { - try { - CPP14Lexer lexer = new CPP14Lexer(CharStreams.fromReader(new StringReader(line))); - Token token; - int advance = 0; - while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) { - switch (token.getType()) { - case CPP14Lexer.LeftBrace: - advance++; - break; - case CPP14Lexer.RightBrace: - advance--; - break; - } - } - 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 CppSymbolPairs(); - } - @NonNull @Override public AnalyzeManager getAnalyzeManager() { @@ -107,8 +78,8 @@ public class CppLanguage extends IDELanguage { } @Override - public CharSequence format(CharSequence content) { - return content; + public SymbolPairMatch getSymbolPairs() { + return new CppSymbolPairs(); } @Override @@ -121,6 +92,35 @@ public class CppLanguage extends IDELanguage { analyzer = null; } + @Override + public CharSequence format(CharSequence content) { + return content; + } + + @Override + public int getIndentAdvance(@NonNull String line) { + try { + CPP14Lexer lexer = new CPP14Lexer(CharStreams.fromReader(new StringReader(line))); + Token token; + int advance = 0; + while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) { + switch (token.getType()) { + case CPP14Lexer.LeftBrace: + advance++; + break; + case CPP14Lexer.RightBrace: + advance--; + break; + } + } + advance = Math.max(0, advance); + return advance * getTabSize(); + } catch (Throwable e) { + LOG.error("Error calculating indent advance", e); + } + return 0; + } + private static class CppSymbolPairs extends SymbolPairMatch { public CppSymbolPairs() { super.putPair('{', new Replacement("{}", 1)); diff --git a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.kt b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.kt index 6b7a1a351..f310976d4 100644 --- a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.kt +++ b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.kt @@ -23,10 +23,8 @@ import com.itsaky.androidide.language.incremental.LineState import com.itsaky.androidide.lexers.groovy.GroovyLexer import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE import io.github.rosemoe.sora.lang.analysis.IncrementalAnalyzeManager.LineTokenizeResult -import io.github.rosemoe.sora.lang.styling.CodeBlock import io.github.rosemoe.sora.lang.styling.Span import io.github.rosemoe.sora.lang.styling.TextStyle -import io.github.rosemoe.sora.text.Content /** @author Akash Yadav */ class GroovyAnalyzer : BaseIncrementalAnalyzeManager(GroovyLexer::class.java) { 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 1f92ae3f4..d73a23993 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 @@ -53,35 +53,6 @@ public class GroovyLanguage extends IDELanguage { completer = new GroovyAutoComplete(); } - @Override - public int getIndentAdvance(@NonNull 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); - } - return 0; - } - - @Override - public SymbolPairMatch getSymbolPairs() { - return new SymbolPairMatch.DefaultSymbolPairs(); - } - @NonNull @Override public AnalyzeManager getAnalyzeManager() { @@ -129,8 +100,8 @@ public class GroovyLanguage extends IDELanguage { } @Override - public CharSequence format(CharSequence content) { - return content; + public SymbolPairMatch getSymbolPairs() { + return new SymbolPairMatch.DefaultSymbolPairs(); } @Override @@ -141,6 +112,35 @@ public class GroovyLanguage extends IDELanguage { @Override public void destroy() {} + @Override + public CharSequence format(CharSequence content) { + return content; + } + + @Override + public int getIndentAdvance(@NonNull 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); + } + return 0; + } + class BraceHandler implements NewlineHandler { @Override diff --git a/app/src/main/java/com/itsaky/androidide/language/incremental/BaseIncrementalAnalyzeManager.java b/app/src/main/java/com/itsaky/androidide/language/incremental/BaseIncrementalAnalyzeManager.java index bcf630e1d..f1c0eec0c 100644 --- a/app/src/main/java/com/itsaky/androidide/language/incremental/BaseIncrementalAnalyzeManager.java +++ b/app/src/main/java/com/itsaky/androidide/language/incremental/BaseIncrementalAnalyzeManager.java @@ -61,11 +61,11 @@ import kotlin.Pair; public abstract class BaseIncrementalAnalyzeManager extends AsyncIncrementalAnalyzeManager { + private static final ILogger LOG = ILogger.newInstance("BaseIncrementalAnalyzeManager"); protected final Lexer lexer; private final int[] multilineStartTypes; private final int[] multilineEndTypes; private final int[] blockTokens; - private static final ILogger LOG = ILogger.newInstance("BaseIncrementalAnalyzeManager"); public BaseIncrementalAnalyzeManager(final Class lexer) { Objects.requireNonNull(lexer, "Cannot create analyzer manager for null lexer"); @@ -92,6 +92,16 @@ public abstract class BaseIncrementalAnalyzeManager } } + @NonNull + protected CharStream createStream(@NonNull CharSequence source) { + Objects.requireNonNull(source); + try { + return CharStreams.fromReader(new CharSequenceReader(source)); + } catch (IOException e) { + throw new RuntimeException("Cannot create CharStream for source", e); + } + } + private void verifyMultilineTypes(@NonNull final int[][] types) { Preconditions.checkState( types.length == 2, "There must be exact two inner int[] in multiline token types"); @@ -102,45 +112,6 @@ public abstract class BaseIncrementalAnalyzeManager Preconditions.checkState(end.length > 0, "Invalid end token types"); } - @SuppressWarnings("UnstableApiUsage") - @NonNull - private Pair, EvictingQueue> - createEvictingQueueForTokens() { - return new Pair<>( - EvictingQueue.create(multilineStartTypes.length), - EvictingQueue.create(multilineEndTypes.length)); - } - - @SuppressWarnings("UnstableApiUsage") - protected boolean isIncompleteTokenStart(EvictingQueue q) { - return matchTokenTypes(this.multilineStartTypes, q); - } - - @SuppressWarnings("UnstableApiUsage") - protected boolean isIncompleteTokenEnd(EvictingQueue q) { - return matchTokenTypes(this.multilineEndTypes, q); - } - - @SuppressWarnings("UnstableApiUsage") - private boolean matchTokenTypes( - @NonNull int[] types, @NonNull EvictingQueue tokens) { - final var arr = tokens.toArray(new IncrementalToken[0]); - for (int i = 0; i < types.length; i++) { - if (types[i] != arr[i].getType()) { - return false; - } - } - return true; - } - - protected boolean isCodeBlockStart(IncrementalToken token) { - return blockTokens.length == 2 && token.getType() == blockTokens[0]; - } - - protected boolean isCodeBlockEnd(IncrementalToken token) { - return blockTokens.length == 2 && token.getType() == blockTokens[1]; - } - /** * Returns the token types which start and end a multiline token. * @@ -152,31 +123,6 @@ public abstract class BaseIncrementalAnalyzeManager */ protected abstract int[][] getMultilineTokenStartEndTypes(); - /** - * Generate spans for the given {@link LineTokenizeResult}. - * - * @param tokens The tokenization result. - * @return The spans for the tokens. - */ - protected abstract List generateSpans( - final LineTokenizeResult tokens); - - /** - * Called when the analyzer finds an incomplete token in a lne. - * - * @param token The incomplete token. - */ - protected abstract void handleIncompleteToken(IncrementalToken token); - - /** - * Gets the next token from lexer and return an {@link IncrementalToken}. - * - * @return The next {@link IncrementalToken}. - */ - protected IncrementalToken nextToken() { - return new IncrementalToken(lexer.nextToken()); - } - /** * Get the token types for left and right braces. * @@ -184,51 +130,6 @@ public abstract class BaseIncrementalAnalyzeManager */ protected abstract int[] getCodeBlockTokens(); - /** - * Pop (remove) required number of tokens after an incomplete token has been encountered.
- * - *

For example:
- * When ['/', '*'] tokens are encountered, the '*' token must be removed. In this case, - * incompleteToken parameter will point to '/' token. - * - *

By default, this method removes only the last token. - * - * @param incompleteToken The token which is the start of the incomplete token. - * @param tokens The list of tokens from which extra tokens must be removed. - */ - protected void popTokensAfterIncomplete( - @NonNull IncrementalToken incompleteToken, @NonNull List tokens) { - tokens.remove(tokens.size() - 1); - } - - @NonNull - protected CharStream createStream(@NonNull CharSequence source) { - Objects.requireNonNull(source); - try { - return CharStreams.fromReader(new CharSequenceReader(source)); - } catch (IOException e) { - throw new RuntimeException("Cannot create CharStream for source", e); - } - } - - protected void handleLineCommentSpan( - @NonNull IncrementalToken token, @NonNull List spans, int offset) { - var commentType = SchemeAndroidIDE.COMMENT; - - // highlight special line comments - var commentText = token.getText(); - if (commentText.length() > 2) { - commentText = commentText.substring(2); - commentText = commentText.trim(); - if ("todo".equalsIgnoreCase(commentText.substring(0, 4))) { - commentType = TODO_COMMENT; - } else if ("fixme".equalsIgnoreCase(commentText.substring(0, 5))) { - commentType = FIXME_COMMENT; - } - } - spans.add(Span.obtain(offset, withoutCompletion(commentType))); - } - @Override public LineState getInitialState() { return new LineState(); @@ -239,6 +140,52 @@ public abstract class BaseIncrementalAnalyzeManager return state.equals(another); } + @Override + public LineTokenizeResult tokenizeLine( + final CharSequence line, final LineState state) { + final var tokens = new ArrayList(); + var newState = 0; + var stateObj = new LineState(); + if (state.state == NORMAL) { + newState = tokenizeNormal(line, 0, tokens, stateObj, state.lexerMode); + } else if (state.state == INCOMPLETE) { + final var result = fillIncomplete(line, tokens, state.lexerMode); + newState = IntPair.getFirst(result); + if (newState == NORMAL) { + newState = + tokenizeNormal(line, IntPair.getSecond(result), tokens, stateObj, state.lexerMode); + } else { + newState = INCOMPLETE; + } + } + stateObj.state = newState; + stateObj.lexerMode = lexer._mode; + return new LineTokenizeResult<>(stateObj, tokens); + } + + @Override + public List generateSpansForLine( + final LineTokenizeResult tokens) { + var result = generateSpans(tokens); + + Objects.requireNonNull(result); + + if (result.isEmpty()) { + result.add(Span.obtain(0, TextStyle.makeStyle(SchemeAndroidIDE.TEXT_NORMAL))); + } + + return result; + } + + /** + * Generate spans for the given {@link LineTokenizeResult}. + * + * @param tokens The tokenization result. + * @return The spans for the tokens. + */ + protected abstract List generateSpans( + final LineTokenizeResult tokens); + @Override public List computeBlocks( final Content text, @@ -292,27 +239,73 @@ public abstract class BaseIncrementalAnalyzeManager return blocks; } - @Override - public LineTokenizeResult tokenizeLine( - final CharSequence line, final LineState state) { - final var tokens = new ArrayList(); - var newState = 0; - var stateObj = new LineState(); - if (state.state == NORMAL) { - newState = tokenizeNormal(line, 0, tokens, stateObj, state.lexerMode); - } else if (state.state == INCOMPLETE) { - final var result = fillIncomplete(line, tokens, state.lexerMode); - newState = IntPair.getFirst(result); - if (newState == NORMAL) { - newState = - tokenizeNormal(line, IntPair.getSecond(result), tokens, stateObj, state.lexerMode); - } else { - newState = INCOMPLETE; + protected boolean isCodeBlockStart(IncrementalToken token) { + return blockTokens.length == 2 && token.getType() == blockTokens[0]; + } + + protected boolean isCodeBlockEnd(IncrementalToken token) { + return blockTokens.length == 2 && token.getType() == blockTokens[1]; + } + + @SuppressWarnings("UnstableApiUsage") + protected boolean isIncompleteTokenStart(EvictingQueue q) { + return matchTokenTypes(this.multilineStartTypes, q); + } + + @SuppressWarnings("UnstableApiUsage") + protected boolean isIncompleteTokenEnd(EvictingQueue q) { + return matchTokenTypes(this.multilineEndTypes, q); + } + + /** + * Called when the analyzer finds an incomplete token in a lne. + * + * @param token The incomplete token. + */ + protected abstract void handleIncompleteToken(IncrementalToken token); + + /** + * Gets the next token from lexer and return an {@link IncrementalToken}. + * + * @return The next {@link IncrementalToken}. + */ + protected IncrementalToken nextToken() { + return new IncrementalToken(lexer.nextToken()); + } + + /** + * Pop (remove) required number of tokens after an incomplete token has been encountered.
+ * + *

For example:
+ * When ['/', '*'] tokens are encountered, the '*' token must be removed. In this case, + * incompleteToken parameter will point to '/' token. + * + *

By default, this method removes only the last token. + * + * @param incompleteToken The token which is the start of the incomplete token. + * @param tokens The list of tokens from which extra tokens must be removed. + */ + protected void popTokensAfterIncomplete( + @NonNull IncrementalToken incompleteToken, @NonNull List tokens) { + tokens.remove(tokens.size() - 1); + } + + protected void handleLineCommentSpan( + @NonNull IncrementalToken token, @NonNull List spans, int offset) { + var commentType = SchemeAndroidIDE.COMMENT; + + // highlight special line comments + var commentText = token.getText(); + if (commentText.length() > 2) { + commentText = commentText.substring(2); + commentText = commentText.trim(); + if ("todo".equalsIgnoreCase(commentText.substring(0, 4))) { + commentType = TODO_COMMENT; + } else if ("fixme".equalsIgnoreCase(commentText.substring(0, 5))) { + commentType = FIXME_COMMENT; } } - stateObj.state = newState; - stateObj.lexerMode = lexer._mode; - return new LineTokenizeResult<>(stateObj, tokens); + spans.add(Span.obtain(offset, withoutCompletion(commentType))); } /** @@ -445,17 +438,24 @@ public abstract class BaseIncrementalAnalyzeManager } } - @Override - public List generateSpansForLine( - final LineTokenizeResult tokens) { - var result = generateSpans(tokens); + @SuppressWarnings("UnstableApiUsage") + @NonNull + private Pair, EvictingQueue> + createEvictingQueueForTokens() { + return new Pair<>( + EvictingQueue.create(multilineStartTypes.length), + EvictingQueue.create(multilineEndTypes.length)); + } - Objects.requireNonNull(result); - - if (result.isEmpty()) { - result.add(Span.obtain(0, TextStyle.makeStyle(SchemeAndroidIDE.TEXT_NORMAL))); + @SuppressWarnings("UnstableApiUsage") + private boolean matchTokenTypes( + @NonNull int[] types, @NonNull EvictingQueue tokens) { + final var arr = tokens.toArray(new IncrementalToken[0]); + for (int i = 0; i < types.length; i++) { + if (types[i] != arr[i].getType()) { + return false; + } } - - return result; + return true; } } diff --git a/app/src/main/java/com/itsaky/androidide/language/java/JavaAnalyzer.kt b/app/src/main/java/com/itsaky/androidide/language/java/JavaAnalyzer.kt index ccf956453..aa6654102 100644 --- a/app/src/main/java/com/itsaky/androidide/language/java/JavaAnalyzer.kt +++ b/app/src/main/java/com/itsaky/androidide/language/java/JavaAnalyzer.kt @@ -30,10 +30,8 @@ import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forKeyword import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forString import com.itsaky.androidide.utils.ILogger import io.github.rosemoe.sora.lang.analysis.IncrementalAnalyzeManager.LineTokenizeResult -import io.github.rosemoe.sora.lang.styling.CodeBlock import io.github.rosemoe.sora.lang.styling.Span import io.github.rosemoe.sora.lang.styling.TextStyle.makeStyle -import io.github.rosemoe.sora.text.Content /** @author Akash Yadav */ class JavaAnalyzer : BaseIncrementalAnalyzeManager(JavaLexer::class.java) { 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 0be2c0060..c62817c2c 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 @@ -63,36 +63,6 @@ public class JavaLanguage extends IDELanguage { this.newlineHandlers[0] = new BraceHandler(); } - @Override - public int getIndentAdvance(@NonNull String line) { - try { - JavaLexer lexer = new JavaLexer(CharStreams.fromReader(new StringReader(line))); - 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); - } - - return 0; - } - - @Override - public SymbolPairMatch getSymbolPairs() { - return new JavaSymbolPairs(); - } - @NonNull @Override public AnalyzeManager getAnalyzeManager() { @@ -127,11 +97,41 @@ public class JavaLanguage extends IDELanguage { return getIndentAdvance(content.getLine(line).substring(0, column)); } + @Override + public int getIndentAdvance(@NonNull String line) { + try { + JavaLexer lexer = new JavaLexer(CharStreams.fromReader(new StringReader(line))); + 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); + } + + return 0; + } + @Override protected ILanguageServer getLanguageServer() { return StudioApp.getInstance().getJavaLanguageServer(); } + @Override + public SymbolPairMatch getSymbolPairs() { + return new JavaSymbolPairs(); + } + @Override public NewlineHandler[] getNewlineHandlers() { return newlineHandlers; diff --git a/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinAnalyzer.kt b/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinAnalyzer.kt index cc9a9df8f..5b4996c31 100644 --- a/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinAnalyzer.kt +++ b/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinAnalyzer.kt @@ -29,10 +29,8 @@ import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forComment import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forKeyword import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forString import io.github.rosemoe.sora.lang.analysis.IncrementalAnalyzeManager.LineTokenizeResult -import io.github.rosemoe.sora.lang.styling.CodeBlock import io.github.rosemoe.sora.lang.styling.Span import io.github.rosemoe.sora.lang.styling.TextStyle.makeStyle -import io.github.rosemoe.sora.text.Content /** * Incremental syntax analyzer for Kotlin. diff --git a/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinLanguage.java b/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinLanguage.java index 26cc0da19..ac4eb62ae 100644 --- a/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinLanguage.java +++ b/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinLanguage.java @@ -22,15 +22,14 @@ import android.os.Bundle; import androidx.annotation.NonNull; import com.itsaky.androidide.language.IDELanguage; - +import com.itsaky.androidide.lexers.kotlin.KotlinLexer; +import com.itsaky.androidide.lexers.kotlin.KotlinParser; import com.itsaky.androidide.utils.ILogger; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.Token; import java.io.StringReader; -import com.itsaky.androidide.lexers.kotlin.KotlinLexer; -import com.itsaky.androidide.lexers.kotlin.KotlinParser; import io.github.rosemoe.sora.lang.analysis.AnalyzeManager; import io.github.rosemoe.sora.lang.completion.CompletionCancelledException; @@ -45,42 +44,13 @@ import io.github.rosemoe.sora.widget.SymbolPairMatch; public class KotlinLanguage extends IDELanguage { private static final ILogger LOG = ILogger.newInstance("KotlinLanguage"); - private KotlinAnalyzer analyzer; private final NewlineHandler[] newlineHandlers = new NewlineHandler[] {new BraceHandler()}; + private KotlinAnalyzer analyzer; public KotlinLanguage() { analyzer = new KotlinAnalyzer(); } - @Override - public int getIndentAdvance(@NonNull String line) { - try { - KotlinLexer lexer = new KotlinLexer(CharStreams.fromReader(new StringReader(line))); - Token token; - int advance = 0; - while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) { - switch (token.getType()) { - case KotlinParser.LCURL: - advance++; - break; - case KotlinParser.RCURL: - advance--; - break; - } - } - 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 KotlinSymbolPairs(); - } - @NonNull @Override public AnalyzeManager getAnalyzeManager() { @@ -109,8 +79,8 @@ public class KotlinLanguage extends IDELanguage { } @Override - public CharSequence format(CharSequence content) { - return content; + public SymbolPairMatch getSymbolPairs() { + return new KotlinSymbolPairs(); } @Override @@ -123,6 +93,35 @@ public class KotlinLanguage extends IDELanguage { analyzer = null; } + @Override + public CharSequence format(CharSequence content) { + return content; + } + + @Override + public int getIndentAdvance(@NonNull String line) { + try { + KotlinLexer lexer = new KotlinLexer(CharStreams.fromReader(new StringReader(line))); + Token token; + int advance = 0; + while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) { + switch (token.getType()) { + case KotlinParser.LCURL: + advance++; + break; + case KotlinParser.RCURL: + advance--; + break; + } + } + advance = Math.max(0, advance); + return advance * getTabSize(); + } catch (Throwable e) { + LOG.error("Error calculating indent advance", e); + } + return 0; + } + private static class KotlinSymbolPairs extends SymbolPairMatch { public KotlinSymbolPairs() { super.putPair('{', new Replacement("{}", 1)); diff --git a/app/src/main/java/com/itsaky/androidide/language/xml/XMLLanguage.java b/app/src/main/java/com/itsaky/androidide/language/xml/XMLLanguage.java index 1aa2faab2..f4a6bfd3c 100755 --- a/app/src/main/java/com/itsaky/androidide/language/xml/XMLLanguage.java +++ b/app/src/main/java/com/itsaky/androidide/language/xml/XMLLanguage.java @@ -57,38 +57,6 @@ public class XMLLanguage extends IDELanguage { this.newlineHandlers = new NewlineHandler[0]; } - @Override - public int getIndentAdvance(@NonNull String content) { - try { - XMLLexer lexer = new XMLLexer(CharStreams.fromReader(new StringReader(content))); - Token token; - int advance = 0; - while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) { - switch (token.getType()) { - case XMLLexer.OPEN: - advance++; - break; - case XMLLexer.CLOSE: - case XMLLexer.SLASH_CLOSE: - advance--; - break; - default: - break; - } - } - advance = Math.max(0, advance); - return advance * getTabSize(); - } catch (Throwable e) { - LOG.error("Failed to compute indent advance", e); - } - return 0; - } - - @Override - public SymbolPairMatch getSymbolPairs() { - return new SymbolPairMatch.DefaultSymbolPairs(); - } - @NonNull @Override public AnalyzeManager getAnalyzeManager() { @@ -125,11 +93,43 @@ public class XMLLanguage extends IDELanguage { return getIndentAdvance(text.trim()); } + @Override + public int getIndentAdvance(@NonNull String content) { + try { + XMLLexer lexer = new XMLLexer(CharStreams.fromReader(new StringReader(content))); + Token token; + int advance = 0; + while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) { + switch (token.getType()) { + case XMLLexer.OPEN: + advance++; + break; + case XMLLexer.CLOSE: + case XMLLexer.SLASH_CLOSE: + advance--; + break; + default: + break; + } + } + advance = Math.max(0, advance); + return advance * getTabSize(); + } catch (Throwable e) { + LOG.error("Failed to compute indent advance", e); + } + return 0; + } + @Override protected ILanguageServer getLanguageServer() { return StudioApp.getInstance().getXMLLanguageServer(); } + @Override + public SymbolPairMatch getSymbolPairs() { + return new SymbolPairMatch.DefaultSymbolPairs(); + } + @Override public NewlineHandler[] getNewlineHandlers() { return newlineHandlers; diff --git a/app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java b/app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java index a903e01c1..a6f48545a 100755 --- a/app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java +++ b/app/src/main/java/com/itsaky/androidide/lsp/IDELanguageClientImpl.java @@ -19,7 +19,6 @@ */ package com.itsaky.androidide.lsp; -import android.os.Trace; import android.view.View; import androidx.annotation.NonNull; @@ -73,25 +72,20 @@ import io.github.rosemoe.sora.text.Content; /** AndroidIDE specific implementation of the LanguageClient */ public class IDELanguageClientImpl implements ILanguageClient { + public static final int MAX_DIAGNOSTIC_FILES = 10; + public static final int MAX_DIAGNOSTIC_ITEMS_PER_FILE = 20; protected static final Gson gson = new Gson(); protected static final ILogger LOG = ILogger.newInstance("AbstractLanguageClient"); private static IDELanguageClientImpl mInstance; private final Map> diagnostics = new HashMap<>(); protected EditorActivityProvider activityProvider; - public static final int MAX_DIAGNOSTIC_FILES = 10; - public static final int MAX_DIAGNOSTIC_ITEMS_PER_FILE = 20; - private IDELanguageClientImpl(EditorActivityProvider provider) { setActivityProvider(provider); } - public static IDELanguageClientImpl getInstance() { - if (mInstance == null) { - throw new IllegalStateException("Client not initialized"); - } - - return mInstance; + public void setActivityProvider(EditorActivityProvider provider) { + this.activityProvider = provider; } public static IDELanguageClientImpl initialize(EditorActivityProvider provider) { @@ -104,6 +98,14 @@ public class IDELanguageClientImpl implements ILanguageClient { return getInstance(); } + public static IDELanguageClientImpl getInstance() { + if (mInstance == null) { + throw new IllegalStateException("Client not initialized"); + } + + return mInstance; + } + public static void shutdown() { mInstance = null; } @@ -112,19 +114,6 @@ public class IDELanguageClientImpl implements ILanguageClient { return mInstance != null; } - public void setActivityProvider(EditorActivityProvider provider) { - this.activityProvider = provider; - } - - protected EditorActivity activity() { - if (activityProvider == null) return null; - return activityProvider.provide(); - } - - private CodeEditorView findEditorByFile(File file) { - return activity().getEditorForFile(file); - } - public void hideDiagnostics() { if (activity() == null || activity().getDiagnosticBinding() == null) { return; @@ -133,21 +122,6 @@ public class IDELanguageClientImpl implements ILanguageClient { activity().getDiagnosticBinding().getRoot().setVisibility(View.GONE); } - private void hideBottomDiagnosticView(final File file) { - if (activity() == null || file == null) { - return; - } - - final var frag = findEditorByFile(file); - if (frag == null || frag.getBinding() == null) { - return; - } - - frag.getBinding().diagnosticTextContainer.setVisibility(View.GONE); - frag.getBinding().diagnosticText.setVisibility(View.GONE); - frag.getBinding().diagnosticText.setClickable(false); - } - @Override public void publishDiagnostics(DiagnosticResult result) { boolean error = result == null; @@ -181,66 +155,17 @@ public class IDELanguageClientImpl implements ILanguageClient { activity().setDiagnosticsAdapter(newDiagnosticsAdapter()); } + protected EditorActivity activity() { + if (activityProvider == null) return null; + return activityProvider.provide(); + } + @Nullable @Override public DiagnosticItem getDiagnosticAt(final File file, final int line, final int column) { return DiagnosticUtil.binarySearchDiagnostic(this.diagnostics.get(file), line, column); } - /** Called by {@link IDEEditor IDEEditor} to show locations in EditorActivity */ - public void showLocations(List locations) { - - // Cannot show anything if the activity() is null - if (activity() == null) { - return; - } - - boolean error = locations == null || locations.isEmpty(); - activity().handleSearchResultVisibility(error); - - if (error) { - activity().setSearchResultAdapter(new SearchListAdapter(null, null, null)); - return; - } - - final Map> results = new HashMap<>(); - for (int i = 0; i < locations.size(); i++) { - try { - final Location loc = locations.get(i); - if (loc == null) { - continue; - } - - final File file = loc.getFile().toFile(); - if (!file.exists() || !file.isFile()) continue; - var frag = findEditorByFile(file); - Content content; - if (frag != null && frag.getEditor() != null) content = frag.getEditor().getText(); - else content = new Content(FileIOUtils.readFile2String(file)); - final List matches = - results.containsKey(file) ? results.get(file) : new ArrayList<>(); - Objects.requireNonNull(matches) - .add( - new SearchResult( - loc.getRange(), - file, - content.getLineString(loc.getRange().getStart().getLine()), - content - .subContent( - loc.getRange().getStart().getLine(), - loc.getRange().getStart().getColumn(), - loc.getRange().getEnd().getLine(), - loc.getRange().getEnd().getColumn()) - .toString())); - results.put(file, matches); - } catch (Throwable th) { - LOG.error("Failed to show file location", th); - } - } - - activity().handleSearchResults(results); - } - @Override public void performCodeAction(File file, CodeActionItem actionItem) { final var editor = activity().getEditorForFile(file); @@ -289,6 +214,108 @@ public class IDELanguageClientImpl implements ILanguageClient { }); } + private Boolean performCodeActionAsync(final IDEEditor editor, final CodeActionItem action) { + LOG.debug("Performing code action:", action); + final var changes = action.getChanges(); + if (changes.isEmpty()) { + return Boolean.FALSE; + } + + for (var change : changes) { + final var path = change.getFile(); + if (path == null) { + continue; + } + + final File file = path.toFile(); + if (!file.exists()) { + continue; + } + + for (TextEdit edit : change.getEdits()) { + final String editorFilepath = + editor.getFile() == null ? "" : editor.getFile().getAbsolutePath(); + if (file.getAbsolutePath().equals(editorFilepath)) { + // Edit is in the same editor which requested the code action + editInEditor(editor, edit); + } else { + var openedFrag = findEditorByFile(file); + + if (openedFrag != null && openedFrag.getEditor() != null) { + // Edit is in another 'opened' file + editInEditor(openedFrag.getEditor(), edit); + } else { + // Edit is in some other file which is not opened + // We should open that file and perform the edit + openedFrag = activity().openFile(file); + if (openedFrag != null && openedFrag.getEditor() != null) { + editInEditor(openedFrag.getEditor(), edit); + } + } + } + } + } + + return Boolean.TRUE; + } + + private void editInEditor(final IDEEditor editor, final TextEdit edit) { + final Range range = edit.getRange(); + final int startLine = range.getStart().getLine(); + final int startCol = range.getStart().getColumn(); + final int endLine = range.getEnd().getLine(); + final int endCol = range.getEnd().getColumn(); + + activity() + .runOnUiThread( + () -> { + if (startLine == endLine && startCol == endCol) { + editor.getText().insert(startLine, startCol, edit.getNewText()); + } else { + editor.getText().replace(startLine, startCol, endLine, endCol, edit.getNewText()); + } + }); + } + + @Override + public com.itsaky.lsp.models.ShowDocumentResult showDocument( + com.itsaky.lsp.models.ShowDocumentParams params) { + boolean success = false; + final var result = new com.itsaky.lsp.models.ShowDocumentResult(false); + if (activity() == null) { + return result; + } + + if (params != null) { + File file = params.getFile().toFile(); + if (file.exists() && file.isFile() && FileUtils.isUtf8(file)) { + final var range = params.getSelection(); + var frag = + activity().getEditorAtIndex(activity().getBinding().tabs.getSelectedTabPosition()); + if (frag != null + && frag.getFile() != null + && frag.getEditor() != null + && frag.getFile().getAbsolutePath().equals(file.getAbsolutePath())) { + if (LSPUtils.isEqual(range.getStart(), range.getEnd())) { + frag.getEditor().setSelection(range.getStart().getLine(), range.getStart().getColumn()); + } else { + frag.getEditor().setSelection(range); + } + } else { + activity().openFileAndSelect(file, range); + } + success = true; + } + } + + result.setSuccess(success); + return result; + } + + public DiagnosticsAdapter newDiagnosticsAdapter() { + return new DiagnosticsAdapter(mapAsGroup(this.diagnostics), activity()); + } + private List mapAsGroup(Map> map) { final var groups = new ArrayList(); var diagnosticMap = map; @@ -364,6 +391,79 @@ public class IDELanguageClientImpl implements ILanguageClient { return result; } + /** Called by {@link IDEEditor IDEEditor} to show locations in EditorActivity */ + public void showLocations(List locations) { + + // Cannot show anything if the activity() is null + if (activity() == null) { + return; + } + + boolean error = locations == null || locations.isEmpty(); + activity().handleSearchResultVisibility(error); + + if (error) { + activity().setSearchResultAdapter(new SearchListAdapter(null, null, null)); + return; + } + + final Map> results = new HashMap<>(); + for (int i = 0; i < locations.size(); i++) { + try { + final Location loc = locations.get(i); + if (loc == null) { + continue; + } + + final File file = loc.getFile().toFile(); + if (!file.exists() || !file.isFile()) continue; + var frag = findEditorByFile(file); + Content content; + if (frag != null && frag.getEditor() != null) content = frag.getEditor().getText(); + else content = new Content(FileIOUtils.readFile2String(file)); + final List matches = + results.containsKey(file) ? results.get(file) : new ArrayList<>(); + Objects.requireNonNull(matches) + .add( + new SearchResult( + loc.getRange(), + file, + content.getLineString(loc.getRange().getStart().getLine()), + content + .subContent( + loc.getRange().getStart().getLine(), + loc.getRange().getStart().getColumn(), + loc.getRange().getEnd().getLine(), + loc.getRange().getEnd().getColumn()) + .toString())); + results.put(file, matches); + } catch (Throwable th) { + LOG.error("Failed to show file location", th); + } + } + + activity().handleSearchResults(results); + } + + private CodeEditorView findEditorByFile(File file) { + return activity().getEditorForFile(file); + } + + private void hideBottomDiagnosticView(final File file) { + if (activity() == null || file == null) { + return; + } + + final var frag = findEditorByFile(file); + if (frag == null || frag.getBinding() == null) { + return; + } + + frag.getBinding().diagnosticTextContainer.setVisibility(View.GONE); + frag.getBinding().diagnosticText.setVisibility(View.GONE); + frag.getBinding().diagnosticText.setClickable(false); + } + private void showAvailableQuickfixes(IDEEditor editor, List actions) { final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity()); builder.setTitle(R.string.msg_code_actions); @@ -386,109 +486,7 @@ public class IDELanguageClientImpl implements ILanguageClient { return arr; } - private Boolean performCodeActionAsync(final IDEEditor editor, final CodeActionItem action) { - LOG.debug("Performing code action:", action); - final var changes = action.getChanges(); - if (changes.isEmpty()) { - return Boolean.FALSE; - } - - for (var change : changes) { - final var path = change.getFile(); - if (path == null) { - continue; - } - - final File file = path.toFile(); - if (!file.exists()) { - continue; - } - - for (TextEdit edit : change.getEdits()) { - final String editorFilepath = - editor.getFile() == null ? "" : editor.getFile().getAbsolutePath(); - if (file.getAbsolutePath().equals(editorFilepath)) { - // Edit is in the same editor which requested the code action - editInEditor(editor, edit); - } else { - var openedFrag = findEditorByFile(file); - - if (openedFrag != null && openedFrag.getEditor() != null) { - // Edit is in another 'opened' file - editInEditor(openedFrag.getEditor(), edit); - } else { - // Edit is in some other file which is not opened - // We should open that file and perform the edit - openedFrag = activity().openFile(file); - if (openedFrag != null && openedFrag.getEditor() != null) { - editInEditor(openedFrag.getEditor(), edit); - } - } - } - } - } - - return Boolean.TRUE; - } - private void execCommand(IDEEditor editor, Command command) { editor.executeCommand(command); } - - private void editInEditor(final IDEEditor editor, final TextEdit edit) { - final Range range = edit.getRange(); - final int startLine = range.getStart().getLine(); - final int startCol = range.getStart().getColumn(); - final int endLine = range.getEnd().getLine(); - final int endCol = range.getEnd().getColumn(); - - activity() - .runOnUiThread( - () -> { - if (startLine == endLine && startCol == endCol) { - editor.getText().insert(startLine, startCol, edit.getNewText()); - } else { - editor.getText().replace(startLine, startCol, endLine, endCol, edit.getNewText()); - } - }); - } - - public DiagnosticsAdapter newDiagnosticsAdapter() { - return new DiagnosticsAdapter(mapAsGroup(this.diagnostics), activity()); - } - - @Override - public com.itsaky.lsp.models.ShowDocumentResult showDocument( - com.itsaky.lsp.models.ShowDocumentParams params) { - boolean success = false; - final var result = new com.itsaky.lsp.models.ShowDocumentResult(false); - if (activity() == null) { - return result; - } - - if (params != null) { - File file = params.getFile().toFile(); - if (file.exists() && file.isFile() && FileUtils.isUtf8(file)) { - final var range = params.getSelection(); - var frag = - activity().getEditorAtIndex(activity().getBinding().tabs.getSelectedTabPosition()); - if (frag != null - && frag.getFile() != null - && frag.getEditor() != null - && frag.getFile().getAbsolutePath().equals(file.getAbsolutePath())) { - if (LSPUtils.isEqual(range.getStart(), range.getEnd())) { - frag.getEditor().setSelection(range.getStart().getLine(), range.getStart().getColumn()); - } else { - frag.getEditor().setSelection(range); - } - } else { - activity().openFileAndSelect(file, range); - } - success = true; - } - } - - result.setSuccess(success); - return result; - } } diff --git a/app/src/main/java/com/itsaky/androidide/models/ApkMetadata.java b/app/src/main/java/com/itsaky/androidide/models/ApkMetadata.java index 6ac7d9afd..082b9dad1 100755 --- a/app/src/main/java/com/itsaky/androidide/models/ApkMetadata.java +++ b/app/src/main/java/com/itsaky/androidide/models/ApkMetadata.java @@ -143,6 +143,10 @@ public class ApkMetadata { return elements; } + public void setElements(List elements) { + this.elements = elements; + } + public ArtifactType getArtifactType() { return artifactType; } @@ -151,10 +155,6 @@ public class ApkMetadata { this.artifactType = artifactType; } - public void setElements(List elements) { - this.elements = elements; - } - public int getVersion() { return version; } diff --git a/app/src/main/java/com/itsaky/androidide/models/IconTextListItem.java b/app/src/main/java/com/itsaky/androidide/models/IconTextListItem.java index 13df30bf4..f008fe92b 100755 --- a/app/src/main/java/com/itsaky/androidide/models/IconTextListItem.java +++ b/app/src/main/java/com/itsaky/androidide/models/IconTextListItem.java @@ -21,10 +21,6 @@ import androidx.annotation.DrawableRes; public interface IconTextListItem { - String getText(); - - int getIconResource(); - public static IconTextListItem create(String name, @DrawableRes int icon) { return new IconTextListItem() { @Override @@ -38,4 +34,8 @@ public interface IconTextListItem { } }; } + + String getText(); + + int getIconResource(); } diff --git a/app/src/main/java/com/itsaky/androidide/models/NewProjectDetails.java b/app/src/main/java/com/itsaky/androidide/models/NewProjectDetails.java index 7d1c82715..5f747284c 100755 --- a/app/src/main/java/com/itsaky/androidide/models/NewProjectDetails.java +++ b/app/src/main/java/com/itsaky/androidide/models/NewProjectDetails.java @@ -46,17 +46,12 @@ public class NewProjectDetails { this.savePath = savePath; } - public NewProjectDetails setName(String name) { - this.name = name; - return this; - } - public String getName() { return name; } - public NewProjectDetails setPackageName(String packageName) { - this.packageName = packageName; + public NewProjectDetails setName(String name) { + this.name = name; return this; } @@ -64,8 +59,8 @@ public class NewProjectDetails { return packageName; } - public NewProjectDetails setMinSdk(int minSdk) { - this.minSdk = minSdk; + public NewProjectDetails setPackageName(String packageName) { + this.packageName = packageName; return this; } @@ -73,8 +68,8 @@ public class NewProjectDetails { return minSdk; } - public NewProjectDetails setTargetSdk(int targetSdk) { - this.targetSdk = targetSdk; + public NewProjectDetails setMinSdk(int minSdk) { + this.minSdk = minSdk; return this; } @@ -82,12 +77,17 @@ public class NewProjectDetails { return targetSdk; } - public NewProjectDetails setCppFlags(String cppFlags) { - this.cppFlags = cppFlags; + public NewProjectDetails setTargetSdk(int targetSdk) { + this.targetSdk = targetSdk; return this; } public String getCppFlags() { return cppFlags; } + + public NewProjectDetails setCppFlags(String cppFlags) { + this.cppFlags = cppFlags; + return this; + } } diff --git a/app/src/main/java/com/itsaky/androidide/models/ProjectTemplate.java b/app/src/main/java/com/itsaky/androidide/models/ProjectTemplate.java index 12fb6d9ee..09f8d1dc4 100755 --- a/app/src/main/java/com/itsaky/androidide/models/ProjectTemplate.java +++ b/app/src/main/java/com/itsaky/androidide/models/ProjectTemplate.java @@ -42,39 +42,43 @@ public class ProjectTemplate { this.imageId = imageId; } + public int getId() { + return id; + } + public ProjectTemplate setId(int id) { this.id = id; return this; } - public int getId() { - return id; - } - public ProjectTemplate setName(Context ctx, @StringRes int name) { return setName(ctx.getString(name)); } + public String getName() { + return name; + } + public ProjectTemplate setName(String name) { this.name = name; return this; } - public String getName() { - return name; - } - public ProjectTemplate setDescription(Context ctx, @StringRes int desc) { return setDescription(ctx.getString(desc)); } + public String getDescription() { + return description; + } + public ProjectTemplate setDescription(String description) { this.description = description; return this; } - public String getDescription() { - return description; + public int getImageId() { + return imageId; } public ProjectTemplate setImageId(@DrawableRes int imageId) { @@ -82,10 +86,6 @@ public class ProjectTemplate { return this; } - public int getImageId() { - return imageId; - } - public boolean isSupportJava() { return this.isSupportJava; } diff --git a/app/src/main/java/com/itsaky/androidide/models/UIWidget.java b/app/src/main/java/com/itsaky/androidide/models/UIWidget.java index 7a1c496e4..4b38c34d7 100755 --- a/app/src/main/java/com/itsaky/androidide/models/UIWidget.java +++ b/app/src/main/java/com/itsaky/androidide/models/UIWidget.java @@ -32,9 +32,20 @@ import java.util.Objects; */ public class UIWidget implements IconTextListItem, Parcelable { + public static final Creator CREATOR = + new Creator() { + @Override + public UIWidget createFromParcel(Parcel in) { + return new UIWidget(in); + } + + @Override + public UIWidget[] newArray(int size) { + return new UIWidget[size]; + } + }; private final String name; private final int icon; - private final String clazz; private UIWidget(Parcel in) { @@ -49,25 +60,9 @@ public class UIWidget implements IconTextListItem, Parcelable { this.clazz = clazz.getName(); } - public static final Creator CREATOR = - new Creator() { - @Override - public UIWidget createFromParcel(Parcel in) { - return new UIWidget(in); - } - - @Override - public UIWidget[] newArray(int size) { - return new UIWidget[size]; - } - }; - - public String getName() { - return name; - } - - public int getIcon() { - return icon; + @Override + public int hashCode() { + return Objects.hash(getName(), getIcon(), clazz); } @Override @@ -80,9 +75,12 @@ public class UIWidget implements IconTextListItem, Parcelable { && clazz.equals(widget.clazz); } - @Override - public int hashCode() { - return Objects.hash(getName(), getIcon(), clazz); + public String getName() { + return name; + } + + public int getIcon() { + return icon; } /** diff --git a/app/src/main/java/com/itsaky/androidide/models/UIWidgetGroup.java b/app/src/main/java/com/itsaky/androidide/models/UIWidgetGroup.java index 168dcdebc..fa1fb3b0a 100755 --- a/app/src/main/java/com/itsaky/androidide/models/UIWidgetGroup.java +++ b/app/src/main/java/com/itsaky/androidide/models/UIWidgetGroup.java @@ -38,27 +38,6 @@ import java.util.Objects; */ public class UIWidgetGroup implements IconTextListItem, Parcelable { - /** Name of this group. */ - private final String name; - - /** Children of this group. */ - private final List children; - - /** Is this group currently selected in the group list? */ - private boolean selected; - - public UIWidgetGroup(String name) { - this.name = name; - this.selected = false; - this.children = new ArrayList<>(); - } - - protected UIWidgetGroup(@NonNull Parcel in) { - this.name = in.readString(); - this.children = in.createTypedArrayList(UIWidget.CREATOR); - this.selected = in.readByte() != 0; - } - public static final Creator CREATOR = new Creator() { @@ -76,32 +55,23 @@ public class UIWidgetGroup implements IconTextListItem, Parcelable { return new UIWidgetGroup[size]; } }; + /** Name of this group. */ + private final String name; + /** Children of this group. */ + private final List children; + /** Is this group currently selected in the group list? */ + private boolean selected; - /** - * Get the name of this group. - * - * @return The name of this group. - */ - public String getName() { - return name; + public UIWidgetGroup(String name) { + this.name = name; + this.selected = false; + this.children = new ArrayList<>(); } - /** - * Get the children of this group. - * - * @return The children. - */ - @NonNull - public List getChildren() { - return children; - } - - public void setSelected(boolean selected) { - this.selected = selected; - } - - public boolean isSelected() { - return selected; + protected UIWidgetGroup(@NonNull Parcel in) { + this.name = in.readString(); + this.children = in.createTypedArrayList(UIWidget.CREATOR); + this.selected = in.readByte() != 0; } /** @@ -129,6 +99,11 @@ public class UIWidgetGroup implements IconTextListItem, Parcelable { return children.get(index); } + @Override + public int hashCode() { + return Objects.hash(getName(), getChildren(), isSelected()); + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -139,9 +114,31 @@ public class UIWidgetGroup implements IconTextListItem, Parcelable { && getChildren().equals(group.getChildren()); } - @Override - public int hashCode() { - return Objects.hash(getName(), getChildren(), isSelected()); + /** + * Get the name of this group. + * + * @return The name of this group. + */ + public String getName() { + return name; + } + + /** + * Get the children of this group. + * + * @return The children. + */ + @NonNull + public List getChildren() { + return children; + } + + public boolean isSelected() { + return selected; + } + + public void setSelected(boolean selected) { + this.selected = selected; } @Override diff --git a/app/src/main/java/com/itsaky/androidide/models/XMLAttribute.java b/app/src/main/java/com/itsaky/androidide/models/XMLAttribute.java index c2163f4a1..fbab725f5 100644 --- a/app/src/main/java/com/itsaky/androidide/models/XMLAttribute.java +++ b/app/src/main/java/com/itsaky/androidide/models/XMLAttribute.java @@ -114,14 +114,6 @@ public class XMLAttribute extends UiAttribute implements Parcelable { return new XMLAttribute(INamespace.ANDROID, name, "", false); } - public int getFormat() { - return format; - } - - public void setFormat(int format) { - this.format = format; - } - public int findFormat() { if (getFormat() > 0) { @@ -137,20 +129,18 @@ public class XMLAttribute extends UiAttribute implements Parcelable { return getFormat(); } + public int getFormat() { + return format; + } + + public void setFormat(int format) { + this.format = format; + } + public boolean hasFormat(int format) { return (this.format & format) != 0; } - public Attr getAttr() { - return attr; - } - - public void setAttr(Attr attr) { - Objects.requireNonNull(attr); - this.attr = attr; - setFormat(attr.format); - } - @Override public void apply(String value) { super.apply(value); @@ -175,4 +165,14 @@ public class XMLAttribute extends UiAttribute implements Parcelable { dest.writeParcelable(getAttr(), flags); dest.writeInt(this.format); } + + public Attr getAttr() { + return attr; + } + + public void setAttr(Attr attr) { + Objects.requireNonNull(attr); + this.attr = attr; + setFormat(attr.format); + } } diff --git a/app/src/main/java/com/itsaky/androidide/projects/CachingProject.kt b/app/src/main/java/com/itsaky/androidide/projects/CachingProject.kt index 9b832d5e7..5416fece2 100644 --- a/app/src/main/java/com/itsaky/androidide/projects/CachingProject.kt +++ b/app/src/main/java/com/itsaky/androidide/projects/CachingProject.kt @@ -36,120 +36,121 @@ import java.util.concurrent.* */ class CachingProject(val project: IProject) : IProject { - private val log = ILogger.newInstance(javaClass.simpleName) + private val log = ILogger.newInstance(javaClass.simpleName) - private val mName: String by lazy { this.project.name.get() } - private val mDescription: String by lazy { this.project.description.get() } - private val mProjectPath: String by lazy { this.project.projectPath.get() } - private val mProjectDir: File by lazy { this.project.projectDir.get() } - private val mBuildDir: File by lazy { this.project.buildDir.get() } - private val mBuildScript: File by lazy { this.project.buildScript.get() } - private val mProjectType: Type by lazy { this.project.type.get() } + private val mName: String by lazy { this.project.name.get() } + private val mDescription: String by lazy { this.project.description.get() } + private val mProjectPath: String by lazy { this.project.projectPath.get() } + private val mProjectDir: File by lazy { this.project.projectDir.get() } + private val mBuildDir: File by lazy { this.project.buildDir.get() } + private val mBuildScript: File by lazy { this.project.buildScript.get() } + private val mProjectType: Type by lazy { this.project.type.get() } - private var mFirstAppModule: IdeAndroidModule? = null - private val mCachedVariants: MutableMap = mutableMapOf() - private val mModules: MutableList = mutableListOf() + private var mFirstAppModule: IdeAndroidModule? = null + private val mCachedVariants: MutableMap = mutableMapOf() + private val mModules: MutableList = mutableListOf() - override fun isProjectInitialized(): CompletableFuture { - return CompletableFuture.completedFuture(true) - } + override fun isProjectInitialized(): CompletableFuture { + return CompletableFuture.completedFuture(true) + } - override fun getName(): CompletableFuture { - return CompletableFuture.completedFuture(this.mName) - } + override fun getName(): CompletableFuture { + return CompletableFuture.completedFuture(this.mName) + } - override fun getDescription(): CompletableFuture { - return CompletableFuture.completedFuture(mDescription) - } + override fun getDescription(): CompletableFuture { + return CompletableFuture.completedFuture(mDescription) + } - override fun getProjectPath(): CompletableFuture { - return CompletableFuture.completedFuture(mProjectPath) - } + override fun getProjectPath(): CompletableFuture { + return CompletableFuture.completedFuture(mProjectPath) + } - override fun getType(): CompletableFuture { - return CompletableFuture.completedFuture(mProjectType) - } + override fun getType(): CompletableFuture { + return CompletableFuture.completedFuture(mProjectType) + } - override fun getProjectDir(): CompletableFuture { - return CompletableFuture.completedFuture(mProjectDir) - } + override fun getProjectDir(): CompletableFuture { + return CompletableFuture.completedFuture(mProjectDir) + } - override fun getBuildDir(): CompletableFuture { - return CompletableFuture.completedFuture(mBuildDir) - } + override fun getBuildDir(): CompletableFuture { + return CompletableFuture.completedFuture(mBuildDir) + } - override fun getBuildScript(): CompletableFuture { - return CompletableFuture.completedFuture(mBuildScript) - } + override fun getBuildScript(): CompletableFuture { + return CompletableFuture.completedFuture(mBuildScript) + } - override fun getTasks(): CompletableFuture> { - return this.project.tasks - } + override fun getTasks(): CompletableFuture> { + return this.project.tasks + } - override fun getModules(): CompletableFuture> { - return this.project.modules - } + override fun getModules(): CompletableFuture> { + return this.project.modules + } - override fun listModules(): CompletableFuture> { - return if (this.mModules.isNotEmpty()) { - log.debug("Using cached module data...") - CompletableFuture.completedFuture(mModules) - } else { - return this.project.listModules().whenComplete { modules, err -> - if (err != null || modules == null) { - log.debug("Unable to fetch module data from tooling server", err) - return@whenComplete - } - - if (modules.isEmpty()) { - log.debug("Empty module data returned by tooling server. Ignoring...") - return@whenComplete - } - - mModules.clear() - mModules.addAll(modules) - } - } - } - - override fun getVariantData(request: VariantDataRequest): CompletableFuture { - val cached = mCachedVariants[request] - if (cached != null) { - return CompletableFuture.completedFuture(cached) + override fun listModules(): CompletableFuture> { + return if (this.mModules.isNotEmpty()) { + log.debug("Using cached module data...") + CompletableFuture.completedFuture(mModules) + } else { + return this.project.listModules().whenComplete { modules, err -> + if (err != null || modules == null) { + log.debug("Unable to fetch module data from tooling server", err) + return@whenComplete } - return this.project.getVariantData(request).whenComplete { t, u -> - if (t == null) { - log.warn( - "Unable to get variant data for " + - "variant ${request.variantName} ('${request.projectPath}') from tooling server", - u) - return@whenComplete - } - - mCachedVariants[request] = t - } - } - - override fun findByPath(path: String): CompletableFuture { - return this.project.findByPath(path) - } - - override fun findAndroidModules(): CompletableFuture> { - return this.project.findAndroidModules() - } - - override fun findFirstAndroidModule(): CompletableFuture { - return this.project.findFirstAndroidModule() - } - - override fun findFirstAndroidAppModule(): CompletableFuture { - if (mFirstAppModule != null) { - return CompletableFuture.completedFuture(mFirstAppModule) + if (modules.isEmpty()) { + log.debug("Empty module data returned by tooling server. Ignoring...") + return@whenComplete } - return this.project.findFirstAndroidAppModule().whenComplete { module, _ -> - this.mFirstAppModule = module - } + mModules.clear() + mModules.addAll(modules) + } } + } + + override fun getVariantData(request: VariantDataRequest): CompletableFuture { + val cached = mCachedVariants[request] + if (cached != null) { + return CompletableFuture.completedFuture(cached) + } + + return this.project.getVariantData(request).whenComplete { t, u -> + if (t == null) { + log.warn( + "Unable to get variant data for " + + "variant ${request.variantName} ('${request.projectPath}') from tooling server", + u + ) + return@whenComplete + } + + mCachedVariants[request] = t + } + } + + override fun findByPath(path: String): CompletableFuture { + return this.project.findByPath(path) + } + + override fun findAndroidModules(): CompletableFuture> { + return this.project.findAndroidModules() + } + + override fun findFirstAndroidModule(): CompletableFuture { + return this.project.findFirstAndroidModule() + } + + override fun findFirstAndroidAppModule(): CompletableFuture { + if (mFirstAppModule != null) { + return CompletableFuture.completedFuture(mFirstAppModule) + } + + return this.project.findFirstAndroidAppModule().whenComplete { module, _ -> + this.mFirstAppModule = module + } + } } diff --git a/app/src/main/java/com/itsaky/androidide/projects/ProjectManager.kt b/app/src/main/java/com/itsaky/androidide/projects/ProjectManager.kt index 82eb01480..656c285a0 100644 --- a/app/src/main/java/com/itsaky/androidide/projects/ProjectManager.kt +++ b/app/src/main/java/com/itsaky/androidide/projects/ProjectManager.kt @@ -37,171 +37,171 @@ import java.util.concurrent.* */ @Suppress("MemberVisibilityCanBePrivate") object ProjectManager { - private val log = ILogger.newInstance(javaClass.simpleName) + private val log = ILogger.newInstance(javaClass.simpleName) - var rootProject: IProject? = null - set(value) { - field = if (value == null) null else CachingProject(value) + var rootProject: IProject? = null + set(value) { + field = if (value == null) null else CachingProject(value) - // Cache the module data in advance for future use - if (field != null) { - field!!.listModules() - } - } - var projectPath: String? = null + // Cache the module data in advance for future use + if (field != null) { + field!!.listModules() + } + } + var projectPath: String? = null - fun isInitialized() = this.rootProject != null && this.rootProject!!.isProjectInitialized.get() + fun isInitialized() = this.rootProject != null && this.rootProject!!.isProjectInitialized.get() - fun checkInit(): Boolean { - if (isInitialized()) { - return true - } - - log.warn("Project is not initialized yet!") - return false + fun checkInit(): Boolean { + if (isInitialized()) { + return true } - fun getProjectDir(): File? = if (!checkInit()) null else File(getProjectDirPath()!!) + log.warn("Project is not initialized yet!") + return false + } - fun getProjectDirPath(): String? { - return projectPath + fun getProjectDir(): File? = if (!checkInit()) null else File(getProjectDirPath()!!) + + fun getProjectDirPath(): String? { + return projectPath + } + + fun generateSources(builder: BuildService?) { + if (builder == null) { + log.warn("Cannot generate sources. BuildService is null.") + return } - fun generateSources(builder: BuildService?) { - if (builder == null) { - log.warn("Cannot generate sources. BuildService is null.") - return - } + getApplicationModule().whenCompleteAsync { app, _ -> + if (app == null) { + log.warn("Cannot run resource and source generation task. No application module found.") + return@whenCompleteAsync + } - getApplicationModule().whenCompleteAsync { app, _ -> - if (app == null) { - log.warn( - "Cannot run resource and source generation task. No application module found.") - return@whenCompleteAsync - } + val debug = app.simpleVariants.firstOrNull { it.name == "debug" } + if (debug == null) { + log.warn("No debug variant found in application project ${app.name}") + return@whenCompleteAsync + } - val debug = app.simpleVariants.firstOrNull { it.name == "debug" } - if (debug == null) { - log.warn("No debug variant found in application project ${app.name}") - return@whenCompleteAsync - } + val mainArtifact = app.simpleVariants.first { it.name == "debug" }.mainArtifact + val genResourcesTask = mainArtifact.resGenTaskName + val genSourcesTask = mainArtifact.sourceGenTaskName + builder + .executeProjectTasks( + app.projectPath, + genResourcesTask ?: "", + genSourcesTask, - val mainArtifact = app.simpleVariants.first { it.name == "debug" }.mainArtifact - val genResourcesTask = mainArtifact.resGenTaskName - val genSourcesTask = mainArtifact.sourceGenTaskName - builder - .executeProjectTasks( - app.projectPath, - genResourcesTask ?: "", - genSourcesTask, - - // If view binding is enabled, generate the view binding classes too - if (app.viewBindingOptions != null && app.viewBindingOptions!!.isEnabled) - "dataBindingGenBaseClassesDebug" - else "") - .whenComplete { result, taskErr -> - if (!result.isSuccessful || taskErr != null) { - log.warn( - "Execution for tasks '$genResourcesTask' and '$genSourcesTask' failed.", - taskErr ?: "") - } else { - notifyProjectUpdate() - } - } + // If view binding is enabled, generate the view binding classes too + if (app.viewBindingOptions != null && app.viewBindingOptions!!.isEnabled) + "dataBindingGenBaseClassesDebug" + else "" + ) + .whenComplete { result, taskErr -> + if (!result.isSuccessful || taskErr != null) { + log.warn( + "Execution for tasks '$genResourcesTask' and '$genSourcesTask' failed.", + taskErr ?: "" + ) + } else { + notifyProjectUpdate() + } } } + } - fun getApplicationModule(): CompletableFuture { - if (rootProject == null) { - log.error( - "No root project instance is set. Is the project initialization process finished?") - return CompletableFuture.completedFuture(null) - } - - return rootProject!!.findFirstAndroidAppModule().whenComplete { app, err -> - if (app == null || err != null) { - log.debug("An error occurred while fetching model for application module.") - return@whenComplete - } - } + fun getApplicationModule(): CompletableFuture { + if (rootProject == null) { + log.error("No root project instance is set. Is the project initialization process finished?") + return CompletableFuture.completedFuture(null) } - fun getApplicationResDirectories(): CompletableFuture?> { - return getApplicationModule().thenApplyAsync { - if (it == null) null else collectResDirectories(it) - } + return rootProject!!.findFirstAndroidAppModule().whenComplete { app, err -> + if (app == null || err != null) { + log.debug("An error occurred while fetching model for application module.") + return@whenComplete + } + } + } + + fun getApplicationResDirectories(): CompletableFuture?> { + return getApplicationModule().thenApplyAsync { + if (it == null) null else collectResDirectories(it) + } + } + + fun notifyProjectUpdate() { + log.debug("Updating class paths in language servers...") + getApplicationModule().whenCompleteAsync { it, _ -> + if (it == null) { + log.debug("Cannot find application module. Unable to update class paths to LSP.") + return@whenCompleteAsync + } + + val server = StudioApp.getInstance().javaLanguageServer + val classPaths = collectClassPaths(it) + val sourceDirs = collectSourceDirs(it) + val configuration = JavaServerConfiguration(classPaths, sourceDirs) + server.configurationChanged(configuration) + } + } + + fun collectResDirectories(android: IdeAndroidModule) = + ProjectDataCollector.collectResDirectories(this.rootProject!!, android) + + fun collectClassPaths(app: IdeAndroidModule) = + ProjectDataCollector.collectClassPaths(this.rootProject!!, app) + + fun collectSourceDirs(app: IdeAndroidModule): Set = + ProjectDataCollector.collectSourceDirs(this.rootProject!!, app) + + @Suppress("unused") + fun collectProjectDependencies(project: IProject, app: IdeAndroidModule) = + ProjectDataCollector.collectProjectDependencies(project, app) + + @Suppress("unused") + fun collectSourceDirs(projects: List) = + ProjectDataCollector.collectSourceDirs(projects) + + @Suppress("unused") + fun collectSources(java: IdeJavaModule) = ProjectDataCollector.collectSources(java) + + @Suppress("unused") + fun collectSources(android: IdeAndroidModule) = ProjectDataCollector.collectSources(android) + + fun findModuleForFile(file: File): CompletableFuture { + if (!checkInit()) { + return CompletableFuture.completedFuture(null) } - fun notifyProjectUpdate() { - log.debug("Updating class paths in language servers...") - getApplicationModule().whenCompleteAsync { it, _ -> - if (it == null) { - log.debug("Cannot find application module. Unable to update class paths to LSP.") - return@whenCompleteAsync + val notAvailable = ":::::" + return rootProject!! + .listModules() + .thenApplyAsync { modules -> + val path = file.canonicalPath + + var longestPath = "" + for (module in modules) { + val modulePath = module.projectDir.canonicalPath + if (path.startsWith(modulePath)) { + if (longestPath.length < modulePath.length) { + longestPath = modulePath } - - val server = StudioApp.getInstance().javaLanguageServer - val classPaths = collectClassPaths(it) - val sourceDirs = collectSourceDirs(it) - val configuration = JavaServerConfiguration(classPaths, sourceDirs) - server.configurationChanged(configuration) - } - } - - fun collectResDirectories(android: IdeAndroidModule) = - ProjectDataCollector.collectResDirectories(this.rootProject!!, android) - - fun collectClassPaths(app: IdeAndroidModule) = - ProjectDataCollector.collectClassPaths(this.rootProject!!, app) - - fun collectSourceDirs(app: IdeAndroidModule): Set = - ProjectDataCollector.collectSourceDirs(this.rootProject!!, app) - - @Suppress("unused") - fun collectProjectDependencies(project: IProject, app: IdeAndroidModule) = - ProjectDataCollector.collectProjectDependencies(project, app) - - @Suppress("unused") - fun collectSourceDirs(projects: List) = - ProjectDataCollector.collectSourceDirs(projects) - - @Suppress("unused") - fun collectSources(java: IdeJavaModule) = ProjectDataCollector.collectSources(java) - - @Suppress("unused") - fun collectSources(android: IdeAndroidModule) = ProjectDataCollector.collectSources(android) - - fun findModuleForFile(file: File): CompletableFuture { - if (!checkInit()) { - return CompletableFuture.completedFuture(null) + } } - val notAvailable = ":::::" - return rootProject!! - .listModules() - .thenApplyAsync { modules -> - val path = file.canonicalPath + if (longestPath.isEmpty()) { + return@thenApplyAsync notAvailable + } - var longestPath = "" - for (module in modules) { - val modulePath = module.projectDir.canonicalPath - if (path.startsWith(modulePath)) { - if (longestPath.length < modulePath.length) { - longestPath = modulePath - } - } - } - - if (longestPath.isEmpty()) { - return@thenApplyAsync notAvailable - } - - return@thenApplyAsync longestPath - } - .thenApplyAsync { - if (it == notAvailable) { - return@thenApplyAsync null - } else return@thenApplyAsync rootProject!!.findByPath(it).get() - } - } + return@thenApplyAsync longestPath + } + .thenApplyAsync { + if (it == notAvailable) { + return@thenApplyAsync null + } else return@thenApplyAsync rootProject!!.findByPath(it).get() + } + } } diff --git a/app/src/main/java/com/itsaky/androidide/projects/ProjectResourceTable.java b/app/src/main/java/com/itsaky/androidide/projects/ProjectResourceTable.java index 5147438ca..102b107e0 100644 --- a/app/src/main/java/com/itsaky/androidide/projects/ProjectResourceTable.java +++ b/app/src/main/java/com/itsaky/androidide/projects/ProjectResourceTable.java @@ -80,56 +80,6 @@ public class ProjectResourceTable implements IResourceTable { return value; } - @Override - public String findBoolean(@NonNull String name) { - final var table = ValuesTableFactory.getTable(resDir); - - if (table == null) { - return name; - } - - final var resource = table.findBoolean(name); - if (resource == null) { - return name; - } - - final var val = resource.getValue(); - if (val.startsWith("@bool/")) { - final var ref = val.substring("@bool/".length()); - if (ref.equals(name)) { - // recursive reference - return name; - } - } - - return val; - } - - @Override - public String findInteger(@NonNull String name) { - final var table = ValuesTableFactory.getTable(resDir); - - if (table == null) { - return name; - } - - final var resource = table.findInteger(name); - if (resource == null) { - return name; - } - - final var val = resource.getValue(); - if (val.startsWith("@integer/")) { - final var ref = val.substring("@integer/".length()); - if (ref.equals(name)) { - // recursive reference - return name; - } - } - - return val; - } - @Override public String findColor(@NonNull String name) { final var table = ValuesTableFactory.getTable(resDir); @@ -193,6 +143,68 @@ public class ProjectResourceTable implements IResourceTable { return value; } + @Override + public String findBoolean(@NonNull String name) { + final var table = ValuesTableFactory.getTable(resDir); + + if (table == null) { + return name; + } + + final var resource = table.findBoolean(name); + if (resource == null) { + return name; + } + + final var val = resource.getValue(); + if (val.startsWith("@bool/")) { + final var ref = val.substring("@bool/".length()); + if (ref.equals(name)) { + // recursive reference + return name; + } + } + + return val; + } + + @Override + public String findInteger(@NonNull String name) { + final var table = ValuesTableFactory.getTable(resDir); + + if (table == null) { + return name; + } + + final var resource = table.findInteger(name); + if (resource == null) { + return name; + } + + final var val = resource.getValue(); + if (val.startsWith("@integer/")) { + final var ref = val.substring("@integer/".length()); + if (ref.equals(name)) { + // recursive reference + return name; + } + } + + return val; + } + + @Override + public void setInflatingFile(@NonNull File file) { + if (file.getParentFile() == null) { + throw new IllegalArgumentException("Invalid inflating file"); + } + + setupDirectories( + file // layout file + .getParentFile() // layout dir + .getParentFile()); // res dir + } + @NonNull @Override public Collection listResourceNames(@Nullable String type) { @@ -223,45 +235,6 @@ public class ProjectResourceTable implements IResourceTable { } } - private void listAndAddTo(HashSet set, @NonNull File dir, String type) { - final var files = dir.listFiles(); - if (files == null) { - return; - } - - for (var file : files) { - var name = file.getName(); - if (!name.contains(".")) { - continue; - } - - name = name.substring(0, name.lastIndexOf('.')); - name = String.format(Locale.ROOT, "@%s/%s", type, name); - set.add(name); - } - } - - @NonNull - private Collection listAllResourceNames() { - final var set = new HashSet<>(listResourceNames("color")); - set.addAll(listResourceNames("drawable")); - set.addAll(listResourceNames("mipmap")); - set.addAll(listResourceNames("layout")); - return set; - } - - @Override - public void setInflatingFile(@NonNull File file) { - if (file.getParentFile() == null) { - throw new IllegalArgumentException("Invalid inflating file"); - } - - setupDirectories( - file // layout file - .getParentFile() // layout dir - .getParentFile()); // res dir - } - private void setupDirectories(File resDir) { if (resDir == null) { @@ -296,6 +269,33 @@ public class ProjectResourceTable implements IResourceTable { return null; } + private void listAndAddTo(HashSet set, @NonNull File dir, String type) { + final var files = dir.listFiles(); + if (files == null) { + return; + } + + for (var file : files) { + var name = file.getName(); + if (!name.contains(".")) { + continue; + } + + name = name.substring(0, name.lastIndexOf('.')); + name = String.format(Locale.ROOT, "@%s/%s", type, name); + set.add(name); + } + } + + @NonNull + private Collection listAllResourceNames() { + final var set = new HashSet<>(listResourceNames("color")); + set.addAll(listResourceNames("drawable")); + set.addAll(listResourceNames("mipmap")); + set.addAll(listResourceNames("layout")); + return set; + } + private static class NameStartsWith implements FileFilter { private final String name; diff --git a/app/src/main/java/com/itsaky/androidide/services/BuildService.kt b/app/src/main/java/com/itsaky/androidide/services/BuildService.kt index c3b55b04f..e6f4ce41e 100644 --- a/app/src/main/java/com/itsaky/androidide/services/BuildService.kt +++ b/app/src/main/java/com/itsaky/androidide/services/BuildService.kt @@ -30,42 +30,42 @@ import java.util.concurrent.* */ interface BuildService { - /** - * Initialize the project. - * - * @param rootDir The root directory of the project to initialize. - * @return A [CompletableFuture] which returns an [InitializeResult] when the project - * initialization process finishes. - */ - fun initializeProject(rootDir: String): CompletableFuture + /** + * Initialize the project. + * + * @param rootDir The root directory of the project to initialize. + * @return A [CompletableFuture] which returns an [InitializeResult] when the project + * initialization process finishes. + */ + fun initializeProject(rootDir: String): CompletableFuture - /** - * Execute the given tasks. - * - * @param tasks The tasks to execute. If the fully qualified path of the task is not specified, - * then it will be executed in the root project directory. - * @return A [CompletableFuture] which returns a list of [TaskExecutionResult]. The result - * contains a list of tasks that were executed and the result of the whole execution. - * @see BuildService.executeProjectTasks - */ - fun executeTasks(vararg tasks: String): CompletableFuture + /** + * Execute the given tasks. + * + * @param tasks The tasks to execute. If the fully qualified path of the task is not specified, + * then it will be executed in the root project directory. + * @return A [CompletableFuture] which returns a list of [TaskExecutionResult]. The result + * contains a list of tasks that were executed and the result of the whole execution. + * @see BuildService.executeProjectTasks + */ + fun executeTasks(vararg tasks: String): CompletableFuture - /** - * Execute the given tasks of the given project. - * - * @param projectPath The path of the project. All the tasks will be executed in this project. - * @param tasks The tasks to execute. These may or may not be fully qualified names of the - * project. If the task name is not fully qualified, then it will be executed in the given - * project path. For example, if the project path is ':app' and the task is 'assembleDebug', - * then, ':app:assembleDebug' task will be executed. - * @return A [CompletableFuture] which returns a list of [TaskExecutionResult]. The result - * contains a list of tasks that were executed and the result of the whole execution. - */ - fun executeProjectTasks( - projectPath: String, - vararg tasks: String - ): CompletableFuture + /** + * Execute the given tasks of the given project. + * + * @param projectPath The path of the project. All the tasks will be executed in this project. + * @param tasks The tasks to execute. These may or may not be fully qualified names of the + * project. If the task name is not fully qualified, then it will be executed in the given project + * path. For example, if the project path is ':app' and the task is 'assembleDebug', then, + * ':app:assembleDebug' task will be executed. + * @return A [CompletableFuture] which returns a list of [TaskExecutionResult]. The result + * contains a list of tasks that were executed and the result of the whole execution. + */ + fun executeProjectTasks( + projectPath: String, + vararg tasks: String + ): CompletableFuture - /** Cancel any running build. */ - fun cancelCurrentBuild(): CompletableFuture + /** Cancel any running build. */ + fun cancelCurrentBuild(): CompletableFuture } diff --git a/app/src/main/java/com/itsaky/androidide/services/GradleBuildService.java b/app/src/main/java/com/itsaky/androidide/services/GradleBuildService.java index 335296fea..2405982c7 100644 --- a/app/src/main/java/com/itsaky/androidide/services/GradleBuildService.java +++ b/app/src/main/java/com/itsaky/androidide/services/GradleBuildService.java @@ -321,12 +321,6 @@ public class GradleBuildService extends Service implements BuildService, IToolin .thenApply(this::markBuildAsFinished); } - private String getGradleInstallationPath() { - return StudioApp.getInstance() - .getPrefManager() - .getString(BuildPreferences.KEY_CUSTOM_GRADLE_INSTALLATION, ""); - } - @NonNull @Override public CompletableFuture executeProjectTasks( @@ -353,6 +347,12 @@ public class GradleBuildService extends Service implements BuildService, IToolin return server.cancelCurrentBuild(); } + private String getGradleInstallationPath() { + return StudioApp.getInstance() + .getPrefManager() + .getString(BuildPreferences.KEY_CUSTOM_GRADLE_INSTALLATION, ""); + } + private void checkServerStarted() { if (!isToolingServerStarted) { throw new IllegalStateException("Tooling API server has not been started"); diff --git a/app/src/main/java/com/itsaky/androidide/services/LogReceiver.java b/app/src/main/java/com/itsaky/androidide/services/LogReceiver.java index 8d27bf5c2..05626ce5b 100755 --- a/app/src/main/java/com/itsaky/androidide/services/LogReceiver.java +++ b/app/src/main/java/com/itsaky/androidide/services/LogReceiver.java @@ -29,12 +29,10 @@ import com.itsaky.androidide.utils.ILogger; public class LogReceiver extends BroadcastReceiver { - private LogListener listener; - public static final String APPEND_LOG = "com.itsaky.androidide.logs.APPEND_LOG"; public static final String EXTRA_LINE = "log_line"; - private final ILogger LOG = ILogger.newInstance("LogReceiver"); + private LogListener listener; public LogReceiver setLogListener(LogListener listener) { this.listener = listener; diff --git a/app/src/main/java/com/itsaky/androidide/tasks/callables/ListDirectoryCallable.java b/app/src/main/java/com/itsaky/androidide/tasks/callables/ListDirectoryCallable.java index 8d5fad31c..ea2d5c78b 100755 --- a/app/src/main/java/com/itsaky/androidide/tasks/callables/ListDirectoryCallable.java +++ b/app/src/main/java/com/itsaky/androidide/tasks/callables/ListDirectoryCallable.java @@ -29,6 +29,14 @@ import java.io.FileFilter; public class ListDirectoryCallable implements java.util.concurrent.Callable { + private final FileFilter ARCHIVE_FILTER = + new FileFilter() { + + @Override + public boolean accept(File p1) { + return p1.isFile() && (p1.getName().endsWith(".tar.xz") || p1.getName().endsWith(".zip")); + } + }; private File file; public ListDirectoryCallable(File file) { @@ -39,13 +47,4 @@ public class ListDirectoryCallable implements java.util.concurrent.Callable new") + private IAttribute layoutHeightAttr() { + return new UiAttribute(INamespace.ANDROID, "layout_height", "wrap_content"); + } + + @NonNull + @Contract(" -> new") + private IAttribute layoutWidthAttr() { + return new UiAttribute(INamespace.ANDROID, "layout_width", "wrap_content"); + } + /** * Finds the index at which the placeholder will be added in the current view group. * @@ -180,30 +189,6 @@ public class WidgetDragListener implements View.OnDragListener { return src; } - private RectF leftHalf(RectF src) { - final RectF result = new RectF(src); - result.right -= result.width() / 2; - return src; - } - - private RectF rightHalf(RectF src) { - final RectF result = new RectF(src); - result.left += result.width() / 2; - return src; - } - - @NonNull - @Contract(" -> new") - private IAttribute layoutHeightAttr() { - return new UiAttribute(INamespace.ANDROID, "layout_height", "wrap_content"); - } - - @NonNull - @Contract(" -> new") - private IAttribute layoutWidthAttr() { - return new UiAttribute(INamespace.ANDROID, "layout_width", "wrap_content"); - } - @NonNull private IView createView(@NonNull UIWidget widget) { try { @@ -223,6 +208,18 @@ public class WidgetDragListener implements View.OnDragListener { } } + private RectF leftHalf(RectF src) { + final RectF result = new RectF(src); + result.right -= result.width() / 2; + return src; + } + + private RectF rightHalf(RectF src) { + final RectF result = new RectF(src); + result.left += result.width() / 2; + return src; + } + public static interface OnViewAddedListener { void onViewAdded(IView view); } diff --git a/app/src/main/java/com/itsaky/androidide/ui/WidgetTouchListener.java b/app/src/main/java/com/itsaky/androidide/ui/WidgetTouchListener.java index bbfaf7caa..d45b6f1f8 100644 --- a/app/src/main/java/com/itsaky/androidide/ui/WidgetTouchListener.java +++ b/app/src/main/java/com/itsaky/androidide/ui/WidgetTouchListener.java @@ -62,11 +62,6 @@ public class WidgetTouchListener extends GestureDetector.SimpleOnGestureListener return mGestureDetector.onTouchEvent(motionEvent); } - @Override - public boolean onDown(MotionEvent e) { - return true; // We would like to get notified about further events. - } - // TODO Should we use onSingleTapConfirmed instead? @Override public boolean onSingleTapUp(MotionEvent e) { @@ -90,6 +85,11 @@ public class WidgetTouchListener extends GestureDetector.SimpleOnGestureListener } } + @Override + public boolean onDown(MotionEvent e) { + return true; // We would like to get notified about further events. + } + /** * A listener which is invoked when we detect a single click event. * diff --git a/app/src/main/java/com/itsaky/androidide/utils/DialogUtils.java b/app/src/main/java/com/itsaky/androidide/utils/DialogUtils.java index a03f3fdde..b2f923760 100644 --- a/app/src/main/java/com/itsaky/androidide/utils/DialogUtils.java +++ b/app/src/main/java/com/itsaky/androidide/utils/DialogUtils.java @@ -34,18 +34,6 @@ import org.jetbrains.annotations.Contract; */ public class DialogUtils { - /** - * Creates a new MaterialAlertDialogBuilder with the app's default style. - * - * @param context The context for the dialog builder. - * @return The new MaterialAlertDialogBuilder instance. - */ - @NonNull - @Contract("_ -> new") - public static MaterialAlertDialogBuilder newMaterialDialogBuilder(Context context) { - return new MaterialAlertDialogBuilder(context, R.style.AppTheme_MaterialAlertDialog); - } - /** * Create a new alert dialog with two buttons: Yes and No. This method * simply calls {@link #newYesNoDialog(Context, String, String, DialogInterface.OnClickListener, @@ -98,4 +86,16 @@ public class DialogUtils { return builder; } + + /** + * Creates a new MaterialAlertDialogBuilder with the app's default style. + * + * @param context The context for the dialog builder. + * @return The new MaterialAlertDialogBuilder instance. + */ + @NonNull + @Contract("_ -> new") + public static MaterialAlertDialogBuilder newMaterialDialogBuilder(Context context) { + return new MaterialAlertDialogBuilder(context, R.style.AppTheme_MaterialAlertDialog); + } } diff --git a/app/src/main/java/com/itsaky/androidide/utils/EditorActivityActions.kt b/app/src/main/java/com/itsaky/androidide/utils/EditorActivityActions.kt index 5f502a39b..b9f1cb0d6 100644 --- a/app/src/main/java/com/itsaky/androidide/utils/EditorActivityActions.kt +++ b/app/src/main/java/com/itsaky/androidide/utils/EditorActivityActions.kt @@ -46,39 +46,39 @@ import com.itsaky.androidide.actions.text.UndoAction * @author Akash Yadav */ class EditorActivityActions { - companion object { - @JvmStatic - fun register(context: Context) { - val registry = ActionsRegistry.getInstance() + companion object { + @JvmStatic + fun register(context: Context) { + val registry = ActionsRegistry.getInstance() - // Toolbar actions - registry.registerAction(UndoAction(context)) - registry.registerAction(RedoAction(context)) - registry.registerAction(QuickRunAction(context)) - registry.registerAction(RunActionMenu(context)) - registry.registerAction(SaveFileAction(context)) - registry.registerAction(PreviewLayoutAction(context)) - registry.registerAction(FindActionMenu(context)) - registry.registerAction(FileTreeAction(context)) - registry.registerAction(DaemonStatusAction(context)) - registry.registerAction(CancelBuildAction(context)) - registry.registerAction(ProjectSyncAction(context)) + // Toolbar actions + registry.registerAction(UndoAction(context)) + registry.registerAction(RedoAction(context)) + registry.registerAction(QuickRunAction(context)) + registry.registerAction(RunActionMenu(context)) + registry.registerAction(SaveFileAction(context)) + registry.registerAction(PreviewLayoutAction(context)) + registry.registerAction(FindActionMenu(context)) + registry.registerAction(FileTreeAction(context)) + registry.registerAction(DaemonStatusAction(context)) + registry.registerAction(CancelBuildAction(context)) + registry.registerAction(ProjectSyncAction(context)) - // editor text actions - registry.registerAction(ExpandSelectionAction(context)) - registry.registerAction(SelectAllAction(context)) - registry.registerAction(CutAction(context)) - registry.registerAction(CopyAction(context)) - registry.registerAction(PasteAction(context)) - registry.registerAction(FormatCodeAction(context)) + // editor text actions + registry.registerAction(ExpandSelectionAction(context)) + registry.registerAction(SelectAllAction(context)) + registry.registerAction(CutAction(context)) + registry.registerAction(CopyAction(context)) + registry.registerAction(PasteAction(context)) + registry.registerAction(FormatCodeAction(context)) - // editor search actions - registry.registerAction(GoToNextAction(context)) - registry.registerAction(GoToPreviousAction(context)) - registry.registerAction(ReplaceAction(context)) - } - - @JvmStatic - fun clear() = ActionsRegistry.getInstance().clearActions(ActionItem.Location.EDITOR_TOOLBAR) + // editor search actions + registry.registerAction(GoToNextAction(context)) + registry.registerAction(GoToPreviousAction(context)) + registry.registerAction(ReplaceAction(context)) } + + @JvmStatic + fun clear() = ActionsRegistry.getInstance().clearActions(ActionItem.Location.EDITOR_TOOLBAR) + } } diff --git a/app/src/main/java/com/itsaky/androidide/utils/EditorBottomSheetBehavior.java b/app/src/main/java/com/itsaky/androidide/utils/EditorBottomSheetBehavior.java index ce7919600..843c65b35 100644 --- a/app/src/main/java/com/itsaky/androidide/utils/EditorBottomSheetBehavior.java +++ b/app/src/main/java/com/itsaky/androidide/utils/EditorBottomSheetBehavior.java @@ -31,8 +31,8 @@ import com.itsaky.androidide.databinding.LayoutEditorBottomSheetBinding; public class EditorBottomSheetBehavior extends BottomSheetBehavior { - private LayoutEditorBottomSheetBinding binding; private static final ILogger LOG = ILogger.newInstance("EditorBottomSheetBehavior"); + private LayoutEditorBottomSheetBinding binding; public EditorBottomSheetBehavior(Context context, AttributeSet attributeSet) { super(context, attributeSet); diff --git a/app/src/main/java/com/itsaky/androidide/utils/HighlightRangeHelper.java b/app/src/main/java/com/itsaky/androidide/utils/HighlightRangeHelper.java index 838f7a9fa..cf47ad57e 100644 --- a/app/src/main/java/com/itsaky/androidide/utils/HighlightRangeHelper.java +++ b/app/src/main/java/com/itsaky/androidide/utils/HighlightRangeHelper.java @@ -44,6 +44,7 @@ import java.util.List; public class HighlightRangeHelper { + private static final ILogger LOG = ILogger.newInstance("HighlightRangeHelper"); private final List highlights; public HighlightRangeHelper(List highlights) { @@ -54,6 +55,36 @@ public class HighlightRangeHelper { return isOfKind(line, column, ENUM_TYPE); } + /** + * Check if the token at the given index has the given token kind. + * + * @param line The line to check. + * @param column The column to check. + * @param kind The kind to check. + * @return {@code true} if the token at the given position has the given kind, {@code false} + * otherwise. + */ + public boolean isOfKind(int line, int column, HighlightTokenKind kind) { + final var position = new Position(line, column); + int left = 0; + int right = this.highlights.size(); + int mid; + while (left < right) { + mid = (left + right) / 2; + var token = this.highlights.get(mid); + var range = token.getRange(); + var pos = range.getStart().compareTo(position); + if (pos < 0) { + left = mid + 1; + } else if (pos > 0) { + right = mid - 1; + } else { + return token.getKind() == kind; + } + } + return false; + } + public boolean isAnnotationType(int line, int column) { return isOfKind(line, column, ANNOTATION); } @@ -121,36 +152,4 @@ public class HighlightRangeHelper { public boolean isLocal(int line, int column) { return isOfKind(line, column, LOCAL_VARIABLE); } - - /** - * Check if the token at the given index has the given token kind. - * - * @param line The line to check. - * @param column The column to check. - * @param kind The kind to check. - * @return {@code true} if the token at the given position has the given kind, {@code false} - * otherwise. - */ - public boolean isOfKind(int line, int column, HighlightTokenKind kind) { - final var position = new Position(line, column); - int left = 0; - int right = this.highlights.size(); - int mid; - while (left < right) { - mid = (left + right) / 2; - var token = this.highlights.get(mid); - var range = token.getRange(); - var pos = range.getStart().compareTo(position); - if (pos < 0) { - left = mid + 1; - } else if (pos > 0) { - right = mid - 1; - } else { - return token.getKind() == kind; - } - } - return false; - } - - private static final ILogger LOG = ILogger.newInstance("HighlightRangeHelper"); } diff --git a/app/src/main/java/com/itsaky/androidide/utils/LSPUtils.java b/app/src/main/java/com/itsaky/androidide/utils/LSPUtils.java index bd1df4135..7145d2469 100755 --- a/app/src/main/java/com/itsaky/androidide/utils/LSPUtils.java +++ b/app/src/main/java/com/itsaky/androidide/utils/LSPUtils.java @@ -19,8 +19,6 @@ **************************************************************************************/ package com.itsaky.androidide.utils; -import com.itsaky.lsp.models.DiagnosticItem; -import com.itsaky.lsp.models.DiagnosticSeverity; import com.itsaky.lsp.models.Position; import com.itsaky.lsp.models.Range; @@ -31,19 +29,41 @@ public class LSPUtils { public static final Position Position_ofZero; public static final Range Range_ofZero; + public static final Comparator RANGE_START_COMPARATOR = + (r1, r2) -> { + // Check if ranges are null + if (r1 == null && r2 != null) { + return 1; + } else if (r1 != null && r2 == null) { + return -1; + } else if (r1 == null) { + return 0; + } else { + final int r1StartLine = r1.getStart().getLine(); + final int r1StartCol = r1.getStart().getColumn(); + final int r2StartLine = r2.getStart().getLine(); + final int r2StartCol = r2.getStart().getColumn(); + + // Compare by lines + if (r1StartLine < r2StartLine) { + return -1; + } else if (r1StartLine > r2StartLine) { + return 1; + } else { + + // Lines are same + // Compare by columns + // Both ranges have same start position + return Integer.compare(r1StartCol, r2StartCol); + } + } + }; static { Position_ofZero = new Position(0, 0); Range_ofZero = new Range(Position_ofZero, Position_ofZero); } - public static boolean isEqual(Position p1, Position p2) { - if (p1 == null) return false; - if (p2 == null) return false; - - return p1.getLine() == p2.getLine() && p1.getColumn() == p2.getColumn(); - } - public static boolean isInRange(Range range, Position position) { final int line = position.getLine(); final int column = position.getColumn(); @@ -67,6 +87,13 @@ public class LSPUtils { && LSPUtils.isEqual(r1.getEnd(), r2.getEnd()); } + public static boolean isEqual(Position p1, Position p2) { + if (p1 == null) return false; + if (p2 == null) return false; + + return p1.getLine() == p2.getLine() && p1.getColumn() == p2.getColumn(); + } + public static Range getSingleLineRange(int line, int column, int length) { final Range range = new Range(); range.setStart(new Position(line, column)); @@ -102,34 +129,4 @@ public class LSPUtils { return -1; } - - public static final Comparator RANGE_START_COMPARATOR = - (r1, r2) -> { - // Check if ranges are null - if (r1 == null && r2 != null) { - return 1; - } else if (r1 != null && r2 == null) { - return -1; - } else if (r1 == null) { - return 0; - } else { - final int r1StartLine = r1.getStart().getLine(); - final int r1StartCol = r1.getStart().getColumn(); - final int r2StartLine = r2.getStart().getLine(); - final int r2StartCol = r2.getStart().getColumn(); - - // Compare by lines - if (r1StartLine < r2StartLine) { - return -1; - } else if (r1StartLine > r2StartLine) { - return 1; - } else { - - // Lines are same - // Compare by columns - // Both ranges have same start position - return Integer.compare(r1StartCol, r2StartCol); - } - } - }; } diff --git a/app/src/main/java/com/itsaky/androidide/utils/ListingFileRedirect.kt b/app/src/main/java/com/itsaky/androidide/utils/ListingFileRedirect.kt index 5eff140b6..d6e1ea55b 100644 --- a/app/src/main/java/com/itsaky/androidide/utils/ListingFileRedirect.kt +++ b/app/src/main/java/com/itsaky/androidide/utils/ListingFileRedirect.kt @@ -23,21 +23,21 @@ import java.util.* class ListingFileRedirect { - companion object { - /** Redirect file will have this marker as the first line as comment. */ - private const val REDIRECT_MARKER = "#- File Locator -" + companion object { + /** Redirect file will have this marker as the first line as comment. */ + private const val REDIRECT_MARKER = "#- File Locator -" - /** Property name in a [Properties] for the metadata file location. */ - private const val REDIRECT_PROPERTY_NAME = "listingFile" + /** Property name in a [Properties] for the metadata file location. */ + private const val REDIRECT_PROPERTY_NAME = "listingFile" - @JvmStatic - fun maybeExtractRedirectedFile(redirectFile: File): File? { - val fileContent = redirectFile.readText() - return if (fileContent.startsWith(REDIRECT_MARKER)) { - val fileLocator = Properties().also { it.load(StringReader(fileContent)) } - val file = File(fileLocator.getProperty(REDIRECT_PROPERTY_NAME)) - if (!file.isAbsolute) redirectFile.parentFile!!.resolve(file) else file - } else null - } + @JvmStatic + fun maybeExtractRedirectedFile(redirectFile: File): File? { + val fileContent = redirectFile.readText() + return if (fileContent.startsWith(REDIRECT_MARKER)) { + val fileLocator = Properties().also { it.load(StringReader(fileContent)) } + val file = File(fileLocator.getProperty(REDIRECT_PROPERTY_NAME)) + if (!file.isAbsolute) redirectFile.parentFile!!.resolve(file) else file + } else null } + } } diff --git a/app/src/main/java/com/itsaky/androidide/utils/ProjectWriter.java b/app/src/main/java/com/itsaky/androidide/utils/ProjectWriter.java index 53b2dd158..62848034e 100755 --- a/app/src/main/java/com/itsaky/androidide/utils/ProjectWriter.java +++ b/app/src/main/java/com/itsaky/androidide/utils/ProjectWriter.java @@ -46,8 +46,6 @@ import java.util.zip.ZipInputStream; public class ProjectWriter { - private static ProjectWriterCallback callback; - private static final String XML_TEMPLATE_PATH = "templates/xml"; private static final String SOURCE_PATH_REGEX = "/.*/src/.*/java|kt"; private static final String FILE_EXT_REGEX = ".*/java|kt|gradle|xml|txt|cpp"; @@ -58,6 +56,7 @@ public class ProjectWriter { PACKAGE_NAME_CPP = "{$package_name_cpp}", CPP_FLAGS = "$cpp_flags", LIBRARY_NAME = "$library_name"; + private static ProjectWriterCallback callback; @NonNull public static String createMenu() { diff --git a/app/src/main/java/com/itsaky/androidide/utils/Symbols.java b/app/src/main/java/com/itsaky/androidide/utils/Symbols.java index 452e09966..59845a2a2 100755 --- a/app/src/main/java/com/itsaky/androidide/utils/Symbols.java +++ b/app/src/main/java/com/itsaky/androidide/utils/Symbols.java @@ -41,15 +41,6 @@ public class Symbols { return new Symbol[0]; } - public static String createTabSpaces() { - int size = StudioApp.getInstance().getPrefManager().getEditorTabSize(); - StringBuilder tab = new StringBuilder(); - for (int i = 1; i <= size; i++) { - tab.append(" "); - } - return tab.toString(); - } - public static Symbol[] javaSymbols() { return new Symbol[] { new Symbol("↹", "\t"), @@ -105,4 +96,13 @@ public class Symbols { new Symbol("-") }; } + + public static String createTabSpaces() { + int size = StudioApp.getInstance().getPrefManager().getEditorTabSize(); + StringBuilder tab = new StringBuilder(); + for (int i = 1; i <= size; i++) { + tab.append(" "); + } + return tab.toString(); + } } diff --git a/app/src/main/java/com/itsaky/androidide/viewmodel/WizardViewModel.java b/app/src/main/java/com/itsaky/androidide/viewmodel/WizardViewModel.java index f12e7c5cb..367479b5b 100644 --- a/app/src/main/java/com/itsaky/androidide/viewmodel/WizardViewModel.java +++ b/app/src/main/java/com/itsaky/androidide/viewmodel/WizardViewModel.java @@ -8,14 +8,13 @@ import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import com.blankj.utilcode.util.CollectionUtils; - import com.itsaky.androidide.R; import com.itsaky.androidide.interfaces.ProjectWriterCallback; import com.itsaky.androidide.models.NewProjectDetails; import com.itsaky.androidide.models.ProjectTemplate; - import com.itsaky.androidide.tasks.TaskExecutor; import com.itsaky.androidide.tasks.callables.ProjectCreatorCallable; + import java.io.File; import java.util.ArrayList; import java.util.List; diff --git a/app/src/main/java/com/itsaky/androidide/views/MaterialBanner.java b/app/src/main/java/com/itsaky/androidide/views/MaterialBanner.java index dd0c8a205..d71c70101 100755 --- a/app/src/main/java/com/itsaky/androidide/views/MaterialBanner.java +++ b/app/src/main/java/com/itsaky/androidide/views/MaterialBanner.java @@ -103,17 +103,17 @@ public class MaterialBanner extends CoordinatorLayout { Animation anim = new Animation() { + @Override + public boolean willChangeBounds() { + return true; + } + @Override public void applyTransformation(float time, Transformation t) { p.height = time == 1f ? -2 : (int) (height * time); root.setLayoutParams(p); root.requestLayout(); } - - @Override - public boolean willChangeBounds() { - return true; - } }; anim.setDuration(200); @@ -126,6 +126,11 @@ public class MaterialBanner extends CoordinatorLayout { final int initialHeight = getMeasuredHeight(); Animation animation = new Animation() { + @Override + public boolean willChangeBounds() { + return true; + } + @Override public void applyTransformation(float interpolatedTime, Transformation t) { if (interpolatedTime == 1f) { @@ -136,11 +141,6 @@ public class MaterialBanner extends CoordinatorLayout { root.requestLayout(); } } - - @Override - public boolean willChangeBounds() { - return true; - } }; animation.setDuration((long) (initialHeight / getResources().getDisplayMetrics().density)); root.startAnimation(animation); @@ -150,40 +150,40 @@ public class MaterialBanner extends CoordinatorLayout { return setIcon(ContextCompat.getDrawable(getContext(), icon)); } - public MaterialBanner setPositive(@StringRes int text, OnPositiveClickListener listener) { - return setPositive(getContext().getString(text), listener); - } - - public MaterialBanner setNegative(@StringRes int text, OnNegativeClickListener listener) { - return setNegative(getContext().getString(text), listener); - } - - public MaterialBanner setContentText(@StringRes int text) { - return setContentText(getContext().getString(text)); - } - - public MaterialBanner setContentText(String contentText) { - this.contentText = contentText; - return this; - } - public MaterialBanner setIcon(Drawable icon) { this.iconDrawable = icon; return this; } + public MaterialBanner setPositive(@StringRes int text, OnPositiveClickListener listener) { + return setPositive(getContext().getString(text), listener); + } + public MaterialBanner setPositive(String text, OnPositiveClickListener listener) { this.rightButtonText = text; this.positiveListener = listener; return this; } + public MaterialBanner setNegative(@StringRes int text, OnNegativeClickListener listener) { + return setNegative(getContext().getString(text), listener); + } + public MaterialBanner setNegative(String text, OnNegativeClickListener listener) { this.leftButtonText = text; this.negativeListener = listener; return this; } + public MaterialBanner setContentText(@StringRes int text) { + return setContentText(getContext().getString(text)); + } + + public MaterialBanner setContentText(String contentText) { + this.contentText = contentText; + return this; + } + public MaterialBanner setBannerBackgroundColor(int backgroundColor) { this.backgroundColor = backgroundColor; return this; diff --git a/app/src/main/java/com/itsaky/androidide/views/ScalableTabLayout.kt b/app/src/main/java/com/itsaky/androidide/views/ScalableTabLayout.kt index 9984988fa..f9a2e2e3c 100644 --- a/app/src/main/java/com/itsaky/androidide/views/ScalableTabLayout.kt +++ b/app/src/main/java/com/itsaky/androidide/views/ScalableTabLayout.kt @@ -1,4 +1,4 @@ -package com.itsaky.androidide.views; +package com.itsaky.androidide.views import android.content.Context import android.util.AttributeSet @@ -7,31 +7,31 @@ import com.google.android.material.tabs.TabLayout // https://stackoverflow.com/a/65605542 class ScalableTabLayout : TabLayout { - constructor(context: Context) : super(context) - constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) - constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super( - context, - attrs, - defStyleAttr - ) - - override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { - val tabLayout = getChildAt(0) as ViewGroup - val childCount = tabLayout.childCount - if (childCount > 0) { - val widthPixels = MeasureSpec.getSize(widthMeasureSpec) - val tabMinWidth = widthPixels / childCount - var remainderPixels = widthPixels % childCount - for (i in 0 until childCount) { - val it = tabLayout.getChildAt(i) - if (remainderPixels > 0) { - it.minimumWidth = tabMinWidth + 1 - remainderPixels-- - } else { - it.minimumWidth = tabMinWidth - } - } + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + constructor( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int + ) : super(context, attrs, defStyleAttr) + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + val tabLayout = getChildAt(0) as ViewGroup + val childCount = tabLayout.childCount + if (childCount > 0) { + val widthPixels = MeasureSpec.getSize(widthMeasureSpec) + val tabMinWidth = widthPixels / childCount + var remainderPixels = widthPixels % childCount + for (i in 0 until childCount) { + val it = tabLayout.getChildAt(i) + if (remainderPixels > 0) { + it.minimumWidth = tabMinWidth + 1 + remainderPixels-- + } else { + it.minimumWidth = tabMinWidth } - super.onMeasure(widthMeasureSpec, heightMeasureSpec) + } } -} \ No newline at end of file + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + } +} diff --git a/app/src/main/java/com/itsaky/androidide/views/SymbolInputView.java b/app/src/main/java/com/itsaky/androidide/views/SymbolInputView.java index 8d9b6f0dd..86745a856 100755 --- a/app/src/main/java/com/itsaky/androidide/views/SymbolInputView.java +++ b/app/src/main/java/com/itsaky/androidide/views/SymbolInputView.java @@ -68,14 +68,14 @@ public class SymbolInputView extends RecyclerView { this(both, both, offset); } - public Symbol(String label, String commit) { - this(label, commit, 1); - } - public Symbol(String label, String commit, int offset) { this.label = label; this.commit = commit; this.offset = offset; } + + public Symbol(String label, String commit) { + this(label, commit, 1); + } } } diff --git a/app/src/main/java/com/itsaky/androidide/views/editor/BaseEditorWindow.java b/app/src/main/java/com/itsaky/androidide/views/editor/BaseEditorWindow.java index 9d8f7bb39..d72bb8eaa 100644 --- a/app/src/main/java/com/itsaky/androidide/views/editor/BaseEditorWindow.java +++ b/app/src/main/java/com/itsaky/androidide/views/editor/BaseEditorWindow.java @@ -83,8 +83,13 @@ public abstract class BaseEditorWindow extends EditorPopupWindow { return text; } - protected View getRootView() { - return this.text; + protected Drawable createBackground() { + GradientDrawable background = new GradientDrawable(); + background.setShape(GradientDrawable.RECTANGLE); + background.setColor(0xff212121); + background.setStroke(1, 0xffffffff); + background.setCornerRadius(8); + return background; } public void displayWindow() { @@ -99,13 +104,8 @@ public abstract class BaseEditorWindow extends EditorPopupWindow { show(); } - protected Drawable createBackground() { - GradientDrawable background = new GradientDrawable(); - background.setShape(GradientDrawable.RECTANGLE); - background.setColor(0xff212121); - background.setStroke(1, 0xffffffff); - background.setCornerRadius(8); - return background; + protected View getRootView() { + return this.text; } @NonNull diff --git a/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java b/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java index d659924d5..f8feee910 100644 --- a/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java +++ b/app/src/main/java/com/itsaky/androidide/views/editor/CodeEditorView.java @@ -134,6 +134,11 @@ public class CodeEditorView extends FrameLayout { return bundle; } + @Nullable + public File getFile() { + return file; + } + private void handleContentChange(@NonNull ContentChangeEvent event) { isModified = true; @@ -157,96 +162,54 @@ public class CodeEditorView extends FrameLayout { } } - public boolean save() { - final var file = getFile(); - if (file == null) { - LOG.error("Cannot save file. File instance is null."); - return false; - } - - if (!isModified() && file.exists()) { - LOG.info(file.getName()); - LOG.info("File was not modified. Skipping save operation."); - return false; - } - - final var text = getEditor().getText().toString(); - + private void closeCurrentTag(String text, int line, int col) { try { - FileUtil.writeFile(file, text); - notifySaved(); - isModified = false; - return true; - } catch (IOException io) { - LOG.error("Failed to save file", file, io); - return false; + XMLLexer lexer = new XMLLexer(CharStreams.fromReader(new StringReader(text))); + Token token; + boolean wasSlash = false, wasOpen = false; + ArrayList currentNames = new ArrayList<>(); + while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) { + final int type = token.getType(); + if (type == XMLLexer.OPEN) { + wasOpen = true; + } else if (type == XMLLexer.Name) { + if (wasOpen && wasSlash && currentNames.size() > 0) { + currentNames.remove(0); + } else if (wasOpen) { + currentNames.add(0, token.getText()); + wasOpen = false; + } + } else if (type == XMLLexer.OPEN_SLASH) { + int l = token.getLine() - 1; + int c = token.getCharPositionInLine(); + if (l == line && c == col) { + break; + } else if (currentNames.size() > 0) { + currentNames.remove(0); + } + } else if (type == XMLLexer.SLASH_CLOSE || type == XMLLexer.SPECIAL_CLOSE) { + if (currentNames.size() > 0 && token.getText().trim().endsWith("/>")) { + currentNames.remove(0); + } + } else if (type == XMLLexer.SLASH) { + wasSlash = true; + } else { + wasOpen = wasSlash = false; + } + } + + if (currentNames.size() > 0) { + binding.editor.getText().insert(line, col + 2, currentNames.get(0)); + } + } catch (Throwable th) { + LOG.error("Unable to close current tag", th); } } - @NonNull - private List getLines(Content text) { - final var count = text.getLineCount(); - final var result = new ArrayList(); - - for (int i = 0; i < count; i++) { - result.add(text.getLine(i)); - } - - return result; - } - public IDEEditor getEditor() { return binding.editor; } - public LayoutCodeEditorBinding getBinding() { - return binding; - } - - @Nullable - public File getFile() { - return file; - } - - public String getText() { - return binding.editor.getText().toString(); - } - - public boolean isModified() { - return isModified; - } - - public void onPause() { - // unimplemented - } - - public void onResume() { - configureEditorIfNeeded(); - } - - public void onEditorSelected() { - final var editor = getEditor(); - if (editor != null) { - editor.onEditorSelected(); - } - } - - public void undo() { - if (binding.editor.canUndo()) { - binding.editor.undo(); - } - } - - public void redo() { - if (binding.editor.canRedo()) { - binding.editor.redo(); - } - } - - public void beginSearch() { - binding.editor.beginSearchMode(); - } - protected void postRead() { binding.editor.setEditorLanguage(createLanguage(file)); binding.editor.setLanguageServer(createLanguageServer(file)); @@ -381,48 +344,34 @@ public class CodeEditorView extends FrameLayout { isFirstCreate = false; } - private void closeCurrentTag(String text, int line, int col) { - try { - XMLLexer lexer = new XMLLexer(CharStreams.fromReader(new StringReader(text))); - Token token; - boolean wasSlash = false, wasOpen = false; - ArrayList currentNames = new ArrayList<>(); - while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) { - final int type = token.getType(); - if (type == XMLLexer.OPEN) { - wasOpen = true; - } else if (type == XMLLexer.Name) { - if (wasOpen && wasSlash && currentNames.size() > 0) { - currentNames.remove(0); - } else if (wasOpen) { - currentNames.add(0, token.getText()); - wasOpen = false; - } - } else if (type == XMLLexer.OPEN_SLASH) { - int l = token.getLine() - 1; - int c = token.getCharPositionInLine(); - if (l == line && c == col) { - break; - } else if (currentNames.size() > 0) { - currentNames.remove(0); - } - } else if (type == XMLLexer.SLASH_CLOSE || type == XMLLexer.SPECIAL_CLOSE) { - if (currentNames.size() > 0 && token.getText().trim().endsWith("/>")) { - currentNames.remove(0); - } - } else if (type == XMLLexer.SLASH) { - wasSlash = true; - } else { - wasOpen = wasSlash = false; - } - } - - if (currentNames.size() > 0) { - binding.editor.getText().insert(line, col + 2, currentNames.get(0)); - } - } catch (Throwable th) { - LOG.error("Unable to close current tag", th); + public boolean save() { + final var file = getFile(); + if (file == null) { + LOG.error("Cannot save file. File instance is null."); + return false; } + + if (!isModified() && file.exists()) { + LOG.info(file.getName()); + LOG.info("File was not modified. Skipping save operation."); + return false; + } + + final var text = getEditor().getText().toString(); + + try { + FileUtil.writeFile(file, text); + notifySaved(); + isModified = false; + return true; + } catch (IOException io) { + LOG.error("Failed to save file", file, io); + return false; + } + } + + public boolean isModified() { + return isModified; } private void notifySaved() { @@ -433,9 +382,60 @@ public class CodeEditorView extends FrameLayout { } } + public LayoutCodeEditorBinding getBinding() { + return binding; + } + + public String getText() { + return binding.editor.getText().toString(); + } + + public void onPause() { + // unimplemented + } + + public void onResume() { + configureEditorIfNeeded(); + } + + public void onEditorSelected() { + final var editor = getEditor(); + if (editor != null) { + editor.onEditorSelected(); + } + } + + public void undo() { + if (binding.editor.canUndo()) { + binding.editor.undo(); + } + } + + public void redo() { + if (binding.editor.canRedo()) { + binding.editor.redo(); + } + } + + public void beginSearch() { + binding.editor.beginSearchMode(); + } + /** Mark this files as saved. Even if it not saved. */ public void markAsSaved() { isModified = false; notifySaved(); } + + @NonNull + private List getLines(Content text) { + final var count = text.getLineCount(); + final var result = new ArrayList(); + + for (int i = 0; i < count; i++) { + result.add(text.getLine(i)); + } + + return result; + } } diff --git a/app/src/main/java/com/itsaky/androidide/views/editor/IDEEditor.java b/app/src/main/java/com/itsaky/androidide/views/editor/IDEEditor.java index cb28b7e49..0d422c0f7 100644 --- a/app/src/main/java/com/itsaky/androidide/views/editor/IDEEditor.java +++ b/app/src/main/java/com/itsaky/androidide/views/editor/IDEEditor.java @@ -121,49 +121,6 @@ public class IDEEditor extends CodeEditor { getDiagnosticWindow().showDiagnostic(mLanguageClient.getDiagnosticAt(getFile(), line, column)); } - /** - * Notify the language server that the content of this file has been changed. - * - * @param event The content change event. - */ - private void handleContentChange(ContentChangeEvent event, Unsubscribe unsubscribe) { - if (getFile() == null || mLanguageServer == null) { - return; - } - - CompletableFuture.runAsync( - () -> { - final var documentHandler = mLanguageServer.getDocumentHandler(); - final var file = getFile().toPath(); - if (documentHandler.accepts(file)) { - documentHandler.onContentChange( - new DocumentChangeEvent(file, getText(), mFileVersion + 1)); - } - - checkForSignatureHelp(event); - }); - } - - public static int createInputFlags() { - var flags = - EditorInfo.TYPE_CLASS_TEXT - | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE - | EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS; - if (StudioApp.getInstance() - .getPrefManager() - .getBoolean(PreferenceManager.KEY_EDITOR_FLAG_PASSWORD, true)) { - flags |= EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; - } - - return flags; - } - - @Nullable - private DiagnosticItem binarySearchDiagnostic( - @NonNull List diagnostics, int line, int column) { - return DiagnosticUtil.binarySearchDiagnostic(diagnostics, line, column); - } - private DiagnosticWindow getDiagnosticWindow() { if (mDiagnosticWindow == null) { mDiagnosticWindow = new DiagnosticWindow(this); @@ -223,6 +180,29 @@ public class IDEEditor extends CodeEditor { } } + /** + * Notify the language server that the content of this file has been changed. + * + * @param event The content change event. + */ + private void handleContentChange(ContentChangeEvent event, Unsubscribe unsubscribe) { + if (getFile() == null || mLanguageServer == null) { + return; + } + + CompletableFuture.runAsync( + () -> { + final var documentHandler = mLanguageServer.getDocumentHandler(); + final var file = getFile().toPath(); + if (documentHandler.accepts(file)) { + documentHandler.onContentChange( + new DocumentChangeEvent(file, getText(), mFileVersion + 1)); + } + + checkForSignatureHelp(event); + }); + } + /** * Checks if the content change event should trigger signature help. Signature help trigger * characters are : @@ -292,6 +272,20 @@ public class IDEEditor extends CodeEditor { return mSignatureHelpWindow; } + public static int createInputFlags() { + var flags = + EditorInfo.TYPE_CLASS_TEXT + | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE + | EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS; + if (StudioApp.getInstance() + .getPrefManager() + .getBoolean(PreferenceManager.KEY_EDITOR_FLAG_PASSWORD, true)) { + flags |= EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; + } + + return flags; + } + /** * Set the selection of this editor to the given position. * @@ -506,15 +500,6 @@ public class IDEEditor extends CodeEditor { } } - /** - * Dismisses the given dialog on the UI thread. - * - * @param dialog The dialog to dismiss. - */ - private void dismissOnUiThread(@NonNull final Dialog dialog) { - ThreadUtils.runOnUiThread(dialog::dismiss); - } - /** * Checks if the given range is valid for this editor's text. * @@ -570,6 +555,15 @@ public class IDEEditor extends CodeEditor { return column >= 0 && column < getText().getColumnCount(line); } + /** + * Dismisses the given dialog on the UI thread. + * + * @param dialog The dialog to dismiss. + */ + private void dismissOnUiThread(@NonNull final Dialog dialog) { + ThreadUtils.runOnUiThread(dialog::dismiss); + } + /** * If any language server instance is set, finds the references to of the token at the current * cursor position. @@ -850,4 +844,10 @@ public class IDEEditor extends CodeEditor { LOG.error("Unable start search action mode. Activity must inherit AppCompatActivity."); } } + + @Nullable + private DiagnosticItem binarySearchDiagnostic( + @NonNull List diagnostics, int line, int column) { + return DiagnosticUtil.binarySearchDiagnostic(diagnostics, line, column); + } } diff --git a/app/src/main/java/com/itsaky/androidide/views/editor/SearchActionMode.kt b/app/src/main/java/com/itsaky/androidide/views/editor/SearchActionMode.kt index ef7054fc3..bf3c1b015 100644 --- a/app/src/main/java/com/itsaky/androidide/views/editor/SearchActionMode.kt +++ b/app/src/main/java/com/itsaky/androidide/views/editor/SearchActionMode.kt @@ -38,70 +38,70 @@ import io.github.rosemoe.sora.widget.EditorSearcher.SearchOptions */ class SearchActionMode(val editor: IDEEditor) : ActionMode.Callback { - @SuppressLint("RestrictedApi") - override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean { + @SuppressLint("RestrictedApi") + override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean { - if (mode == null) { - return false - } - - if (menu is MenuBuilder) { - menu.setOptionalIconsVisible(true) - } - - val search = SearchView(editor.context) - search.setOnQueryTextListener(OnQueryListener(editor)) - search.isIconified = false - search.queryHint = editor.context.getString(R.string.text_to_search) - search.performClick() - search.setIconifiedByDefault(false) - mode.customView = search - return true + if (mode == null) { + return false } - override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean { - if (mode == null || menu == null) { - return false - } - - menu.clear() - val registry = ActionsRegistry.getInstance() - registry.fillMenu(createData(mode), ActionItem.Location.EDITOR_SEARCH_ACTION_MODE, menu) - - return true + if (menu is MenuBuilder) { + menu.setOptionalIconsVisible(true) } - override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean { - return true + val search = SearchView(editor.context) + search.setOnQueryTextListener(OnQueryListener(editor)) + search.isIconified = false + search.queryHint = editor.context.getString(R.string.text_to_search) + search.performClick() + search.setIconifiedByDefault(false) + mode.customView = search + return true + } + + override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean { + if (mode == null || menu == null) { + return false } - override fun onDestroyActionMode(mode: ActionMode?) { + menu.clear() + val registry = ActionsRegistry.getInstance() + registry.fillMenu(createData(mode), ActionItem.Location.EDITOR_SEARCH_ACTION_MODE, menu) + + return true + } + + override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean { + return true + } + + override fun onDestroyActionMode(mode: ActionMode?) { + editor.searcher.stopSearch() + } + + private fun createData(mode: ActionMode): ActionData { + return ActionData().apply { + put(Context::class.java, editor.context) + put(IDEEditor::class.java, editor) + put(CodeEditor::class.java, editor) + put(ActionMode::class.java, mode) + } + } + + private class OnQueryListener(val editor: IDEEditor) : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String?): Boolean { + editor.searcher.gotoNext() + return false + } + + override fun onQueryTextChange(newText: String?): Boolean { + if (TextUtils.isEmpty(newText)) { editor.searcher.stopSearch() + return false + } + + editor.searcher.search(newText!!, SearchOptions(false, false)) + return false } - - private fun createData(mode: ActionMode): ActionData { - return ActionData().apply { - put(Context::class.java, editor.context) - put(IDEEditor::class.java, editor) - put(CodeEditor::class.java, editor) - put(ActionMode::class.java, mode) - } - } - - private class OnQueryListener(val editor: IDEEditor) : SearchView.OnQueryTextListener { - override fun onQueryTextSubmit(query: String?): Boolean { - editor.searcher.gotoNext() - return false - } - - override fun onQueryTextChange(newText: String?): Boolean { - if (TextUtils.isEmpty(newText)) { - editor.searcher.stopSearch() - return false - } - - editor.searcher.search(newText!!, SearchOptions(false, false)) - return false - } - } + } } diff --git a/app/src/main/java/com/itsaky/androidide/views/transition/CubicBezierArc.java b/app/src/main/java/com/itsaky/androidide/views/transition/CubicBezierArc.java index dc49823b4..d2f3a3a81 100755 --- a/app/src/main/java/com/itsaky/androidide/views/transition/CubicBezierArc.java +++ b/app/src/main/java/com/itsaky/androidide/views/transition/CubicBezierArc.java @@ -57,6 +57,58 @@ public class CubicBezierArc { calculateControlPoints(angle, start, end); } + private void calculateControlPoints(float angle, Point start, Point end) { + double angleRadians = Math.toRadians(angle); + + float deltaX = start.x - end.x; + float deltaY = start.y - end.y; + float halfChordLength = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY) / 2; + float radius = halfChordLength / (float) Math.sin(angleRadians / 2.0f); + // The length of the line from the start or end point to the control point + float controlLength = (float) ((4f / 3f) * Math.tan(angleRadians / 4)) * radius; + + float angleToControl = (float) Math.toDegrees(Math.atan(controlLength / radius)); + + // The mid point of the line from start to end + Point midPointChord = new Point((start.x + end.x) / 2, (start.y + end.y) / 2); + + // Angle between line from circle centre to control point, and line between control points + float chordRadiusAngle = 180 - 90 - (angle / 2); + + Point center = getTrianglePoint(chordRadiusAngle, midPointChord, end); + controlPoint2 = getTrianglePoint(angleToControl, end, center); + controlPoint1 = getReflectedPointAboutLine(center, midPointChord, controlPoint2); + + // Get reflected control points + reflectedControlPoint1 = getReflectedPointAboutLine(start, end, controlPoint1); + reflectedControlPoint2 = getReflectedPointAboutLine(start, end, controlPoint2); + } + + private Point getTrianglePoint(float angle, Point a, Point b) { + double angleRadians = Math.toRadians(angle); + Point thirdPoint = new Point(); + thirdPoint.x = (float) Math.tan(angleRadians) * (b.y - a.y) * -1; + thirdPoint.y = (float) Math.tan(angleRadians) * (b.x - a.x); + thirdPoint.x = thirdPoint.x + a.x; + thirdPoint.y = thirdPoint.y + a.y; + return thirdPoint; + } + + private Point getReflectedPointAboutLine(Point start, Point end, Point reflect) { + Point reflectedPoint = new Point(); + if (start.x != end.x) { + float m = (start.y - end.y) / (start.x - end.x); + float c = end.y - (m * end.x); + float d = (reflect.x + (reflect.y - c) * m) / (1 + (m * m)); + reflectedPoint.x = (2 * d) - reflect.x; + reflectedPoint.y = (2 * d * m) - reflect.y + (2 * c); + } else { + reflectedPoint.y = reflect.y; + reflectedPoint.x = start.x - (reflect.x - start.x); + } + return reflectedPoint; + } + /** * Returns the start point * @@ -122,56 +174,4 @@ public class CubicBezierArc { this.y = y; } } - - private Point getTrianglePoint(float angle, Point a, Point b) { - double angleRadians = Math.toRadians(angle); - Point thirdPoint = new Point(); - thirdPoint.x = (float) Math.tan(angleRadians) * (b.y - a.y) * -1; - thirdPoint.y = (float) Math.tan(angleRadians) * (b.x - a.x); - thirdPoint.x = thirdPoint.x + a.x; - thirdPoint.y = thirdPoint.y + a.y; - return thirdPoint; - } - - private Point getReflectedPointAboutLine(Point start, Point end, Point reflect) { - Point reflectedPoint = new Point(); - if (start.x != end.x) { - float m = (start.y - end.y) / (start.x - end.x); - float c = end.y - (m * end.x); - float d = (reflect.x + (reflect.y - c) * m) / (1 + (m * m)); - reflectedPoint.x = (2 * d) - reflect.x; - reflectedPoint.y = (2 * d * m) - reflect.y + (2 * c); - } else { - reflectedPoint.y = reflect.y; - reflectedPoint.x = start.x - (reflect.x - start.x); - } - return reflectedPoint; - } - - private void calculateControlPoints(float angle, Point start, Point end) { - double angleRadians = Math.toRadians(angle); - - float deltaX = start.x - end.x; - float deltaY = start.y - end.y; - float halfChordLength = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY) / 2; - float radius = halfChordLength / (float) Math.sin(angleRadians / 2.0f); - // The length of the line from the start or end point to the control point - float controlLength = (float) ((4f / 3f) * Math.tan(angleRadians / 4)) * radius; - - float angleToControl = (float) Math.toDegrees(Math.atan(controlLength / radius)); - - // The mid point of the line from start to end - Point midPointChord = new Point((start.x + end.x) / 2, (start.y + end.y) / 2); - - // Angle between line from circle centre to control point, and line between control points - float chordRadiusAngle = 180 - 90 - (angle / 2); - - Point center = getTrianglePoint(chordRadiusAngle, midPointChord, end); - controlPoint2 = getTrianglePoint(angleToControl, end, center); - controlPoint1 = getReflectedPointAboutLine(center, midPointChord, controlPoint2); - - // Get reflected control points - reflectedControlPoint1 = getReflectedPointAboutLine(start, end, controlPoint1); - reflectedControlPoint2 = getReflectedPointAboutLine(start, end, controlPoint2); - } } diff --git a/app/src/main/java/com/itsaky/androidide/views/transition/ProgressTransition.java b/app/src/main/java/com/itsaky/androidide/views/transition/ProgressTransition.java index a87bece50..3890bbde7 100755 --- a/app/src/main/java/com/itsaky/androidide/views/transition/ProgressTransition.java +++ b/app/src/main/java/com/itsaky/androidide/views/transition/ProgressTransition.java @@ -45,6 +45,21 @@ public class ProgressTransition extends Transition { } }; + @Override + public Animator createAnimator( + ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { + if (startValues != null && endValues != null && endValues.view instanceof ProgressBar) { + ProgressBar progressBar = (ProgressBar) endValues.view; + int start = (Integer) startValues.values.get(PROPNAME_PROGRESS); + int end = (Integer) endValues.values.get(PROPNAME_PROGRESS); + if (start != end) { + progressBar.setProgress(start); + return ObjectAnimator.ofInt(progressBar, PROGRESS_PROPERTY, end); + } + } + return null; + } + @Override public void captureStartValues(TransitionValues transitionValues) { captureValues(transitionValues); @@ -61,19 +76,4 @@ public class ProgressTransition extends Transition { transitionValues.values.put(PROPNAME_PROGRESS, progressBar.getProgress()); } } - - @Override - public Animator createAnimator( - ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { - if (startValues != null && endValues != null && endValues.view instanceof ProgressBar) { - ProgressBar progressBar = (ProgressBar) endValues.view; - int start = (Integer) startValues.values.get(PROPNAME_PROGRESS); - int end = (Integer) endValues.values.get(PROPNAME_PROGRESS); - if (start != end) { - progressBar.setProgress(start); - return ObjectAnimator.ofInt(progressBar, PROGRESS_PROPERTY, end); - } - } - return null; - } } diff --git a/app/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt b/app/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt index 3984f26e4..13b0b7f91 100644 --- a/app/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt +++ b/app/src/main/java/io/github/rosemoe/sora/widget/IDEEditorSearcher.kt @@ -36,82 +36,84 @@ import java.util.concurrent.* */ open class IDEEditorSearcher(editor: IDEEditor) : EditorSearcher(editor) { - private val log = ILogger.newInstance(javaClass.simpleName) + private val log = ILogger.newInstance(javaClass.simpleName) - protected fun getEditor(): CodeEditor { - try { - val field = EditorSearcher::class.java.getDeclaredField("mEditor") - field.isAccessible = true - return field.get(this) as CodeEditor - } catch (error: Throwable) { - throw RuntimeException("Unable get instance of editor", error) - } + protected fun getEditor(): CodeEditor { + try { + val field = EditorSearcher::class.java.getDeclaredField("mEditor") + field.isAccessible = true + return field.get(this) as CodeEditor + } catch (error: Throwable) { + throw RuntimeException("Unable get instance of editor", error) + } + } + + fun replaceAllAsync(replacement: String): CompletableFuture { + val dialog = + ProgressDialog.show( + getEditor().context, + getEditor().context.getString(string.replaceAll), + getEditor().context.getString(string.msg_replacing), + true, + false + ) + return CompletableFuture.runAsync { replaceAll(replacement) } + .thenAccept { getEditor().post { dialog.dismiss() } } + } + + override fun replaceAll(replacement: String) { + checkState() + if (!isResultValid) { + Toast.makeText(getEditor().context, "Editor is still preparing", Toast.LENGTH_SHORT).show() + return } - fun replaceAllAsync(replacement: String): CompletableFuture { - val dialog = - ProgressDialog.show( - getEditor().context, - getEditor().context.getString(string.replaceAll), - getEditor().context.getString(string.msg_replacing), - true, - false) - return CompletableFuture.runAsync { replaceAll(replacement) }.thenAccept { - getEditor().post { dialog.dismiss() } + val res = mLastResults + try { + val sb = getEditor().text.toStringBuilder() + val newLength = replacement.length + if (mOptions.useRegex) { + var delta = 0 + for (i in 0 until res.size()) { + val region = res[i] + val start = IntPair.getFirst(region) + val end = IntPair.getSecond(region) + val oldLength = end - start + sb.replace(start + delta, end + delta, replacement) + delta += newLength - oldLength } - } - - override fun replaceAll(replacement: String) { - checkState() - if (!isResultValid) { - Toast.makeText(getEditor().context, "Editor is still preparing", Toast.LENGTH_SHORT) - .show() - return - } - - val res = mLastResults - try { - val sb = getEditor().text.toStringBuilder() - val newLength = replacement.length - if (mOptions.useRegex) { - var delta = 0 - for (i in 0 until res.size()) { - val region = res[i] - val start = IntPair.getFirst(region) - val end = IntPair.getSecond(region) - val oldLength = end - start - sb.replace(start + delta, end + delta, replacement) - delta += newLength - oldLength - } - } else { - var fromIndex = 0 - var foundIndex: Int - while (TextUtils.indexOf(sb, mPattern, mOptions.ignoreCase, fromIndex).also { - foundIndex = it - } != -1) { - sb.replace(foundIndex, foundIndex + mPattern.length, replacement) - fromIndex = foundIndex + newLength - } - } - getEditor().post { - val pos = getEditor().cursor.left() - // stopSearch(); - getEditor() - .text - .replace( - 0, - 0, - getEditor().lineCount - 1, - getEditor().text.getColumnCount(getEditor().lineCount - 1), - sb) - getEditor().setSelectionAround(pos.line, pos.column) - } - } catch (e: Exception) { - log.error("Failed to replace", e) + } else { + var fromIndex = 0 + var foundIndex: Int + while ( + TextUtils.indexOf(sb, mPattern, mOptions.ignoreCase, fromIndex).also { + foundIndex = it + } != -1 + ) { + sb.replace(foundIndex, foundIndex + mPattern.length, replacement) + fromIndex = foundIndex + newLength } + } + getEditor().post { + val pos = getEditor().cursor.left() + // stopSearch(); + getEditor() + .text + .replace( + 0, + 0, + getEditor().lineCount - 1, + getEditor().text.getColumnCount(getEditor().lineCount - 1), + sb + ) + getEditor().setSelectionAround(pos.line, pos.column) + } + } catch (e: Exception) { + log.error("Failed to replace", e) } + } - private fun checkState() { - check(hasQuery()) { "pattern not set" } - } + private fun checkState() { + check(hasQuery()) { "pattern not set" } + } } diff --git a/app/src/main/res/anim/popup_in.xml b/app/src/main/res/anim/popup_in.xml index b5ffae7db..13a57039c 100755 --- a/app/src/main/res/anim/popup_in.xml +++ b/app/src/main/res/anim/popup_in.xml @@ -1,19 +1,19 @@ - + - + android:fromAlpha="0.0" + android:interpolator="@android:anim/accelerate_interpolator" + android:repeatCount="0" + android:toAlpha="1.0" /> - + diff --git a/app/src/main/res/anim/popup_out.xml b/app/src/main/res/anim/popup_out.xml index 37bf78b05..91e6b41bc 100755 --- a/app/src/main/res/anim/popup_out.xml +++ b/app/src/main/res/anim/popup_out.xml @@ -1,19 +1,19 @@ - + - + android:fromAlpha="1.0" + android:interpolator="@android:anim/accelerate_interpolator" + android:repeatCount="0" + android:toAlpha="0.0" /> - + diff --git a/app/src/main/res/animator/dialog_in.xml b/app/src/main/res/animator/dialog_in.xml index c83bedc4c..98ee6726d 100755 --- a/app/src/main/res/animator/dialog_in.xml +++ b/app/src/main/res/animator/dialog_in.xml @@ -1,13 +1,13 @@ - - + + diff --git a/app/src/main/res/animator/dialog_out.xml b/app/src/main/res/animator/dialog_out.xml index 526c9ce9a..55f7c5164 100755 --- a/app/src/main/res/animator/dialog_out.xml +++ b/app/src/main/res/animator/dialog_out.xml @@ -1,13 +1,13 @@ - - + + diff --git a/app/src/main/res/drawable-xhdpi/ic_font_ligatures.xml b/app/src/main/res/drawable-xhdpi/ic_font_ligatures.xml index 08912b137..b990c7e64 100644 --- a/app/src/main/res/drawable-xhdpi/ic_font_ligatures.xml +++ b/app/src/main/res/drawable-xhdpi/ic_font_ligatures.xml @@ -1,8 +1,10 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_design_container_background.xml b/app/src/main/res/drawable/bg_design_container_background.xml index df80c8860..c1d6ec620 100755 --- a/app/src/main/res/drawable/bg_design_container_background.xml +++ b/app/src/main/res/drawable/bg_design_container_background.xml @@ -1,5 +1,4 @@ - - - + - - - + + + - - + + diff --git a/app/src/main/res/drawable/bg_shadow.xml b/app/src/main/res/drawable/bg_shadow.xml index 53576c272..b4f13e508 100755 --- a/app/src/main/res/drawable/bg_shadow.xml +++ b/app/src/main/res/drawable/bg_shadow.xml @@ -1,14 +1,12 @@ - - - - - - + + + + + + diff --git a/app/src/main/res/drawable/bg_widget_drag_placeholder.xml b/app/src/main/res/drawable/bg_widget_drag_placeholder.xml index 35c68f3b8..9749b261b 100755 --- a/app/src/main/res/drawable/bg_widget_drag_placeholder.xml +++ b/app/src/main/res/drawable/bg_widget_drag_placeholder.xml @@ -1,5 +1,4 @@ - - - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_file_copy_path.xml b/app/src/main/res/drawable/ic_file_copy_path.xml index 23ce17bb4..dd9c85f76 100755 --- a/app/src/main/res/drawable/ic_file_copy_path.xml +++ b/app/src/main/res/drawable/ic_file_copy_path.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M19,21H8V7H19M19,5H8A2,2 0,0 0,6 7V21A2,2 0,0 0,8 23H19A2,2 0,0 0,21 21V7A2,2 0,0 0,19 5M16,1H4A2,2 0,0 0,2 3V17H4V3H16V1Z" /> diff --git a/app/src/main/res/drawable/ic_file_rename.xml b/app/src/main/res/drawable/ic_file_rename.xml index 5629e285c..17d23e7bd 100755 --- a/app/src/main/res/drawable/ic_file_rename.xml +++ b/app/src/main/res/drawable/ic_file_rename.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M14.06,9L15,9.94L5.92,19H5V18.08L14.06,9M17.66,3C17.41,3 17.15,3.1 16.96,3.29L15.13,5.12L18.88,8.87L20.71,7.04C21.1,6.65 21.1,6 20.71,5.63L18.37,3.29C18.17,3.09 17.92,3 17.66,3M14.06,6.19L3,17.25V21H6.75L17.81,9.94L14.06,6.19Z" /> diff --git a/app/src/main/res/drawable/ic_file_unknown.xml b/app/src/main/res/drawable/ic_file_unknown.xml index bf2460d1b..478ca48da 100755 --- a/app/src/main/res/drawable/ic_file_unknown.xml +++ b/app/src/main/res/drawable/ic_file_unknown.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M14,2H6A2,2 0,0 0,4 4V20A2,2 0,0 0,6 22H18A2,2 0,0 0,20 20V8L14,2M18,20H6V4H13V9H18V20Z" /> diff --git a/app/src/main/res/drawable/ic_filetype_activity.xml b/app/src/main/res/drawable/ic_filetype_activity.xml index 43b1cd9a5..212025f66 100755 --- a/app/src/main/res/drawable/ic_filetype_activity.xml +++ b/app/src/main/res/drawable/ic_filetype_activity.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M7,1A2,2 0,0 0,5 3V21A2,2 0,0 0,7 23H17A2,2 0,0 0,19 21V3A2,2 0,0 0,17 1H7M7,4H17V20H7V4M9,6V10H10.5V7.5H13V6H9M13.5,14V16.5H11V18H15V14H13.5Z" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_filetype_enum.xml b/app/src/main/res/drawable/ic_filetype_enum.xml index fbbdcc459..08ba15c8f 100755 --- a/app/src/main/res/drawable/ic_filetype_enum.xml +++ b/app/src/main/res/drawable/ic_filetype_enum.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M3,5A2,2 0,0 1,5 3H19A2,2 0,0 1,21 5V19A2,2 0,0 1,19 21H5C3.89,21 3,20.1 3,19V5M6,6V18H10V16H8V8H10V6H6M16,16H14V18H18V6H14V8H16V16Z" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_filetype_interface.xml b/app/src/main/res/drawable/ic_filetype_interface.xml index e60b0df80..cc2374d67 100755 --- a/app/src/main/res/drawable/ic_filetype_interface.xml +++ b/app/src/main/res/drawable/ic_filetype_interface.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M10.59,13.41C11,13.8 11,14.44 10.59,14.83C10.2,15.22 9.56,15.22 9.17,14.83C7.22,12.88 7.22,9.71 9.17,7.76V7.76L12.71,4.22C14.66,2.27 17.83,2.27 19.78,4.22C21.73,6.17 21.73,9.34 19.78,11.29L18.29,12.78C18.3,11.96 18.17,11.14 17.89,10.36L18.36,9.88C19.54,8.71 19.54,6.81 18.36,5.64C17.19,4.46 15.29,4.46 14.12,5.64L10.59,9.17C9.41,10.34 9.41,12.24 10.59,13.41M13.41,9.17C13.8,8.78 14.44,8.78 14.83,9.17C16.78,11.12 16.78,14.29 14.83,16.24V16.24L11.29,19.78C9.34,21.73 6.17,21.73 4.22,19.78C2.27,17.83 2.27,14.66 4.22,12.71L5.71,11.22C5.7,12.04 5.83,12.86 6.11,13.65L5.64,14.12C4.46,15.29 4.46,17.19 5.64,18.36C6.81,19.54 8.71,19.54 9.88,18.36L13.41,14.83C14.59,13.66 14.59,11.76 13.41,10.59C13,10.2 13,9.56 13.41,9.17Z" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_filter.xml b/app/src/main/res/drawable/ic_filter.xml index dba631d07..b5a662cbf 100755 --- a/app/src/main/res/drawable/ic_filter.xml +++ b/app/src/main/res/drawable/ic_filter.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M15,19.88C15.04,20.18 14.94,20.5 14.71,20.71C14.32,21.1 13.69,21.1 13.3,20.71L9.29,16.7C9.06,16.47 8.96,16.16 9,15.87V10.75L4.21,4.62C3.87,4.19 3.95,3.56 4.38,3.22C4.57,3.08 4.78,3 5,3V3H19V3C19.22,3 19.43,3.08 19.62,3.22C20.05,3.56 20.13,4.19 19.79,4.62L15,10.75V19.88M7.04,5L11,10.06V15.58L13,17.58V10.05L16.96,5H7.04Z" /> diff --git a/app/src/main/res/drawable/ic_folder.xml b/app/src/main/res/drawable/ic_folder.xml index ac60509e4..569b8c858 100755 --- a/app/src/main/res/drawable/ic_folder.xml +++ b/app/src/main/res/drawable/ic_folder.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M10,4H4C2.89,4 2,4.89 2,6V18A2,2 0,0 0,4 20H20A2,2 0,0 0,22 18V8C22,6.89 21.1,6 20,6H12L10,4Z" /> diff --git a/app/src/main/res/drawable/ic_format_code.xml b/app/src/main/res/drawable/ic_format_code.xml index f60369d77..7f9cf7194 100644 --- a/app/src/main/res/drawable/ic_format_code.xml +++ b/app/src/main/res/drawable/ic_format_code.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_github.xml b/app/src/main/res/drawable/ic_github.xml index 2068fb5d1..656a49146 100644 --- a/app/src/main/res/drawable/ic_github.xml +++ b/app/src/main/res/drawable/ic_github.xml @@ -1,5 +1,4 @@ - - - - - - - - + + + + + + diff --git a/app/src/main/res/drawable/ic_help.xml b/app/src/main/res/drawable/ic_help.xml index 7b898d20a..ef3fca908 100755 --- a/app/src/main/res/drawable/ic_help.xml +++ b/app/src/main/res/drawable/ic_help.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M11,18H13V16H11V18M12,2A10,10 0,0 0,2 12A10,10 0,0 0,12 22A10,10 0,0 0,22 12A10,10 0,0 0,12 2M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,6A4,4 0,0 0,8 10H10A2,2 0,0 1,12 8A2,2 0,0 1,14 10C14,12 11,11.75 11,15H13C13,12.75 16,12.5 16,10A4,4 0,0 0,12 6Z" /> diff --git a/app/src/main/res/drawable/ic_hexadecimal.xml b/app/src/main/res/drawable/ic_hexadecimal.xml index 0889d6d78..56f47b1f9 100755 --- a/app/src/main/res/drawable/ic_hexadecimal.xml +++ b/app/src/main/res/drawable/ic_hexadecimal.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M7,7C5.9,7 5,7.9 5,9V15C5,16.11 5.9,17 7,17H9C10.11,17 11,16.11 11,15V9C11,7.9 10.11,7 9,7H7M7,9H9V15H7V9M17.6,17L15.5,14.9L13.4,17L12,15.6L14.1,13.5L12,11.4L13.4,10L15.5,12.1L17.6,10L19,11.4L16.9,13.5L19,15.6L17.6,17Z" /> diff --git a/app/src/main/res/drawable/ic_info.xml b/app/src/main/res/drawable/ic_info.xml index 66773f0fb..036c14c32 100755 --- a/app/src/main/res/drawable/ic_info.xml +++ b/app/src/main/res/drawable/ic_info.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0,0 0,2 12A10,10 0,0 0,12 22A10,10 0,0 0,22 12A10,10 0,0 0,12 2M11,17H13V11H11V17Z" /> diff --git a/app/src/main/res/drawable/ic_language_gradle.xml b/app/src/main/res/drawable/ic_language_gradle.xml index a6c24b57b..817d0057e 100755 --- a/app/src/main/res/drawable/ic_language_gradle.xml +++ b/app/src/main/res/drawable/ic_language_gradle.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M6,2C4.89,2 4,2.9 4,4V20C4,21.11 4.89,22 6,22H12V20H6V4H13V9H18V12H20V8L14,2M18,14C17.87,14 17.76,14.09 17.74,14.21L17.55,15.53C17.25,15.66 16.96,15.82 16.7,16L15.46,15.5C15.35,15.5 15.22,15.5 15.15,15.63L14.15,17.36C14.09,17.47 14.11,17.6 14.21,17.68L15.27,18.5C15.25,18.67 15.24,18.83 15.24,19C15.24,19.17 15.25,19.33 15.27,19.5L14.21,20.32C14.12,20.4 14.09,20.53 14.15,20.64L15.15,22.37C15.21,22.5 15.34,22.5 15.46,22.5L16.7,22C16.96,22.18 17.24,22.35 17.55,22.47L17.74,23.79C17.76,23.91 17.86,24 18,24H20C20.11,24 20.22,23.91 20.24,23.79L20.43,22.47C20.73,22.34 21,22.18 21.27,22L22.5,22.5C22.63,22.5 22.76,22.5 22.83,22.37L23.83,20.64C23.89,20.53 23.86,20.4 23.77,20.32L22.7,19.5C22.72,19.33 22.74,19.17 22.74,19C22.74,18.83 22.73,18.67 22.7,18.5L23.76,17.68C23.85,17.6 23.88,17.47 23.82,17.36L22.82,15.63C22.76,15.5 22.63,15.5 22.5,15.5L21.27,16C21,15.82 20.73,15.65 20.42,15.53L20.23,14.21C20.22,14.09 20.11,14 20,14M19,17.5C19.83,17.5 20.5,18.17 20.5,19C20.5,19.83 19.83,20.5 19,20.5C18.16,20.5 17.5,19.83 17.5,19C17.5,18.17 18.17,17.5 19,17.5Z" /> diff --git a/app/src/main/res/drawable/ic_language_java.xml b/app/src/main/res/drawable/ic_language_java.xml index 3df377479..71fb21890 100755 --- a/app/src/main/res/drawable/ic_language_java.xml +++ b/app/src/main/res/drawable/ic_language_java.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M16.5,6.08C16.5,6.08 9.66,7.79 12.94,11.56C13.91,12.67 12.69,13.67 12.69,13.67C12.69,13.67 15.14,12.42 14,10.82C12.94,9.35 12.14,8.62 16.5,6.08M12.03,7.28C16.08,4.08 14,2 14,2C14.84,5.3 11.04,6.3 9.67,8.36C8.73,9.76 10.13,11.27 12,13C11.29,11.3 8.78,9.84 12.03,7.28M9.37,17.47C6.29,18.33 11.25,20.1 15.16,18.43C14.78,18.28 14.41,18.1 14.06,17.89C12.7,18.2 11.3,18.26 9.92,18.07C8.61,17.91 9.37,17.47 9.37,17.47M14.69,15.79C12.94,16.17 11.13,16.26 9.35,16.05C8.04,15.92 8.9,15.28 8.9,15.28C5.5,16.41 10.78,17.68 15.5,16.3C15.21,16.19 14.93,16 14.69,15.79M18.11,19.09C18.11,19.09 18.68,19.56 17.5,19.92C15.22,20.6 8.07,20.81 6.09,19.95C5.38,19.64 6.72,19.21 7.14,19.12C7.37,19.06 7.6,19.04 7.83,19.04C7.04,18.5 2.7,20.14 5.64,20.6C13.61,21.9 20.18,20 18.11,19.09M15.37,14.23C15.66,14.04 15.97,13.88 16.29,13.74C16.29,13.74 14.78,14 13.27,14.14C11.67,14.3 10.06,14.32 8.46,14.2C6.11,13.89 9.75,13 9.75,13C8.65,13 7.57,13.26 6.59,13.75C4.54,14.75 11.69,15.2 15.37,14.23M16.27,16.65C16.25,16.69 16.23,16.72 16.19,16.75C21.2,15.44 19.36,12.11 16.96,12.94C16.83,13 16.72,13.08 16.65,13.19C16.79,13.14 16.93,13.1 17.08,13.07C18.28,12.83 20,14.7 16.27,16.65M16.4,21.26C13.39,21.78 10.31,21.82 7.28,21.4C7.28,21.4 7.74,21.78 10.09,21.93C13.69,22.16 19.22,21.8 19.35,20.1C19.38,20.11 19.12,20.75 16.4,21.26Z" /> diff --git a/app/src/main/res/drawable/ic_language_json.xml b/app/src/main/res/drawable/ic_language_json.xml index 6791c9f87..05869f0b0 100755 --- a/app/src/main/res/drawable/ic_language_json.xml +++ b/app/src/main/res/drawable/ic_language_json.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M5,3H7V5H5V10A2,2 0,0 1,3 12A2,2 0,0 1,5 14V19H7V21H5C3.93,20.73 3,20.1 3,19V15A2,2 0,0 0,1 13H0V11H1A2,2 0,0 0,3 9V5A2,2 0,0 1,5 3M19,3A2,2 0,0 1,21 5V9A2,2 0,0 0,23 11H24V13H23A2,2 0,0 0,21 15V19A2,2 0,0 1,19 21H17V19H19V14A2,2 0,0 1,21 12A2,2 0,0 1,19 10V5H17V3H19M12,15A1,1 0,0 1,13 16A1,1 0,0 1,12 17A1,1 0,0 1,11 16A1,1 0,0 1,12 15M8,15A1,1 0,0 1,9 16A1,1 0,0 1,8 17A1,1 0,0 1,7 16A1,1 0,0 1,8 15M16,15A1,1 0,0 1,17 16A1,1 0,0 1,16 17A1,1 0,0 1,15 16A1,1 0,0 1,16 15Z" /> diff --git a/app/src/main/res/drawable/ic_language_kotlin.xml b/app/src/main/res/drawable/ic_language_kotlin.xml index d82087271..ef868cecf 100755 --- a/app/src/main/res/drawable/ic_language_kotlin.xml +++ b/app/src/main/res/drawable/ic_language_kotlin.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M2,2H22L12,12L22,22H2Z" /> diff --git a/app/src/main/res/drawable/ic_language_xml.xml b/app/src/main/res/drawable/ic_language_xml.xml index adb411f2c..c8e209525 100755 --- a/app/src/main/res/drawable/ic_language_xml.xml +++ b/app/src/main/res/drawable/ic_language_xml.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M12.89,3L14.85,3.4L11.11,21L9.15,20.6L12.89,3M19.59,12L16,8.41V5.58L22.42,12L16,18.41V15.58L19.59,12M1.58,12L8,5.58V8.41L4.41,12L8,15.58V18.41L1.58,12Z" /> diff --git a/app/src/main/res/drawable/ic_lint.xml b/app/src/main/res/drawable/ic_lint.xml index 2a0a6efc1..2285c2abd 100755 --- a/app/src/main/res/drawable/ic_lint.xml +++ b/app/src/main/res/drawable/ic_lint.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M6.59,3.41L2,8L6.59,12.6L8,11.18L4.82,8L8,4.82L6.59,3.41M12.41,3.41L11,4.82L14.18,8L11,11.18L12.41,12.6L17,8L12.41,3.41M21.59,11.59L13.5,19.68L9.83,16L8.42,17.41L13.5,22.5L23,13L21.59,11.59Z" /> diff --git a/app/src/main/res/drawable/ic_loupe.xml b/app/src/main/res/drawable/ic_loupe.xml index 1e7c1cb67..166f15ae4 100644 --- a/app/src/main/res/drawable/ic_loupe.xml +++ b/app/src/main/res/drawable/ic_loupe.xml @@ -3,7 +3,7 @@ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> - + diff --git a/app/src/main/res/drawable/ic_min_sdk.xml b/app/src/main/res/drawable/ic_min_sdk.xml index 05058c6c4..d7964bc21 100755 --- a/app/src/main/res/drawable/ic_min_sdk.xml +++ b/app/src/main/res/drawable/ic_min_sdk.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M6.5,2.27L20,10.14L6.5,18L5.5,16.27L16.03,10.14L5.5,4L6.5,2.27M20,20V22H5V20H20Z" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_new_file.xml b/app/src/main/res/drawable/ic_new_file.xml index 4923032da..02dff1d78 100755 --- a/app/src/main/res/drawable/ic_new_file.xml +++ b/app/src/main/res/drawable/ic_new_file.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M12,14V11H10V14H7V16H10V19H12V16H15V14M14,2H6A2,2 0,0 0,4 4V20A2,2 0,0 0,6 22H18A2,2 0,0 0,20 20V8L14,2M18,20H6V4H13V9H18" /> diff --git a/app/src/main/res/drawable/ic_new_folder.xml b/app/src/main/res/drawable/ic_new_folder.xml index 7443727a5..502c93097 100755 --- a/app/src/main/res/drawable/ic_new_folder.xml +++ b/app/src/main/res/drawable/ic_new_folder.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M12,12H14V10H16V12H18V14H16V16H14V14H12V12M22,8V18C22,19.11 21.11,20 20,20H4C2.89,20 2,19.11 2,18V6C2,4.89 2.89,4 4,4H10L12,6H20C21.11,6 22,6.89 22,8M20,8H4V18H20V8Z" /> diff --git a/app/src/main/res/drawable/ic_open_project.xml b/app/src/main/res/drawable/ic_open_project.xml index 468b2935b..45e1879a5 100755 --- a/app/src/main/res/drawable/ic_open_project.xml +++ b/app/src/main/res/drawable/ic_open_project.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M6.1,10L4,18V8H21A2,2 0,0 0,19 6H12L10,4H4A2,2 0,0 0,2 6V18A2,2 0,0 0,4 20H19C19.9,20 20.7,19.4 20.9,18.5L23.2,10H6.1M19,18H6L7.6,12H20.6L19,18Z" /> diff --git a/app/src/main/res/drawable/ic_package.xml b/app/src/main/res/drawable/ic_package.xml index 984fefa42..629dd4587 100755 --- a/app/src/main/res/drawable/ic_package.xml +++ b/app/src/main/res/drawable/ic_package.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M21,16.5C21,16.88 20.79,17.21 20.47,17.38L12.57,21.82C12.41,21.94 12.21,22 12,22C11.79,22 11.59,21.94 11.43,21.82L3.53,17.38C3.21,17.21 3,16.88 3,16.5V7.5C3,7.12 3.21,6.79 3.53,6.62L11.43,2.18C11.59,2.06 11.79,2 12,2C12.21,2 12.41,2.06 12.57,2.18L20.47,6.62C20.79,6.79 21,7.12 21,7.5V16.5M12,4.15L10.11,5.22L16,8.61L17.96,7.5L12,4.15M6.04,7.5L12,10.85L13.96,9.75L8.08,6.35L6.04,7.5M5,15.91L11,19.29V12.58L5,9.21V15.91M19,15.91V9.21L13,12.58V19.29L19,15.91Z" /> diff --git a/app/src/main/res/drawable/ic_password_input.xml b/app/src/main/res/drawable/ic_password_input.xml index 23e9bdeff..3b2f75eab 100644 --- a/app/src/main/res/drawable/ic_password_input.xml +++ b/app/src/main/res/drawable/ic_password_input.xml @@ -1,8 +1,10 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_pick_color.xml b/app/src/main/res/drawable/ic_pick_color.xml index 70a396c1e..51a2d7051 100644 --- a/app/src/main/res/drawable/ic_pick_color.xml +++ b/app/src/main/res/drawable/ic_pick_color.xml @@ -1,8 +1,10 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_preview_layout.xml b/app/src/main/res/drawable/ic_preview_layout.xml index dc2d065be..e040f3672 100755 --- a/app/src/main/res/drawable/ic_preview_layout.xml +++ b/app/src/main/res/drawable/ic_preview_layout.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M19,19H5V5H19M19,3H5A2,2 0,0 0,3 5V19A2,2 0,0 0,5 21H19A2,2 0,0 0,21 19V5A2,2 0,0 0,19 3M13.96,12.29L11.21,15.83L9.25,13.47L6.5,17H17.5L13.96,12.29Z" /> diff --git a/app/src/main/res/drawable/ic_rebuild.xml b/app/src/main/res/drawable/ic_rebuild.xml index c87311fbf..cc9237aad 100755 --- a/app/src/main/res/drawable/ic_rebuild.xml +++ b/app/src/main/res/drawable/ic_rebuild.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M12,5V1L7,6L12,11V7A6,6 0,0 1,18 13A6,6 0,0 1,12 19A6,6 0,0 1,6 13H4A8,8 0,0 0,12 21A8,8 0,0 0,20 13A8,8 0,0 0,12 5Z" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_redo.xml b/app/src/main/res/drawable/ic_redo.xml index 381f0fc4a..1db16790b 100755 --- a/app/src/main/res/drawable/ic_redo.xml +++ b/app/src/main/res/drawable/ic_redo.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M18.4,10.6C16.55,9 14.15,8 11.5,8C6.85,8 2.92,11.03 1.54,15.22L3.9,16C4.95,12.81 7.95,10.5 11.5,10.5C13.45,10.5 15.23,11.22 16.62,12.38L13,16H22V7L18.4,10.6Z" /> diff --git a/app/src/main/res/drawable/ic_run.xml b/app/src/main/res/drawable/ic_run.xml index eb0a107e2..791e626ad 100755 --- a/app/src/main/res/drawable/ic_run.xml +++ b/app/src/main/res/drawable/ic_run.xml @@ -2,9 +2,9 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M8,5.14V19.14L19,12.14L8,5.14Z" /> diff --git a/app/src/main/res/drawable/ic_run_debug.xml b/app/src/main/res/drawable/ic_run_debug.xml index 9e7449128..95459450b 100755 --- a/app/src/main/res/drawable/ic_run_debug.xml +++ b/app/src/main/res/drawable/ic_run_debug.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M15,9A1,1 0,0 1,14 8A1,1 0,0 1,15 7A1,1 0,0 1,16 8A1,1 0,0 1,15 9M9,9A1,1 0,0 1,8 8A1,1 0,0 1,9 7A1,1 0,0 1,10 8A1,1 0,0 1,9 9M16.12,4.37L18.22,2.27L17.4,1.44L15.09,3.75C14.16,3.28 13.11,3 12,3C10.88,3 9.84,3.28 8.91,3.75L6.6,1.44L5.78,2.27L7.88,4.37C6.14,5.64 5,7.68 5,10V11H19V10C19,7.68 17.86,5.64 16.12,4.37M5,16C5,19.86 8.13,23 12,23A7,7 0,0 0,19 16V12H5V16Z" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_run_outline.xml b/app/src/main/res/drawable/ic_run_outline.xml index feb3e3672..4caed50ca 100755 --- a/app/src/main/res/drawable/ic_run_outline.xml +++ b/app/src/main/res/drawable/ic_run_outline.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M8.5,8.64L13.77,12L8.5,15.36V8.64M6.5,5V19L17.5,12" /> diff --git a/app/src/main/res/drawable/ic_run_release.xml b/app/src/main/res/drawable/ic_run_release.xml index 7754d4d6e..ddb8924bf 100755 --- a/app/src/main/res/drawable/ic_run_release.xml +++ b/app/src/main/res/drawable/ic_run_release.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M3,20.5V3.5C3,2.91 3.34,2.39 3.84,2.15L13.69,12L3.84,21.85C3.34,21.6 3,21.09 3,20.5M16.81,15.12L6.05,21.34L14.54,12.85L16.81,15.12M20.16,10.81C20.5,11.08 20.75,11.5 20.75,12C20.75,12.5 20.53,12.9 20.18,13.18L17.89,14.5L15.39,12L17.89,9.5L20.16,10.81M6.05,2.66L16.81,8.88L14.54,11.15L6.05,2.66Z" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_save.xml b/app/src/main/res/drawable/ic_save.xml index 166dd6e6b..e10924fe8 100755 --- a/app/src/main/res/drawable/ic_save.xml +++ b/app/src/main/res/drawable/ic_save.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M17,3H5C3.89,3 3,3.9 3,5V19C3,20.1 3.89,21 5,21H19C20.1,21 21,20.1 21,19V7L17,3M19,19H5V5H16.17L19,7.83V19M12,12C10.34,12 9,13.34 9,15S10.34,18 12,18 15,16.66 15,15 13.66,12 12,12M6,6H15V10H6V6Z" /> diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml index 090645da3..3c52c84f1 100755 --- a/app/src/main/res/drawable/ic_search.xml +++ b/app/src/main/res/drawable/ic_search.xml @@ -2,9 +2,9 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M9.5,3A6.5,6.5 0,0 1,16 9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0,0 1,3 9.5A6.5,6.5 0,0 1,9.5 3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z" /> diff --git a/app/src/main/res/drawable/ic_search_file.xml b/app/src/main/res/drawable/ic_search_file.xml index f1b43f9d0..94a4e863d 100755 --- a/app/src/main/res/drawable/ic_search_file.xml +++ b/app/src/main/res/drawable/ic_search_file.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M14,2H6A2,2 0,0 0,4 4V20A2,2 0,0 0,6 22H13C12.59,21.75 12.2,21.44 11.86,21.1C11.53,20.77 11.25,20.4 11,20H6V4H13V9H18V10.18C18.71,10.34 19.39,10.61 20,11V8L14,2M20.31,18.9C21.64,16.79 21,14 18.91,12.68C16.8,11.35 14,12 12.69,14.08C11.35,16.19 12,18.97 14.09,20.3C15.55,21.23 17.41,21.23 18.88,20.32L22,23.39L23.39,22L20.31,18.9M16.5,19A2.5,2.5 0,0 1,14 16.5A2.5,2.5 0,0 1,16.5 14A2.5,2.5 0,0 1,19 16.5A2.5,2.5 0,0 1,16.5 19Z" /> diff --git a/app/src/main/res/drawable/ic_search_next.xml b/app/src/main/res/drawable/ic_search_next.xml index 8fd5a6c6b..6fcd95931 100644 --- a/app/src/main/res/drawable/ic_search_next.xml +++ b/app/src/main/res/drawable/ic_search_next.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_search_previous.xml b/app/src/main/res/drawable/ic_search_previous.xml index d81a72f3b..ec79e302b 100644 --- a/app/src/main/res/drawable/ic_search_previous.xml +++ b/app/src/main/res/drawable/ic_search_previous.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_search_project.xml b/app/src/main/res/drawable/ic_search_project.xml index fd9218f3b..ed6be9136 100755 --- a/app/src/main/res/drawable/ic_search_project.xml +++ b/app/src/main/res/drawable/ic_search_project.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M16.5,12C19,12 21,14 21,16.5C21,17.38 20.75,18.21 20.31,18.9L23.39,22L22,23.39L18.88,20.32C18.19,20.75 17.37,21 16.5,21C14,21 12,19 12,16.5C12,14 14,12 16.5,12M16.5,14A2.5,2.5 0,0 0,14 16.5A2.5,2.5 0,0 0,16.5 19A2.5,2.5 0,0 0,19 16.5A2.5,2.5 0,0 0,16.5 14M19,8H3V18H10.17C10.34,18.72 10.63,19.39 11,20H3C1.89,20 1,19.1 1,18V6C1,4.89 1.89,4 3,4H9L11,6H19A2,2 0,0 1,21 8V11.81C20.42,11.26 19.75,10.81 19,10.5V8Z" /> diff --git a/app/src/main/res/drawable/ic_search_replace.xml b/app/src/main/res/drawable/ic_search_replace.xml index dcd686788..4c5883a49 100644 --- a/app/src/main/res/drawable/ic_search_replace.xml +++ b/app/src/main/res/drawable/ic_search_replace.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_settings.xml b/app/src/main/res/drawable/ic_settings.xml index bc5fd887a..a8a60d8c9 100755 --- a/app/src/main/res/drawable/ic_settings.xml +++ b/app/src/main/res/drawable/ic_settings.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M12,8A4,4 0,0 1,16 12A4,4 0,0 1,12 16A4,4 0,0 1,8 12A4,4 0,0 1,12 8M12,10A2,2 0,0 0,10 12A2,2 0,0 0,12 14A2,2 0,0 0,14 12A2,2 0,0 0,12 10M10,22C9.75,22 9.54,21.82 9.5,21.58L9.13,18.93C8.5,18.68 7.96,18.34 7.44,17.94L4.95,18.95C4.73,19.03 4.46,18.95 4.34,18.73L2.34,15.27C2.21,15.05 2.27,14.78 2.46,14.63L4.57,12.97L4.5,12L4.57,11L2.46,9.37C2.27,9.22 2.21,8.95 2.34,8.73L4.34,5.27C4.46,5.05 4.73,4.96 4.95,5.05L7.44,6.05C7.96,5.66 8.5,5.32 9.13,5.07L9.5,2.42C9.54,2.18 9.75,2 10,2H14C14.25,2 14.46,2.18 14.5,2.42L14.87,5.07C15.5,5.32 16.04,5.66 16.56,6.05L19.05,5.05C19.27,4.96 19.54,5.05 19.66,5.27L21.66,8.73C21.79,8.95 21.73,9.22 21.54,9.37L19.43,11L19.5,12L19.43,13L21.54,14.63C21.73,14.78 21.79,15.05 21.66,15.27L19.66,18.73C19.54,18.95 19.27,19.04 19.05,18.95L16.56,17.95C16.04,18.34 15.5,18.68 14.87,18.93L14.5,21.58C14.46,21.82 14.25,22 14,22H10M11.25,4L10.88,6.61C9.68,6.86 8.62,7.5 7.85,8.39L5.44,7.35L4.69,8.65L6.8,10.2C6.4,11.37 6.4,12.64 6.8,13.8L4.68,15.36L5.43,16.66L7.86,15.62C8.63,16.5 9.68,17.14 10.87,17.38L11.24,20H12.76L13.13,17.39C14.32,17.14 15.37,16.5 16.14,15.62L18.57,16.66L19.32,15.36L17.2,13.81C17.6,12.64 17.6,11.37 17.2,10.2L19.31,8.65L18.56,7.35L16.15,8.39C15.38,7.5 14.32,6.86 13.12,6.62L12.75,4H11.25Z" /> diff --git a/app/src/main/res/drawable/ic_share.xml b/app/src/main/res/drawable/ic_share.xml index 4f1446be1..4e779736a 100755 --- a/app/src/main/res/drawable/ic_share.xml +++ b/app/src/main/res/drawable/ic_share.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12S8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8C19.66,8 21,6.66 21,5S19.66,2 18,2 15,3.34 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9C4.34,9 3,10.34 3,12S4.34,15 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91S20.92,20.61 20.92,19C20.92,17.39 19.61,16.08 18,16.08M18,4C18.55,4 19,4.45 19,5S18.55,6 18,6 17,5.55 17,5 17.45,4 18,4M6,13C5.45,13 5,12.55 5,12S5.45,11 6,11 7,11.45 7,12 6.55,13 6,13M18,20C17.45,20 17,19.55 17,19S17.45,18 18,18 19,18.45 19,19 18.55,20 18,20Z" /> diff --git a/app/src/main/res/drawable/ic_stop_daemons.xml b/app/src/main/res/drawable/ic_stop_daemons.xml index b8cdb470c..b90cea49b 100755 --- a/app/src/main/res/drawable/ic_stop_daemons.xml +++ b/app/src/main/res/drawable/ic_stop_daemons.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M12,2A10,10 0,0 0,2 12A10,10 0,0 0,12 22A10,10 0,0 0,22 12A10,10 0,0 0,12 2M12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4M9,9V15H15V9" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_sync.xml b/app/src/main/res/drawable/ic_sync.xml index f16a1b06e..49e364ba0 100755 --- a/app/src/main/res/drawable/ic_sync.xml +++ b/app/src/main/res/drawable/ic_sync.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_tab.xml b/app/src/main/res/drawable/ic_tab.xml index b06319833..440a0cac4 100755 --- a/app/src/main/res/drawable/ic_tab.xml +++ b/app/src/main/res/drawable/ic_tab.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M20,18H22V6H20M11.59,7.41L15.17,11H1V13H15.17L11.59,16.58L13,18L19,12L13,6L11.59,7.41Z" /> diff --git a/app/src/main/res/drawable/ic_target_sdk.xml b/app/src/main/res/drawable/ic_target_sdk.xml index 3df5e1f3e..091b1a82e 100755 --- a/app/src/main/res/drawable/ic_target_sdk.xml +++ b/app/src/main/res/drawable/ic_target_sdk.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M11,2V4.07C7.38,4.53 4.53,7.38 4.07,11H2V13H4.07C4.53,16.62 7.38,19.47 11,19.93V22H13V19.93C16.62,19.47 19.47,16.62 19.93,13H22V11H19.93C19.47,7.38 16.62,4.53 13,4.07V2M11,6.08V8H13V6.09C15.5,6.5 17.5,8.5 17.92,11H16V13H17.91C17.5,15.5 15.5,17.5 13,17.92V16H11V17.91C8.5,17.5 6.5,15.5 6.08,13H8V11H6.09C6.5,8.5 8.5,6.5 11,6.08M12,11A1,1 0,0 0,11 12A1,1 0,0 0,12 13A1,1 0,0 0,13 12A1,1 0,0 0,12 11Z" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_telegram.xml b/app/src/main/res/drawable/ic_telegram.xml index 8b5b8e770..ee1e9085a 100755 --- a/app/src/main/res/drawable/ic_telegram.xml +++ b/app/src/main/res/drawable/ic_telegram.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M9.78,18.65L10.06,14.42L17.74,7.5C18.08,7.19 17.67,7.04 17.22,7.31L7.74,13.3L3.64,12C2.76,11.75 2.75,11.14 3.84,10.7L19.81,4.54C20.54,4.21 21.24,4.72 20.96,5.84L18.24,18.65C18.05,19.56 17.5,19.78 16.74,19.36L12.6,16.3L10.61,18.23C10.38,18.46 10.19,18.65 9.78,18.65Z" /> diff --git a/app/src/main/res/drawable/ic_terminal.xml b/app/src/main/res/drawable/ic_terminal.xml index 21e97a288..420234453 100755 --- a/app/src/main/res/drawable/ic_terminal.xml +++ b/app/src/main/res/drawable/ic_terminal.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M21.83,4C22.32,4 22.63,4.4 22.5,4.89L19.34,19.11C19.23,19.6 18.75,20 18.26,20H2.17C1.68,20 1.37,19.6 1.5,19.11L4.66,4.89C4.77,4.4 5.25,4 5.74,4H21.83M15.83,16H11.83C11.37,16 11,16.38 11,16.84C11,17.31 11.37,17.69 11.83,17.69H15.83C16.3,17.69 16.68,17.31 16.68,16.84C16.68,16.38 16.3,16 15.83,16M5.78,16.28C5.38,16.56 5.29,17.11 5.57,17.5C5.85,17.92 6.41,18 6.81,17.73C14.16,12.56 14.21,12.5 14.26,12.47C14.44,12.31 14.53,12.09 14.54,11.87C14.55,11.67 14.5,11.5 14.38,11.31L9.46,6.03C9.13,5.67 8.57,5.65 8.21,6C7.85,6.32 7.83,6.88 8.16,7.24L12.31,11.68L5.78,16.28Z" /> diff --git a/app/src/main/res/drawable/ic_text_lower.xml b/app/src/main/res/drawable/ic_text_lower.xml index aec47d537..b0878283c 100644 --- a/app/src/main/res/drawable/ic_text_lower.xml +++ b/app/src/main/res/drawable/ic_text_lower.xml @@ -1,8 +1,10 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_text_size.xml b/app/src/main/res/drawable/ic_text_size.xml index a260f895b..5bb47df3d 100755 --- a/app/src/main/res/drawable/ic_text_size.xml +++ b/app/src/main/res/drawable/ic_text_size.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M2,4V7H7V19H10V7H15V4H2M21,9H12V12H15V19H18V12H21V9Z" /> diff --git a/app/src/main/res/drawable/ic_undo.xml b/app/src/main/res/drawable/ic_undo.xml index 102999c3d..4d4421d99 100755 --- a/app/src/main/res/drawable/ic_undo.xml +++ b/app/src/main/res/drawable/ic_undo.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M12.5,8C9.85,8 7.45,9 5.6,10.6L2,7V16H11L7.38,12.38C8.77,11.22 10.54,10.5 12.5,10.5C16.04,10.5 19.05,12.81 20.1,16L22.47,15.22C21.08,11.03 17.15,8 12.5,8Z" /> diff --git a/app/src/main/res/drawable/ic_view_select_parent.xml b/app/src/main/res/drawable/ic_view_select_parent.xml index 4754c87c9..80cd99cf3 100644 --- a/app/src/main/res/drawable/ic_view_select_parent.xml +++ b/app/src/main/res/drawable/ic_view_select_parent.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_website.xml b/app/src/main/res/drawable/ic_website.xml index cd54f775c..5eea09784 100755 --- a/app/src/main/res/drawable/ic_website.xml +++ b/app/src/main/res/drawable/ic_website.xml @@ -2,10 +2,10 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M16.36,14C16.44,13.34 16.5,12.68 16.5,12C16.5,11.32 16.44,10.66 16.36,10H19.74C19.9,10.64 20,11.31 20,12C20,12.69 19.9,13.36 19.74,14M14.59,19.56C15.19,18.45 15.65,17.25 15.97,16H18.92C17.96,17.65 16.43,18.93 14.59,19.56M14.34,14H9.66C9.56,13.34 9.5,12.68 9.5,12C9.5,11.32 9.56,10.65 9.66,10H14.34C14.43,10.65 14.5,11.32 14.5,12C14.5,12.68 14.43,13.34 14.34,14M12,19.96C11.17,18.76 10.5,17.43 10.09,16H13.91C13.5,17.43 12.83,18.76 12,19.96M8,8H5.08C6.03,6.34 7.57,5.06 9.4,4.44C8.8,5.55 8.35,6.75 8,8M5.08,16H8C8.35,17.25 8.8,18.45 9.4,19.56C7.57,18.93 6.03,17.65 5.08,16M4.26,14C4.1,13.36 4,12.69 4,12C4,11.31 4.1,10.64 4.26,10H7.64C7.56,10.66 7.5,11.32 7.5,12C7.5,12.68 7.56,13.34 7.64,14M12,4.03C12.83,5.23 13.5,6.57 13.91,8H10.09C10.5,6.57 11.17,5.23 12,4.03M18.92,8H15.97C15.65,6.75 15.19,5.55 14.59,4.44C16.43,5.07 17.96,6.34 18.92,8M12,2C6.47,2 2,6.5 2,12A10,10 0,0 0,12 22A10,10 0,0 0,22 12A10,10 0,0 0,12 2Z" /> diff --git a/app/src/main/res/drawable/ic_widget_button.xml b/app/src/main/res/drawable/ic_widget_button.xml index 6f92f63e4..4f4c8cc20 100755 --- a/app/src/main/res/drawable/ic_widget_button.xml +++ b/app/src/main/res/drawable/ic_widget_button.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_widget_checkbox.xml b/app/src/main/res/drawable/ic_widget_checkbox.xml index 298bc37e8..738c6892c 100755 --- a/app/src/main/res/drawable/ic_widget_checkbox.xml +++ b/app/src/main/res/drawable/ic_widget_checkbox.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_widget_checked_textview.xml b/app/src/main/res/drawable/ic_widget_checked_textview.xml index e24b9835c..92b83f17f 100755 --- a/app/src/main/res/drawable/ic_widget_checked_textview.xml +++ b/app/src/main/res/drawable/ic_widget_checked_textview.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_widget_edittext.xml b/app/src/main/res/drawable/ic_widget_edittext.xml index 5034d67b1..3f5e59806 100755 --- a/app/src/main/res/drawable/ic_widget_edittext.xml +++ b/app/src/main/res/drawable/ic_widget_edittext.xml @@ -1,8 +1,10 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_widget_image.xml b/app/src/main/res/drawable/ic_widget_image.xml index d35859dd5..0a221c919 100755 --- a/app/src/main/res/drawable/ic_widget_image.xml +++ b/app/src/main/res/drawable/ic_widget_image.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_widget_linear.xml b/app/src/main/res/drawable/ic_widget_linear.xml index 563ecabab..1d9e7b20c 100644 --- a/app/src/main/res/drawable/ic_widget_linear.xml +++ b/app/src/main/res/drawable/ic_widget_linear.xml @@ -19,24 +19,24 @@ android:height="24dp" android:viewportWidth="512" android:viewportHeight="512"> - - - - + + + + diff --git a/app/src/main/res/drawable/ic_widget_progress.xml b/app/src/main/res/drawable/ic_widget_progress.xml index 3062e2fce..605ea95cf 100755 --- a/app/src/main/res/drawable/ic_widget_progress.xml +++ b/app/src/main/res/drawable/ic_widget_progress.xml @@ -1,38 +1,38 @@ - - - - + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_widget_radio_button.xml b/app/src/main/res/drawable/ic_widget_radio_button.xml index 3937722fa..cd02753b7 100755 --- a/app/src/main/res/drawable/ic_widget_radio_button.xml +++ b/app/src/main/res/drawable/ic_widget_radio_button.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_widget_seekbar.xml b/app/src/main/res/drawable/ic_widget_seekbar.xml index 1b0c1cdc7..ebc94f101 100755 --- a/app/src/main/res/drawable/ic_widget_seekbar.xml +++ b/app/src/main/res/drawable/ic_widget_seekbar.xml @@ -1,8 +1,10 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_widget_spinner.xml b/app/src/main/res/drawable/ic_widget_spinner.xml index 3dbfedba2..f5b891d07 100755 --- a/app/src/main/res/drawable/ic_widget_spinner.xml +++ b/app/src/main/res/drawable/ic_widget_spinner.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_widget_textview.xml b/app/src/main/res/drawable/ic_widget_textview.xml index fb98d9932..ee5982a93 100755 --- a/app/src/main/res/drawable/ic_widget_textview.xml +++ b/app/src/main/res/drawable/ic_widget_textview.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_wrap_text.xml b/app/src/main/res/drawable/ic_wrap_text.xml index 1dbb4c0fd..b7988d532 100644 --- a/app/src/main/res/drawable/ic_wrap_text.xml +++ b/app/src/main/res/drawable/ic_wrap_text.xml @@ -1,10 +1,10 @@ - + android:viewportHeight="24"> + diff --git a/app/src/main/res/drawable/ic_xml_attribute.xml b/app/src/main/res/drawable/ic_xml_attribute.xml index ab8fd2549..f4b1aba06 100644 --- a/app/src/main/res/drawable/ic_xml_attribute.xml +++ b/app/src/main/res/drawable/ic_xml_attribute.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index 3db8d2d3d..39015db90 100755 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -1,86 +1,86 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + app:layoutDescription="@xml/motion_about"> - - - + + + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/icon_anchor"> - + - + - - - - - + + + + - + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/subtitle"> + - + android:id="@+id/items" + layout="@layout/layout_about_items" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="24dp" /> + diff --git a/app/src/main/res/layout/activity_crash_handler.xml b/app/src/main/res/layout/activity_crash_handler.xml index 7f49b1383..33c3a5c79 100644 --- a/app/src/main/res/layout/activity_crash_handler.xml +++ b/app/src/main/res/layout/activity_crash_handler.xml @@ -1,5 +1,4 @@ - - - + tools:context=".DesignerActivity"> + android:background="@drawable/bg_design_container_background" + android:orientation="vertical" /> diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 89c8edf8c..7b1e1619f 100755 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -6,19 +6,19 @@ android:layout_height="match_parent" android:animateLayoutChanges="true" android:orientation="vertical" - tools:ignore="RtlCompat" > + tools:ignore="RtlCompat"> + android:gravity="center" + app:layout_behavior="com.google.android.material.appbar.AppBarLayout$Behavior"> + android:layout_height="wrap_content"> - - - + - + + + + + + + + + + + - - - - - - - - diff --git a/app/src/main/res/layout/activity_splash.xml b/app/src/main/res/layout/activity_splash.xml index ad9957364..f459d6c21 100755 --- a/app/src/main/res/layout/activity_splash.xml +++ b/app/src/main/res/layout/activity_splash.xml @@ -3,32 +3,32 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + - + - + diff --git a/app/src/main/res/layout/activity_terminal.xml b/app/src/main/res/layout/activity_terminal.xml index e5836b387..c1eaa6685 100755 --- a/app/src/main/res/layout/activity_terminal.xml +++ b/app/src/main/res/layout/activity_terminal.xml @@ -2,15 +2,15 @@ - + android:layout_height="match_parent" + android:orientation="vertical"> + - + android:background="@android:color/black" + tools:ignore="MissingClass" /> + diff --git a/app/src/main/res/layout/fragment_non_editable_editor.xml b/app/src/main/res/layout/fragment_non_editable_editor.xml index d4881a809..ab606d7ff 100644 --- a/app/src/main/res/layout/fragment_non_editable_editor.xml +++ b/app/src/main/res/layout/fragment_non_editable_editor.xml @@ -15,8 +15,7 @@ ~ along with AndroidIDE. If not, see . --> - \ No newline at end of file + android:layout_height="match_parent" /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_about_items.xml b/app/src/main/res/layout/layout_about_items.xml index 789355e8c..916596569 100755 --- a/app/src/main/res/layout/layout_about_items.xml +++ b/app/src/main/res/layout/layout_about_items.xml @@ -1,157 +1,156 @@ - + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:paddingTop="16dp"> - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/app/src/main/res/layout/layout_attr_editor_sheet_item.xml b/app/src/main/res/layout/layout_attr_editor_sheet_item.xml index e97e19d4c..ca2d133c4 100644 --- a/app/src/main/res/layout/layout_attr_editor_sheet_item.xml +++ b/app/src/main/res/layout/layout_attr_editor_sheet_item.xml @@ -15,32 +15,31 @@ ~ along with AndroidIDE. If not, see . --> - + android:focusable="true" + android:gravity="center_vertical" + android:orientation="horizontal" + android:paddingStart="8dp" + android:paddingEnd="8dp"> + android:src="@drawable/ic_xml_attribute" /> + android:textColor="@color/primaryTextColorLight" + android:textSize="16sp" + tools:text="layout_height" /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_banner.xml b/app/src/main/res/layout/layout_banner.xml index ac022d767..9d532f316 100755 --- a/app/src/main/res/layout/layout_banner.xml +++ b/app/src/main/res/layout/layout_banner.xml @@ -6,58 +6,58 @@ android:layout_height="wrap_content" android:orientation="vertical"> - + + android:id="@+id/contentTextView" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginTop="24dp" + android:layout_marginEnd="16dp" + android:layout_marginBottom="8dp" + android:alpha="0.8" + android:letterSpacing="0.02" + android:lineSpacingExtra="7sp" + android:textColor="@color/primaryTextColor" + android:textSize="14sp" + android:textStyle="normal" + app:layout_constraintBottom_toTopOf="@+id/rightButton" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/contentIconView" + app:layout_constraintTop_toTopOf="parent" /> + android:id="@+id/leftButton" + style="@style/Widget.MaterialComponents.Button.TextButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:textColor="@color/primaryTextColor" + android:textSize="14sp" + app:layout_constraintBottom_toBottomOf="@+id/rightButton" + app:layout_constraintEnd_toStartOf="@+id/rightButton" + app:layout_constraintTop_toTopOf="@+id/rightButton" + app:rippleColor="@color/primaryDarkColor" /> + android:id="@+id/rightButton" + style="@style/Widget.MaterialComponents.Button.TextButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="16dp" + android:layout_marginBottom="9dp" + android:textColor="@color/primaryTextColor" + android:textSize="14sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:rippleColor="@color/primaryDarkColor" /> diff --git a/app/src/main/res/layout/layout_code_editor.xml b/app/src/main/res/layout/layout_code_editor.xml index 02cb4faec..a333fcab8 100644 --- a/app/src/main/res/layout/layout_code_editor.xml +++ b/app/src/main/res/layout/layout_code_editor.xml @@ -1,36 +1,35 @@ - - + + - + android:layout_height="0dp" + android:layout_weight="1.0" /> + + android:padding="8dp" + android:singleLine="true" + android:textColor="@color/primaryTextColor" + android:textSize="12sp" /> - + diff --git a/app/src/main/res/layout/layout_completion_item.xml b/app/src/main/res/layout/layout_completion_item.xml index 9407c5256..7633aea20 100755 --- a/app/src/main/res/layout/layout_completion_item.xml +++ b/app/src/main/res/layout/layout_completion_item.xml @@ -1,88 +1,88 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/relativeLayout2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@color/primaryLightColor" + android:gravity="center_vertical" + tools:ignore="SmallSp"> - + - + - + - + - + diff --git a/app/src/main/res/layout/layout_crash_report.xml b/app/src/main/res/layout/layout_crash_report.xml index dcab09f31..479b4251b 100644 --- a/app/src/main/res/layout/layout_crash_report.xml +++ b/app/src/main/res/layout/layout_crash_report.xml @@ -49,8 +49,8 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" - android:layout_marginEnd="16dp" android:layout_marginTop="4dp" + android:layout_marginEnd="16dp" android:text="@string/msg_report_crash" android:textColor="@color/secondaryTextColor_light" android:textSize="14sp" @@ -79,7 +79,7 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:textColor="@color/secondaryColor" - tools:text="This is an error message."/> + tools:text="This is an error message." /> diff --git a/app/src/main/res/layout/layout_create_file_java.xml b/app/src/main/res/layout/layout_create_file_java.xml index 19b0e0b85..c6a2bc9be 100755 --- a/app/src/main/res/layout/layout_create_file_java.xml +++ b/app/src/main/res/layout/layout_create_file_java.xml @@ -1,88 +1,87 @@ - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/app/src/main/res/layout/layout_diagnostic_group.xml b/app/src/main/res/layout/layout_diagnostic_group.xml index e499b2c8e..438b74b19 100755 --- a/app/src/main/res/layout/layout_diagnostic_group.xml +++ b/app/src/main/res/layout/layout_diagnostic_group.xml @@ -1,23 +1,22 @@ - - + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@color/content_background" + android:orientation="vertical" + tools:ignore="Overdraw"> + - + layout="@layout/layout_diagnostic_item" /> + + android:paddingEnd="16dp" /> diff --git a/app/src/main/res/layout/layout_diagnostic_info.xml b/app/src/main/res/layout/layout_diagnostic_info.xml index 744d49f31..5802b8baa 100755 --- a/app/src/main/res/layout/layout_diagnostic_info.xml +++ b/app/src/main/res/layout/layout_diagnostic_info.xml @@ -1,9 +1,9 @@ + android:textIsSelectable="true" + android:textSize="11sp" /> diff --git a/app/src/main/res/layout/layout_diagnostic_item.xml b/app/src/main/res/layout/layout_diagnostic_item.xml index c56c560a2..074288293 100755 --- a/app/src/main/res/layout/layout_diagnostic_item.xml +++ b/app/src/main/res/layout/layout_diagnostic_item.xml @@ -1,35 +1,34 @@ - + android:clickable="true" + android:descendantFocusability="blocksDescendants" + android:focusable="true" + android:foreground="?attr/selectableItemBackground" + android:gravity="top|start" + android:orientation="horizontal" + android:paddingStart="16dp" + android:paddingTop="8dp" + android:paddingEnd="16dp" + android:paddingBottom="8dp" + tools:ignore="UnusedAttribute"> - + - + diff --git a/app/src/main/res/layout/layout_dialog_text_input.xml b/app/src/main/res/layout/layout_dialog_text_input.xml index 4094eafbd..9bd755d00 100755 --- a/app/src/main/res/layout/layout_dialog_text_input.xml +++ b/app/src/main/res/layout/layout_dialog_text_input.xml @@ -1,29 +1,28 @@ - + - + - + - + diff --git a/app/src/main/res/layout/layout_editor_file_tree.xml b/app/src/main/res/layout/layout_editor_file_tree.xml index 0d938169e..8b321a23b 100755 --- a/app/src/main/res/layout/layout_editor_file_tree.xml +++ b/app/src/main/res/layout/layout_editor_file_tree.xml @@ -1,26 +1,25 @@ - - - + - + + + diff --git a/app/src/main/res/layout/layout_filetree_item.xml b/app/src/main/res/layout/layout_filetree_item.xml index 699ef086f..b48828e3c 100755 --- a/app/src/main/res/layout/layout_filetree_item.xml +++ b/app/src/main/res/layout/layout_filetree_item.xml @@ -1,31 +1,30 @@ - + - + - + - + diff --git a/app/src/main/res/layout/layout_loading_wizard.xml b/app/src/main/res/layout/layout_loading_wizard.xml index b4044cfaf..e78747820 100644 --- a/app/src/main/res/layout/layout_loading_wizard.xml +++ b/app/src/main/res/layout/layout_loading_wizard.xml @@ -9,9 +9,9 @@ + android:layout_height="wrap_content" + android:indeterminate="true" /> + android:text="@string/wizard_loading" + android:textSize="24sp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_nav_header.xml b/app/src/main/res/layout/layout_nav_header.xml index 0a8f81545..d9db6286c 100755 --- a/app/src/main/res/layout/layout_nav_header.xml +++ b/app/src/main/res/layout/layout_nav_header.xml @@ -1,32 +1,31 @@ - + - + - + diff --git a/app/src/main/res/layout/layout_optionssheet_item.xml b/app/src/main/res/layout/layout_optionssheet_item.xml index d4bee6311..3697ca99c 100755 --- a/app/src/main/res/layout/layout_optionssheet_item.xml +++ b/app/src/main/res/layout/layout_optionssheet_item.xml @@ -1,33 +1,33 @@ + android:orientation="vertical" + android:paddingTop="16dp" + android:paddingBottom="16dp"> - + - + diff --git a/app/src/main/res/layout/layout_progress_sheet.xml b/app/src/main/res/layout/layout_progress_sheet.xml index 62826ea11..eb9ea5b19 100755 --- a/app/src/main/res/layout/layout_progress_sheet.xml +++ b/app/src/main/res/layout/layout_progress_sheet.xml @@ -1,51 +1,50 @@ - + - + - + - + - + diff --git a/app/src/main/res/layout/layout_search_project.xml b/app/src/main/res/layout/layout_search_project.xml index bee069eda..885f1addc 100755 --- a/app/src/main/res/layout/layout_search_project.xml +++ b/app/src/main/res/layout/layout_search_project.xml @@ -1,64 +1,63 @@ - - + + android:paddingTop="12dp" + android:paddingEnd="16dp" + android:paddingBottom="12dp"> + android:layout_marginBottom="8dp" + android:text="@string/msg_search_modules" /> + android:layout_marginBottom="16dp" + android:orientation="vertical" /> + android:layout_width="match_parent" + android:layout_height="wrap_content" /> - + + android:layout_width="match_parent" + android:layout_height="wrap_content" /> - + - + diff --git a/app/src/main/res/layout/layout_search_result_group.xml b/app/src/main/res/layout/layout_search_result_group.xml index b734ab7db..07876b3ad 100755 --- a/app/src/main/res/layout/layout_search_result_group.xml +++ b/app/src/main/res/layout/layout_search_result_group.xml @@ -1,64 +1,63 @@ - + - - - - - - - - - + + + + + + + + + android:clipToPadding="false" + android:fillViewport="true" + android:paddingStart="16dp" + tools:ignore="RtlSymmetry"> - + - + diff --git a/app/src/main/res/layout/layout_search_result_item.xml b/app/src/main/res/layout/layout_search_result_item.xml index 52cc2bc0c..400175671 100755 --- a/app/src/main/res/layout/layout_search_result_item.xml +++ b/app/src/main/res/layout/layout_search_result_item.xml @@ -1,26 +1,25 @@ - + - + diff --git a/app/src/main/res/layout/layout_sheet.xml b/app/src/main/res/layout/layout_sheet.xml index 4ce02192f..82146be26 100755 --- a/app/src/main/res/layout/layout_sheet.xml +++ b/app/src/main/res/layout/layout_sheet.xml @@ -1,46 +1,45 @@ - + android:layout_height="match_parent" + android:orientation="vertical">