mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Reformat all source file
This commit is contained in:
@@ -1,9 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: Bug
|
name: Bug about: Report a bug in the IDE. title: ""
|
||||||
about: Report a bug in the IDE.
|
labels: bug assignees: ''
|
||||||
title: ""
|
|
||||||
labels: bug
|
|
||||||
assignees: ''
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
@@ -20,9 +17,10 @@ Steps to reproduce the behavior:
|
|||||||
<!-- If applicable, add screenshots to help explain your problem. -->
|
<!-- If applicable, add screenshots to help explain your problem. -->
|
||||||
|
|
||||||
**Details (please complete the following information):**
|
**Details (please complete the following information):**
|
||||||
- Device: [e.g. realme 3 Pro]
|
|
||||||
- OS: [e.g. Android 11]
|
- Device: [e.g. realme 3 Pro]
|
||||||
- AndroidIDE Version [e.g. v1.0.3-alpha]
|
- OS: [e.g. Android 11]
|
||||||
|
- AndroidIDE Version [e.g. v1.0.3-alpha]
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
<!-- Add any other context about the problem here. -->
|
<!-- Add any other context about the problem here. -->
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: Feature request
|
name: Feature request about: Suggest an idea for this project title: "[FEATURE]: "
|
||||||
about: Suggest an idea for this project
|
labels: feature assignees: ''
|
||||||
title: "[FEATURE]: "
|
|
||||||
labels: feature
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+56
-70
@@ -2,124 +2,110 @@
|
|||||||
|
|
||||||
## Our Pledge
|
## Our Pledge
|
||||||
|
|
||||||
We as members, contributors, and leaders pledge to make participation in our
|
We as members, contributors, and leaders pledge to make participation in our community a
|
||||||
community a harassment-free experience for everyone, regardless of age, body
|
harassment-free experience for everyone, regardless of age, body size, visible or invisible
|
||||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
disability, ethnicity, sex characteristics, gender identity and expression, level of experience,
|
||||||
identity and expression, level of experience, education, socio-economic status,
|
education, socio-economic status, nationality, personal appearance, race, religion, or sexual
|
||||||
nationality, personal appearance, race, religion, or sexual identity
|
identity and orientation.
|
||||||
and orientation.
|
|
||||||
|
|
||||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and
|
||||||
diverse, inclusive, and healthy community.
|
healthy community.
|
||||||
|
|
||||||
## Our Standards
|
## Our Standards
|
||||||
|
|
||||||
Examples of behavior that contributes to a positive environment for our
|
Examples of behavior that contributes to a positive environment for our community include:
|
||||||
community include:
|
|
||||||
|
|
||||||
* Demonstrating empathy and kindness toward other people
|
* Demonstrating empathy and kindness toward other people
|
||||||
* Being respectful of differing opinions, viewpoints, and experiences
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
* Giving and gracefully accepting constructive feedback
|
* Giving and gracefully accepting constructive feedback
|
||||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the
|
||||||
and learning from the experience
|
experience
|
||||||
* Focusing on what is best not just for us as individuals, but for the
|
* Focusing on what is best not just for us as individuals, but for the overall community
|
||||||
overall community
|
|
||||||
|
|
||||||
Examples of unacceptable behavior include:
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
* The use of sexualized language or imagery, and sexual attention or
|
* The use of sexualized language or imagery, and sexual attention or advances of any kind
|
||||||
advances of any kind
|
|
||||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
* Public or private harassment
|
* Public or private harassment
|
||||||
* Publishing others' private information, such as a physical or email
|
* Publishing others' private information, such as a physical or email address, without their
|
||||||
address, without their explicit permission
|
explicit permission
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||||
professional setting
|
|
||||||
|
|
||||||
## Enforcement Responsibilities
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
Community leaders are responsible for clarifying and enforcing our standards of
|
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior
|
||||||
acceptable behavior and will take appropriate and fair corrective action in
|
and will take appropriate and fair corrective action in response to any behavior that they deem
|
||||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
inappropriate, threatening, offensive, or harmful.
|
||||||
or harmful.
|
|
||||||
|
|
||||||
Community leaders have the right and responsibility to remove, edit, or reject
|
Community leaders have the right and responsibility to remove, edit, or reject comments, commits,
|
||||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and
|
||||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
will communicate reasons for moderation decisions when appropriate.
|
||||||
decisions when appropriate.
|
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
This Code of Conduct applies within all community spaces, and also applies when
|
This Code of Conduct applies within all community spaces, and also applies when an individual is
|
||||||
an individual is officially representing the community in public spaces.
|
officially representing the community in public spaces. Examples of representing our community
|
||||||
Examples of representing our community include using an official e-mail address,
|
include using an official e-mail address, posting via an official social media account, or acting as
|
||||||
posting via an official social media account, or acting as an appointed
|
an appointed representative at an online or offline event.
|
||||||
representative at an online or offline event.
|
|
||||||
|
|
||||||
## Enforcement
|
## Enforcement
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community
|
||||||
reported to the community leaders responsible for enforcement either by opening
|
leaders responsible for enforcement either by opening an issue or writing me an email
|
||||||
an issue or writing me an email at [itsaky01@gmail.com](mailto:itsaky01@gmail.com).
|
at [itsaky01@gmail.com](mailto:itsaky01@gmail.com). All complaints will be reviewed and investigated
|
||||||
All complaints will be reviewed and investigated promptly and fairly.
|
promptly and fairly.
|
||||||
|
|
||||||
All community leaders are obligated to respect the privacy and security of the
|
All community leaders are obligated to respect the privacy and security of the reporter of any
|
||||||
reporter of any incident.
|
incident.
|
||||||
|
|
||||||
## Enforcement Guidelines
|
## Enforcement Guidelines
|
||||||
|
|
||||||
Community leaders will follow these Community Impact Guidelines in determining
|
Community leaders will follow these Community Impact Guidelines in determining the consequences for
|
||||||
the consequences for any action they deem in violation of this Code of Conduct:
|
any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
### 1. Correction
|
### 1. Correction
|
||||||
|
|
||||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or
|
||||||
unprofessional or unwelcome in the community.
|
unwelcome in the community.
|
||||||
|
|
||||||
**Consequence**: A private, written warning from community leaders, providing
|
**Consequence**: A private, written warning from community leaders, providing clarity around the
|
||||||
clarity around the nature of the violation and an explanation of why the
|
nature of the violation and an explanation of why the behavior was inappropriate. A public apology
|
||||||
behavior was inappropriate. A public apology may be requested.
|
may be requested.
|
||||||
|
|
||||||
### 2. Warning
|
### 2. Warning
|
||||||
|
|
||||||
**Community Impact**: A violation through a single incident or series
|
**Community Impact**: A violation through a single incident or series of actions.
|
||||||
of actions.
|
|
||||||
|
|
||||||
**Consequence**: A warning with consequences for continued behavior. No
|
**Consequence**: A warning with consequences for continued behavior. No interaction with the people
|
||||||
interaction with the people involved, including unsolicited interaction with
|
involved, including unsolicited interaction with those enforcing the Code of Conduct, for a
|
||||||
those enforcing the Code of Conduct, for a specified period of time. This
|
specified period of time. This includes avoiding interactions in community spaces as well as
|
||||||
includes avoiding interactions in community spaces as well as external channels
|
external channels like social media. Violating these terms may lead to a temporary or permanent ban.
|
||||||
like social media. Violating these terms may lead to a temporary or
|
|
||||||
permanent ban.
|
|
||||||
|
|
||||||
### 3. Temporary Ban
|
### 3. Temporary Ban
|
||||||
|
|
||||||
**Community Impact**: A serious violation of community standards, including
|
**Community Impact**: A serious violation of community standards, including sustained inappropriate
|
||||||
sustained inappropriate behavior.
|
behavior.
|
||||||
|
|
||||||
**Consequence**: A temporary ban from any sort of interaction or public
|
**Consequence**: A temporary ban from any sort of interaction or public communication with the
|
||||||
communication with the community for a specified period of time. No public or
|
community for a specified period of time. No public or private interaction with the people involved,
|
||||||
private interaction with the people involved, including unsolicited interaction
|
including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this
|
||||||
with those enforcing the Code of Conduct, is allowed during this period.
|
period. Violating these terms may lead to a permanent ban.
|
||||||
Violating these terms may lead to a permanent ban.
|
|
||||||
|
|
||||||
### 4. Permanent Ban
|
### 4. Permanent Ban
|
||||||
|
|
||||||
**Community Impact**: Demonstrating a pattern of violation of community
|
**Community Impact**: Demonstrating a pattern of violation of community standards, including
|
||||||
standards, including sustained inappropriate behavior, harassment of an
|
sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement
|
||||||
individual, or aggression toward or disparagement of classes of individuals.
|
of classes of individuals.
|
||||||
|
|
||||||
**Consequence**: A permanent ban from any sort of public interaction within
|
**Consequence**: A permanent ban from any sort of public interaction within the community.
|
||||||
the community.
|
|
||||||
|
|
||||||
## Attribution
|
## Attribution
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at
|
||||||
version 2.0, available at
|
|
||||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
Community Impact Guidelines were inspired
|
||||||
enforcement ladder](https://github.com/mozilla/diversity).
|
by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
[homepage]: https://www.contributor-covenant.org
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
|||||||
+22
-8
@@ -1,8 +1,12 @@
|
|||||||
# Contributing
|
# 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
|
## Propose a change
|
||||||
|
|
||||||
To contribute to this project,
|
To contribute to this project,
|
||||||
|
|
||||||
- Fork the repo.
|
- Fork the repo.
|
||||||
- Clone the forked repo to your local machine.
|
- Clone the forked repo to your local machine.
|
||||||
- Open the project in Android Studio.
|
- Open the project in Android Studio.
|
||||||
@@ -10,19 +14,29 @@ To contribute to this project,
|
|||||||
- Create a pull request.
|
- Create a pull request.
|
||||||
|
|
||||||
## Note
|
## Note
|
||||||
|
|
||||||
`Java 11` is required to build the project.
|
`Java 11` is required to build the project.
|
||||||
|
|
||||||
Building AndroidIDE in AndroidIDE itself is also possible. But that might require some modifications.
|
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.
|
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
|
## 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.
|
The source code must have `2-space` indentation (soft tab). Java source code is formatted
|
||||||
You can execute `./gradlew formatJavaSources` in terminal to format all Java source files using Google Java Format.
|
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**.
|
**This will commit the changes as well**.
|
||||||
|
|
||||||
## Issues & Pull requests
|
## 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).
|
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).
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
</p><br>
|
</p><br>
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- [x] Gradle support.
|
- [x] Gradle support.
|
||||||
- [x] `JDK 11` and `JDK 17` available for use.
|
- [x] `JDK 11` and `JDK 17` available for use.
|
||||||
- [x] Terminal with necessary packages.
|
- [x] Terminal with necessary packages.
|
||||||
@@ -32,34 +33,50 @@
|
|||||||
- [x] Git (*)
|
- [x] Git (*)
|
||||||
|
|
||||||
Items marked with `(*)` are partially implemented.
|
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
|
## 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
|
## 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.
|
- 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
|
## Contributing
|
||||||
|
|
||||||
See the [contributing guide](./CONTRIBUTING.md).
|
See the [contributing guide](./CONTRIBUTING.md).
|
||||||
|
|
||||||
## Thanks to
|
## 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)
|
- [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)
|
- [Bogdan Melnychuk](https://github.com/bmelnychuk)
|
||||||
- [George Fraser](https://github.com/georgewfraser) for the [Java Language Server](https://github.com/georgewfraser/java-language-server)
|
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.
|
And all the developers who have contributed to this project.
|
||||||
|
|
||||||
## Contact Us
|
## Contact Us
|
||||||
|
|
||||||
- [Website](https://androidide.com)
|
- [Website](https://androidide.com)
|
||||||
- [Telegram](https://t.me/androidide_discussions)
|
- [Telegram](https://t.me/androidide_discussions)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
```
|
```
|
||||||
AndroidIDE is free software: you can redistribute it and/or modify
|
AndroidIDE is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
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 <https://www.gnu.org/licenses/>.
|
along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':common')
|
implementation project (':common')
|
||||||
implementation common.editor
|
implementation common.editor
|
||||||
implementation common.utilcode
|
implementation common.utilcode
|
||||||
|
|
||||||
@@ -38,5 +38,5 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral ()
|
||||||
}
|
}
|
||||||
+3
-3
@@ -153,10 +153,10 @@ class DefaultActionsRegistry : ActionsRegistry() {
|
|||||||
if (result == null || (result is Boolean && !result) || error != null) {
|
if (result == null || (result is Boolean && !result) || error != null) {
|
||||||
log.error(
|
log.error(
|
||||||
"An error occurred when performing action '${it.title}'. Action failed in ${System.currentTimeMillis() - start}ms",
|
"An error occurred when performing action '${it.title}'. Action failed in ${System.currentTimeMillis() - start}ms",
|
||||||
error)
|
error
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
log.info(
|
log.info("Action '${it.title}' completed in ${System.currentTimeMillis() - start}ms")
|
||||||
"Action '${it.title}' completed in ${System.currentTimeMillis() - start}ms")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadUtils.runOnUiThread {
|
ThreadUtils.runOnUiThread {
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
|
|
||||||
<solid android:color="@color/colorPrimary" />
|
<solid android:color="@color/colorPrimary" />
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/coordinator"
|
android:id="@+id/coordinator"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
@@ -19,7 +17,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
android:background="@color/colorPrimary"
|
android:background="@color/colorPrimary"
|
||||||
app:popupOverlay="@style/ThemeOverlay.MaterialComponents.Light"/>
|
app:popupOverlay="@style/ThemeOverlay.MaterialComponents.Light" />
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/menu_item1"
|
android:id="@+id/menu_item1"
|
||||||
android:name="Menu Item"/>
|
android:name="Menu Item" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
@@ -38,12 +38,6 @@ public class AboutActivity extends StudioActivity {
|
|||||||
public static final String JDK_SOURCE = "https://github.com/itsaky/OpenJDK-Android";
|
public static final String JDK_SOURCE = "https://github.com/itsaky/OpenJDK-Android";
|
||||||
private ActivityAboutBinding binding;
|
private ActivityAboutBinding binding;
|
||||||
|
|
||||||
@Override
|
|
||||||
protected View bindLayout() {
|
|
||||||
binding = ActivityAboutBinding.inflate(getLayoutInflater());
|
|
||||||
return binding.getRoot();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -64,6 +58,12 @@ public class AboutActivity extends StudioActivity {
|
|||||||
items.website.setOnClickListener(v -> getApp().openWebsite());
|
items.website.setOnClickListener(v -> getApp().openWebsite());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected View bindLayout() {
|
||||||
|
binding = ActivityAboutBinding.inflate(getLayoutInflater());
|
||||||
|
return binding.getRoot();
|
||||||
|
}
|
||||||
|
|
||||||
private void setupTranslations() {
|
private void setupTranslations() {
|
||||||
final var translations = createTranslationsList();
|
final var translations = createTranslationsList();
|
||||||
final var sb = new StringBuilder();
|
final var sb = new StringBuilder();
|
||||||
|
|||||||
@@ -26,16 +26,9 @@ import com.itsaky.androidide.fragments.CrashReportFragment;
|
|||||||
|
|
||||||
public class CrashHandlerActivity extends StudioActivity {
|
public class CrashHandlerActivity extends StudioActivity {
|
||||||
|
|
||||||
private ActivityCrashHandlerBinding binding;
|
|
||||||
|
|
||||||
public static final String REPORT_ACTION = "com.itsaky.androidide.REPORT_CRASH";
|
public static final String REPORT_ACTION = "com.itsaky.androidide.REPORT_CRASH";
|
||||||
public static final String TRACE_KEY = "crash_trace";
|
public static final String TRACE_KEY = "crash_trace";
|
||||||
|
private ActivityCrashHandlerBinding binding;
|
||||||
@Override
|
|
||||||
protected View bindLayout() {
|
|
||||||
binding = ActivityCrashHandlerBinding.inflate(getLayoutInflater());
|
|
||||||
return binding.getRoot();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -57,6 +50,12 @@ public class CrashHandlerActivity extends StudioActivity {
|
|||||||
.commit();
|
.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected View bindLayout() {
|
||||||
|
binding = ActivityCrashHandlerBinding.inflate(getLayoutInflater());
|
||||||
|
return binding.getRoot();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|||||||
@@ -189,6 +189,12 @@ public class DesignerActivity extends StudioActivity
|
|||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void finishWithError() {
|
||||||
|
final var intent = new Intent();
|
||||||
|
setResult(-123, intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
private void finishWithResult(String result) {
|
private void finishWithResult(String result) {
|
||||||
final var intent = new Intent();
|
final var intent = new Intent();
|
||||||
intent.putExtra(KEY_GENERATED_CODE, result);
|
intent.putExtra(KEY_GENERATED_CODE, result);
|
||||||
@@ -196,12 +202,6 @@ public class DesignerActivity extends StudioActivity
|
|||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void finishWithError() {
|
|
||||||
final var intent = new Intent();
|
|
||||||
setResult(-123, intent);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDragStarted(View view) {
|
public void onDragStarted(View view) {
|
||||||
mBinding.getRoot().closeDrawer(GravityCompat.START);
|
mBinding.getRoot().closeDrawer(GravityCompat.START);
|
||||||
|
|||||||
@@ -429,12 +429,6 @@ public class EditorActivity extends StudioActivity
|
|||||||
return openFile(file, null);
|
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) {
|
public CodeEditorView openFile(File file, com.itsaky.lsp.models.Range selection) {
|
||||||
if (selection == null) {
|
if (selection == null) {
|
||||||
selection = com.itsaky.lsp.models.Range.NONE;
|
selection = com.itsaky.lsp.models.Range.NONE;
|
||||||
@@ -536,6 +530,12 @@ public class EditorActivity extends StudioActivity
|
|||||||
return mFileOptionsFragment;
|
return mFileOptionsFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void hideViewOptions() {
|
||||||
|
if (mEditorBottomSheet.getState() != BottomSheetBehavior.STATE_COLLAPSED) {
|
||||||
|
mEditorBottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ActivityEditorBinding getBinding() {
|
public ActivityEditorBinding getBinding() {
|
||||||
return mBinding;
|
return mBinding;
|
||||||
}
|
}
|
||||||
@@ -790,17 +790,6 @@ public class EditorActivity extends StudioActivity
|
|||||||
return saveAll(notify, false);
|
return saveAll(notify, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private CompletableFuture<ArrayList<String>> 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) {
|
public boolean saveAll(boolean notify, boolean canProcessResources) {
|
||||||
SaveResult result = saveAllResult();
|
SaveResult result = saveAllResult();
|
||||||
|
|
||||||
@@ -819,10 +808,6 @@ public class EditorActivity extends StudioActivity
|
|||||||
return result.gradleSaved;
|
return result.gradleSaved;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompletableFuture<List<File>> getResourceDirs() {
|
|
||||||
return ProjectManager.INSTANCE.getApplicationResDirectories();
|
|
||||||
}
|
|
||||||
|
|
||||||
public SaveResult saveAllResult() {
|
public SaveResult saveAllResult() {
|
||||||
SaveResult result = new SaveResult();
|
SaveResult result = new SaveResult();
|
||||||
for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) {
|
for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) {
|
||||||
@@ -832,15 +817,6 @@ public class EditorActivity extends StudioActivity
|
|||||||
return result;
|
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) {
|
public void saveResult(int index, SaveResult result) {
|
||||||
if (index >= 0 && index < mViewModel.getOpenedFileCount()) {
|
if (index >= 0 && index < mViewModel.getOpenedFileCount()) {
|
||||||
var frag = getEditorAtIndex(index);
|
var frag = getEditorAtIndex(index);
|
||||||
@@ -879,6 +855,15 @@ public class EditorActivity extends StudioActivity
|
|||||||
ThreadUtils.runOnUiThread(() -> mViewModel.setFilesModified(finalModified));
|
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() {
|
public MaterialBanner getSyncBanner() {
|
||||||
return mBinding
|
return mBinding
|
||||||
.syncBanner
|
.syncBanner
|
||||||
@@ -925,6 +910,18 @@ public class EditorActivity extends StudioActivity
|
|||||||
setStatus(text, Gravity.CENTER);
|
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() {
|
protected void onProjectInitialized() {
|
||||||
ProjectManager.INSTANCE.notifyProjectUpdate();
|
ProjectManager.INSTANCE.notifyProjectUpdate();
|
||||||
ThreadUtils.runOnUiThread(
|
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 {
|
try {
|
||||||
runOnUiThread(
|
//noinspection ConstantConditions
|
||||||
() -> {
|
final var rootProject = ProjectManager.INSTANCE.getRootProject();
|
||||||
mBinding.bottomSheet.statusText.setGravity(gravity);
|
|
||||||
mBinding.bottomSheet.statusText.setText(text);
|
var projectName = rootProject.getName().get();
|
||||||
});
|
if (projectName.isEmpty()) {
|
||||||
} catch (Throwable th) {
|
projectName = new File(ProjectManager.INSTANCE.getProjectDirPath()).getName();
|
||||||
LOG.error("Failed to update status text", th);
|
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 CompletableFuture<List<File>> getResourceDirs() {
|
||||||
|
return ProjectManager.INSTANCE.getApplicationResDirectories();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private CompletableFuture<ArrayList<String>> getResourceDirPaths() {
|
||||||
|
return getResourceDirs()
|
||||||
|
.thenApply(
|
||||||
|
files ->
|
||||||
|
files.stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(File::getAbsolutePath)
|
||||||
|
.collect(Collectors.toCollection(ArrayList::new)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void assembleDebug(boolean installApk) {
|
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<TaskExecutionResult> installableTaskResultConsumer(@NonNull String variantName) {
|
public Consumer<TaskExecutionResult> installableTaskResultConsumer(@NonNull String variantName) {
|
||||||
return result -> {
|
return result -> {
|
||||||
if (result != null && result.isSuccessful()) {
|
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) {
|
public void install(@NonNull File apk) {
|
||||||
runOnUiThread(
|
runOnUiThread(
|
||||||
() -> {
|
() -> {
|
||||||
@@ -1079,14 +1107,14 @@ public class EditorActivity extends StudioActivity
|
|||||||
execTasks(null, "lintRelease");
|
execTasks(null, "lintRelease");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cleanAndRebuild() {
|
|
||||||
execTasks(null, "clean", "build");
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
////////////// PRIVATE APIS /////////////////////
|
////////////// PRIVATE APIS /////////////////////
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public void cleanAndRebuild() {
|
||||||
|
execTasks(null, "clean", "build");
|
||||||
|
}
|
||||||
|
|
||||||
public AlertDialog getFindInProjectDialog() {
|
public AlertDialog getFindInProjectDialog() {
|
||||||
return mFindInProjectDialog == null ? createFindInProjectDialog() : mFindInProjectDialog;
|
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) {
|
private void showCompilerModuleInstallError(Throwable error) {
|
||||||
final var stacktrace = ThrowableUtils.getFullStackTrace(error);
|
final var stacktrace = ThrowableUtils.getFullStackTrace(error);
|
||||||
final var builder = DialogUtils.newMaterialDialogBuilder(this);
|
final var builder = DialogUtils.newMaterialDialogBuilder(this);
|
||||||
@@ -1260,6 +1276,18 @@ public class EditorActivity extends StudioActivity
|
|||||||
builder.show();
|
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() {
|
private void dispatchOnPauseToEditors() {
|
||||||
CompletableFuture.runAsync(
|
CompletableFuture.runAsync(
|
||||||
() -> {
|
() -> {
|
||||||
@@ -1544,34 +1572,6 @@ public class EditorActivity extends StudioActivity
|
|||||||
startActivity(intent);
|
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() {
|
private void setupDiagnosticInfo() {
|
||||||
GradientDrawable gd = new GradientDrawable();
|
GradientDrawable gd = new GradientDrawable();
|
||||||
gd.setShape(GradientDrawable.RECTANGLE);
|
gd.setShape(GradientDrawable.RECTANGLE);
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import android.view.View;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
import com.itsaky.androidide.app.StudioActivity;
|
import com.itsaky.androidide.app.StudioActivity;
|
||||||
import com.itsaky.androidide.databinding.ActivityMainBinding;
|
import com.itsaky.androidide.databinding.ActivityMainBinding;
|
||||||
import com.itsaky.androidide.fragments.MainFragment;
|
import com.itsaky.androidide.fragments.MainFragment;
|
||||||
@@ -42,19 +41,12 @@ import java.io.File;
|
|||||||
public class MainActivity extends StudioActivity {
|
public class MainActivity extends StudioActivity {
|
||||||
private ActivityMainBinding binding;
|
private ActivityMainBinding binding;
|
||||||
|
|
||||||
public void openProject(@NonNull File root) {
|
|
||||||
ProjectManager.INSTANCE.setProjectPath(root.getAbsolutePath());
|
|
||||||
startActivity(new Intent(this, EditorActivity.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
openLastProject();
|
openLastProject();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showMoveRequired() {}
|
|
||||||
|
|
||||||
private void openLastProject() {
|
private void openLastProject() {
|
||||||
binding
|
binding
|
||||||
.getRoot()
|
.getRoot()
|
||||||
@@ -90,12 +82,6 @@ public class MainActivity extends StudioActivity {
|
|||||||
.commit();
|
.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected View bindLayout() {
|
|
||||||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
|
||||||
return binding.getRoot();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void askProjectOpenPermission(File root) {
|
private void askProjectOpenPermission(File root) {
|
||||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
|
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
|
||||||
builder.setTitle(R.string.title_confirm_open_project);
|
builder.setTitle(R.string.title_confirm_open_project);
|
||||||
@@ -106,6 +92,11 @@ public class MainActivity extends StudioActivity {
|
|||||||
builder.show();
|
builder.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void openProject(@NonNull File root) {
|
||||||
|
ProjectManager.INSTANCE.setProjectPath(root.getAbsolutePath());
|
||||||
|
startActivity(new Intent(this, EditorActivity.class));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStorageGranted() {}
|
protected void onStorageGranted() {}
|
||||||
|
|
||||||
@@ -115,9 +106,17 @@ public class MainActivity extends StudioActivity {
|
|||||||
finishAffinity();
|
finishAffinity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected View bindLayout() {
|
||||||
|
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||||
|
return binding.getRoot();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
binding = null;
|
binding = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showMoveRequired() {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,12 +34,6 @@ public class PreferencesActivity extends StudioActivity {
|
|||||||
private ActivityPreferencesBinding binding;
|
private ActivityPreferencesBinding binding;
|
||||||
private IDEPreferences mPref;
|
private IDEPreferences mPref;
|
||||||
|
|
||||||
@Override
|
|
||||||
protected View bindLayout() {
|
|
||||||
binding = ActivityPreferencesBinding.inflate(getLayoutInflater());
|
|
||||||
return binding.getRoot();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -53,6 +47,12 @@ public class PreferencesActivity extends StudioActivity {
|
|||||||
loadFragment(getPrefsFragment());
|
loadFragment(getPrefsFragment());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected View bindLayout() {
|
||||||
|
binding = ActivityPreferencesBinding.inflate(getLayoutInflater());
|
||||||
|
return binding.getRoot();
|
||||||
|
}
|
||||||
|
|
||||||
private IDEPreferences getPrefsFragment() {
|
private IDEPreferences getPrefsFragment() {
|
||||||
return mPref == null ? mPref = new IDEPreferences() : mPref;
|
return mPref == null ? mPref = new IDEPreferences() : mPref;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,14 +49,14 @@ public class SplashActivity extends StudioActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStorageAlreadyGranted() {
|
protected void onStorageGranted() {
|
||||||
super.onStorageAlreadyGranted();
|
super.onStorageGranted();
|
||||||
proceed();
|
proceed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStorageGranted() {
|
protected void onStorageAlreadyGranted() {
|
||||||
super.onStorageGranted();
|
super.onStorageAlreadyGranted();
|
||||||
proceed();
|
proceed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,21 +70,196 @@ public class TerminalActivity extends StudioActivity
|
|||||||
private static final ILogger LOG = ILogger.newInstance("TerminalActivity");
|
private static final ILogger LOG = ILogger.newInstance("TerminalActivity");
|
||||||
private static final byte[] SOURCES_LIST_CONTENT =
|
private static final byte[] SOURCES_LIST_CONTENT =
|
||||||
"deb https://androidide.com/packages/apt/termux-main/ stable main".getBytes();
|
"deb https://androidide.com/packages/apt/termux-main/ stable main".getBytes();
|
||||||
|
private static final String KEY_FONT_SIZE = "terminal_fontSize";
|
||||||
private ActivityTerminalBinding binding;
|
private ActivityTerminalBinding binding;
|
||||||
private TerminalView terminal;
|
private TerminalView terminal;
|
||||||
private TerminalSession session;
|
private TerminalSession session;
|
||||||
private boolean isVisible = false;
|
private boolean isVisible = false;
|
||||||
private KeyListener listener;
|
private KeyListener listener;
|
||||||
|
|
||||||
private int MIN_FONT_SIZE;
|
private int MIN_FONT_SIZE;
|
||||||
private int MAX_FONT_SIZE;
|
private int MAX_FONT_SIZE;
|
||||||
private int DEFAULT_FONT_SIZE;
|
private int DEFAULT_FONT_SIZE;
|
||||||
private static final String KEY_FONT_SIZE = "terminal_fontSize";
|
|
||||||
|
public boolean isVisible() {
|
||||||
|
return isVisible;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected View bindLayout() {
|
public boolean onKeyUp(int keyCode, KeyEvent e) {
|
||||||
binding = ActivityTerminalBinding.inflate(getLayoutInflater());
|
return false;
|
||||||
return binding.getRoot();
|
}
|
||||||
|
|
||||||
|
@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
|
@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) {
|
private void showInstallationError(Throwable throwable) {
|
||||||
|
|
||||||
binding.virtualKeyTable.setVisibility(View.GONE);
|
binding.virtualKeyTable.setVisibility(View.GONE);
|
||||||
@@ -198,41 +379,6 @@ public class TerminalActivity extends StudioActivity
|
|||||||
return listener == null ? listener = new KeyListener(terminal) : listener;
|
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) {
|
private TerminalSession createSession(final String workingDirectory) {
|
||||||
final Map<String, String> environment = getEnvironment();
|
final Map<String, String> environment = getEnvironment();
|
||||||
final String[] env = new String[environment.size()];
|
final String[] env = new String[environment.size()];
|
||||||
@@ -322,6 +468,60 @@ public class TerminalActivity extends StudioActivity
|
|||||||
return sizes;
|
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 static final class KeyListener implements VirtualKeysView.IVirtualKeysView {
|
||||||
|
|
||||||
private final TerminalView terminal;
|
private final TerminalView terminal;
|
||||||
@@ -404,205 +604,4 @@ public class TerminalActivity extends StudioActivity
|
|||||||
return false;
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ abstract class BaseEditorAction : ActionItem {
|
|||||||
override var visible: Boolean = true
|
override var visible: Boolean = true
|
||||||
override var enabled: Boolean = true
|
override var enabled: Boolean = true
|
||||||
override var icon: Drawable? = null
|
override var icon: Drawable? = null
|
||||||
override var requiresUIThread: Boolean =
|
override var requiresUIThread: Boolean = true // all editor actions must be executed on UI thread
|
||||||
true // all editor actions must be executed on UI thread
|
|
||||||
override var location: ActionItem.Location = ActionItem.Location.EDITOR_TEXT_ACTIONS
|
override var location: ActionItem.Location = ActionItem.Location.EDITOR_TEXT_ACTIONS
|
||||||
|
|
||||||
override fun prepare(data: ActionData) {
|
override fun prepare(data: ActionData) {
|
||||||
@@ -56,7 +55,8 @@ abstract class BaseEditorAction : ActionItem {
|
|||||||
|
|
||||||
fun tintDrawable(context: Context, drawable: Drawable): Drawable {
|
fun tintDrawable(context: Context, drawable: Drawable): Drawable {
|
||||||
drawable.setTint(
|
drawable.setTint(
|
||||||
ContextCompat.getColor(context, com.itsaky.androidide.R.color.primaryIconColor))
|
ContextCompat.getColor(context, com.itsaky.androidide.R.color.primaryIconColor)
|
||||||
|
)
|
||||||
return drawable
|
return drawable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ class CancelBuildAction() : EditorActivityAction() {
|
|||||||
log.warn(
|
log.warn(
|
||||||
"Unable to enqueue cancellation request",
|
"Unable to enqueue cancellation request",
|
||||||
result.failureReason,
|
result.failureReason,
|
||||||
result.failureReason!!.message)
|
result.failureReason!!.message
|
||||||
|
)
|
||||||
return@whenComplete
|
return@whenComplete
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import com.itsaky.androidide.R
|
|||||||
import com.itsaky.androidide.R.string
|
import com.itsaky.androidide.R.string
|
||||||
import com.itsaky.androidide.actions.ActionData
|
import com.itsaky.androidide.actions.ActionData
|
||||||
import com.itsaky.androidide.actions.BaseBuildAction
|
import com.itsaky.androidide.actions.BaseBuildAction
|
||||||
import com.itsaky.androidide.actions.EditorActivityAction
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers a project sync request.
|
* Triggers a project sync request.
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
package com.itsaky.androidide.actions.editor
|
package com.itsaky.androidide.actions.editor
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.itsaky.androidide.R
|
|
||||||
import com.itsaky.androidide.actions.ActionData
|
import com.itsaky.androidide.actions.ActionData
|
||||||
import com.itsaky.androidide.actions.BaseEditorAction
|
import com.itsaky.androidide.actions.BaseEditorAction
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
package com.itsaky.androidide.actions.editor
|
package com.itsaky.androidide.actions.editor
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.itsaky.androidide.R
|
|
||||||
import com.itsaky.androidide.actions.ActionData
|
import com.itsaky.androidide.actions.ActionData
|
||||||
import com.itsaky.androidide.actions.BaseEditorAction
|
import com.itsaky.androidide.actions.BaseEditorAction
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
package com.itsaky.androidide.actions.editor
|
package com.itsaky.androidide.actions.editor
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.itsaky.androidide.R
|
|
||||||
import com.itsaky.androidide.actions.ActionData
|
import com.itsaky.androidide.actions.ActionData
|
||||||
import com.itsaky.androidide.actions.BaseEditorAction
|
import com.itsaky.androidide.actions.BaseEditorAction
|
||||||
|
|
||||||
|
|||||||
@@ -49,8 +49,7 @@ class PreviewLayoutAction() : EditorRelatedAction() {
|
|||||||
visible =
|
visible =
|
||||||
isXml &&
|
isXml &&
|
||||||
file.parentFile != null &&
|
file.parentFile != null &&
|
||||||
Regex(FileOptionsHandler.LAYOUT_RES_PATH_REGEX)
|
Regex(FileOptionsHandler.LAYOUT_RES_PATH_REGEX).matches(file.parentFile!!.absolutePath)
|
||||||
.matches(file.parentFile!!.absolutePath)
|
|
||||||
enabled = visible
|
enabled = visible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,20 @@ public class AttrListAdapter extends IconTextAdapter<Attr> {
|
|||||||
this.attrs = attrs;
|
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
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Attr getItemAt(int index) {
|
public Attr getItemAt(int index) {
|
||||||
@@ -62,18 +76,4 @@ public class AttrListAdapter extends IconTextAdapter<Attr> {
|
|||||||
public String getItemText(int index) {
|
public String getItemText(int index) {
|
||||||
return getItemAt(index).name;
|
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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,29 @@ public class EditorBottomSheetTabAdapter extends FragmentStateAdapter {
|
|||||||
return getFragmentById(getItemId(index));
|
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<Fragment> getFragments() {
|
||||||
|
try {
|
||||||
|
final var field = FragmentStateAdapter.class.getDeclaredField("mFragments");
|
||||||
|
field.setAccessible(true);
|
||||||
|
return (LongSparseArray<Fragment>) field.get(this);
|
||||||
|
} catch (Throwable th) {
|
||||||
|
LOG.error("Unable to reflect fragment list from adapter.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Fragment createFragment(int position) {
|
public Fragment createFragment(int position) {
|
||||||
@@ -98,21 +121,6 @@ public class EditorBottomSheetTabAdapter extends FragmentStateAdapter {
|
|||||||
return findFragmentByClass(SimpleOutputFragment.class);
|
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
|
@Nullable
|
||||||
private <T extends Fragment> T findFragmentByClass(Class<T> clazz) {
|
private <T extends Fragment> T findFragmentByClass(Class<T> clazz) {
|
||||||
final var name = clazz.getName();
|
final var name = clazz.getName();
|
||||||
@@ -126,26 +134,18 @@ public class EditorBottomSheetTabAdapter extends FragmentStateAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public Fragment getFragmentById(long itemId) {
|
public AppLogFragment getLogFragment() {
|
||||||
final var fragments = getFragments();
|
return findFragmentByClass(AppLogFragment.class);
|
||||||
if (fragments != null) {
|
|
||||||
return fragments.get(itemId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private LongSparseArray<Fragment> getFragments() {
|
public DiagnosticsListFragment getDiagnosticsFragment() {
|
||||||
try {
|
return findFragmentByClass(DiagnosticsListFragment.class);
|
||||||
final var field = FragmentStateAdapter.class.getDeclaredField("mFragments");
|
|
||||||
field.setAccessible(true);
|
|
||||||
return (LongSparseArray<Fragment>) field.get(this);
|
|
||||||
} catch (Throwable th) {
|
|
||||||
LOG.error("Unable to reflect fragment list from adapter.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
@Nullable
|
||||||
|
public SearchResultFragment getSearchResultFragment() {
|
||||||
|
return findFragmentByClass(SearchResultFragment.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends Fragment> int findIndexOfFragmentByClass(@NonNull Class<T> tClass) {
|
public <T extends Fragment> int findIndexOfFragmentByClass(@NonNull Class<T> tClass) {
|
||||||
|
|||||||
@@ -98,14 +98,6 @@ public abstract class IconTextAdapter<E> extends RecyclerView.Adapter<IconTextAd
|
|||||||
@NonNull
|
@NonNull
|
||||||
public abstract String getItemText(int index);
|
public abstract String getItemText(int index);
|
||||||
|
|
||||||
public interface OnBindListener<T> {
|
|
||||||
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 static class VH extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
public final LayoutSimpleIconTextBinding binding;
|
public final LayoutSimpleIconTextBinding binding;
|
||||||
@@ -115,4 +107,12 @@ public abstract class IconTextAdapter<E> extends RecyclerView.Adapter<IconTextAd
|
|||||||
this.binding = binding;
|
this.binding = binding;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface OnBindListener<T> {
|
||||||
|
default boolean onBind(T item, VH holder, int position) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
default void postBind(T item, VH holder, int position) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,11 @@ class LogLinesAdapter : RecyclerView.Adapter<VH>() {
|
|||||||
else -> -0xa0a0b
|
else -> -0xa0a0b
|
||||||
}
|
}
|
||||||
spannable.setSpan(
|
spannable.setSpan(
|
||||||
ForegroundColorSpan(color), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
ForegroundColorSpan(color),
|
||||||
|
0,
|
||||||
|
spannable.length,
|
||||||
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||||
|
)
|
||||||
return LogWrapper(line, spannable).apply { visible = true }
|
return LogWrapper(line, spannable).apply { visible = true }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ public class SimpleIconTextAdapter extends IconTextAdapter<IconTextListItem> {
|
|||||||
this.items = items;
|
this.items = items;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return items.size();
|
||||||
|
} @NonNull
|
||||||
@Override
|
@Override
|
||||||
public IconTextListItem getItemAt(int index) {
|
public IconTextListItem getItemAt(int index) {
|
||||||
return items.get(index);
|
return items.get(index);
|
||||||
@@ -53,8 +56,5 @@ public class SimpleIconTextAdapter extends IconTextAdapter<IconTextListItem> {
|
|||||||
return getItemAt(index).getText();
|
return getItemAt(index).getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
return items.size();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,18 @@ import java.util.List;
|
|||||||
|
|
||||||
public class SymbolInputAdapter extends RecyclerView.Adapter<SymbolInputAdapter.VH> {
|
public class SymbolInputAdapter extends RecyclerView.Adapter<SymbolInputAdapter.VH> {
|
||||||
|
|
||||||
|
private static final List<Character> pairs;
|
||||||
|
|
||||||
|
static {
|
||||||
|
pairs = new ArrayList<>();
|
||||||
|
pairs.add('}');
|
||||||
|
pairs.add(')');
|
||||||
|
pairs.add(']');
|
||||||
|
pairs.add('"');
|
||||||
|
pairs.add('\'');
|
||||||
|
pairs.add('>');
|
||||||
|
}
|
||||||
|
|
||||||
private final IDEEditor editor;
|
private final IDEEditor editor;
|
||||||
private Symbol[] symbols;
|
private Symbol[] symbols;
|
||||||
|
|
||||||
@@ -73,15 +85,6 @@ public class SymbolInputAdapter extends RecyclerView.Adapter<SymbolInputAdapter.
|
|||||||
return symbols == null ? 0 : symbols.length;
|
return symbols == null ? 0 : symbols.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class VH extends RecyclerView.ViewHolder {
|
|
||||||
LayoutSymbolItemBinding binding;
|
|
||||||
|
|
||||||
public VH(LayoutSymbolItemBinding binding) {
|
|
||||||
super(binding.getRoot());
|
|
||||||
this.binding = binding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void insertSymbol(String text, int selectionOffset) {
|
void insertSymbol(String text, int selectionOffset) {
|
||||||
if (selectionOffset < 0 || selectionOffset > text.length()) {
|
if (selectionOffset < 0 || selectionOffset > text.length()) {
|
||||||
return;
|
return;
|
||||||
@@ -108,15 +111,12 @@ public class SymbolInputAdapter extends RecyclerView.Adapter<SymbolInputAdapter.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final List<Character> pairs;
|
public static class VH extends RecyclerView.ViewHolder {
|
||||||
|
LayoutSymbolItemBinding binding;
|
||||||
|
|
||||||
static {
|
public VH(LayoutSymbolItemBinding binding) {
|
||||||
pairs = new ArrayList<>();
|
super(binding.getRoot());
|
||||||
pairs.add('}');
|
this.binding = binding;
|
||||||
pairs.add(')');
|
}
|
||||||
pairs.add(']');
|
|
||||||
pairs.add('"');
|
|
||||||
pairs.add('\'');
|
|
||||||
pairs.add('>');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package com.itsaky.androidide.adapters;
|
package com.itsaky.androidide.adapters;
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@@ -13,23 +11,17 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
import com.blankj.utilcode.util.ConvertUtils;
|
import com.blankj.utilcode.util.ConvertUtils;
|
||||||
import com.google.android.material.imageview.ShapeableImageView;
|
import com.google.android.material.imageview.ShapeableImageView;
|
||||||
import com.google.android.material.shape.CornerFamily;
|
import com.google.android.material.shape.CornerFamily;
|
||||||
|
|
||||||
import com.itsaky.androidide.databinding.WizardTemplateItemBinding;
|
import com.itsaky.androidide.databinding.WizardTemplateItemBinding;
|
||||||
import com.itsaky.androidide.models.ProjectTemplate;
|
import com.itsaky.androidide.models.ProjectTemplate;
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class WizardTemplateAdapter extends RecyclerView.Adapter<WizardTemplateAdapter.ViewHolder> {
|
public class WizardTemplateAdapter extends RecyclerView.Adapter<WizardTemplateAdapter.ViewHolder> {
|
||||||
|
|
||||||
public interface OnItemClickListener {
|
|
||||||
void onItemClick(ProjectTemplate item, int position);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final List<ProjectTemplate> mItems = new ArrayList<>();
|
private final List<ProjectTemplate> mItems = new ArrayList<>();
|
||||||
private OnItemClickListener mListener;
|
private OnItemClickListener mListener;
|
||||||
|
|
||||||
public WizardTemplateAdapter() {}
|
public WizardTemplateAdapter() {}
|
||||||
|
|
||||||
public void setOnItemClickListener(OnItemClickListener listener) {
|
public void setOnItemClickListener(OnItemClickListener listener) {
|
||||||
@@ -117,4 +109,8 @@ public class WizardTemplateAdapter extends RecyclerView.Adapter<WizardTemplateAd
|
|||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface OnItemClickListener {
|
||||||
|
void onItemClick(ProjectTemplate item, int position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,10 +88,6 @@ public class XMLAttributeListAdapter extends RecyclerView.Adapter<XMLAttributeLi
|
|||||||
return attributes.size();
|
return attributes.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface OnClickListener {
|
|
||||||
void onClick(LayoutAttrEditorSheetItemBinding binding, XMLAttribute attribute);
|
|
||||||
}
|
|
||||||
|
|
||||||
static class VH extends RecyclerView.ViewHolder {
|
static class VH extends RecyclerView.ViewHolder {
|
||||||
LayoutAttrEditorSheetItemBinding binding;
|
LayoutAttrEditorSheetItemBinding binding;
|
||||||
|
|
||||||
@@ -100,4 +96,8 @@ public class XMLAttributeListAdapter extends RecyclerView.Adapter<XMLAttributeLi
|
|||||||
this.binding = binding;
|
this.binding = binding;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface OnClickListener {
|
||||||
|
void onClick(LayoutAttrEditorSheetItemBinding binding, XMLAttribute attribute);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,12 +34,48 @@ import com.itsaky.androidide.R;
|
|||||||
import com.itsaky.androidide.utils.ILogger;
|
import com.itsaky.androidide.utils.ILogger;
|
||||||
|
|
||||||
public abstract class StudioActivity extends AppCompatActivity {
|
public abstract class StudioActivity extends AppCompatActivity {
|
||||||
private boolean toRequestStorage = true;
|
|
||||||
|
|
||||||
public static final String TAG = "StudioActivity";
|
public static final String TAG = "StudioActivity";
|
||||||
public static final int REQCODE_STORAGE = 1009;
|
public static final int REQCODE_STORAGE = 1009;
|
||||||
|
|
||||||
protected static ILogger LOG = ILogger.newInstance("StudioActivity");
|
protected static ILogger LOG = ILogger.newInstance("StudioActivity");
|
||||||
|
private boolean toRequestStorage = true;
|
||||||
|
|
||||||
|
public void loadFrament(Fragment frag, View view) {
|
||||||
|
loadFragment(frag, view.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadFragment(Fragment fragment, int id) {
|
||||||
|
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
|
||||||
|
transaction.replace(id, fragment);
|
||||||
|
transaction.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StudioApp getApp() {
|
||||||
|
return (StudioApp) getApplication();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColorPrimary() {
|
||||||
|
return getTypedValueForAttr(R.attr.colorPrimary).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypedValue getTypedValueForAttr(@AttrRes int attrRes) {
|
||||||
|
TypedValue typedValue = new TypedValue();
|
||||||
|
getTheme().resolveAttribute(attrRes, typedValue, true);
|
||||||
|
return typedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColorPrimaryDark() {
|
||||||
|
return getTypedValueForAttr(R.attr.colorPrimaryDark).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColorAccent() {
|
||||||
|
return getTypedValueForAttr(R.attr.colorAccent).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int dpToPx(int dp) {
|
||||||
|
return (int)
|
||||||
|
TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -65,10 +101,6 @@ public abstract class StudioActivity extends AppCompatActivity {
|
|||||||
== PackageManager.PERMISSION_GRANTED;
|
== PackageManager.PERMISSION_GRANTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void doNotRequestStorage() {
|
|
||||||
this.toRequestStorage = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void requestStorage() {
|
protected void requestStorage() {
|
||||||
if (isStoragePermissionGranted()) {
|
if (isStoragePermissionGranted()) {
|
||||||
onStorageGranted();
|
onStorageGranted();
|
||||||
@@ -82,43 +114,7 @@ public abstract class StudioActivity extends AppCompatActivity {
|
|||||||
REQCODE_STORAGE);
|
REQCODE_STORAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadFrament(Fragment frag, View view) {
|
protected void onStorageGranted() {}
|
||||||
loadFragment(frag, view.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadFragment(Fragment fragment, int id) {
|
|
||||||
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
|
|
||||||
transaction.replace(id, fragment);
|
|
||||||
transaction.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
public StudioApp getApp() {
|
|
||||||
return (StudioApp) getApplication();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TypedValue getTypedValueForAttr(@AttrRes int attrRes) {
|
|
||||||
TypedValue typedValue = new TypedValue();
|
|
||||||
getTheme().resolveAttribute(attrRes, typedValue, true);
|
|
||||||
return typedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getColorPrimary() {
|
|
||||||
return getTypedValueForAttr(R.attr.colorPrimary).data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getColorPrimaryDark() {
|
|
||||||
return getTypedValueForAttr(R.attr.colorPrimaryDark).data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getColorAccent() {
|
|
||||||
return getTypedValueForAttr(R.attr.colorAccent).data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int dpToPx(int dp) {
|
|
||||||
return (int)
|
|
||||||
TypedValue.applyDimension(
|
|
||||||
TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(
|
public void onRequestPermissionsResult(
|
||||||
@@ -134,11 +130,13 @@ public abstract class StudioActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
protected void onStorageAlreadyGranted() {}
|
protected void onStorageAlreadyGranted() {}
|
||||||
|
|
||||||
protected void onStorageGranted() {}
|
|
||||||
|
|
||||||
protected void onStorageDenied() {}
|
protected void onStorageDenied() {}
|
||||||
|
|
||||||
protected void onSetContentView() {}
|
protected void onSetContentView() {}
|
||||||
|
|
||||||
protected abstract View bindLayout();
|
protected abstract View bindLayout();
|
||||||
|
|
||||||
|
protected void doNotRequestStorage() {
|
||||||
|
this.toRequestStorage = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,110 +94,16 @@ public class IDEDocumentsProvider extends DocumentsProvider {
|
|||||||
return new File(Environment.DEFAULT_HOME);
|
return new File(Environment.DEFAULT_HOME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* <p>The 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
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChildDocument(String parentDocumentId, String documentId) {
|
||||||
|
return documentId.startsWith(parentDocumentId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String createDocument(String parentDocumentId, String mimeType, String displayName)
|
public String createDocument(String parentDocumentId, String mimeType, String displayName)
|
||||||
throws FileNotFoundException {
|
throws FileNotFoundException {
|
||||||
@@ -231,9 +137,62 @@ public class IDEDocumentsProvider extends DocumentsProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDocumentType(String documentId) throws FileNotFoundException {
|
public Cursor queryRoots(String[] projection) {
|
||||||
File file = getFileForDocId(documentId);
|
final MatrixCursor result =
|
||||||
return getMimeType(file);
|
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.
|
||||||
|
*
|
||||||
|
* <p>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
|
@Override
|
||||||
@@ -277,8 +236,27 @@ public class IDEDocumentsProvider extends DocumentsProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isChildDocument(String parentDocumentId, String documentId) {
|
public String getDocumentType(String documentId) throws FileNotFoundException {
|
||||||
return documentId.startsWith(parentDocumentId);
|
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_FLAGS, flags);
|
||||||
row.add(Document.COLUMN_ICON, R.mipmap.ic_launcher);
|
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";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,6 @@
|
|||||||
|
|
||||||
package com.itsaky.androidide.fragments;
|
package com.itsaky.androidide.fragments;
|
||||||
|
|
||||||
import abhishekti7.unicorn.filepicker.UnicornFilePicker;
|
|
||||||
import abhishekti7.unicorn.filepicker.ui.FilePickerActivity;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
@@ -41,31 +38,12 @@ import com.itsaky.toaster.Toaster;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import abhishekti7.unicorn.filepicker.UnicornFilePicker;
|
||||||
|
import abhishekti7.unicorn.filepicker.ui.FilePickerActivity;
|
||||||
|
|
||||||
public class BaseFragment extends Fragment {
|
public class BaseFragment extends Fragment {
|
||||||
|
|
||||||
private FileChoserCallback dirCallback;
|
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<Intent> startForResult =
|
ActivityResultLauncher<Intent> startForResult =
|
||||||
registerForActivityResult(
|
registerForActivityResult(
|
||||||
new ActivityResultContracts.StartActivityForResult(),
|
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 {
|
interface FileChoserCallback {
|
||||||
void picketDictionary(File file);
|
void picketDictionary(File file);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,14 @@ public class DiagnosticsListFragment extends Fragment {
|
|||||||
unsavedAdapter = null;
|
unsavedAdapter = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
unsavedAdapter = null;
|
||||||
|
list = null;
|
||||||
|
emptyView = null;
|
||||||
|
}
|
||||||
|
|
||||||
public void setAdapter(@NonNull DiagnosticsAdapter adapter) {
|
public void setAdapter(@NonNull DiagnosticsAdapter adapter) {
|
||||||
if (list != null) {
|
if (list != null) {
|
||||||
list.setAdapter(adapter);
|
list.setAdapter(adapter);
|
||||||
@@ -84,12 +92,4 @@ public class DiagnosticsListFragment extends Fragment {
|
|||||||
list.setVisibility(error ? View.GONE : View.VISIBLE);
|
list.setVisibility(error ? View.GONE : View.VISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
unsavedAdapter = null;
|
|
||||||
list = null;
|
|
||||||
emptyView = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,6 +95,14 @@ public class FileTreeFragment extends BottomSheetDialogFragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
binding = null;
|
||||||
|
mFileTreeView = null;
|
||||||
|
mFileActionListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(
|
public View onCreateView(
|
||||||
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
@@ -201,12 +209,6 @@ public class FileTreeFragment extends BottomSheetDialogFragment
|
|||||||
__ -> finalWhenDone.run());
|
__ -> 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) {
|
private void getNodeFromFiles(File[] files, TreeNode parent) {
|
||||||
Arrays.sort(files, new FileTreeCallable.SortFileName());
|
Arrays.sort(files, new FileTreeCallable.SortFileName());
|
||||||
Arrays.sort(files, new FileTreeCallable.SortFolder());
|
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
|
@Override
|
||||||
public boolean onLongClick(TreeNode node, Object value) {
|
public boolean onLongClick(TreeNode node, Object value) {
|
||||||
if (mFileActionListener != null) {
|
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 {
|
public interface FileActionListener {
|
||||||
CodeEditorView openFile(File file);
|
CodeEditorView openFile(File file);
|
||||||
|
|
||||||
|
|||||||
@@ -1,34 +1,22 @@
|
|||||||
package com.itsaky.androidide.fragments;
|
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.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
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.EditorActivity;
|
||||||
import com.itsaky.androidide.PreferencesActivity;
|
import com.itsaky.androidide.PreferencesActivity;
|
||||||
import com.itsaky.androidide.R;
|
import com.itsaky.androidide.R;
|
||||||
import com.itsaky.androidide.TerminalActivity;
|
import com.itsaky.androidide.TerminalActivity;
|
||||||
import com.itsaky.androidide.app.StudioApp;
|
|
||||||
import com.itsaky.androidide.databinding.FragmentMainBinding;
|
import com.itsaky.androidide.databinding.FragmentMainBinding;
|
||||||
import com.itsaky.androidide.projects.ProjectManager;
|
import com.itsaky.androidide.projects.ProjectManager;
|
||||||
import com.itsaky.androidide.utils.DialogUtils;
|
|
||||||
import com.itsaky.toaster.Toaster;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class MainFragment extends BaseFragment
|
public class MainFragment extends BaseFragment
|
||||||
implements WizardFragment.OnProjectCreatedListener, View.OnClickListener {
|
implements WizardFragment.OnProjectCreatedListener, View.OnClickListener {
|
||||||
@@ -97,13 +85,13 @@ public class MainFragment extends BaseFragment
|
|||||||
.commit();
|
.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void gotoSettings() {
|
|
||||||
startActivity(new Intent(requireActivity(), PreferencesActivity.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void openProject(@NonNull File root) {
|
public void openProject(@NonNull File root) {
|
||||||
ProjectManager.INSTANCE.setProjectPath(root.getAbsolutePath());
|
ProjectManager.INSTANCE.setProjectPath(root.getAbsolutePath());
|
||||||
startActivity(new Intent(requireActivity(), EditorActivity.class));
|
startActivity(new Intent(requireActivity(), EditorActivity.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void gotoSettings() {
|
||||||
|
startActivity(new Intent(requireActivity(), PreferencesActivity.class));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,12 @@ public abstract class NonEditableEditorFragment extends Fragment {
|
|||||||
editor.setColorScheme(new SchemeAndroidIDE());
|
editor.setColorScheme(new SchemeAndroidIDE());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
binding = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public IDEEditor getEditor() {
|
public IDEEditor getEditor() {
|
||||||
if (binding == null) {
|
if (binding == null) {
|
||||||
@@ -71,10 +77,4 @@ public abstract class NonEditableEditorFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
return binding.editor;
|
return binding.editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
binding = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,14 @@ public class SearchResultFragment extends Fragment {
|
|||||||
unsavedAdapter = null;
|
unsavedAdapter = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
unsavedAdapter = null;
|
||||||
|
list = null;
|
||||||
|
emptyView = null;
|
||||||
|
}
|
||||||
|
|
||||||
public void setAdapter(@NonNull final SearchListAdapter adapter) {
|
public void setAdapter(@NonNull final SearchListAdapter adapter) {
|
||||||
if (list == null) {
|
if (list == null) {
|
||||||
unsavedAdapter = adapter;
|
unsavedAdapter = adapter;
|
||||||
@@ -82,12 +90,4 @@ public class SearchResultFragment extends Fragment {
|
|||||||
list.setVisibility(error ? View.GONE : View.VISIBLE);
|
list.setVisibility(error ? View.GONE : View.VISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
unsavedAdapter = null;
|
|
||||||
list = null;
|
|
||||||
emptyView = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import androidx.transition.TransitionManager;
|
|||||||
|
|
||||||
import com.google.android.material.transition.MaterialFadeThrough;
|
import com.google.android.material.transition.MaterialFadeThrough;
|
||||||
import com.google.android.material.transition.MaterialSharedAxis;
|
import com.google.android.material.transition.MaterialSharedAxis;
|
||||||
|
|
||||||
import com.itsaky.androidide.R;
|
import com.itsaky.androidide.R;
|
||||||
import com.itsaky.androidide.adapters.WizardTemplateAdapter;
|
import com.itsaky.androidide.adapters.WizardTemplateAdapter;
|
||||||
import com.itsaky.androidide.app.StudioApp;
|
import com.itsaky.androidide.app.StudioApp;
|
||||||
@@ -68,9 +67,6 @@ public class WizardFragment extends BaseFragment {
|
|||||||
private OnProjectCreatedListener mListener;
|
private OnProjectCreatedListener mListener;
|
||||||
|
|
||||||
private boolean mLast;
|
private boolean mLast;
|
||||||
private int minSdkIndex;
|
|
||||||
private int targetSdkIndex;
|
|
||||||
|
|
||||||
private final OnBackPressedCallback onBackPressedCallback =
|
private final OnBackPressedCallback onBackPressedCallback =
|
||||||
new OnBackPressedCallback(true) {
|
new OnBackPressedCallback(true) {
|
||||||
@Override
|
@Override
|
||||||
@@ -78,6 +74,8 @@ public class WizardFragment extends BaseFragment {
|
|||||||
onNavigateBack();
|
onNavigateBack();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
private int minSdkIndex;
|
||||||
|
private int targetSdkIndex;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
@@ -171,12 +169,6 @@ public class WizardFragment extends BaseFragment {
|
|||||||
initDetailsView();
|
initDetailsView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
onBackPressedCallback.setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
@@ -190,6 +182,28 @@ public class WizardFragment extends BaseFragment {
|
|||||||
mProgressSheet = null;
|
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) {
|
private String getSelectedItem(int pos, AutoCompleteTextView view) {
|
||||||
return view.getAdapter().getItem(pos).toString();
|
return view.getAdapter().getItem(pos).toString();
|
||||||
}
|
}
|
||||||
@@ -516,18 +530,6 @@ public class WizardFragment extends BaseFragment {
|
|||||||
setSaveLocation();
|
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() {
|
private void createProgressSheet() {
|
||||||
mProgressSheet = new ProgressSheet();
|
mProgressSheet = new ProgressSheet();
|
||||||
mProgressSheet.setShowShadow(false);
|
mProgressSheet.setShowShadow(false);
|
||||||
@@ -554,8 +556,4 @@ public class WizardFragment extends BaseFragment {
|
|||||||
public interface OnProjectCreatedListener {
|
public interface OnProjectCreatedListener {
|
||||||
void openProject(File project);
|
void openProject(File project);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOnProjectCreatedListener(OnProjectCreatedListener listener) {
|
|
||||||
mListener = listener;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,15 +41,6 @@ public abstract class AbstractReferenceEditor extends BaseValueEditorFragment {
|
|||||||
private static final ILogger LOG = ILogger.newInstance("ReferenceEditor");
|
private static final ILogger LOG = ILogger.newInstance("ReferenceEditor");
|
||||||
public TextWatcherAdapter resInputWatcher;
|
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) {
|
protected void setupReferenceInput(MaterialAutoCompleteTextView referenceInput) {
|
||||||
Objects.requireNonNull(referenceInput);
|
Objects.requireNonNull(referenceInput);
|
||||||
|
|
||||||
@@ -78,6 +69,15 @@ public abstract class AbstractReferenceEditor extends BaseValueEditorFragment {
|
|||||||
.whenComplete((list, error) -> handleAutoCompleteResult(list, error, referenceInput));
|
.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(
|
protected void handleAutoCompleteResult(
|
||||||
List<String> list, Throwable error, AutoCompleteTextView referenceInput) {
|
List<String> list, Throwable error, AutoCompleteTextView referenceInput) {
|
||||||
if (list == null || list.isEmpty()) {
|
if (list == null || list.isEmpty()) {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
package com.itsaky.androidide.fragments.attr;
|
package com.itsaky.androidide.fragments.attr;
|
||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -77,28 +76,6 @@ public class ColorEditor extends AbstractReferenceEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<String> computeReferenceItems() {
|
|
||||||
final var list = new ArrayList<String>();
|
|
||||||
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() {
|
private Dialog getColorPickerDialog() {
|
||||||
|
|
||||||
if (colorPickerDialog == null) {
|
if (colorPickerDialog == null) {
|
||||||
@@ -122,4 +99,26 @@ public class ColorEditor extends AbstractReferenceEditor {
|
|||||||
|
|
||||||
return colorPickerDialog;
|
return colorPickerDialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<String> computeReferenceItems() {
|
||||||
|
final var list = new ArrayList<String>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,13 @@ public class FlagEditor extends FixedValueEditor {
|
|||||||
// Does noting...
|
// 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) {
|
protected void onCheckChangedMultiple(CompoundButton compoundButton, boolean b) {
|
||||||
final var items = new ArrayList<String>();
|
final var items = new ArrayList<String>();
|
||||||
for (var id : this.chipGroup.getCheckedChipIds()) {
|
for (var id : this.chipGroup.getCheckedChipIds()) {
|
||||||
@@ -78,11 +85,4 @@ public class FlagEditor extends FixedValueEditor {
|
|||||||
|
|
||||||
notifyValueChanged(TextUtils.join("|", items));
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -31,6 +31,11 @@ import com.itsaky.androidide.managers.PreferenceManager;
|
|||||||
|
|
||||||
public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
|
public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreatePreferences(Bundle p1, String p2) {
|
||||||
|
setPreferencesFromResource(com.itsaky.androidide.R.xml.ide_prefs, p2);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(
|
public View onCreateView(
|
||||||
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
@@ -43,11 +48,6 @@ public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreatePreferences(Bundle p1, String p2) {
|
|
||||||
setPreferencesFromResource(com.itsaky.androidide.R.xml.ide_prefs, p2);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected PreferenceManager getPrefManager() {
|
protected PreferenceManager getPrefManager() {
|
||||||
return StudioApp.getInstance().getPrefManager();
|
return StudioApp.getInstance().getPrefManager();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,15 +120,6 @@ public class BuildPreferences extends BasePreferenceFragment
|
|||||||
return true;
|
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() {
|
private void changeGradleDist() {
|
||||||
final var builder = DialogUtils.newMaterialDialogBuilder(getContext());
|
final var builder = DialogUtils.newMaterialDialogBuilder(getContext());
|
||||||
final var binding = LayoutDialogTextInputBinding.inflate(getLayoutInflater());
|
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 = new ProgressSheet().setMessage(getString(R.string.please_wait))
|
||||||
: progressSheet;
|
: 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,20 +123,6 @@ public class IDEPreferences extends BasePreferenceFragment
|
|||||||
: mBuildPreferences;
|
: 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
|
@Override
|
||||||
public boolean onPreferenceClick(@NonNull Preference preference) {
|
public boolean onPreferenceClick(@NonNull Preference preference) {
|
||||||
final String key = preference.getKey();
|
final String key = preference.getKey();
|
||||||
@@ -156,4 +142,18 @@ public class IDEPreferences extends BasePreferenceFragment
|
|||||||
}
|
}
|
||||||
return true;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,19 +102,6 @@ public class AttrEditorSheet extends BottomSheetDialogFragment
|
|||||||
setupViewData();
|
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() {
|
private void setupViewData() {
|
||||||
binding.actionsList.setAdapter(new SimpleIconTextAdapter(VIEW_ACTIONS).setOnBindListener(this));
|
binding.actionsList.setAdapter(new SimpleIconTextAdapter(VIEW_ACTIONS).setOnBindListener(this));
|
||||||
|
|
||||||
@@ -132,10 +119,6 @@ public class AttrEditorSheet extends BottomSheetDialogFragment
|
|||||||
this::onAttrClick));
|
this::onAttrClick));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectedView(IView view) {
|
|
||||||
this.selectedView = view;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onAttrClick(
|
private void onAttrClick(
|
||||||
LayoutAttrEditorSheetItemBinding binding, @NonNull XMLAttribute attribute) {
|
LayoutAttrEditorSheetItemBinding binding, @NonNull XMLAttribute attribute) {
|
||||||
|
|
||||||
@@ -158,6 +141,42 @@ public class AttrEditorSheet extends BottomSheetDialogFragment
|
|||||||
getValueEditorSheet(attribute).show(getChildFragmentManager(), "attr_value_editor_sheet");
|
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
|
@Override
|
||||||
public void postBind(
|
public void postBind(
|
||||||
IconTextListItem item, @NonNull SimpleIconTextAdapter.VH holder, int position) {
|
IconTextListItem item, @NonNull SimpleIconTextAdapter.VH holder, int position) {
|
||||||
@@ -276,11 +295,6 @@ public class AttrEditorSheet extends BottomSheetDialogFragment
|
|||||||
return new ArrayList<>(attributes);
|
return new ArrayList<>(attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void accept(Attr attr) {
|
|
||||||
addNewAttribute(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private AttributeListSheet getAttrListSheet() {
|
private AttributeListSheet getAttrListSheet() {
|
||||||
if (mAttrListSheet == null) {
|
if (mAttrListSheet == null) {
|
||||||
@@ -291,15 +305,15 @@ public class AttrEditorSheet extends BottomSheetDialogFragment
|
|||||||
return mAttrListSheet;
|
return mAttrListSheet;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@Override
|
||||||
public AttrValueEditorSheet getValueEditorSheet(@NonNull XMLAttribute attribute) {
|
public void accept(Attr attr) {
|
||||||
|
addNewAttribute(attr);
|
||||||
if (mValueEditorSheet == null) {
|
|
||||||
mValueEditorSheet = AttrValueEditorSheet.newInstance(attribute);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mValueEditorSheet.setAttribute(attribute);
|
private void addNewAttribute(@NonNull Attr attr) {
|
||||||
return mValueEditorSheet;
|
final XMLAttribute attribute = new XMLAttribute(attr.namespace, attr.name, "", false);
|
||||||
|
attribute.setAttr(attr);
|
||||||
|
showValueEditorSheet(attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
* 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}.
|
* used in {@link com.itsaky.androidide.DesignerActivity DesignerActivity}.
|
||||||
|
|||||||
@@ -53,12 +53,6 @@ public class AttributeListSheet extends BottomSheetDialogFragment {
|
|||||||
private Consumer<Attr> clickConsumer;
|
private Consumer<Attr> clickConsumer;
|
||||||
private List<Attr> mItems;
|
private List<Attr> mItems;
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setStyle(DialogFragment.STYLE_NORMAL, R.style.AppTheme_SheetAboveKeyboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(
|
public View onCreateView(
|
||||||
@@ -104,6 +98,16 @@ public class AttributeListSheet extends BottomSheetDialogFragment {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
update(mItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(List<Attr> items) {
|
||||||
|
if (binding != null) {
|
||||||
|
binding.attrList.setAdapter(new AttrListAdapter(clickConsumer, items));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setItems(@Nullable List<Attr> items) {
|
public void setItems(@Nullable List<Attr> items) {
|
||||||
if (items == null) {
|
if (items == null) {
|
||||||
items = new ArrayList<>();
|
items = new ArrayList<>();
|
||||||
@@ -120,13 +124,9 @@ public class AttributeListSheet extends BottomSheetDialogFragment {
|
|||||||
this.clickConsumer = (Consumer<Attr>) getParentFragment();
|
this.clickConsumer = (Consumer<Attr>) getParentFragment();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update() {
|
@Override
|
||||||
update(mItems);
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
}
|
super.onCreate(savedInstanceState);
|
||||||
|
setStyle(DialogFragment.STYLE_NORMAL, R.style.AppTheme_SheetAboveKeyboard);
|
||||||
public void update(List<Attr> items) {
|
|
||||||
if (binding != null) {
|
|
||||||
binding.attrList.setAdapter(new AttrListAdapter(clickConsumer, items));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-8
@@ -47,6 +47,8 @@ public abstract class BaseBottomSheetFragment extends BottomSheetDialogFragment
|
|||||||
return mDialog;
|
return mDialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void onShow() {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(
|
public View onCreateView(
|
||||||
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
@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() {
|
protected boolean shouldHideTitle() {
|
||||||
return !titleEnabled;
|
return !titleEnabled;
|
||||||
}
|
}
|
||||||
@@ -123,6 +121,12 @@ public abstract class BaseBottomSheetFragment extends BottomSheetDialogFragment
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract void bind(LinearLayout container);
|
||||||
|
|
||||||
|
public boolean isShowing() {
|
||||||
|
return mDialog != null && mDialog.isShowing();
|
||||||
|
}
|
||||||
|
|
||||||
public void setShowShadow(boolean enabled) {
|
public void setShowShadow(boolean enabled) {
|
||||||
this.shadowEnabled = enabled;
|
this.shadowEnabled = enabled;
|
||||||
}
|
}
|
||||||
@@ -131,10 +135,6 @@ public abstract class BaseBottomSheetFragment extends BottomSheetDialogFragment
|
|||||||
this.titleEnabled = enabled;
|
this.titleEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onShow() {}
|
|
||||||
|
|
||||||
protected abstract void bind(LinearLayout container);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
|
|||||||
@@ -42,13 +42,6 @@ public class OptionsListFragment extends BaseBottomSheetFragment {
|
|||||||
private RecyclerView mList;
|
private RecyclerView mList;
|
||||||
private OnOptionsClickListener listener;
|
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
|
@Override
|
||||||
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, 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
|
@Override
|
||||||
public void onAttach(@NonNull Context context) {
|
public void onAttach(@NonNull Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
@@ -90,11 +95,6 @@ public class OptionsListFragment extends BaseBottomSheetFragment {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getTitle() {
|
|
||||||
return getString(com.itsaky.androidide.R.string.file_options);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static interface OnOptionsClickListener {
|
public static interface OnOptionsClickListener {
|
||||||
void onOptionsClick(SheetOption option);
|
void onOptionsClick(SheetOption option);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,12 +41,6 @@ public class ProgressSheet extends BaseBottomSheetFragment {
|
|||||||
private boolean subMessageEnabled = false;
|
private boolean subMessageEnabled = false;
|
||||||
private boolean welcomeTextEnabled = false;
|
private boolean welcomeTextEnabled = false;
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void bind(LinearLayout container) {
|
|
||||||
binding = LayoutProgressSheetBinding.inflate(LayoutInflater.from(getContext()));
|
|
||||||
container.addView(binding.getRoot());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
@@ -85,6 +79,12 @@ public class ProgressSheet extends BaseBottomSheetFragment {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void bind(LinearLayout container) {
|
||||||
|
binding = LayoutProgressSheetBinding.inflate(LayoutInflater.from(getContext()));
|
||||||
|
container.addView(binding.getRoot());
|
||||||
|
}
|
||||||
|
|
||||||
public ProgressSheet setWelcomeTextEnabled(boolean enabled) {
|
public ProgressSheet setWelcomeTextEnabled(boolean enabled) {
|
||||||
this.welcomeTextEnabled = enabled;
|
this.welcomeTextEnabled = enabled;
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -40,34 +40,6 @@ public class TextSheetFragment extends BaseBottomSheetFragment {
|
|||||||
return this;
|
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) {
|
public TextSheetFragment setTitleText(int res) {
|
||||||
super.setTitle(res);
|
super.setTitle(res);
|
||||||
return this;
|
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() {
|
private IDEEditor getEditor() {
|
||||||
return editor == null ? createEditor() : editor;
|
return editor == null ? createEditor() : editor;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,6 @@ import androidx.annotation.NonNull;
|
|||||||
|
|
||||||
import com.itsaky.androidide.app.StudioApp;
|
import com.itsaky.androidide.app.StudioApp;
|
||||||
import com.itsaky.lsp.api.ILanguageServer;
|
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;
|
import io.github.rosemoe.sora.lang.Language;
|
||||||
|
|
||||||
@@ -40,10 +35,6 @@ public abstract class IDELanguage implements Language {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTabSize() {
|
|
||||||
return StudioApp.getInstance().getPrefManager().getEditorTabSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence format(CharSequence text) {
|
public CharSequence format(CharSequence text) {
|
||||||
final var server = getLanguageServer();
|
final var server = getLanguageServer();
|
||||||
@@ -54,11 +45,15 @@ public abstract class IDELanguage implements Language {
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getIndentAdvance(@NonNull String line) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ILanguageServer getLanguageServer() {
|
protected ILanguageServer getLanguageServer() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getTabSize() {
|
||||||
|
return StudioApp.getInstance().getPrefManager().getEditorTabSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIndentAdvance(@NonNull String line) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,13 +26,9 @@ import com.itsaky.androidide.utils.ILogger;
|
|||||||
|
|
||||||
import java.lang.Override;
|
import java.lang.Override;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
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.lang.styling.Span;
|
||||||
import io.github.rosemoe.sora.text.Content;
|
|
||||||
|
|
||||||
public class CppAnalyzer extends BaseIncrementalAnalyzeManager {
|
public class CppAnalyzer extends BaseIncrementalAnalyzeManager {
|
||||||
|
|
||||||
@@ -43,8 +39,10 @@ public class CppAnalyzer extends BaseIncrementalAnalyzeManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int[] getCodeBlockTokens() {
|
protected int[][] getMultilineTokenStartEndTypes() {
|
||||||
return new int[] {LeftBrace, RightBrace};
|
final var start = new int[] {Div, Star};
|
||||||
|
final var end = new int[] {Star, Div};
|
||||||
|
return new int[][] {start, end};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -215,15 +213,13 @@ public class CppAnalyzer extends BaseIncrementalAnalyzeManager {
|
|||||||
return spans;
|
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
|
@Override
|
||||||
protected void handleIncompleteToken(final IncrementalToken token) {
|
protected void handleIncompleteToken(final IncrementalToken token) {
|
||||||
token.type = BlockComment;
|
token.type = BlockComment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int[] getCodeBlockTokens() {
|
||||||
|
return new int[] {LeftBrace, RightBrace};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,42 +43,13 @@ import io.github.rosemoe.sora.widget.SymbolPairMatch;
|
|||||||
public class CppLanguage extends IDELanguage {
|
public class CppLanguage extends IDELanguage {
|
||||||
|
|
||||||
private static final ILogger LOG = ILogger.newInstance("CppLanguage");
|
private static final ILogger LOG = ILogger.newInstance("CppLanguage");
|
||||||
private CppAnalyzer analyzer;
|
|
||||||
private final NewlineHandler[] newlineHandlers = new NewlineHandler[] {new BraceHandler()};
|
private final NewlineHandler[] newlineHandlers = new NewlineHandler[] {new BraceHandler()};
|
||||||
|
private CppAnalyzer analyzer;
|
||||||
|
|
||||||
public CppLanguage() {
|
public CppLanguage() {
|
||||||
analyzer = new CppAnalyzer();
|
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
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public AnalyzeManager getAnalyzeManager() {
|
public AnalyzeManager getAnalyzeManager() {
|
||||||
@@ -107,8 +78,8 @@ public class CppLanguage extends IDELanguage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence format(CharSequence content) {
|
public SymbolPairMatch getSymbolPairs() {
|
||||||
return content;
|
return new CppSymbolPairs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -121,6 +92,35 @@ public class CppLanguage extends IDELanguage {
|
|||||||
analyzer = null;
|
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 {
|
private static class CppSymbolPairs extends SymbolPairMatch {
|
||||||
public CppSymbolPairs() {
|
public CppSymbolPairs() {
|
||||||
super.putPair('{', new Replacement("{}", 1));
|
super.putPair('{', new Replacement("{}", 1));
|
||||||
|
|||||||
@@ -23,10 +23,8 @@ import com.itsaky.androidide.language.incremental.LineState
|
|||||||
import com.itsaky.androidide.lexers.groovy.GroovyLexer
|
import com.itsaky.androidide.lexers.groovy.GroovyLexer
|
||||||
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE
|
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE
|
||||||
import io.github.rosemoe.sora.lang.analysis.IncrementalAnalyzeManager.LineTokenizeResult
|
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.Span
|
||||||
import io.github.rosemoe.sora.lang.styling.TextStyle
|
import io.github.rosemoe.sora.lang.styling.TextStyle
|
||||||
import io.github.rosemoe.sora.text.Content
|
|
||||||
|
|
||||||
/** @author Akash Yadav */
|
/** @author Akash Yadav */
|
||||||
class GroovyAnalyzer : BaseIncrementalAnalyzeManager(GroovyLexer::class.java) {
|
class GroovyAnalyzer : BaseIncrementalAnalyzeManager(GroovyLexer::class.java) {
|
||||||
|
|||||||
@@ -53,35 +53,6 @@ public class GroovyLanguage extends IDELanguage {
|
|||||||
completer = new GroovyAutoComplete();
|
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
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public AnalyzeManager getAnalyzeManager() {
|
public AnalyzeManager getAnalyzeManager() {
|
||||||
@@ -129,8 +100,8 @@ public class GroovyLanguage extends IDELanguage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence format(CharSequence content) {
|
public SymbolPairMatch getSymbolPairs() {
|
||||||
return content;
|
return new SymbolPairMatch.DefaultSymbolPairs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -141,6 +112,35 @@ public class GroovyLanguage extends IDELanguage {
|
|||||||
@Override
|
@Override
|
||||||
public void destroy() {}
|
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 {
|
class BraceHandler implements NewlineHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+139
-139
@@ -61,11 +61,11 @@ import kotlin.Pair;
|
|||||||
public abstract class BaseIncrementalAnalyzeManager
|
public abstract class BaseIncrementalAnalyzeManager
|
||||||
extends AsyncIncrementalAnalyzeManager<LineState, IncrementalToken> {
|
extends AsyncIncrementalAnalyzeManager<LineState, IncrementalToken> {
|
||||||
|
|
||||||
|
private static final ILogger LOG = ILogger.newInstance("BaseIncrementalAnalyzeManager");
|
||||||
protected final Lexer lexer;
|
protected final Lexer lexer;
|
||||||
private final int[] multilineStartTypes;
|
private final int[] multilineStartTypes;
|
||||||
private final int[] multilineEndTypes;
|
private final int[] multilineEndTypes;
|
||||||
private final int[] blockTokens;
|
private final int[] blockTokens;
|
||||||
private static final ILogger LOG = ILogger.newInstance("BaseIncrementalAnalyzeManager");
|
|
||||||
|
|
||||||
public BaseIncrementalAnalyzeManager(final Class<? extends Lexer> lexer) {
|
public BaseIncrementalAnalyzeManager(final Class<? extends Lexer> lexer) {
|
||||||
Objects.requireNonNull(lexer, "Cannot create analyzer manager for null 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) {
|
private void verifyMultilineTypes(@NonNull final int[][] types) {
|
||||||
Preconditions.checkState(
|
Preconditions.checkState(
|
||||||
types.length == 2, "There must be exact two inner int[] in multiline token types");
|
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");
|
Preconditions.checkState(end.length > 0, "Invalid end token types");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("UnstableApiUsage")
|
|
||||||
@NonNull
|
|
||||||
private Pair<EvictingQueue<IncrementalToken>, EvictingQueue<IncrementalToken>>
|
|
||||||
createEvictingQueueForTokens() {
|
|
||||||
return new Pair<>(
|
|
||||||
EvictingQueue.create(multilineStartTypes.length),
|
|
||||||
EvictingQueue.create(multilineEndTypes.length));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("UnstableApiUsage")
|
|
||||||
protected boolean isIncompleteTokenStart(EvictingQueue<IncrementalToken> q) {
|
|
||||||
return matchTokenTypes(this.multilineStartTypes, q);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("UnstableApiUsage")
|
|
||||||
protected boolean isIncompleteTokenEnd(EvictingQueue<IncrementalToken> q) {
|
|
||||||
return matchTokenTypes(this.multilineEndTypes, q);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("UnstableApiUsage")
|
|
||||||
private boolean matchTokenTypes(
|
|
||||||
@NonNull int[] types, @NonNull EvictingQueue<IncrementalToken> 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.
|
* Returns the token types which start and end a multiline token.
|
||||||
*
|
*
|
||||||
@@ -152,31 +123,6 @@ public abstract class BaseIncrementalAnalyzeManager
|
|||||||
*/
|
*/
|
||||||
protected abstract int[][] getMultilineTokenStartEndTypes();
|
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<Span> generateSpans(
|
|
||||||
final LineTokenizeResult<LineState, IncrementalToken> 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.
|
* Get the token types for left and right braces.
|
||||||
*
|
*
|
||||||
@@ -184,51 +130,6 @@ public abstract class BaseIncrementalAnalyzeManager
|
|||||||
*/
|
*/
|
||||||
protected abstract int[] getCodeBlockTokens();
|
protected abstract int[] getCodeBlockTokens();
|
||||||
|
|
||||||
/**
|
|
||||||
* Pop (remove) required number of tokens after an incomplete token has been encountered. <br>
|
|
||||||
*
|
|
||||||
* <p>For example: <br>
|
|
||||||
* When ['/', '*'] tokens are encountered, the '*' token must be removed. In this case, <code>
|
|
||||||
* incompleteToken</code> parameter will point to '/' token.
|
|
||||||
*
|
|
||||||
* <p>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<IncrementalToken> 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<Span> 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
|
@Override
|
||||||
public LineState getInitialState() {
|
public LineState getInitialState() {
|
||||||
return new LineState();
|
return new LineState();
|
||||||
@@ -239,6 +140,52 @@ public abstract class BaseIncrementalAnalyzeManager
|
|||||||
return state.equals(another);
|
return state.equals(another);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LineTokenizeResult<LineState, IncrementalToken> tokenizeLine(
|
||||||
|
final CharSequence line, final LineState state) {
|
||||||
|
final var tokens = new ArrayList<IncrementalToken>();
|
||||||
|
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<Span> generateSpansForLine(
|
||||||
|
final LineTokenizeResult<LineState, IncrementalToken> 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<Span> generateSpans(
|
||||||
|
final LineTokenizeResult<LineState, IncrementalToken> tokens);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<CodeBlock> computeBlocks(
|
public List<CodeBlock> computeBlocks(
|
||||||
final Content text,
|
final Content text,
|
||||||
@@ -292,27 +239,73 @@ public abstract class BaseIncrementalAnalyzeManager
|
|||||||
return blocks;
|
return blocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected boolean isCodeBlockStart(IncrementalToken token) {
|
||||||
public LineTokenizeResult<LineState, IncrementalToken> tokenizeLine(
|
return blockTokens.length == 2 && token.getType() == blockTokens[0];
|
||||||
final CharSequence line, final LineState state) {
|
}
|
||||||
final var tokens = new ArrayList<IncrementalToken>();
|
|
||||||
var newState = 0;
|
protected boolean isCodeBlockEnd(IncrementalToken token) {
|
||||||
var stateObj = new LineState();
|
return blockTokens.length == 2 && token.getType() == blockTokens[1];
|
||||||
if (state.state == NORMAL) {
|
}
|
||||||
newState = tokenizeNormal(line, 0, tokens, stateObj, state.lexerMode);
|
|
||||||
} else if (state.state == INCOMPLETE) {
|
@SuppressWarnings("UnstableApiUsage")
|
||||||
final var result = fillIncomplete(line, tokens, state.lexerMode);
|
protected boolean isIncompleteTokenStart(EvictingQueue<IncrementalToken> q) {
|
||||||
newState = IntPair.getFirst(result);
|
return matchTokenTypes(this.multilineStartTypes, q);
|
||||||
if (newState == NORMAL) {
|
}
|
||||||
newState =
|
|
||||||
tokenizeNormal(line, IntPair.getSecond(result), tokens, stateObj, state.lexerMode);
|
@SuppressWarnings("UnstableApiUsage")
|
||||||
} else {
|
protected boolean isIncompleteTokenEnd(EvictingQueue<IncrementalToken> q) {
|
||||||
newState = INCOMPLETE;
|
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. <br>
|
||||||
|
*
|
||||||
|
* <p>For example: <br>
|
||||||
|
* When ['/', '*'] tokens are encountered, the '*' token must be removed. In this case, <code>
|
||||||
|
* incompleteToken</code> parameter will point to '/' token.
|
||||||
|
*
|
||||||
|
* <p>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<IncrementalToken> tokens) {
|
||||||
|
tokens.remove(tokens.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleLineCommentSpan(
|
||||||
|
@NonNull IncrementalToken token, @NonNull List<Span> 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;
|
spans.add(Span.obtain(offset, withoutCompletion(commentType)));
|
||||||
stateObj.lexerMode = lexer._mode;
|
|
||||||
return new LineTokenizeResult<>(stateObj, tokens);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -445,17 +438,24 @@ public abstract class BaseIncrementalAnalyzeManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@SuppressWarnings("UnstableApiUsage")
|
||||||
public List<Span> generateSpansForLine(
|
@NonNull
|
||||||
final LineTokenizeResult<LineState, IncrementalToken> tokens) {
|
private Pair<EvictingQueue<IncrementalToken>, EvictingQueue<IncrementalToken>>
|
||||||
var result = generateSpans(tokens);
|
createEvictingQueueForTokens() {
|
||||||
|
return new Pair<>(
|
||||||
Objects.requireNonNull(result);
|
EvictingQueue.create(multilineStartTypes.length),
|
||||||
|
EvictingQueue.create(multilineEndTypes.length));
|
||||||
if (result.isEmpty()) {
|
|
||||||
result.add(Span.obtain(0, TextStyle.makeStyle(SchemeAndroidIDE.TEXT_NORMAL)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
@SuppressWarnings("UnstableApiUsage")
|
||||||
|
private boolean matchTokenTypes(
|
||||||
|
@NonNull int[] types, @NonNull EvictingQueue<IncrementalToken> 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,10 +30,8 @@ import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forKeyword
|
|||||||
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forString
|
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forString
|
||||||
import com.itsaky.androidide.utils.ILogger
|
import com.itsaky.androidide.utils.ILogger
|
||||||
import io.github.rosemoe.sora.lang.analysis.IncrementalAnalyzeManager.LineTokenizeResult
|
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.Span
|
||||||
import io.github.rosemoe.sora.lang.styling.TextStyle.makeStyle
|
import io.github.rosemoe.sora.lang.styling.TextStyle.makeStyle
|
||||||
import io.github.rosemoe.sora.text.Content
|
|
||||||
|
|
||||||
/** @author Akash Yadav */
|
/** @author Akash Yadav */
|
||||||
class JavaAnalyzer : BaseIncrementalAnalyzeManager(JavaLexer::class.java) {
|
class JavaAnalyzer : BaseIncrementalAnalyzeManager(JavaLexer::class.java) {
|
||||||
|
|||||||
@@ -63,36 +63,6 @@ public class JavaLanguage extends IDELanguage {
|
|||||||
this.newlineHandlers[0] = new BraceHandler();
|
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
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public AnalyzeManager getAnalyzeManager() {
|
public AnalyzeManager getAnalyzeManager() {
|
||||||
@@ -127,11 +97,41 @@ public class JavaLanguage extends IDELanguage {
|
|||||||
return getIndentAdvance(content.getLine(line).substring(0, column));
|
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
|
@Override
|
||||||
protected ILanguageServer getLanguageServer() {
|
protected ILanguageServer getLanguageServer() {
|
||||||
return StudioApp.getInstance().getJavaLanguageServer();
|
return StudioApp.getInstance().getJavaLanguageServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SymbolPairMatch getSymbolPairs() {
|
||||||
|
return new JavaSymbolPairs();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NewlineHandler[] getNewlineHandlers() {
|
public NewlineHandler[] getNewlineHandlers() {
|
||||||
return newlineHandlers;
|
return newlineHandlers;
|
||||||
|
|||||||
@@ -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.forKeyword
|
||||||
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forString
|
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forString
|
||||||
import io.github.rosemoe.sora.lang.analysis.IncrementalAnalyzeManager.LineTokenizeResult
|
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.Span
|
||||||
import io.github.rosemoe.sora.lang.styling.TextStyle.makeStyle
|
import io.github.rosemoe.sora.lang.styling.TextStyle.makeStyle
|
||||||
import io.github.rosemoe.sora.text.Content
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Incremental syntax analyzer for Kotlin.
|
* Incremental syntax analyzer for Kotlin.
|
||||||
|
|||||||
@@ -22,15 +22,14 @@ import android.os.Bundle;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.itsaky.androidide.language.IDELanguage;
|
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 com.itsaky.androidide.utils.ILogger;
|
||||||
|
|
||||||
import org.antlr.v4.runtime.CharStreams;
|
import org.antlr.v4.runtime.CharStreams;
|
||||||
import org.antlr.v4.runtime.Token;
|
import org.antlr.v4.runtime.Token;
|
||||||
|
|
||||||
import java.io.StringReader;
|
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.analysis.AnalyzeManager;
|
||||||
import io.github.rosemoe.sora.lang.completion.CompletionCancelledException;
|
import io.github.rosemoe.sora.lang.completion.CompletionCancelledException;
|
||||||
@@ -45,42 +44,13 @@ import io.github.rosemoe.sora.widget.SymbolPairMatch;
|
|||||||
public class KotlinLanguage extends IDELanguage {
|
public class KotlinLanguage extends IDELanguage {
|
||||||
|
|
||||||
private static final ILogger LOG = ILogger.newInstance("KotlinLanguage");
|
private static final ILogger LOG = ILogger.newInstance("KotlinLanguage");
|
||||||
private KotlinAnalyzer analyzer;
|
|
||||||
private final NewlineHandler[] newlineHandlers = new NewlineHandler[] {new BraceHandler()};
|
private final NewlineHandler[] newlineHandlers = new NewlineHandler[] {new BraceHandler()};
|
||||||
|
private KotlinAnalyzer analyzer;
|
||||||
|
|
||||||
public KotlinLanguage() {
|
public KotlinLanguage() {
|
||||||
analyzer = new KotlinAnalyzer();
|
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
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public AnalyzeManager getAnalyzeManager() {
|
public AnalyzeManager getAnalyzeManager() {
|
||||||
@@ -109,8 +79,8 @@ public class KotlinLanguage extends IDELanguage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence format(CharSequence content) {
|
public SymbolPairMatch getSymbolPairs() {
|
||||||
return content;
|
return new KotlinSymbolPairs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -123,6 +93,35 @@ public class KotlinLanguage extends IDELanguage {
|
|||||||
analyzer = null;
|
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 {
|
private static class KotlinSymbolPairs extends SymbolPairMatch {
|
||||||
public KotlinSymbolPairs() {
|
public KotlinSymbolPairs() {
|
||||||
super.putPair('{', new Replacement("{}", 1));
|
super.putPair('{', new Replacement("{}", 1));
|
||||||
|
|||||||
@@ -57,38 +57,6 @@ public class XMLLanguage extends IDELanguage {
|
|||||||
this.newlineHandlers = new NewlineHandler[0];
|
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
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public AnalyzeManager getAnalyzeManager() {
|
public AnalyzeManager getAnalyzeManager() {
|
||||||
@@ -125,11 +93,43 @@ public class XMLLanguage extends IDELanguage {
|
|||||||
return getIndentAdvance(text.trim());
|
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
|
@Override
|
||||||
protected ILanguageServer getLanguageServer() {
|
protected ILanguageServer getLanguageServer() {
|
||||||
return StudioApp.getInstance().getXMLLanguageServer();
|
return StudioApp.getInstance().getXMLLanguageServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SymbolPairMatch getSymbolPairs() {
|
||||||
|
return new SymbolPairMatch.DefaultSymbolPairs();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NewlineHandler[] getNewlineHandlers() {
|
public NewlineHandler[] getNewlineHandlers() {
|
||||||
return newlineHandlers;
|
return newlineHandlers;
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.itsaky.androidide.lsp;
|
package com.itsaky.androidide.lsp;
|
||||||
|
|
||||||
import android.os.Trace;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -73,25 +72,20 @@ import io.github.rosemoe.sora.text.Content;
|
|||||||
/** AndroidIDE specific implementation of the LanguageClient */
|
/** AndroidIDE specific implementation of the LanguageClient */
|
||||||
public class IDELanguageClientImpl implements ILanguageClient {
|
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 Gson gson = new Gson();
|
||||||
protected static final ILogger LOG = ILogger.newInstance("AbstractLanguageClient");
|
protected static final ILogger LOG = ILogger.newInstance("AbstractLanguageClient");
|
||||||
private static IDELanguageClientImpl mInstance;
|
private static IDELanguageClientImpl mInstance;
|
||||||
private final Map<File, List<DiagnosticItem>> diagnostics = new HashMap<>();
|
private final Map<File, List<DiagnosticItem>> diagnostics = new HashMap<>();
|
||||||
protected EditorActivityProvider activityProvider;
|
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) {
|
private IDELanguageClientImpl(EditorActivityProvider provider) {
|
||||||
setActivityProvider(provider);
|
setActivityProvider(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IDELanguageClientImpl getInstance() {
|
public void setActivityProvider(EditorActivityProvider provider) {
|
||||||
if (mInstance == null) {
|
this.activityProvider = provider;
|
||||||
throw new IllegalStateException("Client not initialized");
|
|
||||||
}
|
|
||||||
|
|
||||||
return mInstance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IDELanguageClientImpl initialize(EditorActivityProvider provider) {
|
public static IDELanguageClientImpl initialize(EditorActivityProvider provider) {
|
||||||
@@ -104,6 +98,14 @@ public class IDELanguageClientImpl implements ILanguageClient {
|
|||||||
return getInstance();
|
return getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IDELanguageClientImpl getInstance() {
|
||||||
|
if (mInstance == null) {
|
||||||
|
throw new IllegalStateException("Client not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
return mInstance;
|
||||||
|
}
|
||||||
|
|
||||||
public static void shutdown() {
|
public static void shutdown() {
|
||||||
mInstance = null;
|
mInstance = null;
|
||||||
}
|
}
|
||||||
@@ -112,19 +114,6 @@ public class IDELanguageClientImpl implements ILanguageClient {
|
|||||||
return mInstance != null;
|
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() {
|
public void hideDiagnostics() {
|
||||||
if (activity() == null || activity().getDiagnosticBinding() == null) {
|
if (activity() == null || activity().getDiagnosticBinding() == null) {
|
||||||
return;
|
return;
|
||||||
@@ -133,21 +122,6 @@ public class IDELanguageClientImpl implements ILanguageClient {
|
|||||||
activity().getDiagnosticBinding().getRoot().setVisibility(View.GONE);
|
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
|
@Override
|
||||||
public void publishDiagnostics(DiagnosticResult result) {
|
public void publishDiagnostics(DiagnosticResult result) {
|
||||||
boolean error = result == null;
|
boolean error = result == null;
|
||||||
@@ -181,66 +155,17 @@ public class IDELanguageClientImpl implements ILanguageClient {
|
|||||||
activity().setDiagnosticsAdapter(newDiagnosticsAdapter());
|
activity().setDiagnosticsAdapter(newDiagnosticsAdapter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected EditorActivity activity() {
|
||||||
|
if (activityProvider == null) return null;
|
||||||
|
return activityProvider.provide();
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public DiagnosticItem getDiagnosticAt(final File file, final int line, final int column) {
|
public DiagnosticItem getDiagnosticAt(final File file, final int line, final int column) {
|
||||||
return DiagnosticUtil.binarySearchDiagnostic(this.diagnostics.get(file), line, column);
|
return DiagnosticUtil.binarySearchDiagnostic(this.diagnostics.get(file), line, column);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called by {@link IDEEditor IDEEditor} to show locations in EditorActivity */
|
|
||||||
public void showLocations(List<Location> 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<File, List<SearchResult>> 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<SearchResult> 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
|
@Override
|
||||||
public void performCodeAction(File file, CodeActionItem actionItem) {
|
public void performCodeAction(File file, CodeActionItem actionItem) {
|
||||||
final var editor = activity().getEditorForFile(file);
|
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<DiagnosticGroup> mapAsGroup(Map<File, List<DiagnosticItem>> map) {
|
private List<DiagnosticGroup> mapAsGroup(Map<File, List<DiagnosticItem>> map) {
|
||||||
final var groups = new ArrayList<DiagnosticGroup>();
|
final var groups = new ArrayList<DiagnosticGroup>();
|
||||||
var diagnosticMap = map;
|
var diagnosticMap = map;
|
||||||
@@ -364,6 +391,79 @@ public class IDELanguageClientImpl implements ILanguageClient {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Called by {@link IDEEditor IDEEditor} to show locations in EditorActivity */
|
||||||
|
public void showLocations(List<Location> 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<File, List<SearchResult>> 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<SearchResult> 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<CodeActionItem> actions) {
|
private void showAvailableQuickfixes(IDEEditor editor, List<CodeActionItem> actions) {
|
||||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
|
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(activity());
|
||||||
builder.setTitle(R.string.msg_code_actions);
|
builder.setTitle(R.string.msg_code_actions);
|
||||||
@@ -386,109 +486,7 @@ public class IDELanguageClientImpl implements ILanguageClient {
|
|||||||
return arr;
|
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) {
|
private void execCommand(IDEEditor editor, Command command) {
|
||||||
editor.executeCommand(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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,6 +143,10 @@ public class ApkMetadata {
|
|||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setElements(List<Element> elements) {
|
||||||
|
this.elements = elements;
|
||||||
|
}
|
||||||
|
|
||||||
public ArtifactType getArtifactType() {
|
public ArtifactType getArtifactType() {
|
||||||
return artifactType;
|
return artifactType;
|
||||||
}
|
}
|
||||||
@@ -151,10 +155,6 @@ public class ApkMetadata {
|
|||||||
this.artifactType = artifactType;
|
this.artifactType = artifactType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setElements(List<Element> elements) {
|
|
||||||
this.elements = elements;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getVersion() {
|
public int getVersion() {
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,10 +21,6 @@ import androidx.annotation.DrawableRes;
|
|||||||
|
|
||||||
public interface IconTextListItem {
|
public interface IconTextListItem {
|
||||||
|
|
||||||
String getText();
|
|
||||||
|
|
||||||
int getIconResource();
|
|
||||||
|
|
||||||
public static IconTextListItem create(String name, @DrawableRes int icon) {
|
public static IconTextListItem create(String name, @DrawableRes int icon) {
|
||||||
return new IconTextListItem() {
|
return new IconTextListItem() {
|
||||||
@Override
|
@Override
|
||||||
@@ -38,4 +34,8 @@ public interface IconTextListItem {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getText();
|
||||||
|
|
||||||
|
int getIconResource();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,17 +46,12 @@ public class NewProjectDetails {
|
|||||||
this.savePath = savePath;
|
this.savePath = savePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NewProjectDetails setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NewProjectDetails setPackageName(String packageName) {
|
public NewProjectDetails setName(String name) {
|
||||||
this.packageName = packageName;
|
this.name = name;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,8 +59,8 @@ public class NewProjectDetails {
|
|||||||
return packageName;
|
return packageName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NewProjectDetails setMinSdk(int minSdk) {
|
public NewProjectDetails setPackageName(String packageName) {
|
||||||
this.minSdk = minSdk;
|
this.packageName = packageName;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,8 +68,8 @@ public class NewProjectDetails {
|
|||||||
return minSdk;
|
return minSdk;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NewProjectDetails setTargetSdk(int targetSdk) {
|
public NewProjectDetails setMinSdk(int minSdk) {
|
||||||
this.targetSdk = targetSdk;
|
this.minSdk = minSdk;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,12 +77,17 @@ public class NewProjectDetails {
|
|||||||
return targetSdk;
|
return targetSdk;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NewProjectDetails setCppFlags(String cppFlags) {
|
public NewProjectDetails setTargetSdk(int targetSdk) {
|
||||||
this.cppFlags = cppFlags;
|
this.targetSdk = targetSdk;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCppFlags() {
|
public String getCppFlags() {
|
||||||
return cppFlags;
|
return cppFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NewProjectDetails setCppFlags(String cppFlags) {
|
||||||
|
this.cppFlags = cppFlags;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,39 +42,43 @@ public class ProjectTemplate {
|
|||||||
this.imageId = imageId;
|
this.imageId = imageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
public ProjectTemplate setId(int id) {
|
public ProjectTemplate setId(int id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProjectTemplate setName(Context ctx, @StringRes int name) {
|
public ProjectTemplate setName(Context ctx, @StringRes int name) {
|
||||||
return setName(ctx.getString(name));
|
return setName(ctx.getString(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
public ProjectTemplate setName(String name) {
|
public ProjectTemplate setName(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProjectTemplate setDescription(Context ctx, @StringRes int desc) {
|
public ProjectTemplate setDescription(Context ctx, @StringRes int desc) {
|
||||||
return setDescription(ctx.getString(desc));
|
return setDescription(ctx.getString(desc));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
public ProjectTemplate setDescription(String description) {
|
public ProjectTemplate setDescription(String description) {
|
||||||
this.description = description;
|
this.description = description;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDescription() {
|
public int getImageId() {
|
||||||
return description;
|
return imageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProjectTemplate setImageId(@DrawableRes int imageId) {
|
public ProjectTemplate setImageId(@DrawableRes int imageId) {
|
||||||
@@ -82,10 +86,6 @@ public class ProjectTemplate {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getImageId() {
|
|
||||||
return imageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSupportJava() {
|
public boolean isSupportJava() {
|
||||||
return this.isSupportJava;
|
return this.isSupportJava;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,9 +32,20 @@ import java.util.Objects;
|
|||||||
*/
|
*/
|
||||||
public class UIWidget implements IconTextListItem, Parcelable {
|
public class UIWidget implements IconTextListItem, Parcelable {
|
||||||
|
|
||||||
|
public static final Creator<UIWidget> CREATOR =
|
||||||
|
new Creator<UIWidget>() {
|
||||||
|
@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 String name;
|
||||||
private final int icon;
|
private final int icon;
|
||||||
|
|
||||||
private final String clazz;
|
private final String clazz;
|
||||||
|
|
||||||
private UIWidget(Parcel in) {
|
private UIWidget(Parcel in) {
|
||||||
@@ -49,25 +60,9 @@ public class UIWidget implements IconTextListItem, Parcelable {
|
|||||||
this.clazz = clazz.getName();
|
this.clazz = clazz.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Creator<UIWidget> CREATOR =
|
|
||||||
new Creator<UIWidget>() {
|
|
||||||
@Override
|
@Override
|
||||||
public UIWidget createFromParcel(Parcel in) {
|
public int hashCode() {
|
||||||
return new UIWidget(in);
|
return Objects.hash(getName(), getIcon(), clazz);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UIWidget[] newArray(int size) {
|
|
||||||
return new UIWidget[size];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getIcon() {
|
|
||||||
return icon;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -80,9 +75,12 @@ public class UIWidget implements IconTextListItem, Parcelable {
|
|||||||
&& clazz.equals(widget.clazz);
|
&& clazz.equals(widget.clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public String getName() {
|
||||||
public int hashCode() {
|
return name;
|
||||||
return Objects.hash(getName(), getIcon(), clazz);
|
}
|
||||||
|
|
||||||
|
public int getIcon() {
|
||||||
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -38,27 +38,6 @@ import java.util.Objects;
|
|||||||
*/
|
*/
|
||||||
public class UIWidgetGroup implements IconTextListItem, Parcelable {
|
public class UIWidgetGroup implements IconTextListItem, Parcelable {
|
||||||
|
|
||||||
/** Name of this group. */
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
/** Children of this group. */
|
|
||||||
private final List<UIWidget> 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<UIWidgetGroup> CREATOR =
|
public static final Creator<UIWidgetGroup> CREATOR =
|
||||||
new Creator<UIWidgetGroup>() {
|
new Creator<UIWidgetGroup>() {
|
||||||
|
|
||||||
@@ -76,32 +55,23 @@ public class UIWidgetGroup implements IconTextListItem, Parcelable {
|
|||||||
return new UIWidgetGroup[size];
|
return new UIWidgetGroup[size];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/** Name of this group. */
|
||||||
|
private final String name;
|
||||||
|
/** Children of this group. */
|
||||||
|
private final List<UIWidget> children;
|
||||||
|
/** Is this group currently selected in the group list? */
|
||||||
|
private boolean selected;
|
||||||
|
|
||||||
/**
|
public UIWidgetGroup(String name) {
|
||||||
* Get the name of this group.
|
this.name = name;
|
||||||
*
|
this.selected = false;
|
||||||
* @return The name of this group.
|
this.children = new ArrayList<>();
|
||||||
*/
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected UIWidgetGroup(@NonNull Parcel in) {
|
||||||
* Get the children of this group.
|
this.name = in.readString();
|
||||||
*
|
this.children = in.createTypedArrayList(UIWidget.CREATOR);
|
||||||
* @return The children.
|
this.selected = in.readByte() != 0;
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
public List<UIWidget> getChildren() {
|
|
||||||
return children;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSelected(boolean selected) {
|
|
||||||
this.selected = selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSelected() {
|
|
||||||
return selected;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,6 +99,11 @@ public class UIWidgetGroup implements IconTextListItem, Parcelable {
|
|||||||
return children.get(index);
|
return children.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(getName(), getChildren(), isSelected());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
@@ -139,9 +114,31 @@ public class UIWidgetGroup implements IconTextListItem, Parcelable {
|
|||||||
&& getChildren().equals(group.getChildren());
|
&& getChildren().equals(group.getChildren());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public int hashCode() {
|
* Get the name of this group.
|
||||||
return Objects.hash(getName(), getChildren(), isSelected());
|
*
|
||||||
|
* @return The name of this group.
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the children of this group.
|
||||||
|
*
|
||||||
|
* @return The children.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public List<UIWidget> getChildren() {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSelected() {
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelected(boolean selected) {
|
||||||
|
this.selected = selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -114,14 +114,6 @@ public class XMLAttribute extends UiAttribute implements Parcelable {
|
|||||||
return new XMLAttribute(INamespace.ANDROID, name, "", false);
|
return new XMLAttribute(INamespace.ANDROID, name, "", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getFormat() {
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFormat(int format) {
|
|
||||||
this.format = format;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int findFormat() {
|
public int findFormat() {
|
||||||
|
|
||||||
if (getFormat() > 0) {
|
if (getFormat() > 0) {
|
||||||
@@ -137,20 +129,18 @@ public class XMLAttribute extends UiAttribute implements Parcelable {
|
|||||||
return getFormat();
|
return getFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getFormat() {
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFormat(int format) {
|
||||||
|
this.format = format;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasFormat(int format) {
|
public boolean hasFormat(int format) {
|
||||||
return (this.format & format) != 0;
|
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
|
@Override
|
||||||
public void apply(String value) {
|
public void apply(String value) {
|
||||||
super.apply(value);
|
super.apply(value);
|
||||||
@@ -175,4 +165,14 @@ public class XMLAttribute extends UiAttribute implements Parcelable {
|
|||||||
dest.writeParcelable(getAttr(), flags);
|
dest.writeParcelable(getAttr(), flags);
|
||||||
dest.writeInt(this.format);
|
dest.writeInt(this.format);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Attr getAttr() {
|
||||||
|
return attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttr(Attr attr) {
|
||||||
|
Objects.requireNonNull(attr);
|
||||||
|
this.attr = attr;
|
||||||
|
setFormat(attr.format);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,7 +123,8 @@ class CachingProject(val project: IProject) : IProject {
|
|||||||
log.warn(
|
log.warn(
|
||||||
"Unable to get variant data for " +
|
"Unable to get variant data for " +
|
||||||
"variant ${request.variantName} ('${request.projectPath}') from tooling server",
|
"variant ${request.variantName} ('${request.projectPath}') from tooling server",
|
||||||
u)
|
u
|
||||||
|
)
|
||||||
return@whenComplete
|
return@whenComplete
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,8 +75,7 @@ object ProjectManager {
|
|||||||
|
|
||||||
getApplicationModule().whenCompleteAsync { app, _ ->
|
getApplicationModule().whenCompleteAsync { app, _ ->
|
||||||
if (app == null) {
|
if (app == null) {
|
||||||
log.warn(
|
log.warn("Cannot run resource and source generation task. No application module found.")
|
||||||
"Cannot run resource and source generation task. No application module found.")
|
|
||||||
return@whenCompleteAsync
|
return@whenCompleteAsync
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,12 +97,14 @@ object ProjectManager {
|
|||||||
// If view binding is enabled, generate the view binding classes too
|
// If view binding is enabled, generate the view binding classes too
|
||||||
if (app.viewBindingOptions != null && app.viewBindingOptions!!.isEnabled)
|
if (app.viewBindingOptions != null && app.viewBindingOptions!!.isEnabled)
|
||||||
"dataBindingGenBaseClassesDebug"
|
"dataBindingGenBaseClassesDebug"
|
||||||
else "")
|
else ""
|
||||||
|
)
|
||||||
.whenComplete { result, taskErr ->
|
.whenComplete { result, taskErr ->
|
||||||
if (!result.isSuccessful || taskErr != null) {
|
if (!result.isSuccessful || taskErr != null) {
|
||||||
log.warn(
|
log.warn(
|
||||||
"Execution for tasks '$genResourcesTask' and '$genSourcesTask' failed.",
|
"Execution for tasks '$genResourcesTask' and '$genSourcesTask' failed.",
|
||||||
taskErr ?: "")
|
taskErr ?: ""
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
notifyProjectUpdate()
|
notifyProjectUpdate()
|
||||||
}
|
}
|
||||||
@@ -113,8 +114,7 @@ object ProjectManager {
|
|||||||
|
|
||||||
fun getApplicationModule(): CompletableFuture<IdeAndroidModule?> {
|
fun getApplicationModule(): CompletableFuture<IdeAndroidModule?> {
|
||||||
if (rootProject == null) {
|
if (rootProject == null) {
|
||||||
log.error(
|
log.error("No root project instance is set. Is the project initialization process finished?")
|
||||||
"No root project instance is set. Is the project initialization process finished?")
|
|
||||||
return CompletableFuture.completedFuture(null)
|
return CompletableFuture.completedFuture(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,56 +80,6 @@ public class ProjectResourceTable implements IResourceTable {
|
|||||||
return value;
|
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
|
@Override
|
||||||
public String findColor(@NonNull String name) {
|
public String findColor(@NonNull String name) {
|
||||||
final var table = ValuesTableFactory.getTable(resDir);
|
final var table = ValuesTableFactory.getTable(resDir);
|
||||||
@@ -193,6 +143,68 @@ public class ProjectResourceTable implements IResourceTable {
|
|||||||
return value;
|
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
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Collection<String> listResourceNames(@Nullable String type) {
|
public Collection<String> listResourceNames(@Nullable String type) {
|
||||||
@@ -223,45 +235,6 @@ public class ProjectResourceTable implements IResourceTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void listAndAddTo(HashSet<String> 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<String> 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) {
|
private void setupDirectories(File resDir) {
|
||||||
|
|
||||||
if (resDir == null) {
|
if (resDir == null) {
|
||||||
@@ -296,6 +269,33 @@ public class ProjectResourceTable implements IResourceTable {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void listAndAddTo(HashSet<String> 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<String> 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 static class NameStartsWith implements FileFilter {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ interface BuildService {
|
|||||||
*
|
*
|
||||||
* @param projectPath The path of the project. All the tasks will be executed in this 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
|
* @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. If the task name is not fully qualified, then it will be executed in the given project
|
||||||
* project path. For example, if the project path is ':app' and the task is 'assembleDebug',
|
* path. For example, if the project path is ':app' and the task is 'assembleDebug', then,
|
||||||
* then, ':app:assembleDebug' task will be executed.
|
* ':app:assembleDebug' task will be executed.
|
||||||
* @return A [CompletableFuture] which returns a list of [TaskExecutionResult]. The result
|
* @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.
|
* contains a list of tasks that were executed and the result of the whole execution.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -321,12 +321,6 @@ public class GradleBuildService extends Service implements BuildService, IToolin
|
|||||||
.thenApply(this::markBuildAsFinished);
|
.thenApply(this::markBuildAsFinished);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getGradleInstallationPath() {
|
|
||||||
return StudioApp.getInstance()
|
|
||||||
.getPrefManager()
|
|
||||||
.getString(BuildPreferences.KEY_CUSTOM_GRADLE_INSTALLATION, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<TaskExecutionResult> executeProjectTasks(
|
public CompletableFuture<TaskExecutionResult> executeProjectTasks(
|
||||||
@@ -353,6 +347,12 @@ public class GradleBuildService extends Service implements BuildService, IToolin
|
|||||||
return server.cancelCurrentBuild();
|
return server.cancelCurrentBuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getGradleInstallationPath() {
|
||||||
|
return StudioApp.getInstance()
|
||||||
|
.getPrefManager()
|
||||||
|
.getString(BuildPreferences.KEY_CUSTOM_GRADLE_INSTALLATION, "");
|
||||||
|
}
|
||||||
|
|
||||||
private void checkServerStarted() {
|
private void checkServerStarted() {
|
||||||
if (!isToolingServerStarted) {
|
if (!isToolingServerStarted) {
|
||||||
throw new IllegalStateException("Tooling API server has not been started");
|
throw new IllegalStateException("Tooling API server has not been started");
|
||||||
|
|||||||
@@ -29,12 +29,10 @@ import com.itsaky.androidide.utils.ILogger;
|
|||||||
|
|
||||||
public class LogReceiver extends BroadcastReceiver {
|
public class LogReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
private LogListener listener;
|
|
||||||
|
|
||||||
public static final String APPEND_LOG = "com.itsaky.androidide.logs.APPEND_LOG";
|
public static final String APPEND_LOG = "com.itsaky.androidide.logs.APPEND_LOG";
|
||||||
public static final String EXTRA_LINE = "log_line";
|
public static final String EXTRA_LINE = "log_line";
|
||||||
|
|
||||||
private final ILogger LOG = ILogger.newInstance("LogReceiver");
|
private final ILogger LOG = ILogger.newInstance("LogReceiver");
|
||||||
|
private LogListener listener;
|
||||||
|
|
||||||
public LogReceiver setLogListener(LogListener listener) {
|
public LogReceiver setLogListener(LogListener listener) {
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
|
|||||||
@@ -29,6 +29,14 @@ import java.io.FileFilter;
|
|||||||
|
|
||||||
public class ListDirectoryCallable implements java.util.concurrent.Callable<String> {
|
public class ListDirectoryCallable implements java.util.concurrent.Callable<String> {
|
||||||
|
|
||||||
|
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;
|
private File file;
|
||||||
|
|
||||||
public ListDirectoryCallable(File file) {
|
public ListDirectoryCallable(File file) {
|
||||||
@@ -39,13 +47,4 @@ public class ListDirectoryCallable implements java.util.concurrent.Callable<Stri
|
|||||||
public String call() throws Exception {
|
public String call() throws Exception {
|
||||||
return TextUtils.join("\n", FileUtils.listFilesInDirWithFilter(file, ARCHIVE_FILTER, false));
|
return TextUtils.join("\n", FileUtils.listFilesInDirWithFilter(file, ARCHIVE_FILTER, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
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"));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,17 +48,14 @@ import org.jetbrains.annotations.Contract;
|
|||||||
*/
|
*/
|
||||||
public class WidgetDragListener implements View.OnDragListener {
|
public class WidgetDragListener implements View.OnDragListener {
|
||||||
|
|
||||||
|
public static final String ANDROID_NS = "android";
|
||||||
|
private static final ILogger LOG = ILogger.newInstance("WidgetDragListener");
|
||||||
private final IView placeholder;
|
private final IView placeholder;
|
||||||
private final IViewGroup viewGroup;
|
private final IViewGroup viewGroup;
|
||||||
private final OnViewAddedListener addedListener;
|
private final OnViewAddedListener addedListener;
|
||||||
|
|
||||||
private final int PLACEHOLDER_HEIGHT = 20; // in dp
|
private final int PLACEHOLDER_HEIGHT = 20; // in dp
|
||||||
private final int PLACEHOLDER_WIDTH = 40; // in dp
|
private final int PLACEHOLDER_WIDTH = 40; // in dp
|
||||||
|
|
||||||
public static final String ANDROID_NS = "android";
|
|
||||||
|
|
||||||
private static final ILogger LOG = ILogger.newInstance("WidgetDragListener");
|
|
||||||
|
|
||||||
public WidgetDragListener(
|
public WidgetDragListener(
|
||||||
@NonNull Context context, IViewGroup viewGroup, OnViewAddedListener addedListener) {
|
@NonNull Context context, IViewGroup viewGroup, OnViewAddedListener addedListener) {
|
||||||
this.placeholder = new UiView("android.view.View", new View(context), true);
|
this.placeholder = new UiView("android.view.View", new View(context), true);
|
||||||
@@ -127,6 +124,18 @@ public class WidgetDragListener implements View.OnDragListener {
|
|||||||
view.addAttribute(layoutHeightAttr());
|
view.addAttribute(layoutHeightAttr());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the index at which the placeholder will be added in the current view group.
|
* 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;
|
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
|
@NonNull
|
||||||
private IView createView(@NonNull UIWidget widget) {
|
private IView createView(@NonNull UIWidget widget) {
|
||||||
try {
|
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 {
|
public static interface OnViewAddedListener {
|
||||||
void onViewAdded(IView view);
|
void onViewAdded(IView view);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,11 +62,6 @@ public class WidgetTouchListener extends GestureDetector.SimpleOnGestureListener
|
|||||||
return mGestureDetector.onTouchEvent(motionEvent);
|
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?
|
// TODO Should we use onSingleTapConfirmed instead?
|
||||||
@Override
|
@Override
|
||||||
public boolean onSingleTapUp(MotionEvent e) {
|
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.
|
* A listener which is invoked when we detect a single click event.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -34,18 +34,6 @@ import org.jetbrains.annotations.Contract;
|
|||||||
*/
|
*/
|
||||||
public class DialogUtils {
|
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: <span>Yes</span> and <span>No</span>. This method
|
* Create a new alert dialog with two buttons: <span>Yes</span> and <span>No</span>. This method
|
||||||
* simply calls {@link #newYesNoDialog(Context, String, String, DialogInterface.OnClickListener,
|
* simply calls {@link #newYesNoDialog(Context, String, String, DialogInterface.OnClickListener,
|
||||||
@@ -98,4 +86,16 @@ public class DialogUtils {
|
|||||||
|
|
||||||
return builder;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ import com.itsaky.androidide.databinding.LayoutEditorBottomSheetBinding;
|
|||||||
|
|
||||||
public class EditorBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {
|
public class EditorBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {
|
||||||
|
|
||||||
private LayoutEditorBottomSheetBinding binding;
|
|
||||||
private static final ILogger LOG = ILogger.newInstance("EditorBottomSheetBehavior");
|
private static final ILogger LOG = ILogger.newInstance("EditorBottomSheetBehavior");
|
||||||
|
private LayoutEditorBottomSheetBinding binding;
|
||||||
|
|
||||||
public EditorBottomSheetBehavior(Context context, AttributeSet attributeSet) {
|
public EditorBottomSheetBehavior(Context context, AttributeSet attributeSet) {
|
||||||
super(context, attributeSet);
|
super(context, attributeSet);
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import java.util.List;
|
|||||||
|
|
||||||
public class HighlightRangeHelper {
|
public class HighlightRangeHelper {
|
||||||
|
|
||||||
|
private static final ILogger LOG = ILogger.newInstance("HighlightRangeHelper");
|
||||||
private final List<HighlightToken> highlights;
|
private final List<HighlightToken> highlights;
|
||||||
|
|
||||||
public HighlightRangeHelper(List<HighlightToken> highlights) {
|
public HighlightRangeHelper(List<HighlightToken> highlights) {
|
||||||
@@ -54,6 +55,36 @@ public class HighlightRangeHelper {
|
|||||||
return isOfKind(line, column, ENUM_TYPE);
|
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) {
|
public boolean isAnnotationType(int line, int column) {
|
||||||
return isOfKind(line, column, ANNOTATION);
|
return isOfKind(line, column, ANNOTATION);
|
||||||
}
|
}
|
||||||
@@ -121,36 +152,4 @@ public class HighlightRangeHelper {
|
|||||||
public boolean isLocal(int line, int column) {
|
public boolean isLocal(int line, int column) {
|
||||||
return isOfKind(line, column, LOCAL_VARIABLE);
|
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");
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,6 @@
|
|||||||
**************************************************************************************/
|
**************************************************************************************/
|
||||||
package com.itsaky.androidide.utils;
|
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.Position;
|
||||||
import com.itsaky.lsp.models.Range;
|
import com.itsaky.lsp.models.Range;
|
||||||
|
|
||||||
@@ -31,19 +29,41 @@ public class LSPUtils {
|
|||||||
|
|
||||||
public static final Position Position_ofZero;
|
public static final Position Position_ofZero;
|
||||||
public static final Range Range_ofZero;
|
public static final Range Range_ofZero;
|
||||||
|
public static final Comparator<Range> 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 {
|
static {
|
||||||
Position_ofZero = new Position(0, 0);
|
Position_ofZero = new Position(0, 0);
|
||||||
Range_ofZero = new Range(Position_ofZero, Position_ofZero);
|
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) {
|
public static boolean isInRange(Range range, Position position) {
|
||||||
final int line = position.getLine();
|
final int line = position.getLine();
|
||||||
final int column = position.getColumn();
|
final int column = position.getColumn();
|
||||||
@@ -67,6 +87,13 @@ public class LSPUtils {
|
|||||||
&& LSPUtils.isEqual(r1.getEnd(), r2.getEnd());
|
&& 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) {
|
public static Range getSingleLineRange(int line, int column, int length) {
|
||||||
final Range range = new Range();
|
final Range range = new Range();
|
||||||
range.setStart(new Position(line, column));
|
range.setStart(new Position(line, column));
|
||||||
@@ -102,34 +129,4 @@ public class LSPUtils {
|
|||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Comparator<Range> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,8 +46,6 @@ import java.util.zip.ZipInputStream;
|
|||||||
|
|
||||||
public class ProjectWriter {
|
public class ProjectWriter {
|
||||||
|
|
||||||
private static ProjectWriterCallback callback;
|
|
||||||
|
|
||||||
private static final String XML_TEMPLATE_PATH = "templates/xml";
|
private static final String XML_TEMPLATE_PATH = "templates/xml";
|
||||||
private static final String SOURCE_PATH_REGEX = "/.*/src/.*/java|kt";
|
private static final String SOURCE_PATH_REGEX = "/.*/src/.*/java|kt";
|
||||||
private static final String FILE_EXT_REGEX = ".*/java|kt|gradle|xml|txt|cpp";
|
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}",
|
PACKAGE_NAME_CPP = "{$package_name_cpp}",
|
||||||
CPP_FLAGS = "$cpp_flags",
|
CPP_FLAGS = "$cpp_flags",
|
||||||
LIBRARY_NAME = "$library_name";
|
LIBRARY_NAME = "$library_name";
|
||||||
|
private static ProjectWriterCallback callback;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static String createMenu() {
|
public static String createMenu() {
|
||||||
|
|||||||
@@ -41,15 +41,6 @@ public class Symbols {
|
|||||||
return new Symbol[0];
|
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() {
|
public static Symbol[] javaSymbols() {
|
||||||
return new Symbol[] {
|
return new Symbol[] {
|
||||||
new Symbol("↹", "\t"),
|
new Symbol("↹", "\t"),
|
||||||
@@ -105,4 +96,13 @@ public class Symbols {
|
|||||||
new Symbol("-")
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,13 @@ import androidx.lifecycle.LiveData;
|
|||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
import com.blankj.utilcode.util.CollectionUtils;
|
import com.blankj.utilcode.util.CollectionUtils;
|
||||||
|
|
||||||
import com.itsaky.androidide.R;
|
import com.itsaky.androidide.R;
|
||||||
import com.itsaky.androidide.interfaces.ProjectWriterCallback;
|
import com.itsaky.androidide.interfaces.ProjectWriterCallback;
|
||||||
import com.itsaky.androidide.models.NewProjectDetails;
|
import com.itsaky.androidide.models.NewProjectDetails;
|
||||||
import com.itsaky.androidide.models.ProjectTemplate;
|
import com.itsaky.androidide.models.ProjectTemplate;
|
||||||
|
|
||||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||||
import com.itsaky.androidide.tasks.callables.ProjectCreatorCallable;
|
import com.itsaky.androidide.tasks.callables.ProjectCreatorCallable;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|||||||
@@ -103,17 +103,17 @@ public class MaterialBanner extends CoordinatorLayout {
|
|||||||
|
|
||||||
Animation anim =
|
Animation anim =
|
||||||
new Animation() {
|
new Animation() {
|
||||||
|
@Override
|
||||||
|
public boolean willChangeBounds() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyTransformation(float time, Transformation t) {
|
public void applyTransformation(float time, Transformation t) {
|
||||||
p.height = time == 1f ? -2 : (int) (height * time);
|
p.height = time == 1f ? -2 : (int) (height * time);
|
||||||
root.setLayoutParams(p);
|
root.setLayoutParams(p);
|
||||||
root.requestLayout();
|
root.requestLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean willChangeBounds() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
anim.setDuration(200);
|
anim.setDuration(200);
|
||||||
@@ -126,6 +126,11 @@ public class MaterialBanner extends CoordinatorLayout {
|
|||||||
final int initialHeight = getMeasuredHeight();
|
final int initialHeight = getMeasuredHeight();
|
||||||
Animation animation =
|
Animation animation =
|
||||||
new Animation() {
|
new Animation() {
|
||||||
|
@Override
|
||||||
|
public boolean willChangeBounds() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyTransformation(float interpolatedTime, Transformation t) {
|
public void applyTransformation(float interpolatedTime, Transformation t) {
|
||||||
if (interpolatedTime == 1f) {
|
if (interpolatedTime == 1f) {
|
||||||
@@ -136,11 +141,6 @@ public class MaterialBanner extends CoordinatorLayout {
|
|||||||
root.requestLayout();
|
root.requestLayout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean willChangeBounds() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
animation.setDuration((long) (initialHeight / getResources().getDisplayMetrics().density));
|
animation.setDuration((long) (initialHeight / getResources().getDisplayMetrics().density));
|
||||||
root.startAnimation(animation);
|
root.startAnimation(animation);
|
||||||
@@ -150,40 +150,40 @@ public class MaterialBanner extends CoordinatorLayout {
|
|||||||
return setIcon(ContextCompat.getDrawable(getContext(), icon));
|
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) {
|
public MaterialBanner setIcon(Drawable icon) {
|
||||||
this.iconDrawable = icon;
|
this.iconDrawable = icon;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MaterialBanner setPositive(@StringRes int text, OnPositiveClickListener listener) {
|
||||||
|
return setPositive(getContext().getString(text), listener);
|
||||||
|
}
|
||||||
|
|
||||||
public MaterialBanner setPositive(String text, OnPositiveClickListener listener) {
|
public MaterialBanner setPositive(String text, OnPositiveClickListener listener) {
|
||||||
this.rightButtonText = text;
|
this.rightButtonText = text;
|
||||||
this.positiveListener = listener;
|
this.positiveListener = listener;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MaterialBanner setNegative(@StringRes int text, OnNegativeClickListener listener) {
|
||||||
|
return setNegative(getContext().getString(text), listener);
|
||||||
|
}
|
||||||
|
|
||||||
public MaterialBanner setNegative(String text, OnNegativeClickListener listener) {
|
public MaterialBanner setNegative(String text, OnNegativeClickListener listener) {
|
||||||
this.leftButtonText = text;
|
this.leftButtonText = text;
|
||||||
this.negativeListener = listener;
|
this.negativeListener = listener;
|
||||||
return this;
|
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) {
|
public MaterialBanner setBannerBackgroundColor(int backgroundColor) {
|
||||||
this.backgroundColor = backgroundColor;
|
this.backgroundColor = backgroundColor;
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.itsaky.androidide.views;
|
package com.itsaky.androidide.views
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
@@ -9,11 +9,11 @@ import com.google.android.material.tabs.TabLayout
|
|||||||
class ScalableTabLayout : TabLayout {
|
class ScalableTabLayout : TabLayout {
|
||||||
constructor(context: Context) : super(context)
|
constructor(context: Context) : super(context)
|
||||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
constructor(
|
||||||
context,
|
context: Context,
|
||||||
attrs,
|
attrs: AttributeSet?,
|
||||||
defStyleAttr
|
defStyleAttr: Int
|
||||||
)
|
) : super(context, attrs, defStyleAttr)
|
||||||
|
|
||||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||||
val tabLayout = getChildAt(0) as ViewGroup
|
val tabLayout = getChildAt(0) as ViewGroup
|
||||||
|
|||||||
@@ -68,14 +68,14 @@ public class SymbolInputView extends RecyclerView {
|
|||||||
this(both, both, offset);
|
this(both, both, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Symbol(String label, String commit) {
|
|
||||||
this(label, commit, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Symbol(String label, String commit, int offset) {
|
public Symbol(String label, String commit, int offset) {
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.commit = commit;
|
this.commit = commit;
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Symbol(String label, String commit) {
|
||||||
|
this(label, commit, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,8 +83,13 @@ public abstract class BaseEditorWindow extends EditorPopupWindow {
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected View getRootView() {
|
protected Drawable createBackground() {
|
||||||
return this.text;
|
GradientDrawable background = new GradientDrawable();
|
||||||
|
background.setShape(GradientDrawable.RECTANGLE);
|
||||||
|
background.setColor(0xff212121);
|
||||||
|
background.setStroke(1, 0xffffffff);
|
||||||
|
background.setCornerRadius(8);
|
||||||
|
return background;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void displayWindow() {
|
public void displayWindow() {
|
||||||
@@ -99,13 +104,8 @@ public abstract class BaseEditorWindow extends EditorPopupWindow {
|
|||||||
show();
|
show();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Drawable createBackground() {
|
protected View getRootView() {
|
||||||
GradientDrawable background = new GradientDrawable();
|
return this.text;
|
||||||
background.setShape(GradientDrawable.RECTANGLE);
|
|
||||||
background.setColor(0xff212121);
|
|
||||||
background.setStroke(1, 0xffffffff);
|
|
||||||
background.setCornerRadius(8);
|
|
||||||
return background;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|||||||
@@ -134,6 +134,11 @@ public class CodeEditorView extends FrameLayout {
|
|||||||
return bundle;
|
return bundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public File getFile() {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
private void handleContentChange(@NonNull ContentChangeEvent event) {
|
private void handleContentChange(@NonNull ContentChangeEvent event) {
|
||||||
isModified = true;
|
isModified = true;
|
||||||
|
|
||||||
@@ -157,96 +162,54 @@ public class CodeEditorView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean save() {
|
private void closeCurrentTag(String text, int line, int col) {
|
||||||
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 {
|
try {
|
||||||
FileUtil.writeFile(file, text);
|
XMLLexer lexer = new XMLLexer(CharStreams.fromReader(new StringReader(text)));
|
||||||
notifySaved();
|
Token token;
|
||||||
isModified = false;
|
boolean wasSlash = false, wasOpen = false;
|
||||||
return true;
|
ArrayList<String> currentNames = new ArrayList<>();
|
||||||
} catch (IOException io) {
|
while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) {
|
||||||
LOG.error("Failed to save file", file, io);
|
final int type = token.getType();
|
||||||
return false;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
if (currentNames.size() > 0) {
|
||||||
private List<CharSequence> getLines(Content text) {
|
binding.editor.getText().insert(line, col + 2, currentNames.get(0));
|
||||||
final var count = text.getLineCount();
|
}
|
||||||
final var result = new ArrayList<CharSequence>();
|
} catch (Throwable th) {
|
||||||
|
LOG.error("Unable to close current tag", th);
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
result.add(text.getLine(i));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDEEditor getEditor() {
|
public IDEEditor getEditor() {
|
||||||
return binding.editor;
|
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() {
|
protected void postRead() {
|
||||||
binding.editor.setEditorLanguage(createLanguage(file));
|
binding.editor.setEditorLanguage(createLanguage(file));
|
||||||
binding.editor.setLanguageServer(createLanguageServer(file));
|
binding.editor.setLanguageServer(createLanguageServer(file));
|
||||||
@@ -381,48 +344,34 @@ public class CodeEditorView extends FrameLayout {
|
|||||||
isFirstCreate = false;
|
isFirstCreate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void closeCurrentTag(String text, int line, int col) {
|
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 {
|
try {
|
||||||
XMLLexer lexer = new XMLLexer(CharStreams.fromReader(new StringReader(text)));
|
FileUtil.writeFile(file, text);
|
||||||
Token token;
|
notifySaved();
|
||||||
boolean wasSlash = false, wasOpen = false;
|
isModified = false;
|
||||||
ArrayList<String> currentNames = new ArrayList<>();
|
return true;
|
||||||
while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) {
|
} catch (IOException io) {
|
||||||
final int type = token.getType();
|
LOG.error("Failed to save file", file, io);
|
||||||
if (type == XMLLexer.OPEN) {
|
return false;
|
||||||
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) {
|
public boolean isModified() {
|
||||||
binding.editor.getText().insert(line, col + 2, currentNames.get(0));
|
return isModified;
|
||||||
}
|
|
||||||
} catch (Throwable th) {
|
|
||||||
LOG.error("Unable to close current tag", th);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifySaved() {
|
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. */
|
/** Mark this files as saved. Even if it not saved. */
|
||||||
public void markAsSaved() {
|
public void markAsSaved() {
|
||||||
isModified = false;
|
isModified = false;
|
||||||
notifySaved();
|
notifySaved();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private List<CharSequence> getLines(Content text) {
|
||||||
|
final var count = text.getLineCount();
|
||||||
|
final var result = new ArrayList<CharSequence>();
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
result.add(text.getLine(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,49 +121,6 @@ public class IDEEditor extends CodeEditor {
|
|||||||
getDiagnosticWindow().showDiagnostic(mLanguageClient.getDiagnosticAt(getFile(), line, column));
|
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<DiagnosticItem> diagnostics, int line, int column) {
|
|
||||||
return DiagnosticUtil.binarySearchDiagnostic(diagnostics, line, column);
|
|
||||||
}
|
|
||||||
|
|
||||||
private DiagnosticWindow getDiagnosticWindow() {
|
private DiagnosticWindow getDiagnosticWindow() {
|
||||||
if (mDiagnosticWindow == null) {
|
if (mDiagnosticWindow == null) {
|
||||||
mDiagnosticWindow = new DiagnosticWindow(this);
|
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
|
* Checks if the content change event should trigger signature help. Signature help trigger
|
||||||
* characters are :
|
* characters are :
|
||||||
@@ -292,6 +272,20 @@ public class IDEEditor extends CodeEditor {
|
|||||||
return mSignatureHelpWindow;
|
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.
|
* 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.
|
* 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);
|
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
|
* If any language server instance is set, finds the references to of the token at the current
|
||||||
* cursor position.
|
* cursor position.
|
||||||
@@ -850,4 +844,10 @@ public class IDEEditor extends CodeEditor {
|
|||||||
LOG.error("Unable start search action mode. Activity must inherit AppCompatActivity.");
|
LOG.error("Unable start search action mode. Activity must inherit AppCompatActivity.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private DiagnosticItem binarySearchDiagnostic(
|
||||||
|
@NonNull List<DiagnosticItem> diagnostics, int line, int column) {
|
||||||
|
return DiagnosticUtil.binarySearchDiagnostic(diagnostics, line, column);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,58 @@ public class CubicBezierArc {
|
|||||||
calculateControlPoints(angle, start, end);
|
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
|
* Returns the start point
|
||||||
*
|
*
|
||||||
@@ -122,56 +174,4 @@ public class CubicBezierArc {
|
|||||||
this.y = y;
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
@Override
|
||||||
public void captureStartValues(TransitionValues transitionValues) {
|
public void captureStartValues(TransitionValues transitionValues) {
|
||||||
captureValues(transitionValues);
|
captureValues(transitionValues);
|
||||||
@@ -61,19 +76,4 @@ public class ProgressTransition extends Transition {
|
|||||||
transitionValues.values.put(PROPNAME_PROGRESS, progressBar.getProgress());
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,17 +55,16 @@ open class IDEEditorSearcher(editor: IDEEditor) : EditorSearcher(editor) {
|
|||||||
getEditor().context.getString(string.replaceAll),
|
getEditor().context.getString(string.replaceAll),
|
||||||
getEditor().context.getString(string.msg_replacing),
|
getEditor().context.getString(string.msg_replacing),
|
||||||
true,
|
true,
|
||||||
false)
|
false
|
||||||
return CompletableFuture.runAsync { replaceAll(replacement) }.thenAccept {
|
)
|
||||||
getEditor().post { dialog.dismiss() }
|
return CompletableFuture.runAsync { replaceAll(replacement) }
|
||||||
}
|
.thenAccept { getEditor().post { dialog.dismiss() } }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceAll(replacement: String) {
|
override fun replaceAll(replacement: String) {
|
||||||
checkState()
|
checkState()
|
||||||
if (!isResultValid) {
|
if (!isResultValid) {
|
||||||
Toast.makeText(getEditor().context, "Editor is still preparing", Toast.LENGTH_SHORT)
|
Toast.makeText(getEditor().context, "Editor is still preparing", Toast.LENGTH_SHORT).show()
|
||||||
.show()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,9 +85,11 @@ open class IDEEditorSearcher(editor: IDEEditor) : EditorSearcher(editor) {
|
|||||||
} else {
|
} else {
|
||||||
var fromIndex = 0
|
var fromIndex = 0
|
||||||
var foundIndex: Int
|
var foundIndex: Int
|
||||||
while (TextUtils.indexOf(sb, mPattern, mOptions.ignoreCase, fromIndex).also {
|
while (
|
||||||
|
TextUtils.indexOf(sb, mPattern, mOptions.ignoreCase, fromIndex).also {
|
||||||
foundIndex = it
|
foundIndex = it
|
||||||
} != -1) {
|
} != -1
|
||||||
|
) {
|
||||||
sb.replace(foundIndex, foundIndex + mPattern.length, replacement)
|
sb.replace(foundIndex, foundIndex + mPattern.length, replacement)
|
||||||
fromIndex = foundIndex + newLength
|
fromIndex = foundIndex + newLength
|
||||||
}
|
}
|
||||||
@@ -103,7 +104,8 @@ open class IDEEditorSearcher(editor: IDEEditor) : EditorSearcher(editor) {
|
|||||||
0,
|
0,
|
||||||
getEditor().lineCount - 1,
|
getEditor().lineCount - 1,
|
||||||
getEditor().text.getColumnCount(getEditor().lineCount - 1),
|
getEditor().text.getColumnCount(getEditor().lineCount - 1),
|
||||||
sb)
|
sb
|
||||||
|
)
|
||||||
getEditor().setSelectionAround(pos.line, pos.column)
|
getEditor().setSelectionAround(pos.line, pos.column)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<set
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android" >
|
|
||||||
|
|
||||||
<alpha android:fromAlpha="0.0"
|
<alpha
|
||||||
android:toAlpha="1.0"
|
|
||||||
android:interpolator="@android:anim/accelerate_interpolator"
|
|
||||||
android:duration="100"
|
android:duration="100"
|
||||||
android:repeatCount="0"/>
|
android:fromAlpha="0.0"
|
||||||
|
android:interpolator="@android:anim/accelerate_interpolator"
|
||||||
|
android:repeatCount="0"
|
||||||
|
android:toAlpha="1.0" />
|
||||||
|
|
||||||
<scale
|
<scale
|
||||||
android:duration="100"
|
android:duration="100"
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<set
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android" >
|
|
||||||
|
|
||||||
<alpha android:fromAlpha="1.0"
|
<alpha
|
||||||
android:toAlpha="0.0"
|
|
||||||
android:interpolator="@android:anim/accelerate_interpolator"
|
|
||||||
android:duration="100"
|
android:duration="100"
|
||||||
android:repeatCount="0"/>
|
android:fromAlpha="1.0"
|
||||||
|
android:interpolator="@android:anim/accelerate_interpolator"
|
||||||
|
android:repeatCount="0"
|
||||||
|
android:toAlpha="0.0" />
|
||||||
|
|
||||||
<scale
|
<scale
|
||||||
android:duration="100"
|
android:duration="100"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user