mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Use ViewFlipper instead of ViewPager2 for editor fragments
This commit is contained in:
@@ -1,32 +1,32 @@
|
||||
---
|
||||
name: Bug
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG]"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Details (please complete the following information):**
|
||||
- Device: [e.g. realme 3 Pro]
|
||||
- OS: [e.g. Android 11]
|
||||
- AndroidIDE Version [e.g. v1.0.3-alpha]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
---
|
||||
name: Bug
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG]"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Details (please complete the following information):**
|
||||
- Device: [e.g. realme 3 Pro]
|
||||
- OS: [e.g. Android 11]
|
||||
- AndroidIDE Version [e.g. v1.0.3-alpha]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[FEATURE]: "
|
||||
labels: feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[FEATURE]: "
|
||||
labels: feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
name: Android CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build APK
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Cancel previous runs
|
||||
uses: styfle/cancel-workflow-action@0.5.0
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
- name: Clone repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Build debug apk
|
||||
uses: eskatos/gradle-command-action@v1
|
||||
with:
|
||||
arguments: assembleDebug
|
||||
distributions-cache-enabled: true
|
||||
dependencies-cache-enabled: true
|
||||
configuration-cache-enabled: true
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: ${{ !github.head_ref }}
|
||||
with:
|
||||
name: apk-debug
|
||||
name: Android CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build APK
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Cancel previous runs
|
||||
uses: styfle/cancel-workflow-action@0.5.0
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
- name: Clone repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Build debug apk
|
||||
uses: eskatos/gradle-command-action@v1
|
||||
with:
|
||||
arguments: assembleDebug
|
||||
distributions-cache-enabled: true
|
||||
dependencies-cache-enabled: true
|
||||
configuration-cache-enabled: true
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: ${{ !github.head_ref }}
|
||||
with:
|
||||
name: apk-debug
|
||||
path: app/build/outputs/apk/debug
|
||||
Generated
+116
-116
@@ -1,117 +1,117 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<codeStyleSettings language="XML">
|
||||
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<codeStyleSettings language="XML">
|
||||
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
Generated
+4
-4
@@ -1,5 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
||||
Generated
+1
@@ -13,6 +13,7 @@
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/activity_designer.xml" value="0.264" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/activity_download.xml" value="0.1188034188034188" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/activity_editor.xml" value="0.264" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/fragment_editor.xml" value="0.20512820512820512" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/fragment_non_editable_editor.xml" value="0.20512820512820512" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_about_items.xml" value="0.20512820512820512" />
|
||||
<entry key="..\:/Projects/AndroidStudioProjects/AndroidIDE/app/src/main/res/layout/layout_diagnostic_info.xml" value="0.20512820512820512" />
|
||||
|
||||
Generated
+5
-5
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RenderSettings">
|
||||
<option name="useLiveRendering" value="false" />
|
||||
</component>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RenderSettings">
|
||||
<option name="useLiveRendering" value="false" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,127 +1,127 @@
|
||||
# AndroidIDE
|
||||
AndroidIDE is an IDE for Android to develop full featured Android apps on Android smartphones.
|
||||
|
||||
## Main Features
|
||||
- Supports Gradle
|
||||
- Comes with OpenJDK 11.0.12
|
||||
- Provides auto completion for Java and XML. It also has basic (reserved) completion for Gradle files.
|
||||
|
||||
More features will be implemented as we continue to develop this app.
|
||||
|
||||
## Features to be implemented
|
||||
- [x] Advanced Java Auto Complete
|
||||
- [x] Terminal
|
||||
- [x] Custom environment variables (for Build & Terminal)
|
||||
- [x] SDK Manager (Available via terminal)
|
||||
- [x] Gradlew support (with some limitations)
|
||||
- [x] API information for classes and their members (since, removed, deprecated).
|
||||
- [ ] Implement language server for XML and Kotlin
|
||||
- [ ] UI Designer
|
||||
- [x] Layout inflater
|
||||
- [ ] Resolve resource references
|
||||
- [ ] Auto-complete resource values when user edits attributes using the attribute editor
|
||||
- [ ] Drag & Drop (*)
|
||||
- [ ] Visual attribute editor (*)
|
||||
- [ ] Android Widgets (*)
|
||||
- [ ] String Translator
|
||||
- [ ] Asset Studio (Drawable & Icon Maker)
|
||||
- [ ] Git
|
||||
|
||||
Items marked with ```(*)``` are partially implemented.
|
||||
|
||||
## Limitations
|
||||
- Gradle Wrapper is supported. But, it requires that the project's application module is named 'app'.
|
||||
- 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 Android Gradle Plugin versions other than v7.0.2 are supported (due to AAPT2).
|
||||
|
||||
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.
|
||||
|
||||
## Issues/suggestions
|
||||
You should report bugs and suggest features/enhancements [here](https://github.com/itsaky/AndroidIDE/issues).
|
||||
Describe the issue/request briefly. Provide steps to reproduce the issue. If you need any help, you should discuss it [here](https://t.me/androidide_discussions).
|
||||
|
||||
If you open a build related issue, please provide these details:
|
||||
- Device model
|
||||
- Android Version
|
||||
- Full build output with stacktrace.
|
||||
|
||||
## Screenshots
|
||||
| Main Screen | Terminal |
|
||||
| ----------- | -------- |
|
||||
| <img src="images/main_screen.jpg" width="100" height="200" alt="AndroidIDE Main Screen" /> | <img src="images/terminal.jpg" width="100" height="200" alt="AndroidIDE Terminal" /> |
|
||||
|
||||
| Build Output | Java Completion |
|
||||
| ------------ | --------------- |
|
||||
| <img src="images/build_output.jpg" width="100" height="200" alt="AndroidIDE Build Output" /> | <img src="images/java_completion.jpg" width="100" height="200" alt="AndroidIDE Java Completion" /> |
|
||||
|
||||
| XML Completion | Screenshot |
|
||||
| -------------- | ---------- |
|
||||
| Widgets | <img src="images/xml_completion_widgets.jpg" width="100" height="200" alt="AndroidIDE XML Completion - Widgets" />
|
||||
| Attributes | <img src="images/xml_completion_attributes.jpg" width="100" height="200" alt="AndroidIDE XML Completion - Attributes" />
|
||||
| Attribute Values | <img src="images/xml_completion_enums.jpg" width="100" height="200" alt="AndroidIDE XML Completion - Attribute Values" />
|
||||
|
||||
| Gradle Support | File Tree |
|
||||
| -------------- | --------- |
|
||||
| <img src="images/gradle_buildscript.jpg" width="100" height="200" alt="AndroidIDE Gradle Support" /> | <img src="images/file_tree.jpg" width="100" height="200" alt="AndroidIDE File Tree" /> |
|
||||
|
||||
## Posts to help you get started
|
||||
- [Installing AndroidIDE](https://telegra.ph/How-to-install-AndroidIDE-09-11)
|
||||
- Author: [Marvin Stelter](https://github.com/MarvinStelter)
|
||||
- [Create your first project](https://itsaky.github.io/create-your-first-project-in-androidIDE/)
|
||||
- Author: [Akash Yadav](https://github.com/itsaky)
|
||||
|
||||
## Developers
|
||||
This project was initially developed by [Akash Yadav](https://github.com/itsaky) and [Marvin Stelter](https://github.com/MarvinStelter). We appreciate the efforts of anyone who contributes to this project.
|
||||
|
||||
## Contributing
|
||||
You can contribute to AndroidIDE in one of the following ways :
|
||||
- Fix bugs
|
||||
- Implement new features
|
||||
- Report bugs
|
||||
- Suggest a change
|
||||
- Request a feature
|
||||
- Improve the documentation
|
||||
|
||||
If you create new source files, please make sure you include the license header in every file.
|
||||
|
||||
### Things to take care of
|
||||
There are some things that you should take care of while writing related code.
|
||||
|
||||
- Layout Inflater adapters
|
||||
- [Read the docs](./layout-inflater/src/main/java/com/itsaky/layoutinflater/adapters/README.md) about layout inflater adapters.
|
||||
|
||||
### Any other ways to contribute?
|
||||
You can also write blog posts or create YouTube videos to help newbies get started.
|
||||
If you do and want us to feature it here, open an issue.
|
||||
|
||||
## Thanks to
|
||||
- [Rosemoe](https://github.com/Rosemoe) for the awesome [CodeEditor](https://github.com/Rosemoe/sora-editor)
|
||||
- [Termux](https://github.com/termux) for [Terminal Emulator](https://github.com/termux/termux-app)
|
||||
- [Bogdan Melnychuk](https://github.com/bmelnychuk) for [AndroidTreeView](https://github.com/bmelnychuk/AndroidTreeView)
|
||||
- [George Fraser](https://github.com/georgewfraser) for the [Java Language Server](https://github.com/georgewfraser/java-language-server)
|
||||
- [Eclipse Foundation](https://github.com/eclipse) for [LSP4J](https://github.com/eclipse/lsp4j)
|
||||
|
||||
And all the developers who have contributed to this project.
|
||||
|
||||
## Contact Us
|
||||
- [Website](https://androidide.com)
|
||||
- [Telegram](https://t.me/androidide_discussions)
|
||||
|
||||
## License
|
||||
```
|
||||
AndroidIDE is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
AndroidIDE is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with AndroidIDE. If not, see <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.
|
||||
# AndroidIDE
|
||||
AndroidIDE is an IDE for Android to develop full featured Android apps on Android smartphones.
|
||||
|
||||
## Main Features
|
||||
- Supports Gradle
|
||||
- Comes with OpenJDK 11.0.12
|
||||
- Provides auto completion for Java and XML. It also has basic (reserved) completion for Gradle files.
|
||||
|
||||
More features will be implemented as we continue to develop this app.
|
||||
|
||||
## Features to be implemented
|
||||
- [x] Advanced Java Auto Complete
|
||||
- [x] Terminal
|
||||
- [x] Custom environment variables (for Build & Terminal)
|
||||
- [x] SDK Manager (Available via terminal)
|
||||
- [x] Gradlew support (with some limitations)
|
||||
- [x] API information for classes and their members (since, removed, deprecated).
|
||||
- [ ] Implement language server for XML and Kotlin
|
||||
- [ ] UI Designer
|
||||
- [x] Layout inflater
|
||||
- [ ] Resolve resource references
|
||||
- [ ] Auto-complete resource values when user edits attributes using the attribute editor
|
||||
- [ ] Drag & Drop (*)
|
||||
- [ ] Visual attribute editor (*)
|
||||
- [ ] Android Widgets (*)
|
||||
- [ ] String Translator
|
||||
- [ ] Asset Studio (Drawable & Icon Maker)
|
||||
- [ ] Git
|
||||
|
||||
Items marked with ```(*)``` are partially implemented.
|
||||
|
||||
## Limitations
|
||||
- Gradle Wrapper is supported. But, it requires that the project's application module is named 'app'.
|
||||
- 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 Android Gradle Plugin versions other than v7.0.2 are supported (due to AAPT2).
|
||||
|
||||
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.
|
||||
|
||||
## Issues/suggestions
|
||||
You should report bugs and suggest features/enhancements [here](https://github.com/itsaky/AndroidIDE/issues).
|
||||
Describe the issue/request briefly. Provide steps to reproduce the issue. If you need any help, you should discuss it [here](https://t.me/androidide_discussions).
|
||||
|
||||
If you open a build related issue, please provide these details:
|
||||
- Device model
|
||||
- Android Version
|
||||
- Full build output with stacktrace.
|
||||
|
||||
## Screenshots
|
||||
| Main Screen | Terminal |
|
||||
| ----------- | -------- |
|
||||
| <img src="images/main_screen.jpg" width="100" height="200" alt="AndroidIDE Main Screen" /> | <img src="images/terminal.jpg" width="100" height="200" alt="AndroidIDE Terminal" /> |
|
||||
|
||||
| Build Output | Java Completion |
|
||||
| ------------ | --------------- |
|
||||
| <img src="images/build_output.jpg" width="100" height="200" alt="AndroidIDE Build Output" /> | <img src="images/java_completion.jpg" width="100" height="200" alt="AndroidIDE Java Completion" /> |
|
||||
|
||||
| XML Completion | Screenshot |
|
||||
| -------------- | ---------- |
|
||||
| Widgets | <img src="images/xml_completion_widgets.jpg" width="100" height="200" alt="AndroidIDE XML Completion - Widgets" />
|
||||
| Attributes | <img src="images/xml_completion_attributes.jpg" width="100" height="200" alt="AndroidIDE XML Completion - Attributes" />
|
||||
| Attribute Values | <img src="images/xml_completion_enums.jpg" width="100" height="200" alt="AndroidIDE XML Completion - Attribute Values" />
|
||||
|
||||
| Gradle Support | File Tree |
|
||||
| -------------- | --------- |
|
||||
| <img src="images/gradle_buildscript.jpg" width="100" height="200" alt="AndroidIDE Gradle Support" /> | <img src="images/file_tree.jpg" width="100" height="200" alt="AndroidIDE File Tree" /> |
|
||||
|
||||
## Posts to help you get started
|
||||
- [Installing AndroidIDE](https://telegra.ph/How-to-install-AndroidIDE-09-11)
|
||||
- Author: [Marvin Stelter](https://github.com/MarvinStelter)
|
||||
- [Create your first project](https://itsaky.github.io/create-your-first-project-in-androidIDE/)
|
||||
- Author: [Akash Yadav](https://github.com/itsaky)
|
||||
|
||||
## Developers
|
||||
This project was initially developed by [Akash Yadav](https://github.com/itsaky) and [Marvin Stelter](https://github.com/MarvinStelter). We appreciate the efforts of anyone who contributes to this project.
|
||||
|
||||
## Contributing
|
||||
You can contribute to AndroidIDE in one of the following ways :
|
||||
- Fix bugs
|
||||
- Implement new features
|
||||
- Report bugs
|
||||
- Suggest a change
|
||||
- Request a feature
|
||||
- Improve the documentation
|
||||
|
||||
If you create new source files, please make sure you include the license header in every file.
|
||||
|
||||
### Things to take care of
|
||||
There are some things that you should take care of while writing related code.
|
||||
|
||||
- Layout Inflater adapters
|
||||
- [Read the docs](./layout-inflater/src/main/java/com/itsaky/layoutinflater/adapters/README.md) about layout inflater adapters.
|
||||
|
||||
### Any other ways to contribute?
|
||||
You can also write blog posts or create YouTube videos to help newbies get started.
|
||||
If you do and want us to feature it here, open an issue.
|
||||
|
||||
## Thanks to
|
||||
- [Rosemoe](https://github.com/Rosemoe) for the awesome [CodeEditor](https://github.com/Rosemoe/sora-editor)
|
||||
- [Termux](https://github.com/termux) for [Terminal Emulator](https://github.com/termux/termux-app)
|
||||
- [Bogdan Melnychuk](https://github.com/bmelnychuk) for [AndroidTreeView](https://github.com/bmelnychuk/AndroidTreeView)
|
||||
- [George Fraser](https://github.com/georgewfraser) for the [Java Language Server](https://github.com/georgewfraser/java-language-server)
|
||||
- [Eclipse Foundation](https://github.com/eclipse) for [LSP4J](https://github.com/eclipse/lsp4j)
|
||||
|
||||
And all the developers who have contributed to this project.
|
||||
|
||||
## Contact Us
|
||||
- [Website](https://androidide.com)
|
||||
- [Telegram](https://t.me/androidide_discussions)
|
||||
|
||||
## License
|
||||
```
|
||||
AndroidIDE is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
AndroidIDE is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with AndroidIDE. If not, see <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.
|
||||
|
||||
@@ -1,94 +1,94 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.itsaky.androidide">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
|
||||
<application
|
||||
android:name=".app.StudioApp"
|
||||
android:allowBackup="false"
|
||||
android:allowNativeHeapPointerTagging="false"
|
||||
android:hardwareAccelerated="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:largeHeap="true"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
tools:ignore="UnusedAttribute,MediaCapabilities">
|
||||
<activity
|
||||
android:name=".CrashHandlerActivity"
|
||||
android:windowSoftInputMode="stateAlwaysHidden"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.itsaky.androidide.REPORT_CRASH" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".SplashActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|screenSize|uiMode" />
|
||||
<activity
|
||||
android:name=".DesignerActivity"
|
||||
android:configChanges="orientation|screenSize|uiMode"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name=".DownloadActivity"
|
||||
android:configChanges="orientation|screenSize|uiMode" />
|
||||
<activity
|
||||
android:name=".EditorActivity"
|
||||
android:configChanges="orientation|screenSize|uiMode"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name=".PreferencesActivity"
|
||||
android:configChanges="orientation|screenSize" />
|
||||
<activity
|
||||
android:name=".AboutActivity"
|
||||
android:configChanges="orientation|screenSize|uiMode" />
|
||||
<activity
|
||||
android:name=".TerminalActivity"
|
||||
android:configChanges="orientation|screenSize|uiMode"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
|
||||
<service
|
||||
android:name=".services.MessagingService"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver
|
||||
android:name=".services.LogReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.itsaky.androidide.logs.APPEND_LOG" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||
android:resource="@mipmap/ic_launcher" />
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_color"
|
||||
android:resource="@color/secondaryColor" />
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_channel_id"
|
||||
android:value="@string/cms_channel_id_devs" />
|
||||
</application>
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.itsaky.androidide">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
|
||||
<application
|
||||
android:name=".app.StudioApp"
|
||||
android:allowBackup="false"
|
||||
android:allowNativeHeapPointerTagging="false"
|
||||
android:hardwareAccelerated="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:largeHeap="true"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
tools:ignore="UnusedAttribute,MediaCapabilities">
|
||||
<activity
|
||||
android:name=".CrashHandlerActivity"
|
||||
android:windowSoftInputMode="stateAlwaysHidden"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.itsaky.androidide.REPORT_CRASH" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".SplashActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|screenSize|uiMode" />
|
||||
<activity
|
||||
android:name=".DesignerActivity"
|
||||
android:configChanges="orientation|screenSize|uiMode"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name=".DownloadActivity"
|
||||
android:configChanges="orientation|screenSize|uiMode" />
|
||||
<activity
|
||||
android:name=".EditorActivity"
|
||||
android:configChanges="orientation|screenSize|uiMode"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name=".PreferencesActivity"
|
||||
android:configChanges="orientation|screenSize" />
|
||||
<activity
|
||||
android:name=".AboutActivity"
|
||||
android:configChanges="orientation|screenSize|uiMode" />
|
||||
<activity
|
||||
android:name=".TerminalActivity"
|
||||
android:configChanges="orientation|screenSize|uiMode"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
|
||||
<service
|
||||
android:name=".services.MessagingService"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver
|
||||
android:name=".services.LogReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.itsaky.androidide.logs.APPEND_LOG" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||
android:resource="@mipmap/ic_launcher" />
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_color"
|
||||
android:resource="@color/secondaryColor" />
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_channel_id"
|
||||
android:value="@string/cms_channel_id_devs" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
/************************************************************************************
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
*
|
||||
@@ -16,19 +16,15 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide;
|
||||
|
||||
import abhishekti7.unicorn.filepicker.UnicornFilePicker;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.transition.Transition;
|
||||
import androidx.transition.TransitionManager;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
@@ -56,6 +52,8 @@ import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import abhishekti7.unicorn.filepicker.UnicornFilePicker;
|
||||
|
||||
public class MainActivity extends StudioActivity implements View.OnClickListener, ProjectWriterCallback {
|
||||
|
||||
private ActivityMainBinding binding;
|
||||
|
||||
@@ -1,99 +1,99 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.adapters;
|
||||
|
||||
import android.graphics.Typeface;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.databinding.LayoutAttrEditorSheetItemBinding;
|
||||
import com.itsaky.androidide.models.XMLAttribute;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AttributeListAdapter extends RecyclerView.Adapter <AttributeListAdapter.VH> {
|
||||
|
||||
private final List <XMLAttribute> attributes;
|
||||
private final OnClickListener clickListener;
|
||||
|
||||
public AttributeListAdapter (List<XMLAttribute> attributes, OnClickListener clickListener) {
|
||||
this.attributes = attributes;
|
||||
this.clickListener = clickListener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder (@NonNull ViewGroup parent, int viewType) {
|
||||
return new VH (LayoutAttrEditorSheetItemBinding.inflate (LayoutInflater.from (parent.getContext ()), parent, false));
|
||||
}
|
||||
|
||||
// hello th
|
||||
//
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder (@NonNull VH holder, int position) {
|
||||
final var binding = holder.binding;
|
||||
final var attr = attributes.get (position);
|
||||
final var sb = new SpannableStringBuilder ();
|
||||
sb.append (attr.getAttributeName ());
|
||||
if (attr.isApplied ()) {
|
||||
sb.append (" = ");
|
||||
sb.append (attr.getValue (), new StyleSpan (Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
final var length = attr.getValue ().length ();
|
||||
sb.setSpan (new ForegroundColorSpan (ContextCompat.getColor (binding.text.getContext (), R.color.primaryTextColor)),
|
||||
sb.length () - length,
|
||||
sb.length () - 1,
|
||||
SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
binding.text.setText (sb);
|
||||
binding.getRoot ().setOnClickListener (v -> {
|
||||
if (this.clickListener != null) {
|
||||
this.clickListener.onClick (binding, attr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount () {
|
||||
return attributes.size ();
|
||||
}
|
||||
|
||||
static class VH extends RecyclerView.ViewHolder {
|
||||
LayoutAttrEditorSheetItemBinding binding;
|
||||
|
||||
VH (@NonNull LayoutAttrEditorSheetItemBinding binding) {
|
||||
super(binding.getRoot ());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnClickListener {
|
||||
void onClick (LayoutAttrEditorSheetItemBinding binding, XMLAttribute attribute);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.adapters;
|
||||
|
||||
import android.graphics.Typeface;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.databinding.LayoutAttrEditorSheetItemBinding;
|
||||
import com.itsaky.androidide.models.XMLAttribute;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AttributeListAdapter extends RecyclerView.Adapter <AttributeListAdapter.VH> {
|
||||
|
||||
private final List <XMLAttribute> attributes;
|
||||
private final OnClickListener clickListener;
|
||||
|
||||
public AttributeListAdapter (List<XMLAttribute> attributes, OnClickListener clickListener) {
|
||||
this.attributes = attributes;
|
||||
this.clickListener = clickListener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder (@NonNull ViewGroup parent, int viewType) {
|
||||
return new VH (LayoutAttrEditorSheetItemBinding.inflate (LayoutInflater.from (parent.getContext ()), parent, false));
|
||||
}
|
||||
|
||||
// hello th
|
||||
//
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder (@NonNull VH holder, int position) {
|
||||
final var binding = holder.binding;
|
||||
final var attr = attributes.get (position);
|
||||
final var sb = new SpannableStringBuilder ();
|
||||
sb.append (attr.getAttributeName ());
|
||||
if (attr.isApplied ()) {
|
||||
sb.append (" = ");
|
||||
sb.append (attr.getValue (), new StyleSpan (Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
final var length = attr.getValue ().length ();
|
||||
sb.setSpan (new ForegroundColorSpan (ContextCompat.getColor (binding.text.getContext (), R.color.primaryTextColor)),
|
||||
sb.length () - length,
|
||||
sb.length () - 1,
|
||||
SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
binding.text.setText (sb);
|
||||
binding.getRoot ().setOnClickListener (v -> {
|
||||
if (this.clickListener != null) {
|
||||
this.clickListener.onClick (binding, attr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount () {
|
||||
return attributes.size ();
|
||||
}
|
||||
|
||||
static class VH extends RecyclerView.ViewHolder {
|
||||
LayoutAttrEditorSheetItemBinding binding;
|
||||
|
||||
VH (@NonNull LayoutAttrEditorSheetItemBinding binding) {
|
||||
super(binding.getRoot ());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnClickListener {
|
||||
void onClick (LayoutAttrEditorSheetItemBinding binding, XMLAttribute attribute);
|
||||
}
|
||||
}
|
||||
|
||||
+118
-118
@@ -1,118 +1,118 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.adapters;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.fragments.SimpleOutputFragment;
|
||||
import com.itsaky.androidide.fragments.DiagnosticsListFragment;
|
||||
import com.itsaky.androidide.fragments.IDELogFragment;
|
||||
import com.itsaky.androidide.fragments.LogViewFragment;
|
||||
import com.itsaky.androidide.fragments.SearchResultFragment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class EditorBottomSheetTabAdapter extends FragmentStateAdapter {
|
||||
|
||||
private final List<Tab> fragments;
|
||||
|
||||
public EditorBottomSheetTabAdapter (@NonNull FragmentActivity fragmentActivity) {
|
||||
super (fragmentActivity);
|
||||
|
||||
this.fragments = new ArrayList<> ();
|
||||
this.fragments.add (new Tab (fragmentActivity.getString (R.string.build_output), new SimpleOutputFragment ()));
|
||||
this.fragments.add (new Tab (fragmentActivity.getString (R.string.app_logs), new LogViewFragment ()));
|
||||
this.fragments.add (new Tab (fragmentActivity.getString (R.string.ide_logs), new IDELogFragment ()));
|
||||
this.fragments.add (new Tab (fragmentActivity.getString (R.string.view_diags), new DiagnosticsListFragment ()));
|
||||
this.fragments.add (new Tab (fragmentActivity.getString (R.string.view_search_results), new SearchResultFragment ()));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment (int position) {
|
||||
return this.fragments.get (position).fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount () {
|
||||
return fragments.size ();
|
||||
}
|
||||
|
||||
public String getTitle (int position) {
|
||||
return fragments.get (position).title;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public SimpleOutputFragment getBuildOutputFragment () {
|
||||
return Objects.requireNonNull (findFragmentByClass (SimpleOutputFragment.class));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public LogViewFragment getLogFragment () {
|
||||
return Objects.requireNonNull (findFragmentByClass (LogViewFragment.class));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public DiagnosticsListFragment getDiagnosticsFragment () {
|
||||
return Objects.requireNonNull (findFragmentByClass (DiagnosticsListFragment.class));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public SearchResultFragment getSearchResultFragment () {
|
||||
return Objects.requireNonNull (findFragmentByClass (SearchResultFragment.class));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <T extends Fragment> T findFragmentByClass (Class<T> clazz) {
|
||||
for (final var tab : this.fragments) {
|
||||
if (tab.fragment.getClass () == clazz) {
|
||||
return (T) tab.fragment;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T extends Fragment> int findIndexOfFragmentByClass (Class<T> tClass) {
|
||||
for (int i = 0; i < this.fragments.size (); i++) {
|
||||
final var tab = this.fragments.get (i);
|
||||
if (tab.fragment.getClass () == tClass) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static class Tab {
|
||||
|
||||
public Tab (String title, Fragment fragment) {
|
||||
this.title = title;
|
||||
this.fragment = fragment;
|
||||
}
|
||||
|
||||
String title;
|
||||
Fragment fragment;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.adapters;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.fragments.SimpleOutputFragment;
|
||||
import com.itsaky.androidide.fragments.DiagnosticsListFragment;
|
||||
import com.itsaky.androidide.fragments.IDELogFragment;
|
||||
import com.itsaky.androidide.fragments.LogViewFragment;
|
||||
import com.itsaky.androidide.fragments.SearchResultFragment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class EditorBottomSheetTabAdapter extends FragmentStateAdapter {
|
||||
|
||||
private final List<Tab> fragments;
|
||||
|
||||
public EditorBottomSheetTabAdapter (@NonNull FragmentActivity fragmentActivity) {
|
||||
super (fragmentActivity);
|
||||
|
||||
this.fragments = new ArrayList<> ();
|
||||
this.fragments.add (new Tab (fragmentActivity.getString (R.string.build_output), new SimpleOutputFragment ()));
|
||||
this.fragments.add (new Tab (fragmentActivity.getString (R.string.app_logs), new LogViewFragment ()));
|
||||
this.fragments.add (new Tab (fragmentActivity.getString (R.string.ide_logs), new IDELogFragment ()));
|
||||
this.fragments.add (new Tab (fragmentActivity.getString (R.string.view_diags), new DiagnosticsListFragment ()));
|
||||
this.fragments.add (new Tab (fragmentActivity.getString (R.string.view_search_results), new SearchResultFragment ()));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment (int position) {
|
||||
return this.fragments.get (position).fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount () {
|
||||
return fragments.size ();
|
||||
}
|
||||
|
||||
public String getTitle (int position) {
|
||||
return fragments.get (position).title;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public SimpleOutputFragment getBuildOutputFragment () {
|
||||
return Objects.requireNonNull (findFragmentByClass (SimpleOutputFragment.class));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public LogViewFragment getLogFragment () {
|
||||
return Objects.requireNonNull (findFragmentByClass (LogViewFragment.class));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public DiagnosticsListFragment getDiagnosticsFragment () {
|
||||
return Objects.requireNonNull (findFragmentByClass (DiagnosticsListFragment.class));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public SearchResultFragment getSearchResultFragment () {
|
||||
return Objects.requireNonNull (findFragmentByClass (SearchResultFragment.class));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <T extends Fragment> T findFragmentByClass (Class<T> clazz) {
|
||||
for (final var tab : this.fragments) {
|
||||
if (tab.fragment.getClass () == clazz) {
|
||||
return (T) tab.fragment;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T extends Fragment> int findIndexOfFragmentByClass (Class<T> tClass) {
|
||||
for (int i = 0; i < this.fragments.size (); i++) {
|
||||
final var tab = this.fragments.get (i);
|
||||
if (tab.fragment.getClass () == tClass) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static class Tab {
|
||||
|
||||
public Tab (String title, Fragment fragment) {
|
||||
this.title = title;
|
||||
this.fragment = fragment;
|
||||
}
|
||||
|
||||
String title;
|
||||
Fragment fragment;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
*
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.adapters;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
|
||||
import com.itsaky.androidide.fragments.EditorFragment;
|
||||
import com.itsaky.androidide.models.OpenedFile;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class EditorPagerAdapter extends FragmentStateAdapter implements EditorFragment.ModificationStateListener {
|
||||
|
||||
private final List<OpenedFile> mFiles;
|
||||
private final EditorFragment.FileOpenListener mFileOpenListener;
|
||||
|
||||
private static final Logger LOG = Logger.instance ("EditorPagerAdapter");
|
||||
|
||||
public EditorPagerAdapter (final FragmentActivity activity, EditorFragment.FileOpenListener fileOpenListener) {
|
||||
super (activity);
|
||||
this.mFileOpenListener = fileOpenListener;
|
||||
this.mFiles = new ArrayList<> ();
|
||||
}
|
||||
|
||||
public void setFiles (List<OpenedFile> files) {
|
||||
final var result = DiffUtil.calculateDiff (new DiffUtil.Callback () {
|
||||
@Override
|
||||
public int getOldListSize () {
|
||||
return mFiles.size ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNewListSize () {
|
||||
return files.size ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areItemsTheSame (int oldItemPosition, int newItemPosition) {
|
||||
return Objects.equals (mFiles.get (oldItemPosition), files.get (newItemPosition));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame (int oldItemPosition, int newItemPosition) {
|
||||
return areItemsTheSame (oldItemPosition, newItemPosition);
|
||||
}
|
||||
});
|
||||
mFiles.clear ();
|
||||
mFiles.addAll (files);
|
||||
result.dispatchUpdatesTo (this);
|
||||
}
|
||||
|
||||
|
||||
public String getEditorTitle (int index) {
|
||||
return mFiles.get (index).getFile ().getName ();
|
||||
}
|
||||
|
||||
public List<OpenedFile> getFiles () {
|
||||
return mFiles;
|
||||
}
|
||||
|
||||
public OpenedFile getOpenedFile (int index) {
|
||||
return mFiles.get (index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified (EditorFragment editor) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaved (EditorFragment editor) {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment (int position) {
|
||||
return mFiles.get (position)
|
||||
.createFragment (this.mFileOpenListener, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount () {
|
||||
return mFiles.size ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId (int position) {
|
||||
if (mFiles.isEmpty ()) {
|
||||
LOG.warn ("File list is empty but getItemId(int) is being called. Returning -1");
|
||||
return -1;
|
||||
}
|
||||
|
||||
final var id = mFiles.get (position).hashCode ();
|
||||
LOG.debug ("Item ID for editor fragment at position " + position + " is " + id);
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsItem (long itemId) {
|
||||
for (final var file : mFiles) {
|
||||
if (itemId == file.hashCode ()) { // see getItemId(int)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LOG.warn ("EditorPagerAdapter does not contain any file with item ID : " + itemId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/************************************************************************************
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
*
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
|
||||
package com.itsaky.androidide.adapters;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class MainPagerAdapter extends FragmentPagerAdapter
|
||||
{
|
||||
private ArrayList<Fragment> mFragmentList;
|
||||
|
||||
public MainPagerAdapter(FragmentManager manager)
|
||||
{
|
||||
super(manager);
|
||||
mFragmentList = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int p1)
|
||||
{
|
||||
return mFragmentList != null && p1 < mFragmentList.size() ? mFragmentList.get(p1) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount()
|
||||
{
|
||||
return mFragmentList != null ? mFragmentList.size() : 0;
|
||||
}
|
||||
|
||||
public MainPagerAdapter addFragment(Fragment frag)
|
||||
{
|
||||
getFragments().add(frag);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ArrayList<Fragment> getFragments()
|
||||
{
|
||||
return mFragmentList == null ? mFragmentList = new ArrayList<>() : mFragmentList;
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/************************************************************************************
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
*
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
|
||||
package com.itsaky.androidide.adapters;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.itsaky.androidide.databinding.LayoutModuleInfoItemBinding;
|
||||
import com.itsaky.androidide.project.IDEModule;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class ModuleInfoAdapter extends RecyclerView.Adapter<ModuleInfoAdapter.VH> {
|
||||
|
||||
private List<IDEModule> modules;
|
||||
|
||||
public ModuleInfoAdapter(List<IDEModule> modules) {
|
||||
this.modules = modules;
|
||||
if(this.modules == null)
|
||||
this.modules = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleInfoAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
|
||||
return new VH(LayoutModuleInfoItemBinding.inflate(LayoutInflater.from(p1.getContext()), p1, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressLint("SetTextI18n")
|
||||
public void onBindViewHolder(ModuleInfoAdapter.VH p1, int p2) {
|
||||
final IDEModule module = modules.get(p2);
|
||||
final LayoutModuleInfoItemBinding binding = p1.binding;
|
||||
final Context ctx = binding.getRoot().getContext();
|
||||
binding.moduleName.setText(module.displayName.substring(0, 1).toUpperCase(Locale.US) + module.displayName.substring(1));
|
||||
|
||||
SpannableStringBuilder summaryBuilder = new SpannableStringBuilder();
|
||||
if(!module.isLibrary) {
|
||||
summaryBuilder.append(ctx.getString(com.itsaky.androidide.R.string.msg_package_name, module.applicationId));
|
||||
summaryBuilder.append("\n");
|
||||
}
|
||||
summaryBuilder.append(ctx.getString(com.itsaky.androidide.R.string.msg_min_sdk, module.minSdk.apiString));
|
||||
summaryBuilder.append("\n");
|
||||
summaryBuilder.append(ctx.getString(com.itsaky.androidide.R.string.msg_target_sdk, module.targetSdk.apiString));
|
||||
summaryBuilder.append("\n");
|
||||
summaryBuilder.append(ctx.getString(com.itsaky.androidide.R.string.msg_dependency_count, module.dependencies.size()));
|
||||
summaryBuilder.append("\n");
|
||||
summaryBuilder.append(ctx.getString(com.itsaky.androidide.R.string.msg_task_count, module.tasks.size()));
|
||||
|
||||
binding.moduleSummary.setText(summaryBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return modules.size();
|
||||
}
|
||||
|
||||
public class VH extends RecyclerView.ViewHolder {
|
||||
LayoutModuleInfoItemBinding binding;
|
||||
|
||||
public VH(LayoutModuleInfoItemBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,159 +1,156 @@
|
||||
/************************************************************************************
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
package com.itsaky.androidide.app;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import com.blankj.utilcode.util.ThrowableUtils;
|
||||
import com.google.firebase.messaging.FirebaseMessaging;
|
||||
import com.itsaky.androidide.CrashHandlerActivity;
|
||||
import com.itsaky.androidide.language.xml.completion.XMLCompletionService;
|
||||
import com.itsaky.androidide.project.ProjectResourceFinder;
|
||||
import com.itsaky.androidide.services.IDELogService;
|
||||
import com.itsaky.androidide.services.MessagingService;
|
||||
import com.itsaky.inflater.ILayoutInflater;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.LayoutInflaterConfiguration;
|
||||
import com.itsaky.androidide.utils.FileUtil;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.apiinfo.ApiInfo;
|
||||
import com.itsaky.attrinfo.AttrInfo;
|
||||
import com.itsaky.widgets.WidgetInfo;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
public class StudioApp extends BaseApplication {
|
||||
|
||||
private static StudioApp instance;
|
||||
private static ApiInfo mApiInfo;
|
||||
private static AttrInfo mAttrInfo;
|
||||
private static WidgetInfo mWidgetInfo;
|
||||
|
||||
private IResourceFinder mResFinder;
|
||||
private XMLCompletionService mXmlCompletionService;
|
||||
private ILayoutInflater mLayoutInflater;
|
||||
private Thread.UncaughtExceptionHandler uncaughtExceptionHandler;
|
||||
|
||||
public static final boolean DEBUG = com.itsaky.androidide.BuildConfig.DEBUG;
|
||||
|
||||
private static final Logger LOG = Logger.instance("StudioApp");
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
instance = this;
|
||||
this.uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
Thread.setDefaultUncaughtExceptionHandler(this::handleCrash);
|
||||
super.onCreate();
|
||||
|
||||
FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_UPDATE);
|
||||
FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_DEV_MSGS);
|
||||
|
||||
initializeApiInformation();
|
||||
}
|
||||
|
||||
private void handleCrash(Thread thread, Throwable th) {
|
||||
writeException(th);
|
||||
|
||||
try {
|
||||
final var intent = new Intent ();
|
||||
intent.setAction (CrashHandlerActivity.REPORT_ACTION);
|
||||
intent.putExtra (CrashHandlerActivity.TRACE_KEY, ThrowableUtils.getFullStackTrace (th));
|
||||
intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity (intent);
|
||||
|
||||
if(this.uncaughtExceptionHandler != null) {
|
||||
this.uncaughtExceptionHandler.uncaughtException(thread, th);
|
||||
}
|
||||
System.exit (1);
|
||||
} catch (Throwable error) {
|
||||
LOG.error ("Unable to show crash handler activity", error);
|
||||
}
|
||||
}
|
||||
|
||||
public void createInflater (LayoutInflaterConfiguration config) {
|
||||
this.mLayoutInflater = ILayoutInflater.newInstance(config);
|
||||
}
|
||||
|
||||
public LayoutInflaterConfiguration createInflaterConfig(ILayoutInflater.ContextProvider contextProvider, Set<File> resDirs) {
|
||||
return new LayoutInflaterConfiguration.Builder ()
|
||||
.setAttrInfo(this.attrInfo())
|
||||
.setWidgetInfo(this.widgetInfo())
|
||||
.setResourceFinder(mResFinder == null ? mResFinder = new ProjectResourceFinder() : mResFinder)
|
||||
.setResourceDirectories(resDirs)
|
||||
.setContextProvider(contextProvider)
|
||||
.create();
|
||||
}
|
||||
|
||||
public ILayoutInflater getLayoutInflater () {
|
||||
return mLayoutInflater;
|
||||
}
|
||||
|
||||
public IResourceFinder getResFinder () {
|
||||
return this.mResFinder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads API version information from api-versions.xml
|
||||
*/
|
||||
public void initializeApiInformation() {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
mApiInfo = new ApiInfo (StudioApp.this);
|
||||
mAttrInfo = new AttrInfo (StudioApp.this);
|
||||
mWidgetInfo = new WidgetInfo (StudioApp.this);
|
||||
|
||||
mXmlCompletionService = new XMLCompletionService (mAttrInfo, mWidgetInfo);
|
||||
} catch (Throwable th) {
|
||||
LOG.error (getString (com.itsaky.androidide.R.string.err_init_sdkinfo), th);
|
||||
}
|
||||
}, "SDK Information Loader").start();
|
||||
}
|
||||
|
||||
public void stopAllDaemons() {
|
||||
newShell(null).bgAppend("gradle --stop");
|
||||
}
|
||||
|
||||
public XMLCompletionService getXmlCompletionService() {
|
||||
return mXmlCompletionService;
|
||||
}
|
||||
|
||||
public ApiInfo apiInfo() {
|
||||
return mApiInfo;
|
||||
}
|
||||
|
||||
public AttrInfo attrInfo () {
|
||||
return mAttrInfo;
|
||||
}
|
||||
|
||||
public WidgetInfo widgetInfo () {
|
||||
return mWidgetInfo;
|
||||
}
|
||||
|
||||
public boolean isXmlServiceStarted() {
|
||||
return mXmlCompletionService != null;
|
||||
}
|
||||
|
||||
public void setStopGradleDaemon(boolean startGradleDaemon) {
|
||||
}
|
||||
|
||||
public static StudioApp getInstance() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
/************************************************************************************
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
package com.itsaky.androidide.app;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import com.blankj.utilcode.util.ThrowableUtils;
|
||||
import com.google.firebase.messaging.FirebaseMessaging;
|
||||
import com.itsaky.androidide.CrashHandlerActivity;
|
||||
import com.itsaky.androidide.language.xml.completion.XMLCompletionService;
|
||||
import com.itsaky.androidide.project.ProjectResourceFinder;
|
||||
import com.itsaky.androidide.services.MessagingService;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.apiinfo.ApiInfo;
|
||||
import com.itsaky.attrinfo.AttrInfo;
|
||||
import com.itsaky.inflater.ILayoutInflater;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.LayoutInflaterConfiguration;
|
||||
import com.itsaky.widgets.WidgetInfo;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Set;
|
||||
|
||||
public class StudioApp extends BaseApplication {
|
||||
|
||||
private static StudioApp instance;
|
||||
private static ApiInfo mApiInfo;
|
||||
private static AttrInfo mAttrInfo;
|
||||
private static WidgetInfo mWidgetInfo;
|
||||
|
||||
private IResourceFinder mResFinder;
|
||||
private XMLCompletionService mXmlCompletionService;
|
||||
private ILayoutInflater mLayoutInflater;
|
||||
private Thread.UncaughtExceptionHandler uncaughtExceptionHandler;
|
||||
|
||||
public static final boolean DEBUG = com.itsaky.androidide.BuildConfig.DEBUG;
|
||||
|
||||
private static final Logger LOG = Logger.instance("StudioApp");
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
instance = this;
|
||||
this.uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
Thread.setDefaultUncaughtExceptionHandler(this::handleCrash);
|
||||
super.onCreate();
|
||||
|
||||
FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_UPDATE);
|
||||
FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_DEV_MSGS);
|
||||
|
||||
initializeApiInformation();
|
||||
}
|
||||
|
||||
private void handleCrash(Thread thread, Throwable th) {
|
||||
writeException(th);
|
||||
|
||||
try {
|
||||
final var intent = new Intent ();
|
||||
intent.setAction (CrashHandlerActivity.REPORT_ACTION);
|
||||
intent.putExtra (CrashHandlerActivity.TRACE_KEY, ThrowableUtils.getFullStackTrace (th));
|
||||
intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity (intent);
|
||||
|
||||
if(this.uncaughtExceptionHandler != null) {
|
||||
this.uncaughtExceptionHandler.uncaughtException(thread, th);
|
||||
}
|
||||
System.exit (1);
|
||||
} catch (Throwable error) {
|
||||
LOG.error ("Unable to show crash handler activity", error);
|
||||
}
|
||||
}
|
||||
|
||||
public void createInflater (LayoutInflaterConfiguration config) {
|
||||
this.mLayoutInflater = ILayoutInflater.newInstance(config);
|
||||
}
|
||||
|
||||
public LayoutInflaterConfiguration createInflaterConfig(ILayoutInflater.ContextProvider contextProvider, Set<File> resDirs) {
|
||||
return new LayoutInflaterConfiguration.Builder ()
|
||||
.setAttrInfo(this.attrInfo())
|
||||
.setWidgetInfo(this.widgetInfo())
|
||||
.setResourceFinder(mResFinder == null ? mResFinder = new ProjectResourceFinder() : mResFinder)
|
||||
.setResourceDirectories(resDirs)
|
||||
.setContextProvider(contextProvider)
|
||||
.create();
|
||||
}
|
||||
|
||||
public ILayoutInflater getLayoutInflater () {
|
||||
return mLayoutInflater;
|
||||
}
|
||||
|
||||
public IResourceFinder getResFinder () {
|
||||
return this.mResFinder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads API version information from api-versions.xml
|
||||
*/
|
||||
public void initializeApiInformation() {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
mApiInfo = new ApiInfo (StudioApp.this);
|
||||
mAttrInfo = new AttrInfo (StudioApp.this);
|
||||
mWidgetInfo = new WidgetInfo (StudioApp.this);
|
||||
|
||||
mXmlCompletionService = new XMLCompletionService (mAttrInfo, mWidgetInfo);
|
||||
} catch (Throwable th) {
|
||||
LOG.error (getString (com.itsaky.androidide.R.string.err_init_sdkinfo), th);
|
||||
}
|
||||
}, "SDK Information Loader").start();
|
||||
}
|
||||
|
||||
public void stopAllDaemons() {
|
||||
newShell(null).bgAppend("gradle --stop");
|
||||
}
|
||||
|
||||
public XMLCompletionService getXmlCompletionService() {
|
||||
return mXmlCompletionService;
|
||||
}
|
||||
|
||||
public ApiInfo apiInfo() {
|
||||
return mApiInfo;
|
||||
}
|
||||
|
||||
public AttrInfo attrInfo () {
|
||||
return mAttrInfo;
|
||||
}
|
||||
|
||||
public WidgetInfo widgetInfo () {
|
||||
return mWidgetInfo;
|
||||
}
|
||||
|
||||
public boolean isXmlServiceStarted() {
|
||||
return mXmlCompletionService != null;
|
||||
}
|
||||
|
||||
public void setStopGradleDaemon(boolean startGradleDaemon) {
|
||||
}
|
||||
|
||||
public static StudioApp getInstance() {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +1,83 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.itsaky.androidide.adapters.DiagnosticsAdapter;
|
||||
import com.itsaky.androidide.views.EmptyView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DiagnosticsListFragment extends Fragment {
|
||||
|
||||
private RecyclerView list;
|
||||
private EmptyView emptyView;
|
||||
|
||||
private DiagnosticsAdapter unsavedAdapter;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView (@NonNull LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle savedInstanceState) {
|
||||
final var container = new LinearLayout (inflater.getContext ());
|
||||
container.setOrientation (LinearLayout.VERTICAL);
|
||||
container.setLayoutParams (new ViewGroup.LayoutParams (-1, -1));
|
||||
|
||||
this.list = new RecyclerView (inflater.getContext ());
|
||||
this.emptyView = new EmptyView (inflater.getContext ());
|
||||
|
||||
container.addView (list, new LinearLayout.LayoutParams (-1, -1));
|
||||
container.addView (emptyView, new LinearLayout.LayoutParams (-1, -1));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
|
||||
list.setLayoutManager (new LinearLayoutManager (list.getContext ()));
|
||||
list.setAdapter (unsavedAdapter != null ? unsavedAdapter : new DiagnosticsAdapter (new ArrayList<> (), null));
|
||||
unsavedAdapter = null;
|
||||
}
|
||||
|
||||
public void setAdapter (@NonNull DiagnosticsAdapter adapter) {
|
||||
if (list != null) {
|
||||
list.setAdapter (adapter);
|
||||
} else {
|
||||
unsavedAdapter = adapter;
|
||||
}
|
||||
}
|
||||
|
||||
public void handleResultVisibility (boolean error) {
|
||||
if (emptyView != null && list != null) {
|
||||
emptyView.setVisibility (error ? View.VISIBLE : View.GONE);
|
||||
list.setVisibility (error ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.itsaky.androidide.adapters.DiagnosticsAdapter;
|
||||
import com.itsaky.androidide.views.EmptyView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DiagnosticsListFragment extends Fragment {
|
||||
|
||||
private RecyclerView list;
|
||||
private EmptyView emptyView;
|
||||
|
||||
private DiagnosticsAdapter unsavedAdapter;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView (@NonNull LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle savedInstanceState) {
|
||||
final var container = new LinearLayout (inflater.getContext ());
|
||||
container.setOrientation (LinearLayout.VERTICAL);
|
||||
container.setLayoutParams (new ViewGroup.LayoutParams (-1, -1));
|
||||
|
||||
this.list = new RecyclerView (inflater.getContext ());
|
||||
this.emptyView = new EmptyView (inflater.getContext ());
|
||||
|
||||
container.addView (list, new LinearLayout.LayoutParams (-1, -1));
|
||||
container.addView (emptyView, new LinearLayout.LayoutParams (-1, -1));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
|
||||
list.setLayoutManager (new LinearLayoutManager (list.getContext ()));
|
||||
list.setAdapter (unsavedAdapter != null ? unsavedAdapter : new DiagnosticsAdapter (new ArrayList<> (), null));
|
||||
unsavedAdapter = null;
|
||||
}
|
||||
|
||||
public void setAdapter (@NonNull DiagnosticsAdapter adapter) {
|
||||
if (list != null) {
|
||||
list.setAdapter (adapter);
|
||||
} else {
|
||||
unsavedAdapter = adapter;
|
||||
}
|
||||
}
|
||||
|
||||
public void handleResultVisibility (boolean error) {
|
||||
if (emptyView != null && list != null) {
|
||||
emptyView.setVisibility (error ? View.VISIBLE : View.GONE);
|
||||
list.setVisibility (error ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,463 +0,0 @@
|
||||
/************************************************************************************
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
package com.itsaky.androidide.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.blankj.utilcode.util.SizeUtils;
|
||||
import com.itsaky.androidide.adapters.CompletionListAdapter;
|
||||
import com.itsaky.androidide.databinding.FragmentEditorBinding;
|
||||
import com.itsaky.androidide.language.groovy.GroovyLanguage;
|
||||
import com.itsaky.androidide.language.java.JavaLanguage;
|
||||
import com.itsaky.androidide.language.xml.XMLLanguage;
|
||||
import com.itsaky.androidide.lexers.xml.XMLLexer;
|
||||
import com.itsaky.androidide.managers.PreferenceManager;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.itsaky.androidide.tasks.callables.ReadFileTask;
|
||||
import com.itsaky.androidide.utils.LSPUtils;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.androidide.utils.TypefaceUtils;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.github.rosemoe.editor.interfaces.EditorEventListener;
|
||||
import io.github.rosemoe.editor.langs.EmptyLanguage;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
|
||||
public class EditorFragment extends BaseFragment implements EditorEventListener {
|
||||
|
||||
public FragmentEditorBinding mBinding;
|
||||
private File mFile;
|
||||
private boolean isModified = false;
|
||||
private boolean isFirstCreate = false;
|
||||
|
||||
private ModificationStateListener mModificationStateListener;
|
||||
private FileOpenListener mOpenListener;
|
||||
|
||||
private static final Logger LOG = Logger.instance ("EditorFragment");
|
||||
|
||||
public static final String KEY_FILE_PATH = "file_path";
|
||||
public static final String KEY_LINE_START = "line_start";
|
||||
public static final String KEY_LINE_END = "line_end";
|
||||
public static final String KEY_COLUMN_START = "col_start";
|
||||
public static final String KEY_COLUMN_END = "col_end";
|
||||
|
||||
public static final String EXT_JAVA = ".java";
|
||||
public static final String EXT_XML = ".xml";
|
||||
public static final String EXT_HTML = ".html";
|
||||
public static final String EXT_GRADLE = ".gradle";
|
||||
public static final String EXT_GROOVY = ".groovy";
|
||||
public static final String EXT_KOTLIN = ".kt";
|
||||
public static final String EXT_JSON = ".json";
|
||||
|
||||
public EditorFragment setModificationStateListener (ModificationStateListener listener) {
|
||||
this.mModificationStateListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FragmentEditorBinding getBinding () {
|
||||
return mBinding;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static EditorFragment newInstance (@NonNull File file, @NonNull Range selection) {
|
||||
Bundle bundle = new Bundle ();
|
||||
bundle.putString (KEY_FILE_PATH, file.getAbsolutePath ());
|
||||
bundle.putInt (KEY_LINE_START, selection.getStart ().getLine ());
|
||||
bundle.putInt (KEY_LINE_END, selection.getEnd ().getLine ());
|
||||
bundle.putInt (KEY_COLUMN_START, selection.getStart ().getCharacter ());
|
||||
bundle.putInt (KEY_COLUMN_END, selection.getEnd ().getCharacter ());
|
||||
|
||||
EditorFragment frag = new EditorFragment ();
|
||||
frag.setArguments (bundle);
|
||||
return frag;
|
||||
}
|
||||
|
||||
public EditorFragment setFileOpenListener (FileOpenListener openListener) {
|
||||
this.mOpenListener = openListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeEditor getEditor () {
|
||||
if (mBinding == null) {
|
||||
return null;
|
||||
}
|
||||
return mBinding.editor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate (Bundle savedInstanceState) {
|
||||
super.onCreate (savedInstanceState);
|
||||
isFirstCreate = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState (@NonNull Bundle outState) {
|
||||
save ();
|
||||
super.onSaveInstanceState (outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView (@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
mBinding = FragmentEditorBinding.inflate (inflater, container, false);
|
||||
return mBinding.getRoot ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
|
||||
LOG.debug ("onViewCreated() called");
|
||||
mFile = new File (requireArguments ().getString (KEY_FILE_PATH));
|
||||
|
||||
mBinding.editor.setOverScrollEnabled (false);
|
||||
mBinding.editor.setTypefaceText (TypefaceUtils.jetbrainsMono ());
|
||||
mBinding.editor.setTextActionMode (CodeEditor.TextActionMode.ACTION_MODE);
|
||||
mBinding.editor.setHighlightCurrentBlock (true);
|
||||
mBinding.editor.setEventListener (this);
|
||||
mBinding.editor.setAutoCompletionOnComposing (true);
|
||||
mBinding.editor.setAutoCompletionItemAdapter (new CompletionListAdapter ());
|
||||
mBinding.editor.setLineColorsEnabled (true);
|
||||
mBinding.editor.setDividerWidth (SizeUtils.dp2px (1));
|
||||
|
||||
mBinding.diagnosticTextContainer.setVisibility (View.GONE);
|
||||
|
||||
LOG.debug ("Configuring editor from preferences...");
|
||||
configureEditorIfNeeded ();
|
||||
|
||||
LOG.debug ("Reading initial selection range");
|
||||
final Range range = fromArgs (requireArguments ());
|
||||
new TaskExecutor ().executeAsync (new ReadFileTask (mFile), result -> {
|
||||
mBinding.editor.setText (result);
|
||||
postRead ();
|
||||
mBinding.editor.post (() -> {
|
||||
if (LSPUtils.isEqual (range.getStart (), range.getEnd ())) {
|
||||
getEditor ().setSelection (range.getStart ().getLine (), range.getStart ().getCharacter ());
|
||||
} else {
|
||||
getEditor ().setSelectionRegion (range.getStart ().getLine (), range.getStart ().getCharacter (), range.getEnd ().getLine (), range.getEnd ().getCharacter ());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private Range fromArgs (@NonNull Bundle args) {
|
||||
if (!(args.containsKey (KEY_LINE_START)
|
||||
&& args.containsKey (KEY_COLUMN_START)
|
||||
&& args.containsKey (KEY_LINE_END)
|
||||
&& args.containsKey (KEY_COLUMN_END))) {
|
||||
return LSPUtils.Range_ofZero;
|
||||
}
|
||||
|
||||
return new Range (
|
||||
new Position (
|
||||
args.getInt (KEY_LINE_START),
|
||||
args.getInt (KEY_COLUMN_START)),
|
||||
new Position (
|
||||
args.getInt (KEY_LINE_END),
|
||||
args.getInt (KEY_COLUMN_END)
|
||||
));
|
||||
}
|
||||
|
||||
public void setDiagnostics (List<Diagnostic> diags) {
|
||||
if (diags == null) {
|
||||
mBinding.editor.getEditorLanguage ().getAnalyzer ().updateDiagnostics (new HashMap<> ());
|
||||
return;
|
||||
}
|
||||
Map<Integer, Map<Integer, Diagnostic>> map = new HashMap<> ();
|
||||
for (int i = 0; i < diags.size (); i++) {
|
||||
final Diagnostic d = diags.get (i);
|
||||
if (d == null) {
|
||||
continue;
|
||||
}
|
||||
final Range range = d.getRange ();
|
||||
final int line = range.getStart ().getLine ();
|
||||
final int column = range.getStart ().getCharacter ();
|
||||
Map<Integer, Diagnostic> mappedByColumn = map.get (line);
|
||||
if (mappedByColumn == null) {
|
||||
mappedByColumn = new HashMap<> ();
|
||||
}
|
||||
mappedByColumn.put (column, d);
|
||||
map.put (line, mappedByColumn);
|
||||
}
|
||||
mBinding.editor.getEditorLanguage ().getAnalyzer ().updateDiagnostics (map);
|
||||
}
|
||||
|
||||
private void configureEditorIfNeeded () {
|
||||
boolean sizeChanged = isFirstCreate || ConstantsBridge.EDITORPREF_SIZE_CHANGED;
|
||||
boolean flagsChanged = isFirstCreate || ConstantsBridge.EDITORPREF_FLAGS_CHANGED;
|
||||
boolean drawHexChanged = isFirstCreate || ConstantsBridge.EDITORPREF_DRAW_HEX_CHANGED;
|
||||
final PreferenceManager prefs = getStudioActivity ().getApp ().getPrefManager ();
|
||||
if (sizeChanged) {
|
||||
float textSize = prefs.getFloat (PreferenceManager.KEY_EDITOR_FONT_SIZE);
|
||||
if (textSize < 6 || textSize > 32) {
|
||||
textSize = 14;
|
||||
}
|
||||
mBinding.editor.setTextSize (textSize);
|
||||
ConstantsBridge.EDITORPREF_SIZE_CHANGED = false;
|
||||
}
|
||||
|
||||
if (flagsChanged) {
|
||||
int flags = 0;
|
||||
if (prefs.getBoolean (PreferenceManager.KEY_EDITORFLAG_WS_LEADING, true)) {
|
||||
flags |= CodeEditor.FLAG_DRAW_WHITESPACE_LEADING;
|
||||
}
|
||||
if (prefs.getBoolean (PreferenceManager.KEY_EDITORFLAG_WS_TRAILING, false)) {
|
||||
flags |= CodeEditor.FLAG_DRAW_WHITESPACE_TRAILING;
|
||||
}
|
||||
if (prefs.getBoolean (PreferenceManager.KEY_EDITORFLAG_WS_INNER, true)) {
|
||||
flags |= CodeEditor.FLAG_DRAW_WHITESPACE_INNER;
|
||||
}
|
||||
if (prefs.getBoolean (PreferenceManager.KEY_EDITORFLAG_WS_EMPTY_LINE, true)) {
|
||||
flags |= CodeEditor.FLAG_DRAW_WHITESPACE_FOR_EMPTY_LINE;
|
||||
}
|
||||
if (prefs.getBoolean (PreferenceManager.KEY_EDITORFLAG_LINE_BREAK, true)) {
|
||||
flags |= CodeEditor.FLAG_DRAW_LINE_SEPARATOR;
|
||||
}
|
||||
|
||||
mBinding.editor.setNonPrintablePaintingFlags (flags);
|
||||
ConstantsBridge.EDITORPREF_FLAGS_CHANGED = false;
|
||||
}
|
||||
|
||||
if (drawHexChanged) {
|
||||
mBinding.editor.setLineColorsEnabled (prefs.getBoolean (PreferenceManager.KEY_EDITOR_DRAW_HEX, true));
|
||||
ConstantsBridge.EDITORPREF_DRAW_HEX_CHANGED = false;
|
||||
}
|
||||
|
||||
isFirstCreate = false;
|
||||
}
|
||||
|
||||
public String getText () {
|
||||
return mBinding.editor.getText ().toString ();
|
||||
}
|
||||
|
||||
public boolean isModified () {
|
||||
return isModified;
|
||||
}
|
||||
|
||||
public void undo () {
|
||||
if (mBinding.editor.canUndo ()) {
|
||||
mBinding.editor.undo ();
|
||||
}
|
||||
}
|
||||
|
||||
public void redo () {
|
||||
if (mBinding.editor.canRedo ()) {
|
||||
mBinding.editor.redo ();
|
||||
}
|
||||
}
|
||||
|
||||
public void findDefinition () {
|
||||
mBinding.editor.findDefinition ();
|
||||
}
|
||||
|
||||
public void findReferences () {
|
||||
mBinding.editor.findReferences ();
|
||||
}
|
||||
|
||||
public void commentLine () {
|
||||
mBinding.editor.commentLine ();
|
||||
}
|
||||
|
||||
public void uncommentLine () {
|
||||
mBinding.editor.uncommentLine ();
|
||||
}
|
||||
|
||||
public void beginSearch () {
|
||||
mBinding.editor.beginSearchMode ();
|
||||
}
|
||||
|
||||
public void save () {
|
||||
if (mFile == null || mBinding == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String text = mBinding.editor.getText ().toString ();
|
||||
FileIOUtils.writeFileFromString (mFile, text);
|
||||
notifySaved ();
|
||||
isModified = false;
|
||||
}
|
||||
|
||||
public File getFile () {
|
||||
return mFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume () {
|
||||
super.onResume ();
|
||||
configureEditorIfNeeded ();
|
||||
}
|
||||
|
||||
private void postRead () {
|
||||
if (mFile.isFile () && mFile.getName ().endsWith (EXT_JAVA)) {
|
||||
mBinding.editor.setEditorLanguage (new JavaLanguage (getFile ()));
|
||||
} else if (mFile.isFile () && mFile.getName ().endsWith (EXT_XML)) {
|
||||
mBinding.editor.setEditorLanguage (new XMLLanguage (getFile ()));
|
||||
} else if (mFile.isFile () && mFile.getName ().endsWith (EXT_GRADLE)) {
|
||||
mBinding.editor.setEditorLanguage (new GroovyLanguage (getFile ()));
|
||||
} else {
|
||||
mBinding.editor.setEditorLanguage (new EmptyLanguage ());
|
||||
}
|
||||
|
||||
// File must be set only after setting the language
|
||||
// This will make sure that textDocument/didOpen is sent
|
||||
mBinding.editor.setFile (getFile ());
|
||||
|
||||
mBinding.editor.setColorScheme (new SchemeAndroidIDE ());
|
||||
if (mOpenListener != null) {
|
||||
mOpenListener.onOpenSuccessful (getFile (), getText ());
|
||||
}
|
||||
}
|
||||
|
||||
private void closeCurrentTag (String text, int line, int col) {
|
||||
try {
|
||||
XMLLexer lexer = new XMLLexer (CharStreams.fromReader (new StringReader (text)));
|
||||
Token token;
|
||||
boolean wasSlash = false, wasOpen = false;
|
||||
ArrayList<String> currentNames = new ArrayList<> ();
|
||||
while (((token = lexer.nextToken ()) != null && token.getType () != token.EOF)) {
|
||||
final int type = token.getType ();
|
||||
if (type == XMLLexer.OPEN) {
|
||||
wasOpen = true;
|
||||
} else if (type == XMLLexer.Name) {
|
||||
if (wasOpen && wasSlash && currentNames.size () > 0) {
|
||||
currentNames.remove (0);
|
||||
} else if (wasOpen) {
|
||||
currentNames.add (0, token.getText ());
|
||||
wasOpen = false;
|
||||
}
|
||||
} else if (type == XMLLexer.OPEN_SLASH) {
|
||||
int l = token.getLine () - 1;
|
||||
int c = token.getCharPositionInLine ();
|
||||
if (l == line && c == col) {
|
||||
break;
|
||||
} else if (currentNames.size () > 0) {
|
||||
currentNames.remove (0);
|
||||
}
|
||||
} else if (type == XMLLexer.SLASH_CLOSE
|
||||
|| type == XMLLexer.SPECIAL_CLOSE) {
|
||||
if (currentNames.size () > 0 && token.getText ().trim ().endsWith ("/>")) {
|
||||
currentNames.remove (0);
|
||||
}
|
||||
} else if (type == XMLLexer.SLASH) {
|
||||
wasSlash = true;
|
||||
} else {
|
||||
wasOpen = wasSlash = false;
|
||||
}
|
||||
}
|
||||
if (currentNames.size () > 0) {
|
||||
mBinding.editor.getText ().insert (line, col + 2, currentNames.get (0));
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
LOG.error ("Unable to close current tag", th);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRequestFormat (CodeEditor editor, boolean async) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFormatFail (CodeEditor editor, Throwable cause) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFormatSucceed (CodeEditor editor) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewTextSet (CodeEditor editor) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetSelection (int startLine, int startCol, int endLine, int endCol) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterDelete (CodeEditor editor, CharSequence content, int startLine, int startColumn, int endLine, int endColumn, CharSequence deletedContent) {
|
||||
isModified = true;
|
||||
notifyModified ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterInsert (CodeEditor editor, CharSequence content, int startLine, int startColumn, int endLine, int endColumn, CharSequence insertedContent) {
|
||||
isModified = true;
|
||||
|
||||
if (getFile () != null && getFile ().getName ().endsWith (EXT_XML)) {
|
||||
boolean isOpen = false;
|
||||
try {
|
||||
isOpen = editor.getText ().charAt (editor.getCursor ().getLeft () - 2) == '<';
|
||||
} catch (Throwable th) {
|
||||
// ignored
|
||||
}
|
||||
if (isOpen && insertedContent.toString ().equals ("/")) {
|
||||
closeCurrentTag (editor.getText ().toString (), endLine, endColumn);
|
||||
}
|
||||
}
|
||||
|
||||
notifyModified ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeReplace (CodeEditor editor, CharSequence content) {
|
||||
}
|
||||
|
||||
private void notifySaved () {
|
||||
mBinding.editor.didSave ();
|
||||
|
||||
if (mModificationStateListener != null) {
|
||||
mModificationStateListener.onSaved (this);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyModified () {
|
||||
if (mModificationStateListener != null) {
|
||||
mModificationStateListener.onModified (this);
|
||||
}
|
||||
}
|
||||
|
||||
public interface FileOpenListener {
|
||||
void onOpenSuccessful (File file, String text);
|
||||
}
|
||||
|
||||
public interface ModificationStateListener {
|
||||
void onModified (EditorFragment editor);
|
||||
|
||||
void onSaved (EditorFragment editor);
|
||||
}
|
||||
}
|
||||
@@ -28,10 +28,11 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.transition.ChangeBounds;
|
||||
import androidx.transition.TransitionManager;
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.adapters.viewholders.FileTreeViewHolder;
|
||||
@@ -41,11 +42,10 @@ import com.itsaky.androidide.project.AndroidProject;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.itsaky.androidide.tasks.callables.FileTreeCallable;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
import com.itsaky.androidide.views.CodeEditorView;
|
||||
import com.unnamed.b.atv.model.TreeNode;
|
||||
import com.unnamed.b.atv.view.AndroidTreeView;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
|
||||
@@ -65,6 +65,7 @@ public class FileTreeFragment extends BottomSheetDialogFragment implements TreeN
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static FileTreeFragment newInstance(AndroidProject project) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable("project", project);
|
||||
@@ -82,8 +83,7 @@ public class FileTreeFragment extends BottomSheetDialogFragment implements TreeN
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
if (getActivity() == null || getArguments() == null || !getArguments().containsKey("project")) return;
|
||||
mProject = getArguments().getParcelable("project");
|
||||
mProject = requireArguments ().getParcelable("project");
|
||||
|
||||
listProjectFiles();
|
||||
}
|
||||
@@ -102,12 +102,12 @@ public class FileTreeFragment extends BottomSheetDialogFragment implements TreeN
|
||||
expandNode(node);
|
||||
} else {
|
||||
setLoading(node);
|
||||
listNode(node, true);
|
||||
listNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void listNode(TreeNode node, boolean wasClicked) {
|
||||
private void listNode (@NonNull TreeNode node) {
|
||||
node.getChildren().clear();
|
||||
node.setExpanded(false);
|
||||
new TaskExecutor().executeAsync(() -> {
|
||||
@@ -136,13 +136,13 @@ public class FileTreeFragment extends BottomSheetDialogFragment implements TreeN
|
||||
}
|
||||
}
|
||||
|
||||
private void setLoading(TreeNode node) {
|
||||
private void setLoading(@NonNull TreeNode node) {
|
||||
if (node.getViewHolder() instanceof FileTreeViewHolder) {
|
||||
((FileTreeViewHolder) node.getViewHolder()).setLoading();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateChevron(TreeNode node) {
|
||||
private void updateChevron(@NonNull TreeNode node) {
|
||||
if (node.getViewHolder() instanceof FileTreeViewHolder) {
|
||||
((FileTreeViewHolder) node.getViewHolder()).updateChevron(!node.isExpanded());
|
||||
}
|
||||
@@ -180,9 +180,7 @@ public class FileTreeFragment extends BottomSheetDialogFragment implements TreeN
|
||||
|
||||
final File gradleProps = Environment.GRADLE_PROPS;
|
||||
final File gradleHome = Environment.GRADLE_USER_HOME;
|
||||
final File root = StudioApp.getInstance().getRootDir().getParentFile();
|
||||
File projectDir = new File(mProject.getProjectPath());
|
||||
// projectDir = root;
|
||||
mRoot = TreeNode.root(projectDir);
|
||||
if(gradleHome.exists() && gradleHome.isDirectory()) {
|
||||
if(!gradleProps.exists())
|
||||
@@ -235,7 +233,7 @@ public class FileTreeFragment extends BottomSheetDialogFragment implements TreeN
|
||||
}
|
||||
|
||||
public interface FileActionListener {
|
||||
public EditorFragment openFile(File file);
|
||||
public void showFileOptions(File file, TreeNode node);
|
||||
CodeEditorView openFile(File file);
|
||||
void showFileOptions(File file, TreeNode node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.fragments;
|
||||
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
public class IDELogFragment extends SimpleOutputFragment {
|
||||
|
||||
private final Logger.LogListener listener = this::log;
|
||||
|
||||
public IDELogFragment () {
|
||||
Logger.addLogListener (listener);
|
||||
}
|
||||
|
||||
private void log (int priority, String tag, String message) {
|
||||
appendOutput (String.format ("%s: %s", tag, message));
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.fragments;
|
||||
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
public class IDELogFragment extends SimpleOutputFragment {
|
||||
|
||||
private final Logger.LogListener listener = this::log;
|
||||
|
||||
public IDELogFragment () {
|
||||
Logger.addLogListener (listener);
|
||||
}
|
||||
|
||||
private void log (int priority, String tag, String message) {
|
||||
appendOutput (String.format ("%s: %s", tag, message));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,77 +1,77 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.blankj.utilcode.util.ThreadUtils;
|
||||
import com.itsaky.androidide.language.logs.LogLanguageImpl;
|
||||
import com.itsaky.androidide.models.LogLine;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.github.rosemoe.editor.interfaces.EditorLanguage;
|
||||
|
||||
public class LogViewFragment extends NonEditableEditorFragment {
|
||||
|
||||
private final EditorLanguage language = getLanguage ();
|
||||
private final List<LogLine> unsavedLines = new ArrayList<> ();
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
Objects.requireNonNull (getEditor ()).setEditorLanguage (language);
|
||||
|
||||
if (!unsavedLines.isEmpty ()) {
|
||||
for (var line : unsavedLines) {
|
||||
applyToLanguage (line);
|
||||
getEditor ().getText ().append (line.toString ().trim () + "\n");
|
||||
}
|
||||
unsavedLines.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
public void appendLog (LogLine line) {
|
||||
if (getEditor () == null) {
|
||||
unsavedLines.add (line);
|
||||
return;
|
||||
}
|
||||
|
||||
applyToLanguage (line);
|
||||
|
||||
final var lineString = line.toString ();
|
||||
final var msg = lineString.endsWith ("\n") ? lineString : lineString + "\n";
|
||||
ThreadUtils.runOnUiThread (() -> getEditor ().getText ().append (msg));
|
||||
}
|
||||
|
||||
private void applyToLanguage (LogLine line) {
|
||||
if (language instanceof LogLanguageImpl) {
|
||||
((LogLanguageImpl)this.language).addLine (line);
|
||||
}
|
||||
}
|
||||
|
||||
protected EditorLanguage getLanguage () {
|
||||
return new LogLanguageImpl ();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.blankj.utilcode.util.ThreadUtils;
|
||||
import com.itsaky.androidide.language.logs.LogLanguageImpl;
|
||||
import com.itsaky.androidide.models.LogLine;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.github.rosemoe.editor.interfaces.EditorLanguage;
|
||||
|
||||
public class LogViewFragment extends NonEditableEditorFragment {
|
||||
|
||||
private final EditorLanguage language = getLanguage ();
|
||||
private final List<LogLine> unsavedLines = new ArrayList<> ();
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
Objects.requireNonNull (getEditor ()).setEditorLanguage (language);
|
||||
|
||||
if (!unsavedLines.isEmpty ()) {
|
||||
for (var line : unsavedLines) {
|
||||
applyToLanguage (line);
|
||||
getEditor ().getText ().append (line.toString ().trim () + "\n");
|
||||
}
|
||||
unsavedLines.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
public void appendLog (LogLine line) {
|
||||
if (getEditor () == null) {
|
||||
unsavedLines.add (line);
|
||||
return;
|
||||
}
|
||||
|
||||
applyToLanguage (line);
|
||||
|
||||
final var lineString = line.toString ();
|
||||
final var msg = lineString.endsWith ("\n") ? lineString : lineString + "\n";
|
||||
ThreadUtils.runOnUiThread (() -> getEditor ().getText ().append (msg));
|
||||
}
|
||||
|
||||
private void applyToLanguage (LogLine line) {
|
||||
if (language instanceof LogLanguageImpl) {
|
||||
((LogLanguageImpl)this.language).addLine (line);
|
||||
}
|
||||
}
|
||||
|
||||
protected EditorLanguage getLanguage () {
|
||||
return new LogLanguageImpl ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +1,72 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.itsaky.androidide.databinding.FragmentNonEditableEditorBinding;
|
||||
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
|
||||
import com.itsaky.androidide.utils.TypefaceUtils;
|
||||
|
||||
import io.github.rosemoe.editor.langs.EmptyLanguage;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
|
||||
public abstract class NonEditableEditorFragment extends Fragment {
|
||||
|
||||
private FragmentNonEditableEditorBinding binding;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView (@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
this.binding = FragmentNonEditableEditorBinding.inflate (inflater, container, false);
|
||||
return binding.getRoot ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
|
||||
final var editor = binding.getRoot ();
|
||||
editor.setEditable (false);
|
||||
editor.setDividerWidth (0);
|
||||
editor.setEditorLanguage (new EmptyLanguage ());
|
||||
editor.setOverScrollEnabled (false);
|
||||
editor.setTextActionMode (CodeEditor.TextActionMode.ACTION_MODE);
|
||||
editor.setWordwrap (false);
|
||||
editor.setUndoEnabled (false);
|
||||
editor.setTypefaceLineNumber (TypefaceUtils.jetbrainsMono ());
|
||||
editor.setTypefaceText (TypefaceUtils.jetbrainsMono ());
|
||||
editor.setTextSize (12);
|
||||
editor.setColorScheme (new SchemeAndroidIDE ());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public CodeEditor getEditor () {
|
||||
if (binding == null) {
|
||||
return null;
|
||||
}
|
||||
return binding.editor;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.itsaky.androidide.databinding.FragmentNonEditableEditorBinding;
|
||||
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
|
||||
import com.itsaky.androidide.utils.TypefaceUtils;
|
||||
|
||||
import io.github.rosemoe.editor.langs.EmptyLanguage;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
|
||||
public abstract class NonEditableEditorFragment extends Fragment {
|
||||
|
||||
private FragmentNonEditableEditorBinding binding;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView (@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
this.binding = FragmentNonEditableEditorBinding.inflate (inflater, container, false);
|
||||
return binding.getRoot ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
|
||||
final var editor = binding.getRoot ();
|
||||
editor.setEditable (false);
|
||||
editor.setDividerWidth (0);
|
||||
editor.setEditorLanguage (new EmptyLanguage ());
|
||||
editor.setOverScrollEnabled (false);
|
||||
editor.setTextActionMode (CodeEditor.TextActionMode.ACTION_MODE);
|
||||
editor.setWordwrap (false);
|
||||
editor.setUndoEnabled (false);
|
||||
editor.setTypefaceLineNumber (TypefaceUtils.jetbrainsMono ());
|
||||
editor.setTypefaceText (TypefaceUtils.jetbrainsMono ());
|
||||
editor.setTextSize (12);
|
||||
editor.setColorScheme (new SchemeAndroidIDE ());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public CodeEditor getEditor () {
|
||||
if (binding == null) {
|
||||
return null;
|
||||
}
|
||||
return binding.editor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,81 +1,81 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.itsaky.androidide.adapters.SearchListAdapter;
|
||||
import com.itsaky.androidide.views.EmptyView;
|
||||
|
||||
public class SearchResultFragment extends Fragment {
|
||||
|
||||
private RecyclerView list;
|
||||
private EmptyView emptyView;
|
||||
|
||||
private SearchListAdapter unsavedAdapter;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView (@NonNull LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle savedInstanceState) {
|
||||
final var container = new LinearLayout (inflater.getContext ());
|
||||
container.setOrientation (LinearLayout.VERTICAL);
|
||||
container.setLayoutParams (new ViewGroup.LayoutParams (-1, -1));
|
||||
|
||||
this.list = new RecyclerView (inflater.getContext ());
|
||||
this.emptyView = new EmptyView (inflater.getContext ());
|
||||
|
||||
container.addView (list, new LinearLayout.LayoutParams (-1, -1));
|
||||
container.addView (emptyView, new LinearLayout.LayoutParams (-1, -1));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
|
||||
list.setLayoutManager (new LinearLayoutManager (list.getContext ()));
|
||||
list.setAdapter (unsavedAdapter != null ? unsavedAdapter : new SearchListAdapter (null, null, null));
|
||||
unsavedAdapter = null;
|
||||
}
|
||||
|
||||
public void setAdapter (@NonNull final SearchListAdapter adapter) {
|
||||
if (list == null) {
|
||||
unsavedAdapter = adapter;
|
||||
return;
|
||||
}
|
||||
this.list.setAdapter (adapter);
|
||||
}
|
||||
|
||||
public void handleResultVisibility (boolean error) {
|
||||
if (emptyView != null && list != null) {
|
||||
emptyView.setVisibility (error ? View.VISIBLE : View.GONE);
|
||||
list.setVisibility (error ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.itsaky.androidide.adapters.SearchListAdapter;
|
||||
import com.itsaky.androidide.views.EmptyView;
|
||||
|
||||
public class SearchResultFragment extends Fragment {
|
||||
|
||||
private RecyclerView list;
|
||||
private EmptyView emptyView;
|
||||
|
||||
private SearchListAdapter unsavedAdapter;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView (@NonNull LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle savedInstanceState) {
|
||||
final var container = new LinearLayout (inflater.getContext ());
|
||||
container.setOrientation (LinearLayout.VERTICAL);
|
||||
container.setLayoutParams (new ViewGroup.LayoutParams (-1, -1));
|
||||
|
||||
this.list = new RecyclerView (inflater.getContext ());
|
||||
this.emptyView = new EmptyView (inflater.getContext ());
|
||||
|
||||
container.addView (list, new LinearLayout.LayoutParams (-1, -1));
|
||||
container.addView (emptyView, new LinearLayout.LayoutParams (-1, -1));
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
|
||||
list.setLayoutManager (new LinearLayoutManager (list.getContext ()));
|
||||
list.setAdapter (unsavedAdapter != null ? unsavedAdapter : new SearchListAdapter (null, null, null));
|
||||
unsavedAdapter = null;
|
||||
}
|
||||
|
||||
public void setAdapter (@NonNull final SearchListAdapter adapter) {
|
||||
if (list == null) {
|
||||
unsavedAdapter = adapter;
|
||||
return;
|
||||
}
|
||||
this.list.setAdapter (adapter);
|
||||
}
|
||||
|
||||
public void handleResultVisibility (boolean error) {
|
||||
if (emptyView != null && list != null) {
|
||||
emptyView.setVisibility (error ? View.VISIBLE : View.GONE);
|
||||
list.setVisibility (error ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.blankj.utilcode.util.ThreadUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class SimpleOutputFragment extends NonEditableEditorFragment {
|
||||
|
||||
private final List<String> unsavedLines = new ArrayList<> ();
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
|
||||
if (!unsavedLines.isEmpty ()) {
|
||||
for (String line : unsavedLines) {
|
||||
Objects.requireNonNull (getEditor ()).getText ().append (line.trim () + "\n");
|
||||
}
|
||||
unsavedLines.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
public void appendOutput (String output) {
|
||||
if (getEditor () == null) {
|
||||
unsavedLines.add (output);
|
||||
return;
|
||||
}
|
||||
ThreadUtils.runOnUiThread (() -> {
|
||||
final var message = output == null || output.endsWith ("\n") ? output : output + "\n";
|
||||
getEditor ().getText ().append (message);
|
||||
});
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.blankj.utilcode.util.ThreadUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class SimpleOutputFragment extends NonEditableEditorFragment {
|
||||
|
||||
private final List<String> unsavedLines = new ArrayList<> ();
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
|
||||
if (!unsavedLines.isEmpty ()) {
|
||||
for (String line : unsavedLines) {
|
||||
Objects.requireNonNull (getEditor ()).getText ().append (line.trim () + "\n");
|
||||
}
|
||||
unsavedLines.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
public void appendOutput (String output) {
|
||||
if (getEditor () == null) {
|
||||
unsavedLines.add (output);
|
||||
return;
|
||||
}
|
||||
ThreadUtils.runOnUiThread (() -> {
|
||||
final var message = output == null || output.endsWith ("\n") ? output : output + "\n";
|
||||
getEditor ().getText ().append (message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,239 +1,239 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.fragments.sheets;
|
||||
|
||||
import static com.itsaky.attrinfo.models.Attr.BOOLEAN;
|
||||
import static com.itsaky.attrinfo.models.Attr.COLOR;
|
||||
import static com.itsaky.attrinfo.models.Attr.DIMENSION;
|
||||
import static com.itsaky.attrinfo.models.Attr.ENUM;
|
||||
import static com.itsaky.attrinfo.models.Attr.FLAG;
|
||||
import static com.itsaky.attrinfo.models.Attr.STRING;
|
||||
|
||||
import static com.itsaky.androidide.utils.AttributeDialogs.*;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.transition.ChangeBounds;
|
||||
import androidx.transition.TransitionManager;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.adapters.AttributeListAdapter;
|
||||
import com.itsaky.androidide.adapters.SimpleIconTextAdapter;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.colorpicker.ColorPickerView;
|
||||
import com.itsaky.androidide.databinding.LayoutAttrEditorSheetBinding;
|
||||
import com.itsaky.androidide.databinding.LayoutAttrEditorSheetItemBinding;
|
||||
import com.itsaky.androidide.models.IconTextListItem;
|
||||
import com.itsaky.androidide.models.XMLAttribute;
|
||||
import com.itsaky.androidide.utils.AttributeDialogs;
|
||||
import com.itsaky.androidide.utils.DialogUtils;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.inflater.IView;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AttrEditorSheet extends BottomSheetDialogFragment implements SimpleIconTextAdapter.OnBindListener {
|
||||
|
||||
private static final List<IconTextListItem> VIEW_ACTIONS = new ArrayList<> ();
|
||||
|
||||
private IView selectedView;
|
||||
private LayoutAttrEditorSheetBinding binding;
|
||||
|
||||
private OnViewDeletionFailedListener mDeletionFailedListener;
|
||||
|
||||
private static final Logger LOG = Logger.instance ("AttrBottomSheet");
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView (@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
this.binding = LayoutAttrEditorSheetBinding.inflate (LayoutInflater.from (getContext ()), container, false);
|
||||
return binding.getRoot ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
|
||||
if (VIEW_ACTIONS.isEmpty ()) {
|
||||
Collections.addAll (VIEW_ACTIONS,
|
||||
IconTextListItem.create (getString (R.string.title_viewaction_delete), R.drawable.ic_delete),
|
||||
IconTextListItem.create (getString (R.string.msg_viewaction_select_parent), R.drawable.ic_view_select_parent)
|
||||
);
|
||||
}
|
||||
|
||||
setupViewData ();
|
||||
|
||||
AttributeDialogs.init (getActivity ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss (@NonNull DialogInterface dialog) {
|
||||
AttributeDialogs.release ();
|
||||
super.onDismiss (dialog);
|
||||
}
|
||||
|
||||
public AttrEditorSheet setDeletionFailedListener (OnViewDeletionFailedListener listener) {
|
||||
this.mDeletionFailedListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void setupViewData () {
|
||||
binding.actionsList.setAdapter (new SimpleIconTextAdapter (VIEW_ACTIONS).setOnBindListener (this));
|
||||
|
||||
if (this.selectedView == null) {
|
||||
LOG.error ("Cannot edit attributes of a null view.");
|
||||
return;
|
||||
}
|
||||
|
||||
binding.widgetName.setText (this.selectedView.getXmlTag ());
|
||||
binding.attrList.setAdapter (new AttributeListAdapter (
|
||||
this.selectedView.getAttributes ()
|
||||
.stream ()
|
||||
.map (XMLAttribute::new)
|
||||
.collect (Collectors.toList ()), this::onAttrClick)
|
||||
);
|
||||
}
|
||||
|
||||
public void setSelectedView (IView view) {
|
||||
this.selectedView = view;
|
||||
}
|
||||
|
||||
private void onAttrClick (LayoutAttrEditorSheetItemBinding binding, @NonNull XMLAttribute attribute) {
|
||||
|
||||
if (this.selectedView == null) {
|
||||
LOG.error ("Cannot edit attributes of a null view.");
|
||||
return;
|
||||
}
|
||||
|
||||
final var format = attribute.findFormat ();
|
||||
if (format == -1) {
|
||||
StudioApp.getInstance ().toast (getString (R.string.msg_no_attr_format), Toaster.Type.ERROR);
|
||||
LOG.error (getString (R.string.msg_no_attr_format), attribute);
|
||||
return;
|
||||
}
|
||||
|
||||
showEditorDialog (attribute);
|
||||
}
|
||||
|
||||
private void showEditorDialog (@NonNull XMLAttribute attribute) {
|
||||
final var attr = attribute.getAttr ();
|
||||
final var values = attr.possibleValues.toArray (new String[0]);
|
||||
final AttributeDialogs.OnClickListener onDone = (dialog, which, newValue) -> applyNewValue (attribute, newValue);
|
||||
|
||||
final ColorPickerView.OnPickListener mPickListener = (color, hexCode) -> applyNewValue (attribute, hexCode);
|
||||
|
||||
AlertDialog dialog = null;
|
||||
if (attribute.hasFormat (DIMENSION)) {
|
||||
dialog = AttributeDialogs.dimensionEditor (attribute.getValue (), onDone);
|
||||
} else if (attribute.hasFormat (STRING)) {
|
||||
dialog = AttributeDialogs.stringEditor (onDone);
|
||||
} else if (attribute.hasFormat (BOOLEAN)) {
|
||||
dialog = booleanEditor (onDone);
|
||||
} else if (attribute.hasFormat (ENUM)) {
|
||||
dialog = enumEditor (values, attribute.getValue (), onDone);
|
||||
} else if (attribute.hasFormat (FLAG)) {
|
||||
dialog = flagEditor (values, attribute.getValue (), onDone);
|
||||
} else if (attribute.hasFormat (COLOR)) {
|
||||
// TODO Implement something to allow values from colors.xml or ColorStateLists
|
||||
// Make this color picker optional
|
||||
dialog = colorPicker (mPickListener);
|
||||
}
|
||||
|
||||
if (dialog != null) {
|
||||
dialog.show ();
|
||||
}
|
||||
}
|
||||
|
||||
private void applyNewValue (@NonNull XMLAttribute attribute, String newValue) {
|
||||
if (!this.selectedView.updateAttribute (attribute.getNamespace (), attribute.getAttributeName (), newValue)) {
|
||||
StudioApp.getInstance ().toast ("Unable to update this attribute", Toaster.Type.ERROR);
|
||||
} else {
|
||||
// Update the view data
|
||||
// This will make sure that the attributes list has been updated
|
||||
setupViewData ();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBind (IconTextListItem item, @NonNull SimpleIconTextAdapter.VH holder, int position) {
|
||||
final var binding = holder.binding;
|
||||
binding.getRoot ().setOnClickListener (v -> onViewActionClick (position));
|
||||
}
|
||||
|
||||
private void onViewActionClick (int position) {
|
||||
if (this.selectedView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (position == 0) { // Delete
|
||||
DialogUtils.newYesNoDialog (getContext (), (dialog, which) -> {
|
||||
var handled = selectedView.removeFromParent ();
|
||||
if (!handled) {
|
||||
handled = mDeletionFailedListener != null && mDeletionFailedListener.onDeletionFailed (this.selectedView);
|
||||
}
|
||||
if (!handled) {
|
||||
StudioApp.getInstance ().toast (getString(R.string.msg_view_deletion_failed), Toaster.Type.ERROR);
|
||||
} else {
|
||||
dismiss ();
|
||||
}
|
||||
}, (dialog, which) -> dialog.dismiss ()).show ();
|
||||
} else if (position == 1) { // Select parent
|
||||
if (this.selectedView.getParent () == null) {
|
||||
StudioApp.getInstance ().toast (getString (R.string.msg_no_view_parent), Toaster.Type.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedView = this.selectedView.getParent ();
|
||||
|
||||
TransitionManager.beginDelayedTransition (binding.getRoot (), new ChangeBounds ());
|
||||
setupViewData ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener can be used to get notified when we fail to remove
|
||||
* a view from its parent. This is used in {@link com.itsaky.androidide.DesignerActivity DesignerActivity}.
|
||||
*
|
||||
* When the user tries to remove the outermost view of the inflated layout,
|
||||
* AttrEditorSheet fails to remove the view from its parent ({@link IView#getParent()} is null for root XML layout).
|
||||
* In this case, DesignerActivity check if the view that we were trying to delete is the root layout or not.
|
||||
* If it is the root layout, then it deletes the layout from the layout container.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public interface OnViewDeletionFailedListener {
|
||||
|
||||
/**
|
||||
* @param view The view that was not removed from its parent.
|
||||
* @return {@code true} if the listener handled the error. {@code false} otherwise.
|
||||
*/
|
||||
boolean onDeletionFailed (IView view);
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.fragments.sheets;
|
||||
|
||||
import static com.itsaky.attrinfo.models.Attr.BOOLEAN;
|
||||
import static com.itsaky.attrinfo.models.Attr.COLOR;
|
||||
import static com.itsaky.attrinfo.models.Attr.DIMENSION;
|
||||
import static com.itsaky.attrinfo.models.Attr.ENUM;
|
||||
import static com.itsaky.attrinfo.models.Attr.FLAG;
|
||||
import static com.itsaky.attrinfo.models.Attr.STRING;
|
||||
|
||||
import static com.itsaky.androidide.utils.AttributeDialogs.*;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.transition.ChangeBounds;
|
||||
import androidx.transition.TransitionManager;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.adapters.AttributeListAdapter;
|
||||
import com.itsaky.androidide.adapters.SimpleIconTextAdapter;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.colorpicker.ColorPickerView;
|
||||
import com.itsaky.androidide.databinding.LayoutAttrEditorSheetBinding;
|
||||
import com.itsaky.androidide.databinding.LayoutAttrEditorSheetItemBinding;
|
||||
import com.itsaky.androidide.models.IconTextListItem;
|
||||
import com.itsaky.androidide.models.XMLAttribute;
|
||||
import com.itsaky.androidide.utils.AttributeDialogs;
|
||||
import com.itsaky.androidide.utils.DialogUtils;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.inflater.IView;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AttrEditorSheet extends BottomSheetDialogFragment implements SimpleIconTextAdapter.OnBindListener {
|
||||
|
||||
private static final List<IconTextListItem> VIEW_ACTIONS = new ArrayList<> ();
|
||||
|
||||
private IView selectedView;
|
||||
private LayoutAttrEditorSheetBinding binding;
|
||||
|
||||
private OnViewDeletionFailedListener mDeletionFailedListener;
|
||||
|
||||
private static final Logger LOG = Logger.instance ("AttrBottomSheet");
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView (@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
this.binding = LayoutAttrEditorSheetBinding.inflate (LayoutInflater.from (getContext ()), container, false);
|
||||
return binding.getRoot ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated (view, savedInstanceState);
|
||||
|
||||
if (VIEW_ACTIONS.isEmpty ()) {
|
||||
Collections.addAll (VIEW_ACTIONS,
|
||||
IconTextListItem.create (getString (R.string.title_viewaction_delete), R.drawable.ic_delete),
|
||||
IconTextListItem.create (getString (R.string.msg_viewaction_select_parent), R.drawable.ic_view_select_parent)
|
||||
);
|
||||
}
|
||||
|
||||
setupViewData ();
|
||||
|
||||
AttributeDialogs.init (getActivity ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss (@NonNull DialogInterface dialog) {
|
||||
AttributeDialogs.release ();
|
||||
super.onDismiss (dialog);
|
||||
}
|
||||
|
||||
public AttrEditorSheet setDeletionFailedListener (OnViewDeletionFailedListener listener) {
|
||||
this.mDeletionFailedListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void setupViewData () {
|
||||
binding.actionsList.setAdapter (new SimpleIconTextAdapter (VIEW_ACTIONS).setOnBindListener (this));
|
||||
|
||||
if (this.selectedView == null) {
|
||||
LOG.error ("Cannot edit attributes of a null view.");
|
||||
return;
|
||||
}
|
||||
|
||||
binding.widgetName.setText (this.selectedView.getXmlTag ());
|
||||
binding.attrList.setAdapter (new AttributeListAdapter (
|
||||
this.selectedView.getAttributes ()
|
||||
.stream ()
|
||||
.map (XMLAttribute::new)
|
||||
.collect (Collectors.toList ()), this::onAttrClick)
|
||||
);
|
||||
}
|
||||
|
||||
public void setSelectedView (IView view) {
|
||||
this.selectedView = view;
|
||||
}
|
||||
|
||||
private void onAttrClick (LayoutAttrEditorSheetItemBinding binding, @NonNull XMLAttribute attribute) {
|
||||
|
||||
if (this.selectedView == null) {
|
||||
LOG.error ("Cannot edit attributes of a null view.");
|
||||
return;
|
||||
}
|
||||
|
||||
final var format = attribute.findFormat ();
|
||||
if (format == -1) {
|
||||
StudioApp.getInstance ().toast (getString (R.string.msg_no_attr_format), Toaster.Type.ERROR);
|
||||
LOG.error (getString (R.string.msg_no_attr_format), attribute);
|
||||
return;
|
||||
}
|
||||
|
||||
showEditorDialog (attribute);
|
||||
}
|
||||
|
||||
private void showEditorDialog (@NonNull XMLAttribute attribute) {
|
||||
final var attr = attribute.getAttr ();
|
||||
final var values = attr.possibleValues.toArray (new String[0]);
|
||||
final AttributeDialogs.OnClickListener onDone = (dialog, which, newValue) -> applyNewValue (attribute, newValue);
|
||||
|
||||
final ColorPickerView.OnPickListener mPickListener = (color, hexCode) -> applyNewValue (attribute, hexCode);
|
||||
|
||||
AlertDialog dialog = null;
|
||||
if (attribute.hasFormat (DIMENSION)) {
|
||||
dialog = AttributeDialogs.dimensionEditor (attribute.getValue (), onDone);
|
||||
} else if (attribute.hasFormat (STRING)) {
|
||||
dialog = AttributeDialogs.stringEditor (onDone);
|
||||
} else if (attribute.hasFormat (BOOLEAN)) {
|
||||
dialog = booleanEditor (onDone);
|
||||
} else if (attribute.hasFormat (ENUM)) {
|
||||
dialog = enumEditor (values, attribute.getValue (), onDone);
|
||||
} else if (attribute.hasFormat (FLAG)) {
|
||||
dialog = flagEditor (values, attribute.getValue (), onDone);
|
||||
} else if (attribute.hasFormat (COLOR)) {
|
||||
// TODO Implement something to allow values from colors.xml or ColorStateLists
|
||||
// Make this color picker optional
|
||||
dialog = colorPicker (mPickListener);
|
||||
}
|
||||
|
||||
if (dialog != null) {
|
||||
dialog.show ();
|
||||
}
|
||||
}
|
||||
|
||||
private void applyNewValue (@NonNull XMLAttribute attribute, String newValue) {
|
||||
if (!this.selectedView.updateAttribute (attribute.getNamespace (), attribute.getAttributeName (), newValue)) {
|
||||
StudioApp.getInstance ().toast ("Unable to update this attribute", Toaster.Type.ERROR);
|
||||
} else {
|
||||
// Update the view data
|
||||
// This will make sure that the attributes list has been updated
|
||||
setupViewData ();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postBind (IconTextListItem item, @NonNull SimpleIconTextAdapter.VH holder, int position) {
|
||||
final var binding = holder.binding;
|
||||
binding.getRoot ().setOnClickListener (v -> onViewActionClick (position));
|
||||
}
|
||||
|
||||
private void onViewActionClick (int position) {
|
||||
if (this.selectedView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (position == 0) { // Delete
|
||||
DialogUtils.newYesNoDialog (getContext (), (dialog, which) -> {
|
||||
var handled = selectedView.removeFromParent ();
|
||||
if (!handled) {
|
||||
handled = mDeletionFailedListener != null && mDeletionFailedListener.onDeletionFailed (this.selectedView);
|
||||
}
|
||||
if (!handled) {
|
||||
StudioApp.getInstance ().toast (getString(R.string.msg_view_deletion_failed), Toaster.Type.ERROR);
|
||||
} else {
|
||||
dismiss ();
|
||||
}
|
||||
}, (dialog, which) -> dialog.dismiss ()).show ();
|
||||
} else if (position == 1) { // Select parent
|
||||
if (this.selectedView.getParent () == null) {
|
||||
StudioApp.getInstance ().toast (getString (R.string.msg_no_view_parent), Toaster.Type.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedView = this.selectedView.getParent ();
|
||||
|
||||
TransitionManager.beginDelayedTransition (binding.getRoot (), new ChangeBounds ());
|
||||
setupViewData ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener can be used to get notified when we fail to remove
|
||||
* a view from its parent. This is used in {@link com.itsaky.androidide.DesignerActivity DesignerActivity}.
|
||||
*
|
||||
* When the user tries to remove the outermost view of the inflated layout,
|
||||
* AttrEditorSheet fails to remove the view from its parent ({@link IView#getParent()} is null for root XML layout).
|
||||
* In this case, DesignerActivity check if the view that we were trying to delete is the root layout or not.
|
||||
* If it is the root layout, then it deletes the layout from the layout container.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public interface OnViewDeletionFailedListener {
|
||||
|
||||
/**
|
||||
* @param view The view that was not removed from its parent.
|
||||
* @return {@code true} if the listener handled the error. {@code false} otherwise.
|
||||
*/
|
||||
boolean onDeletionFailed (IView view);
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,8 @@ import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import com.itsaky.androidide.databinding.LayoutProgressSheetBinding;
|
||||
import com.blankj.utilcode.util.SizeUtils;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
public class ProgressSheet extends BaseBottomSheetFragment {
|
||||
@@ -39,6 +41,8 @@ public class ProgressSheet extends BaseBottomSheetFragment {
|
||||
private boolean subMessageEnabled = false;
|
||||
private boolean welcomTextEnabled = false;
|
||||
|
||||
private final Logger LOG = Logger.instance ("ProgressSheet");
|
||||
|
||||
@Override
|
||||
protected void bind(LinearLayout container) {
|
||||
binding = LayoutProgressSheetBinding.inflate(LayoutInflater.from(getContext()));
|
||||
@@ -56,14 +60,18 @@ public class ProgressSheet extends BaseBottomSheetFragment {
|
||||
RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) binding.message.getLayoutParams();
|
||||
try {
|
||||
p.removeRule(RelativeLayout.CENTER_VERTICAL);
|
||||
} catch (Throwable th) {}
|
||||
} catch (Throwable th) {
|
||||
LOG.error ("Unable to remove center_vertical rule.", th);
|
||||
}
|
||||
binding.message.setLayoutParams(p);
|
||||
} else {
|
||||
binding.subMessage.setVisibility(View.GONE);
|
||||
RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) binding.message.getLayoutParams();
|
||||
try {
|
||||
p.addRule(RelativeLayout.CENTER_VERTICAL);
|
||||
} catch (Throwable th) {}
|
||||
} catch (Throwable th) {
|
||||
LOG.error ("Unable to remove center_vertical rule.", th);
|
||||
}
|
||||
binding.message.setLayoutParams(p);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,345 +1,344 @@
|
||||
/************************************************************************************
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
package com.itsaky.androidide.handlers;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import com.blankj.utilcode.util.ClipboardUtils;
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.adapters.viewholders.FileTreeViewHolder;
|
||||
import com.itsaky.androidide.databinding.LayoutCreateFileJavaBinding;
|
||||
import com.itsaky.androidide.databinding.LayoutDialogTextInputBinding;
|
||||
import com.itsaky.androidide.fragments.EditorFragment;
|
||||
import com.itsaky.androidide.fragments.sheets.OptionsListFragment;
|
||||
import com.itsaky.androidide.lsp.LSP;
|
||||
import com.itsaky.androidide.models.SheetOption;
|
||||
import com.itsaky.androidide.utils.DialogUtils;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
import com.itsaky.androidide.utils.ProjectWriter;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
import com.unnamed.b.atv.model.TreeNode;
|
||||
import java.io.File;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class FileOptionsHandler extends IDEHandler implements OptionsListFragment.OnOptionsClickListener {
|
||||
|
||||
public static final String RES_PATH_REGEX = "/.*/src/.*/res";
|
||||
public static final String LAYOUTRES_PATH_REGEX = "/.*/src/.*/res/layout";
|
||||
public static final String MENURES_PATH_REGEX = "/.*/src/.*/res/menu";
|
||||
public static final String DRAWABLERES_PATH_REGEX = "/.*/src/.*/res/drawable";
|
||||
public static final String JAVA_PATH_REGEX = "/.*/src/.*/java";
|
||||
|
||||
public FileOptionsHandler(Provider provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if(activity() == null)
|
||||
throwNPE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOptionsClick(SheetOption option) {
|
||||
if(option.extra instanceof File) {
|
||||
final File f = (File) option.extra;
|
||||
switch(option.id) {
|
||||
case 0 :
|
||||
ClipboardUtils.copyText("[AndroidIDE] Copied File Path", f.getAbsolutePath());
|
||||
activity().getApp().toast(R.string.copied, Toaster.Type.SUCCESS);
|
||||
break;
|
||||
case 1 :
|
||||
renameFile(f);
|
||||
break;
|
||||
case 2 :
|
||||
delete(f);
|
||||
break;
|
||||
case 3 :
|
||||
createNewFile(f);
|
||||
break;
|
||||
case 4 :
|
||||
createNewFolder(f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createNewFile(File f) {
|
||||
createNewFile(f, false);
|
||||
}
|
||||
|
||||
private void createNewFile(final File f, boolean forceUnknownType) {
|
||||
if(forceUnknownType) {
|
||||
createNewEmptyFile(f);
|
||||
} else {
|
||||
final boolean isJava = Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + JAVA_PATH_REGEX).matcher(f.getAbsolutePath()).find();
|
||||
final boolean isRes = Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + RES_PATH_REGEX).matcher(f.getAbsolutePath()).find();
|
||||
final boolean isLayoutRes = Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + LAYOUTRES_PATH_REGEX).matcher(f.getAbsolutePath()).find();
|
||||
final boolean isMenuRes = Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + MENURES_PATH_REGEX).matcher(f.getAbsolutePath()).find();
|
||||
final boolean isDrawableRes = Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + DRAWABLERES_PATH_REGEX).matcher(f.getAbsolutePath()).find();
|
||||
if(isJava) {
|
||||
createJavaClass(f);
|
||||
} else if(isLayoutRes && f.getName().equals("layout")) {
|
||||
createLayoutRes(f);
|
||||
} else if(isMenuRes && f.getName().equals("menu")) {
|
||||
createMenuRes(f);
|
||||
} else if(isDrawableRes && f.getName().equals("drawable")) {
|
||||
createDrawableRes(f);
|
||||
} else if(isRes && f.getName().equals("res")) {
|
||||
createNewResource(f);
|
||||
} else {
|
||||
createNewEmptyFile(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createJavaClass(final File f) {
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
final LayoutCreateFileJavaBinding binding = LayoutCreateFileJavaBinding.inflate(activity().getLayoutInflater());
|
||||
builder.setView(binding.getRoot());
|
||||
builder.setTitle(R.string.new_java_class);
|
||||
builder.setPositiveButton(R.string.text_create, (p1, p2) -> {
|
||||
p1.dismiss();
|
||||
final String name = Objects.requireNonNull (binding.name.getEditText ()).getText().toString().trim();
|
||||
final String pkgName = ProjectWriter.getPackageName(f);
|
||||
if(pkgName == null || pkgName.trim().length() <= 0) {
|
||||
activity().getApp().toast(R.string.msg_get_package_failed, Toaster.Type.ERROR);
|
||||
} else {
|
||||
final int id = binding.typeGroup.getCheckedButtonId();
|
||||
final var javaName = name.endsWith (".java") ? name : name.concat (".java");
|
||||
final var className = !name.contains (".") ? name : name.substring (0, name.lastIndexOf ("."));
|
||||
if(id == binding.typeClass.getId()) {
|
||||
createFile(f, javaName, ProjectWriter.createJavaClass(pkgName, className));
|
||||
} else if(id == binding.typeInterface.getId()) {
|
||||
createFile(f, javaName, ProjectWriter.createJavaInterface(pkgName, className));
|
||||
} else if(id == binding.typeEnum.getId()) {
|
||||
createFile(f, javaName, ProjectWriter.createJavaEnum(pkgName, className));
|
||||
} else if(id == binding.typeActivity.getId()) {
|
||||
createFile(f, javaName, ProjectWriter.createActivity(pkgName, className));
|
||||
} else {
|
||||
createFile(f, name, "");
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.setCancelable(false);
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void createLayoutRes(File f) {
|
||||
createNewFileWithContent(Environment.mkdirIfNotExits(f), ProjectWriter.createLayout(), ".xml");
|
||||
}
|
||||
|
||||
private void createMenuRes(File f) {
|
||||
createNewFileWithContent(Environment.mkdirIfNotExits(f), ProjectWriter.createMenu(), ".xml");
|
||||
}
|
||||
|
||||
private void createDrawableRes(File f) {
|
||||
createNewFileWithContent(Environment.mkdirIfNotExits(f), ProjectWriter.createDrawable(), ".xml");
|
||||
}
|
||||
|
||||
private void createNewResource(File f) {
|
||||
final String[] labels = {
|
||||
activity(). getString(R.string.restype_drawable),
|
||||
activity(). getString(R.string.restype_layout),
|
||||
activity(). getString(R.string.restype_menu),
|
||||
activity(). getString(R.string.restype_other)
|
||||
};
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
builder.setTitle(R.string.new_xml_resource);
|
||||
builder.setItems(labels, (dialogInterface, position) -> {
|
||||
if(position == 0) {
|
||||
createDrawableRes(new File(f, "drawable"));
|
||||
} else if(position == 1) {
|
||||
createLayoutRes(new File(f, "layout"));
|
||||
} else if(position == 2) {
|
||||
createMenuRes(new File(f, "menu"));
|
||||
} else if(position == 3) {
|
||||
createNewFile(f, true);
|
||||
}
|
||||
});
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void createNewEmptyFile(File f) {
|
||||
createNewFileWithContent(f, "");
|
||||
}
|
||||
|
||||
private void createNewFileWithContent(File f, String content) {
|
||||
createNewFileWithContent(f, content, null);
|
||||
}
|
||||
|
||||
private void createNewFileWithContent(File folder, String content, String extension) {
|
||||
final LayoutDialogTextInputBinding binding = LayoutDialogTextInputBinding.inflate(activity(). getLayoutInflater());
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
|
||||
Objects.requireNonNull (binding.name.getEditText ()).setHint(R.string.file_name);
|
||||
|
||||
builder.setTitle(R.string.new_file);
|
||||
builder.setMessage(activity(). getString(R.string.msg_can_contain_slashes).concat("\n\n").concat(activity(). getString(R.string.msg_newfile_dest, folder.getAbsolutePath())));
|
||||
builder.setView(binding.getRoot());
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton(R.string.text_create, (p1, p2) -> {
|
||||
p1.dismiss();
|
||||
String name = binding.name.getEditText().getText().toString().trim();
|
||||
if(extension != null && extension.trim().length() > 0) {
|
||||
name = name.endsWith(extension) ? name : name.concat(extension);
|
||||
}
|
||||
createFile(folder, name, content);
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void createFile(File f, String name, String content) {
|
||||
if(name.length() > 0 && name.length() <= 40 && !name.startsWith("/")) {
|
||||
final File file = new File(f, name);
|
||||
if(file.exists()) {
|
||||
activity().getApp().toast(R.string.msg_file_exists, Toaster.Type.ERROR);
|
||||
} else {
|
||||
if(FileIOUtils.writeFileFromString(file, content)) {
|
||||
LSP.notifyFileCreated(file);
|
||||
activity(). getApp().toast(R.string.msg_file_created, Toaster.Type.SUCCESS);
|
||||
if(activity().getLastHoldTreeNode() != null) {
|
||||
TreeNode node = new TreeNode(file);
|
||||
node.setViewHolder(new FileTreeViewHolder(activity()));
|
||||
activity().getLastHoldTreeNode().addChild(node);
|
||||
activity().getFileTreeFragment().expandNode(activity().getLastHoldTreeNode());
|
||||
} else {
|
||||
activity().getFileTreeFragment().listProjectFiles();
|
||||
}
|
||||
} else {
|
||||
activity(). getApp().toast(R.string.msg_file_creation_failed, Toaster.Type.ERROR);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
activity(). getApp().toast(R.string.msg_invalid_name, Toaster.Type.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private void createNewFolder(File f) {
|
||||
final LayoutDialogTextInputBinding binding = LayoutDialogTextInputBinding.inflate(activity(). getLayoutInflater());
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
|
||||
Objects.requireNonNull (binding.name.getEditText ()).setHint(R.string.folder_name);
|
||||
|
||||
builder.setTitle(R.string.new_folder);
|
||||
builder.setMessage(R.string.msg_can_contain_slashes);
|
||||
builder.setView(binding.getRoot());
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton(R.string.text_create, (p1, p2) -> {
|
||||
p1.dismiss();
|
||||
final String name = binding.name.getEditText().getText().toString().trim();
|
||||
if(name.length() > 0 && name.length() <= 40 && !name.startsWith("/")) {
|
||||
final File file = new File(f, name);
|
||||
if(file.exists()) {
|
||||
activity(). getApp().toast(R.string.msg_folder_exists, Toaster.Type.ERROR);
|
||||
} else {
|
||||
if(file.mkdirs()) {
|
||||
activity(). getApp().toast(R.string.msg_folder_created, Toaster.Type.SUCCESS);
|
||||
if(activity().getLastHoldTreeNode() != null) {
|
||||
TreeNode node = new TreeNode(file);
|
||||
node.setViewHolder(new FileTreeViewHolder(activity()));
|
||||
activity().getLastHoldTreeNode().addChild(node);
|
||||
activity().getFileTreeFragment().expandNode(activity().getLastHoldTreeNode());
|
||||
} else {
|
||||
activity().getFileTreeFragment().listProjectFiles();
|
||||
}
|
||||
} else {
|
||||
activity().getApp().toast(R.string.msg_folder_creation_failed, Toaster.Type.ERROR);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
activity(). getApp().toast(R.string.msg_invalid_name, Toaster.Type.ERROR);
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void delete(final File f) {
|
||||
MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
builder
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.setPositiveButton(android.R.string.yes, (p1, p2) -> {
|
||||
p1.dismiss();
|
||||
final boolean deleted = FileUtils.delete(f);
|
||||
activity(). getApp().toast(deleted ? R.string.deleted : R.string.delete_failed, deleted ? Toaster.Type.SUCCESS : Toaster.Type.ERROR);
|
||||
if(deleted) {
|
||||
LSP.notifyFileDeleted(f);
|
||||
if(activity().getLastHoldTreeNode() != null) {
|
||||
TreeNode parent = activity().getLastHoldTreeNode().getParent();
|
||||
parent.deleteChild(activity().getLastHoldTreeNode());
|
||||
activity().getFileTreeFragment().expandNode(parent);
|
||||
} else {
|
||||
activity().getFileTreeFragment().listProjectFiles();
|
||||
}
|
||||
|
||||
EditorFragment frag = activity().getEditorForFile (f);
|
||||
if(frag != null) {
|
||||
activity().closeFile(activity().findIndexOfEditorByFile (frag.getFile ()));
|
||||
}
|
||||
}
|
||||
})
|
||||
.setTitle(R.string.title_confirm_delete)
|
||||
.setMessage(activity().getString(R.string.msg_confirm_delete, String.format("%s [%s]", f.getName(), f.getAbsolutePath())))
|
||||
.setCancelable(false)
|
||||
.create().show();
|
||||
}
|
||||
|
||||
private void renameFile(File f) {
|
||||
final LayoutDialogTextInputBinding binding = LayoutDialogTextInputBinding.inflate(LayoutInflater.from(activity()));
|
||||
MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
|
||||
Objects.requireNonNull (binding.name.getEditText ()).setHint(activity(). getString(R.string.new_name));
|
||||
binding.name.getEditText().setText(f.getName());
|
||||
|
||||
builder.setTitle(R.string.rename_file);
|
||||
builder.setMessage(R.string.msg_rename_file);
|
||||
builder.setView(binding.getRoot());
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.setPositiveButton(R.string.rename_file, (p1, p2) -> {
|
||||
p1.dismiss();
|
||||
String name = binding.name.getEditText().getText().toString().trim();
|
||||
boolean renamed = name.length () > 0 && name.length () <= 40 && FileUtils.rename (f, name);
|
||||
activity(). getApp().toast(renamed ? R.string.renamed : R.string.rename_failed, renamed ? Toaster.Type.SUCCESS : Toaster.Type.ERROR);
|
||||
if(renamed) {
|
||||
LSP.notifyFileRenamed(f, name);
|
||||
if(activity().getLastHoldTreeNode() != null) {
|
||||
TreeNode parent = activity().getLastHoldTreeNode().getParent();
|
||||
parent.deleteChild(activity().getLastHoldTreeNode());
|
||||
TreeNode node = new TreeNode(new File(f.getParentFile(), name));
|
||||
node.setViewHolder(new FileTreeViewHolder(activity()));
|
||||
parent.addChild(node);
|
||||
activity().getFileTreeFragment().expandNode(parent);
|
||||
} else {
|
||||
activity().getFileTreeFragment().listProjectFiles();
|
||||
}
|
||||
}
|
||||
});
|
||||
builder.create().show();
|
||||
}
|
||||
}
|
||||
/************************************************************************************
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
package com.itsaky.androidide.handlers;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import com.blankj.utilcode.util.ClipboardUtils;
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.adapters.viewholders.FileTreeViewHolder;
|
||||
import com.itsaky.androidide.databinding.LayoutCreateFileJavaBinding;
|
||||
import com.itsaky.androidide.databinding.LayoutDialogTextInputBinding;
|
||||
import com.itsaky.androidide.fragments.sheets.OptionsListFragment;
|
||||
import com.itsaky.androidide.lsp.LSP;
|
||||
import com.itsaky.androidide.models.SheetOption;
|
||||
import com.itsaky.androidide.utils.DialogUtils;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
import com.itsaky.androidide.utils.ProjectWriter;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
import com.unnamed.b.atv.model.TreeNode;
|
||||
import java.io.File;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class FileOptionsHandler extends IDEHandler implements OptionsListFragment.OnOptionsClickListener {
|
||||
|
||||
public static final String RES_PATH_REGEX = "/.*/src/.*/res";
|
||||
public static final String LAYOUTRES_PATH_REGEX = "/.*/src/.*/res/layout";
|
||||
public static final String MENURES_PATH_REGEX = "/.*/src/.*/res/menu";
|
||||
public static final String DRAWABLERES_PATH_REGEX = "/.*/src/.*/res/drawable";
|
||||
public static final String JAVA_PATH_REGEX = "/.*/src/.*/java";
|
||||
|
||||
public FileOptionsHandler(Provider provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if(activity() == null)
|
||||
throwNPE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOptionsClick(SheetOption option) {
|
||||
if(option.extra instanceof File) {
|
||||
final File f = (File) option.extra;
|
||||
switch(option.id) {
|
||||
case 0 :
|
||||
ClipboardUtils.copyText("[AndroidIDE] Copied File Path", f.getAbsolutePath());
|
||||
activity().getApp().toast(R.string.copied, Toaster.Type.SUCCESS);
|
||||
break;
|
||||
case 1 :
|
||||
renameFile(f);
|
||||
break;
|
||||
case 2 :
|
||||
delete(f);
|
||||
break;
|
||||
case 3 :
|
||||
createNewFile(f);
|
||||
break;
|
||||
case 4 :
|
||||
createNewFolder(f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createNewFile(File f) {
|
||||
createNewFile(f, false);
|
||||
}
|
||||
|
||||
private void createNewFile(final File f, boolean forceUnknownType) {
|
||||
if(forceUnknownType) {
|
||||
createNewEmptyFile(f);
|
||||
} else {
|
||||
final boolean isJava = Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + JAVA_PATH_REGEX).matcher(f.getAbsolutePath()).find();
|
||||
final boolean isRes = Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + RES_PATH_REGEX).matcher(f.getAbsolutePath()).find();
|
||||
final boolean isLayoutRes = Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + LAYOUTRES_PATH_REGEX).matcher(f.getAbsolutePath()).find();
|
||||
final boolean isMenuRes = Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + MENURES_PATH_REGEX).matcher(f.getAbsolutePath()).find();
|
||||
final boolean isDrawableRes = Pattern.compile(Pattern.quote(androidProject().getProjectPath()) + DRAWABLERES_PATH_REGEX).matcher(f.getAbsolutePath()).find();
|
||||
if(isJava) {
|
||||
createJavaClass(f);
|
||||
} else if(isLayoutRes && f.getName().equals("layout")) {
|
||||
createLayoutRes(f);
|
||||
} else if(isMenuRes && f.getName().equals("menu")) {
|
||||
createMenuRes(f);
|
||||
} else if(isDrawableRes && f.getName().equals("drawable")) {
|
||||
createDrawableRes(f);
|
||||
} else if(isRes && f.getName().equals("res")) {
|
||||
createNewResource(f);
|
||||
} else {
|
||||
createNewEmptyFile(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createJavaClass(final File f) {
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
final LayoutCreateFileJavaBinding binding = LayoutCreateFileJavaBinding.inflate(activity().getLayoutInflater());
|
||||
builder.setView(binding.getRoot());
|
||||
builder.setTitle(R.string.new_java_class);
|
||||
builder.setPositiveButton(R.string.text_create, (p1, p2) -> {
|
||||
p1.dismiss();
|
||||
final String name = Objects.requireNonNull (binding.name.getEditText ()).getText().toString().trim();
|
||||
final String pkgName = ProjectWriter.getPackageName(f);
|
||||
if(pkgName == null || pkgName.trim().length() <= 0) {
|
||||
activity().getApp().toast(R.string.msg_get_package_failed, Toaster.Type.ERROR);
|
||||
} else {
|
||||
final int id = binding.typeGroup.getCheckedButtonId();
|
||||
final var javaName = name.endsWith (".java") ? name : name.concat (".java");
|
||||
final var className = !name.contains (".") ? name : name.substring (0, name.lastIndexOf ("."));
|
||||
if(id == binding.typeClass.getId()) {
|
||||
createFile(f, javaName, ProjectWriter.createJavaClass(pkgName, className));
|
||||
} else if(id == binding.typeInterface.getId()) {
|
||||
createFile(f, javaName, ProjectWriter.createJavaInterface(pkgName, className));
|
||||
} else if(id == binding.typeEnum.getId()) {
|
||||
createFile(f, javaName, ProjectWriter.createJavaEnum(pkgName, className));
|
||||
} else if(id == binding.typeActivity.getId()) {
|
||||
createFile(f, javaName, ProjectWriter.createActivity(pkgName, className));
|
||||
} else {
|
||||
createFile(f, name, "");
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.setCancelable(false);
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void createLayoutRes(File f) {
|
||||
createNewFileWithContent(Environment.mkdirIfNotExits(f), ProjectWriter.createLayout(), ".xml");
|
||||
}
|
||||
|
||||
private void createMenuRes(File f) {
|
||||
createNewFileWithContent(Environment.mkdirIfNotExits(f), ProjectWriter.createMenu(), ".xml");
|
||||
}
|
||||
|
||||
private void createDrawableRes(File f) {
|
||||
createNewFileWithContent(Environment.mkdirIfNotExits(f), ProjectWriter.createDrawable(), ".xml");
|
||||
}
|
||||
|
||||
private void createNewResource(File f) {
|
||||
final String[] labels = {
|
||||
activity(). getString(R.string.restype_drawable),
|
||||
activity(). getString(R.string.restype_layout),
|
||||
activity(). getString(R.string.restype_menu),
|
||||
activity(). getString(R.string.restype_other)
|
||||
};
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
builder.setTitle(R.string.new_xml_resource);
|
||||
builder.setItems(labels, (dialogInterface, position) -> {
|
||||
if(position == 0) {
|
||||
createDrawableRes(new File(f, "drawable"));
|
||||
} else if(position == 1) {
|
||||
createLayoutRes(new File(f, "layout"));
|
||||
} else if(position == 2) {
|
||||
createMenuRes(new File(f, "menu"));
|
||||
} else if(position == 3) {
|
||||
createNewFile(f, true);
|
||||
}
|
||||
});
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void createNewEmptyFile(File f) {
|
||||
createNewFileWithContent(f, "");
|
||||
}
|
||||
|
||||
private void createNewFileWithContent(File f, String content) {
|
||||
createNewFileWithContent(f, content, null);
|
||||
}
|
||||
|
||||
private void createNewFileWithContent(File folder, String content, String extension) {
|
||||
final LayoutDialogTextInputBinding binding = LayoutDialogTextInputBinding.inflate(activity(). getLayoutInflater());
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
|
||||
Objects.requireNonNull (binding.name.getEditText ()).setHint(R.string.file_name);
|
||||
|
||||
builder.setTitle(R.string.new_file);
|
||||
builder.setMessage(activity(). getString(R.string.msg_can_contain_slashes).concat("\n\n").concat(activity(). getString(R.string.msg_newfile_dest, folder.getAbsolutePath())));
|
||||
builder.setView(binding.getRoot());
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton(R.string.text_create, (p1, p2) -> {
|
||||
p1.dismiss();
|
||||
String name = binding.name.getEditText().getText().toString().trim();
|
||||
if(extension != null && extension.trim().length() > 0) {
|
||||
name = name.endsWith(extension) ? name : name.concat(extension);
|
||||
}
|
||||
createFile(folder, name, content);
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void createFile(File f, String name, String content) {
|
||||
if(name.length() > 0 && name.length() <= 40 && !name.startsWith("/")) {
|
||||
final File file = new File(f, name);
|
||||
if(file.exists()) {
|
||||
activity().getApp().toast(R.string.msg_file_exists, Toaster.Type.ERROR);
|
||||
} else {
|
||||
if(FileIOUtils.writeFileFromString(file, content)) {
|
||||
LSP.notifyFileCreated(file);
|
||||
activity(). getApp().toast(R.string.msg_file_created, Toaster.Type.SUCCESS);
|
||||
if(activity().getLastHoldTreeNode() != null) {
|
||||
TreeNode node = new TreeNode(file);
|
||||
node.setViewHolder(new FileTreeViewHolder(activity()));
|
||||
activity().getLastHoldTreeNode().addChild(node);
|
||||
activity().getFileTreeFragment().expandNode(activity().getLastHoldTreeNode());
|
||||
} else {
|
||||
activity().getFileTreeFragment().listProjectFiles();
|
||||
}
|
||||
} else {
|
||||
activity(). getApp().toast(R.string.msg_file_creation_failed, Toaster.Type.ERROR);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
activity(). getApp().toast(R.string.msg_invalid_name, Toaster.Type.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private void createNewFolder(File f) {
|
||||
final LayoutDialogTextInputBinding binding = LayoutDialogTextInputBinding.inflate(activity(). getLayoutInflater());
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
|
||||
Objects.requireNonNull (binding.name.getEditText ()).setHint(R.string.folder_name);
|
||||
|
||||
builder.setTitle(R.string.new_folder);
|
||||
builder.setMessage(R.string.msg_can_contain_slashes);
|
||||
builder.setView(binding.getRoot());
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton(R.string.text_create, (p1, p2) -> {
|
||||
p1.dismiss();
|
||||
final String name = binding.name.getEditText().getText().toString().trim();
|
||||
if(name.length() > 0 && name.length() <= 40 && !name.startsWith("/")) {
|
||||
final File file = new File(f, name);
|
||||
if(file.exists()) {
|
||||
activity(). getApp().toast(R.string.msg_folder_exists, Toaster.Type.ERROR);
|
||||
} else {
|
||||
if(file.mkdirs()) {
|
||||
activity(). getApp().toast(R.string.msg_folder_created, Toaster.Type.SUCCESS);
|
||||
if(activity().getLastHoldTreeNode() != null) {
|
||||
TreeNode node = new TreeNode(file);
|
||||
node.setViewHolder(new FileTreeViewHolder(activity()));
|
||||
activity().getLastHoldTreeNode().addChild(node);
|
||||
activity().getFileTreeFragment().expandNode(activity().getLastHoldTreeNode());
|
||||
} else {
|
||||
activity().getFileTreeFragment().listProjectFiles();
|
||||
}
|
||||
} else {
|
||||
activity().getApp().toast(R.string.msg_folder_creation_failed, Toaster.Type.ERROR);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
activity(). getApp().toast(R.string.msg_invalid_name, Toaster.Type.ERROR);
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void delete(final File f) {
|
||||
MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
builder
|
||||
.setNegativeButton(android.R.string.no, null)
|
||||
.setPositiveButton(android.R.string.yes, (p1, p2) -> {
|
||||
p1.dismiss();
|
||||
final boolean deleted = FileUtils.delete(f);
|
||||
activity(). getApp().toast(deleted ? R.string.deleted : R.string.delete_failed, deleted ? Toaster.Type.SUCCESS : Toaster.Type.ERROR);
|
||||
if(deleted) {
|
||||
LSP.notifyFileDeleted(f);
|
||||
if(activity().getLastHoldTreeNode() != null) {
|
||||
TreeNode parent = activity().getLastHoldTreeNode().getParent();
|
||||
parent.deleteChild(activity().getLastHoldTreeNode());
|
||||
activity().getFileTreeFragment().expandNode(parent);
|
||||
} else {
|
||||
activity().getFileTreeFragment().listProjectFiles();
|
||||
}
|
||||
|
||||
final var frag = activity().getEditorForFile (f);
|
||||
if(frag != null) {
|
||||
activity().closeFile(activity().findIndexOfEditorByFile (frag.getFile ()));
|
||||
}
|
||||
}
|
||||
})
|
||||
.setTitle(R.string.title_confirm_delete)
|
||||
.setMessage(activity().getString(R.string.msg_confirm_delete, String.format("%s [%s]", f.getName(), f.getAbsolutePath())))
|
||||
.setCancelable(false)
|
||||
.create().show();
|
||||
}
|
||||
|
||||
private void renameFile(File f) {
|
||||
final LayoutDialogTextInputBinding binding = LayoutDialogTextInputBinding.inflate(LayoutInflater.from(activity()));
|
||||
MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
|
||||
Objects.requireNonNull (binding.name.getEditText ()).setHint(activity(). getString(R.string.new_name));
|
||||
binding.name.getEditText().setText(f.getName());
|
||||
|
||||
builder.setTitle(R.string.rename_file);
|
||||
builder.setMessage(R.string.msg_rename_file);
|
||||
builder.setView(binding.getRoot());
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.setPositiveButton(R.string.rename_file, (p1, p2) -> {
|
||||
p1.dismiss();
|
||||
String name = binding.name.getEditText().getText().toString().trim();
|
||||
boolean renamed = name.length () > 0 && name.length () <= 40 && FileUtils.rename (f, name);
|
||||
activity(). getApp().toast(renamed ? R.string.renamed : R.string.rename_failed, renamed ? Toaster.Type.SUCCESS : Toaster.Type.ERROR);
|
||||
if(renamed) {
|
||||
LSP.notifyFileRenamed(f, name);
|
||||
if(activity().getLastHoldTreeNode() != null) {
|
||||
TreeNode parent = activity().getLastHoldTreeNode().getParent();
|
||||
parent.deleteChild(activity().getLastHoldTreeNode());
|
||||
TreeNode node = new TreeNode(new File(f.getParentFile(), name));
|
||||
node.setViewHolder(new FileTreeViewHolder(activity()));
|
||||
parent.addChild(node);
|
||||
activity().getFileTreeFragment().expandNode(parent);
|
||||
} else {
|
||||
activity().getFileTreeFragment().listProjectFiles();
|
||||
}
|
||||
}
|
||||
});
|
||||
builder.create().show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public abstract class IDEHandler {
|
||||
/**
|
||||
* An interface to communicate between a handler and its client
|
||||
*/
|
||||
public static interface Provider {
|
||||
public interface Provider {
|
||||
|
||||
/**
|
||||
* Called by handler to get a reference to {@link EditorActivity}
|
||||
|
||||
@@ -1,462 +1,462 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.language.java;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.androidide.lexers.java.JavaLexer;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import com.itsaky.androidide.utils.LSPUtils;
|
||||
import com.itsaky.lsp.SemanticHighlight;
|
||||
import com.itsaky.lsp.services.IDELanguageServer;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.github.rosemoe.editor.langs.AbstractCodeAnalyzer;
|
||||
import io.github.rosemoe.editor.struct.BlockLine;
|
||||
import io.github.rosemoe.editor.text.CharPosition;
|
||||
import io.github.rosemoe.editor.text.Content;
|
||||
import io.github.rosemoe.editor.text.TextAnalyzeResult;
|
||||
import io.github.rosemoe.editor.text.TextAnalyzer;
|
||||
import io.github.rosemoe.editor.widget.EditorColorScheme;
|
||||
|
||||
// TODO request language server for semantic highlights instead of waiting for it to send
|
||||
|
||||
/**
|
||||
* Code analyzer for the Java Language
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class JavaAnalyzer extends AbstractCodeAnalyzer {
|
||||
|
||||
private HighlightRangeHelper helper;
|
||||
private Map<Integer, Map<Integer, Diagnostic>> diagnostics = new HashMap<> ();
|
||||
|
||||
private final Map<Integer, List<Range>> stringMap = new HashMap<> ();
|
||||
|
||||
@Override
|
||||
public void analyze (IDELanguageServer languageServer, File file, @NonNull Content content, TextAnalyzeResult colors, @NonNull TextAnalyzer.AnalyzeThread.Delegate delegate) throws Exception {
|
||||
final var stream = CharStreams.fromReader (new StringReader (content.toString ()));
|
||||
final var lexer = new JavaLexer (stream);
|
||||
final var stack = new Stack<BlockLine> ();
|
||||
|
||||
Token token;
|
||||
int line, column, lastLine = 1, type, currSwitch = 0, maxSwitch = 0;
|
||||
boolean isFirst = true;
|
||||
|
||||
this.stringMap.clear ();
|
||||
|
||||
while (delegate.shouldAnalyze ()) {
|
||||
token = lexer.nextToken ();
|
||||
|
||||
if (token == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
type = token.getType ();
|
||||
line = token.getLine () - 1;
|
||||
column = token.getCharPositionInLine ();
|
||||
|
||||
if (type == JavaLexer.EOF) {
|
||||
lastLine = line;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case JavaLexer.WS:
|
||||
if (isFirst) {
|
||||
colors.addNormalIfNull ();
|
||||
}
|
||||
break;
|
||||
case JavaLexer.ABSTRACT:
|
||||
case JavaLexer.ASSERT:
|
||||
case JavaLexer.BREAK:
|
||||
case JavaLexer.CASE:
|
||||
case JavaLexer.CATCH:
|
||||
case JavaLexer.CLASS:
|
||||
case JavaLexer.CONST:
|
||||
case JavaLexer.CONTINUE:
|
||||
case JavaLexer.DEFAULT:
|
||||
case JavaLexer.DO:
|
||||
case JavaLexer.ELSE:
|
||||
case JavaLexer.EXTENDS:
|
||||
case JavaLexer.FINAL:
|
||||
case JavaLexer.FINALLY:
|
||||
case JavaLexer.FOR:
|
||||
case JavaLexer.IF:
|
||||
case JavaLexer.GOTO:
|
||||
case JavaLexer.IMPLEMENTS:
|
||||
case JavaLexer.IMPORT:
|
||||
case JavaLexer.INSTANCEOF:
|
||||
case JavaLexer.INTERFACE:
|
||||
case JavaLexer.NATIVE:
|
||||
case JavaLexer.NEW:
|
||||
case JavaLexer.PACKAGE:
|
||||
case JavaLexer.PRIVATE:
|
||||
case JavaLexer.PROTECTED:
|
||||
case JavaLexer.PUBLIC:
|
||||
case JavaLexer.RETURN:
|
||||
case JavaLexer.STATIC:
|
||||
case JavaLexer.STRICTFP:
|
||||
case JavaLexer.SUPER:
|
||||
case JavaLexer.SWITCH:
|
||||
case JavaLexer.SYNCHRONIZED:
|
||||
case JavaLexer.THIS:
|
||||
case JavaLexer.THROW:
|
||||
case JavaLexer.THROWS:
|
||||
case JavaLexer.TRANSIENT:
|
||||
case JavaLexer.TRY:
|
||||
case JavaLexer.VOID:
|
||||
case JavaLexer.VOLATILE:
|
||||
case JavaLexer.WHILE:
|
||||
case JavaLexer.VAR:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.KEYWORD);
|
||||
break;
|
||||
case JavaLexer.DECIMAL_LITERAL:
|
||||
case JavaLexer.HEX_LITERAL:
|
||||
case JavaLexer.OCT_LITERAL:
|
||||
case JavaLexer.BINARY_LITERAL:
|
||||
case JavaLexer.FLOAT_LITERAL:
|
||||
case JavaLexer.HEX_FLOAT_LITERAL:
|
||||
case JavaLexer.BOOL_LITERAL:
|
||||
case JavaLexer.CHAR_LITERAL:
|
||||
case JavaLexer.NULL_LITERAL:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.LITERAL);
|
||||
break;
|
||||
case JavaLexer.STRING_LITERAL:
|
||||
checkAndAddHexString (token, EditorColorScheme.LITERAL, colors);
|
||||
|
||||
Position start = new Position (line, column);
|
||||
Position end = new Position (line, column + token.getText ().length ());
|
||||
colors.addStringRange (line, new Range (start, end));
|
||||
|
||||
{
|
||||
List<Range> ranges = stringMap.get (line);
|
||||
if (ranges == null) {
|
||||
ranges = new ArrayList<> ();
|
||||
}
|
||||
ranges.add (new Range (start, end));
|
||||
stringMap.put (line, ranges);
|
||||
}
|
||||
break;
|
||||
case JavaLexer.LPAREN:
|
||||
case JavaLexer.RPAREN:
|
||||
case JavaLexer.LBRACK:
|
||||
case JavaLexer.RBRACK:
|
||||
case JavaLexer.SEMI:
|
||||
case JavaLexer.COMMA:
|
||||
case JavaLexer.ASSIGN:
|
||||
case JavaLexer.GT:
|
||||
case JavaLexer.LT:
|
||||
case JavaLexer.BANG:
|
||||
case JavaLexer.TILDE:
|
||||
case JavaLexer.QUESTION:
|
||||
case JavaLexer.COLON:
|
||||
case JavaLexer.EQUAL:
|
||||
case JavaLexer.GE:
|
||||
case JavaLexer.LE:
|
||||
case JavaLexer.NOTEQUAL:
|
||||
case JavaLexer.AND:
|
||||
case JavaLexer.OR:
|
||||
case JavaLexer.INC:
|
||||
case JavaLexer.DEC:
|
||||
case JavaLexer.ADD:
|
||||
case JavaLexer.SUB:
|
||||
case JavaLexer.MUL:
|
||||
case JavaLexer.DIV:
|
||||
case JavaLexer.BITAND:
|
||||
case JavaLexer.BITOR:
|
||||
case JavaLexer.CARET:
|
||||
case JavaLexer.MOD:
|
||||
case JavaLexer.ADD_ASSIGN:
|
||||
case JavaLexer.SUB_ASSIGN:
|
||||
case JavaLexer.MUL_ASSIGN:
|
||||
case JavaLexer.DIV_ASSIGN:
|
||||
case JavaLexer.AND_ASSIGN:
|
||||
case JavaLexer.OR_ASSIGN:
|
||||
case JavaLexer.XOR_ASSIGN:
|
||||
case JavaLexer.MOD_ASSIGN:
|
||||
case JavaLexer.LSHIFT_ASSIGN:
|
||||
case JavaLexer.RSHIFT_ASSIGN:
|
||||
case JavaLexer.URSHIFT_ASSIGN:
|
||||
case JavaLexer.ARROW:
|
||||
case JavaLexer.COLONCOLON:
|
||||
case JavaLexer.ELLIPSIS:
|
||||
case JavaLexer.DOT:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.OPERATOR);
|
||||
break;
|
||||
case JavaLexer.BOOLEAN:
|
||||
case JavaLexer.BYTE:
|
||||
case JavaLexer.CHAR:
|
||||
case JavaLexer.DOUBLE:
|
||||
case JavaLexer.ENUM:
|
||||
case JavaLexer.FLOAT:
|
||||
case JavaLexer.INT:
|
||||
case JavaLexer.LONG:
|
||||
case JavaLexer.SHORT:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.TYPE_NAME);
|
||||
break;
|
||||
case JavaLexer.BLOCK_COMMENT:
|
||||
case JavaLexer.LINE_COMMENT:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.COMMENT);
|
||||
|
||||
final CharPosition a = content.getIndexer ().getCharPosition (token.getStopIndex ());
|
||||
final Position s = new Position (line, column);
|
||||
final Position e = new Position (a.line, a.column);
|
||||
colors.addCommentRange (line, new Range (s, e));
|
||||
|
||||
String lineComment = token.getText ();
|
||||
if (lineComment != null) {
|
||||
if (lineComment.startsWith ("//")) {
|
||||
lineComment = lineComment.substring (2);
|
||||
}
|
||||
|
||||
// Trim leading whitespaces
|
||||
int startsFrom = 0;
|
||||
for (int i = 0; i < lineComment.length (); i++) {
|
||||
if (Character.isWhitespace (lineComment.charAt (i))) {
|
||||
startsFrom++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lineComment = lineComment.substring (startsFrom);
|
||||
|
||||
final String text = new String (lineComment.toCharArray ());
|
||||
lineComment = lineComment.toLowerCase (Locale.getDefault ());
|
||||
if (lineComment.startsWith ("todo ")) {
|
||||
diagnostics.put (line, Collections.singletonMap (column, LSPUtils.newInfoDiagnostic (line,
|
||||
column,
|
||||
token.getText ().length (),
|
||||
text,
|
||||
token.getText ())));
|
||||
} else if (lineComment.startsWith (ConstantsBridge.CUSTOM_COMMENT_WARNING_TOKEN)) {
|
||||
diagnostics.put (line, Collections.singletonMap (column, LSPUtils.newWarningDiagnostic (line,
|
||||
column,
|
||||
token.getText ().length (),
|
||||
text,
|
||||
token.getText ())));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case JavaLexer.AT:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.ANNOTATION);
|
||||
break;
|
||||
case JavaLexer.IDENTIFIER:
|
||||
if (helper != null && helper.isPackageName (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.PACKAGE_NAME);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isEnumType (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.ENUM_TYPE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isClassName (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.TYPE_NAME);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isAnnotationType (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.ANNOTATION);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isInterface (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.INTERFACE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isEnum (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.ENUM);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isStaticField (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.STATIC_FIELD);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isField (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.FIELD);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isParameter (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.PARAMETER);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isLocal (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.LOCAL_VARIABLE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isExceptionParam (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.EXCEPTION_PARAM);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isMethodDeclaration (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.METHOD_DECLARATION);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isMethodInvocation (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.METHOD_INVOCATION);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isConstructor (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.CONSTRUCTOR);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isStaticInit (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.STATIC_INIT);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isInstanceInit (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.INSTANCE_INIT);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isTypeParam (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.TYPE_PARAM);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isResourceVariable (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.RESOURCE_VARIABLE);
|
||||
break;
|
||||
}
|
||||
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.TEXT_NORMAL);
|
||||
break;
|
||||
case JavaLexer.LBRACE:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.OPERATOR);
|
||||
if (stack.isEmpty ()) {
|
||||
if (currSwitch > maxSwitch) {
|
||||
maxSwitch = currSwitch;
|
||||
}
|
||||
currSwitch = 0;
|
||||
}
|
||||
currSwitch++;
|
||||
BlockLine block = colors.obtainNewBlock ();
|
||||
block.startLine = line;
|
||||
block.startColumn = column;
|
||||
stack.push (block);
|
||||
break;
|
||||
case JavaLexer.RBRACE:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.OPERATOR);
|
||||
if (!stack.isEmpty ()) {
|
||||
BlockLine block2 = stack.pop ();
|
||||
block2.endLine = line;
|
||||
block2.endColumn = column;
|
||||
if (block2.startLine != block2.endLine) {
|
||||
colors.addBlockLine (block2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.TEXT_NORMAL);
|
||||
break;
|
||||
}
|
||||
|
||||
isFirst = false;
|
||||
}
|
||||
|
||||
if (stack.isEmpty () && currSwitch > maxSwitch) {
|
||||
maxSwitch = currSwitch;
|
||||
}
|
||||
colors.determine (lastLine);
|
||||
colors.setSuppressSwitch (maxSwitch + 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSemanticHighlights (SemanticHighlight highlights) {
|
||||
this.helper = new HighlightRangeHelper (highlights);
|
||||
this.helper.sort ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDiagnostics (Map<Integer, Map<Integer, Diagnostic>> diagnostics) {
|
||||
if (diagnostics == null) {
|
||||
diagnostics = new HashMap<> ();
|
||||
}
|
||||
this.diagnostics = diagnostics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Diagnostic findDiagnosticContaining (int line, int column) {
|
||||
final Map<Integer, Diagnostic> mappedByColumn = diagnostics.get (line);
|
||||
if (mappedByColumn != null) {
|
||||
Diagnostic diag = mappedByColumn.get (column);
|
||||
if (diag != null) {
|
||||
return diag;
|
||||
} else {
|
||||
for (Map.Entry<Integer, Diagnostic> entry : mappedByColumn.entrySet ()) {
|
||||
diag = entry.getValue ();
|
||||
final int start = diag.getRange ().getStart ().getCharacter ();
|
||||
final int end = diag.getRange ().getEnd ().getCharacter ();
|
||||
|
||||
if (column >= start && column <= end) {
|
||||
return diag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.findDiagnosticContaining (line, column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Diagnostic> findDiagnosticsContainingLine (int line) {
|
||||
final Map<Integer, Diagnostic> diags = diagnostics.get (line);
|
||||
if (diags == null) {
|
||||
return super.findDiagnosticsContainingLine (line);
|
||||
}
|
||||
return diags.entrySet ().stream ()
|
||||
.filter (d -> d != null && d.getValue () != null)
|
||||
.map (Map.Entry::getValue)
|
||||
.collect (Collectors.toList ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Integer, Diagnostic> getDiagnosticsAtLine (int line) {
|
||||
return diagnostics.get (line);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.language.java;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.androidide.lexers.java.JavaLexer;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import com.itsaky.androidide.utils.LSPUtils;
|
||||
import com.itsaky.lsp.SemanticHighlight;
|
||||
import com.itsaky.lsp.services.IDELanguageServer;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.github.rosemoe.editor.langs.AbstractCodeAnalyzer;
|
||||
import io.github.rosemoe.editor.struct.BlockLine;
|
||||
import io.github.rosemoe.editor.text.CharPosition;
|
||||
import io.github.rosemoe.editor.text.Content;
|
||||
import io.github.rosemoe.editor.text.TextAnalyzeResult;
|
||||
import io.github.rosemoe.editor.text.TextAnalyzer;
|
||||
import io.github.rosemoe.editor.widget.EditorColorScheme;
|
||||
|
||||
// TODO request language server for semantic highlights instead of waiting for it to send
|
||||
|
||||
/**
|
||||
* Code analyzer for the Java Language
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class JavaAnalyzer extends AbstractCodeAnalyzer {
|
||||
|
||||
private HighlightRangeHelper helper;
|
||||
private Map<Integer, Map<Integer, Diagnostic>> diagnostics = new HashMap<> ();
|
||||
|
||||
private final Map<Integer, List<Range>> stringMap = new HashMap<> ();
|
||||
|
||||
@Override
|
||||
public void analyze (IDELanguageServer languageServer, File file, @NonNull Content content, TextAnalyzeResult colors, @NonNull TextAnalyzer.AnalyzeThread.Delegate delegate) throws Exception {
|
||||
final var stream = CharStreams.fromReader (new StringReader (content.toString ()));
|
||||
final var lexer = new JavaLexer (stream);
|
||||
final var stack = new Stack<BlockLine> ();
|
||||
|
||||
Token token;
|
||||
int line, column, lastLine = 1, type, currSwitch = 0, maxSwitch = 0;
|
||||
boolean isFirst = true;
|
||||
|
||||
this.stringMap.clear ();
|
||||
|
||||
while (delegate.shouldAnalyze ()) {
|
||||
token = lexer.nextToken ();
|
||||
|
||||
if (token == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
type = token.getType ();
|
||||
line = token.getLine () - 1;
|
||||
column = token.getCharPositionInLine ();
|
||||
|
||||
if (type == JavaLexer.EOF) {
|
||||
lastLine = line;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case JavaLexer.WS:
|
||||
if (isFirst) {
|
||||
colors.addNormalIfNull ();
|
||||
}
|
||||
break;
|
||||
case JavaLexer.ABSTRACT:
|
||||
case JavaLexer.ASSERT:
|
||||
case JavaLexer.BREAK:
|
||||
case JavaLexer.CASE:
|
||||
case JavaLexer.CATCH:
|
||||
case JavaLexer.CLASS:
|
||||
case JavaLexer.CONST:
|
||||
case JavaLexer.CONTINUE:
|
||||
case JavaLexer.DEFAULT:
|
||||
case JavaLexer.DO:
|
||||
case JavaLexer.ELSE:
|
||||
case JavaLexer.EXTENDS:
|
||||
case JavaLexer.FINAL:
|
||||
case JavaLexer.FINALLY:
|
||||
case JavaLexer.FOR:
|
||||
case JavaLexer.IF:
|
||||
case JavaLexer.GOTO:
|
||||
case JavaLexer.IMPLEMENTS:
|
||||
case JavaLexer.IMPORT:
|
||||
case JavaLexer.INSTANCEOF:
|
||||
case JavaLexer.INTERFACE:
|
||||
case JavaLexer.NATIVE:
|
||||
case JavaLexer.NEW:
|
||||
case JavaLexer.PACKAGE:
|
||||
case JavaLexer.PRIVATE:
|
||||
case JavaLexer.PROTECTED:
|
||||
case JavaLexer.PUBLIC:
|
||||
case JavaLexer.RETURN:
|
||||
case JavaLexer.STATIC:
|
||||
case JavaLexer.STRICTFP:
|
||||
case JavaLexer.SUPER:
|
||||
case JavaLexer.SWITCH:
|
||||
case JavaLexer.SYNCHRONIZED:
|
||||
case JavaLexer.THIS:
|
||||
case JavaLexer.THROW:
|
||||
case JavaLexer.THROWS:
|
||||
case JavaLexer.TRANSIENT:
|
||||
case JavaLexer.TRY:
|
||||
case JavaLexer.VOID:
|
||||
case JavaLexer.VOLATILE:
|
||||
case JavaLexer.WHILE:
|
||||
case JavaLexer.VAR:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.KEYWORD);
|
||||
break;
|
||||
case JavaLexer.DECIMAL_LITERAL:
|
||||
case JavaLexer.HEX_LITERAL:
|
||||
case JavaLexer.OCT_LITERAL:
|
||||
case JavaLexer.BINARY_LITERAL:
|
||||
case JavaLexer.FLOAT_LITERAL:
|
||||
case JavaLexer.HEX_FLOAT_LITERAL:
|
||||
case JavaLexer.BOOL_LITERAL:
|
||||
case JavaLexer.CHAR_LITERAL:
|
||||
case JavaLexer.NULL_LITERAL:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.LITERAL);
|
||||
break;
|
||||
case JavaLexer.STRING_LITERAL:
|
||||
checkAndAddHexString (token, EditorColorScheme.LITERAL, colors);
|
||||
|
||||
Position start = new Position (line, column);
|
||||
Position end = new Position (line, column + token.getText ().length ());
|
||||
colors.addStringRange (line, new Range (start, end));
|
||||
|
||||
{
|
||||
List<Range> ranges = stringMap.get (line);
|
||||
if (ranges == null) {
|
||||
ranges = new ArrayList<> ();
|
||||
}
|
||||
ranges.add (new Range (start, end));
|
||||
stringMap.put (line, ranges);
|
||||
}
|
||||
break;
|
||||
case JavaLexer.LPAREN:
|
||||
case JavaLexer.RPAREN:
|
||||
case JavaLexer.LBRACK:
|
||||
case JavaLexer.RBRACK:
|
||||
case JavaLexer.SEMI:
|
||||
case JavaLexer.COMMA:
|
||||
case JavaLexer.ASSIGN:
|
||||
case JavaLexer.GT:
|
||||
case JavaLexer.LT:
|
||||
case JavaLexer.BANG:
|
||||
case JavaLexer.TILDE:
|
||||
case JavaLexer.QUESTION:
|
||||
case JavaLexer.COLON:
|
||||
case JavaLexer.EQUAL:
|
||||
case JavaLexer.GE:
|
||||
case JavaLexer.LE:
|
||||
case JavaLexer.NOTEQUAL:
|
||||
case JavaLexer.AND:
|
||||
case JavaLexer.OR:
|
||||
case JavaLexer.INC:
|
||||
case JavaLexer.DEC:
|
||||
case JavaLexer.ADD:
|
||||
case JavaLexer.SUB:
|
||||
case JavaLexer.MUL:
|
||||
case JavaLexer.DIV:
|
||||
case JavaLexer.BITAND:
|
||||
case JavaLexer.BITOR:
|
||||
case JavaLexer.CARET:
|
||||
case JavaLexer.MOD:
|
||||
case JavaLexer.ADD_ASSIGN:
|
||||
case JavaLexer.SUB_ASSIGN:
|
||||
case JavaLexer.MUL_ASSIGN:
|
||||
case JavaLexer.DIV_ASSIGN:
|
||||
case JavaLexer.AND_ASSIGN:
|
||||
case JavaLexer.OR_ASSIGN:
|
||||
case JavaLexer.XOR_ASSIGN:
|
||||
case JavaLexer.MOD_ASSIGN:
|
||||
case JavaLexer.LSHIFT_ASSIGN:
|
||||
case JavaLexer.RSHIFT_ASSIGN:
|
||||
case JavaLexer.URSHIFT_ASSIGN:
|
||||
case JavaLexer.ARROW:
|
||||
case JavaLexer.COLONCOLON:
|
||||
case JavaLexer.ELLIPSIS:
|
||||
case JavaLexer.DOT:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.OPERATOR);
|
||||
break;
|
||||
case JavaLexer.BOOLEAN:
|
||||
case JavaLexer.BYTE:
|
||||
case JavaLexer.CHAR:
|
||||
case JavaLexer.DOUBLE:
|
||||
case JavaLexer.ENUM:
|
||||
case JavaLexer.FLOAT:
|
||||
case JavaLexer.INT:
|
||||
case JavaLexer.LONG:
|
||||
case JavaLexer.SHORT:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.TYPE_NAME);
|
||||
break;
|
||||
case JavaLexer.BLOCK_COMMENT:
|
||||
case JavaLexer.LINE_COMMENT:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.COMMENT);
|
||||
|
||||
final CharPosition a = content.getIndexer ().getCharPosition (token.getStopIndex ());
|
||||
final Position s = new Position (line, column);
|
||||
final Position e = new Position (a.line, a.column);
|
||||
colors.addCommentRange (line, new Range (s, e));
|
||||
|
||||
String lineComment = token.getText ();
|
||||
if (lineComment != null) {
|
||||
if (lineComment.startsWith ("//")) {
|
||||
lineComment = lineComment.substring (2);
|
||||
}
|
||||
|
||||
// Trim leading whitespaces
|
||||
int startsFrom = 0;
|
||||
for (int i = 0; i < lineComment.length (); i++) {
|
||||
if (Character.isWhitespace (lineComment.charAt (i))) {
|
||||
startsFrom++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lineComment = lineComment.substring (startsFrom);
|
||||
|
||||
final String text = new String (lineComment.toCharArray ());
|
||||
lineComment = lineComment.toLowerCase (Locale.getDefault ());
|
||||
if (lineComment.startsWith ("todo ")) {
|
||||
diagnostics.put (line, Collections.singletonMap (column, LSPUtils.newInfoDiagnostic (line,
|
||||
column,
|
||||
token.getText ().length (),
|
||||
text,
|
||||
token.getText ())));
|
||||
} else if (lineComment.startsWith (ConstantsBridge.CUSTOM_COMMENT_WARNING_TOKEN)) {
|
||||
diagnostics.put (line, Collections.singletonMap (column, LSPUtils.newWarningDiagnostic (line,
|
||||
column,
|
||||
token.getText ().length (),
|
||||
text,
|
||||
token.getText ())));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case JavaLexer.AT:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.ANNOTATION);
|
||||
break;
|
||||
case JavaLexer.IDENTIFIER:
|
||||
if (helper != null && helper.isPackageName (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.PACKAGE_NAME);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isEnumType (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.ENUM_TYPE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isClassName (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.TYPE_NAME);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isAnnotationType (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.ANNOTATION);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isInterface (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.INTERFACE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isEnum (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.ENUM);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isStaticField (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.STATIC_FIELD);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isField (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.FIELD);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isParameter (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.PARAMETER);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isLocal (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.LOCAL_VARIABLE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isExceptionParam (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.EXCEPTION_PARAM);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isMethodDeclaration (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.METHOD_DECLARATION);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isMethodInvocation (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.METHOD_INVOCATION);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isConstructor (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.CONSTRUCTOR);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isStaticInit (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.STATIC_INIT);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isInstanceInit (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.INSTANCE_INIT);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isTypeParam (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.TYPE_PARAM);
|
||||
break;
|
||||
}
|
||||
|
||||
if (helper != null && helper.isResourceVariable (line, column)) {
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.RESOURCE_VARIABLE);
|
||||
break;
|
||||
}
|
||||
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.TEXT_NORMAL);
|
||||
break;
|
||||
case JavaLexer.LBRACE:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.OPERATOR);
|
||||
if (stack.isEmpty ()) {
|
||||
if (currSwitch > maxSwitch) {
|
||||
maxSwitch = currSwitch;
|
||||
}
|
||||
currSwitch = 0;
|
||||
}
|
||||
currSwitch++;
|
||||
BlockLine block = colors.obtainNewBlock ();
|
||||
block.startLine = line;
|
||||
block.startColumn = column;
|
||||
stack.push (block);
|
||||
break;
|
||||
case JavaLexer.RBRACE:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.OPERATOR);
|
||||
if (!stack.isEmpty ()) {
|
||||
BlockLine block2 = stack.pop ();
|
||||
block2.endLine = line;
|
||||
block2.endColumn = column;
|
||||
if (block2.startLine != block2.endLine) {
|
||||
colors.addBlockLine (block2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
colors.addIfNeeded (line, column, EditorColorScheme.TEXT_NORMAL);
|
||||
break;
|
||||
}
|
||||
|
||||
isFirst = false;
|
||||
}
|
||||
|
||||
if (stack.isEmpty () && currSwitch > maxSwitch) {
|
||||
maxSwitch = currSwitch;
|
||||
}
|
||||
colors.determine (lastLine);
|
||||
colors.setSuppressSwitch (maxSwitch + 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSemanticHighlights (SemanticHighlight highlights) {
|
||||
this.helper = new HighlightRangeHelper (highlights);
|
||||
this.helper.sort ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDiagnostics (Map<Integer, Map<Integer, Diagnostic>> diagnostics) {
|
||||
if (diagnostics == null) {
|
||||
diagnostics = new HashMap<> ();
|
||||
}
|
||||
this.diagnostics = diagnostics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Diagnostic findDiagnosticContaining (int line, int column) {
|
||||
final Map<Integer, Diagnostic> mappedByColumn = diagnostics.get (line);
|
||||
if (mappedByColumn != null) {
|
||||
Diagnostic diag = mappedByColumn.get (column);
|
||||
if (diag != null) {
|
||||
return diag;
|
||||
} else {
|
||||
for (Map.Entry<Integer, Diagnostic> entry : mappedByColumn.entrySet ()) {
|
||||
diag = entry.getValue ();
|
||||
final int start = diag.getRange ().getStart ().getCharacter ();
|
||||
final int end = diag.getRange ().getEnd ().getCharacter ();
|
||||
|
||||
if (column >= start && column <= end) {
|
||||
return diag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.findDiagnosticContaining (line, column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Diagnostic> findDiagnosticsContainingLine (int line) {
|
||||
final Map<Integer, Diagnostic> diags = diagnostics.get (line);
|
||||
if (diags == null) {
|
||||
return super.findDiagnosticsContainingLine (line);
|
||||
}
|
||||
return diags.entrySet ().stream ()
|
||||
.filter (d -> d != null && d.getValue () != null)
|
||||
.map (Map.Entry::getValue)
|
||||
.collect (Collectors.toList ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Integer, Diagnostic> getDiagnosticsAtLine (int line) {
|
||||
return diagnostics.get (line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/************************************************************************************
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
*
|
||||
@@ -16,15 +16,14 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
|
||||
*/
|
||||
package com.itsaky.androidide.lsp;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.transition.TransitionManager;
|
||||
|
||||
@@ -39,9 +38,7 @@ import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.adapters.DiagnosticsAdapter;
|
||||
import com.itsaky.androidide.adapters.SearchListAdapter;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.databinding.FragmentEditorBinding;
|
||||
import com.itsaky.androidide.databinding.LayoutDiagnosticInfoBinding;
|
||||
import com.itsaky.androidide.fragments.EditorFragment;
|
||||
import com.itsaky.androidide.fragments.sheets.ProgressSheet;
|
||||
import com.itsaky.androidide.interfaces.EditorActivityProvider;
|
||||
import com.itsaky.androidide.models.DiagnosticGroup;
|
||||
@@ -50,11 +47,39 @@ import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.itsaky.androidide.utils.DialogUtils;
|
||||
import com.itsaky.androidide.utils.LSPUtils;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.androidide.views.CodeEditorView;
|
||||
import com.itsaky.lsp.SemanticHighlight;
|
||||
import com.itsaky.lsp.services.IDELanguageClient;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
import io.github.rosemoe.editor.text.Content;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
|
||||
import org.eclipse.lsp4j.ApplyWorkspaceEditParams;
|
||||
import org.eclipse.lsp4j.ApplyWorkspaceEditResponse;
|
||||
import org.eclipse.lsp4j.CodeAction;
|
||||
import org.eclipse.lsp4j.Command;
|
||||
import org.eclipse.lsp4j.ConfigurationParams;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.LocationLink;
|
||||
import org.eclipse.lsp4j.LogTraceParams;
|
||||
import org.eclipse.lsp4j.MessageActionItem;
|
||||
import org.eclipse.lsp4j.MessageParams;
|
||||
import org.eclipse.lsp4j.ParameterInformation;
|
||||
import org.eclipse.lsp4j.ProgressParams;
|
||||
import org.eclipse.lsp4j.PublishDiagnosticsParams;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.RegistrationParams;
|
||||
import org.eclipse.lsp4j.SetTraceParams;
|
||||
import org.eclipse.lsp4j.ShowDocumentParams;
|
||||
import org.eclipse.lsp4j.ShowDocumentResult;
|
||||
import org.eclipse.lsp4j.ShowMessageRequestParams;
|
||||
import org.eclipse.lsp4j.SignatureHelp;
|
||||
import org.eclipse.lsp4j.SignatureInformation;
|
||||
import org.eclipse.lsp4j.TextEdit;
|
||||
import org.eclipse.lsp4j.UnregistrationParams;
|
||||
import org.eclipse.lsp4j.WorkDoneProgressCreateParams;
|
||||
import org.eclipse.lsp4j.WorkspaceFolder;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
@@ -62,35 +87,12 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
import org.eclipse.lsp4j.CodeAction;
|
||||
import org.eclipse.lsp4j.Command;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.LocationLink;
|
||||
import org.eclipse.lsp4j.LogTraceParams;
|
||||
import org.eclipse.lsp4j.MessageParams;
|
||||
import org.eclipse.lsp4j.ParameterInformation;
|
||||
import org.eclipse.lsp4j.PublishDiagnosticsParams;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.ShowDocumentParams;
|
||||
import org.eclipse.lsp4j.ShowDocumentResult;
|
||||
import org.eclipse.lsp4j.SignatureHelp;
|
||||
import org.eclipse.lsp4j.SignatureInformation;
|
||||
import org.eclipse.lsp4j.TextEdit;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.eclipse.lsp4j.SetTraceParams;
|
||||
import org.eclipse.lsp4j.ApplyWorkspaceEditResponse;
|
||||
import org.eclipse.lsp4j.WorkDoneProgressCreateParams;
|
||||
import org.eclipse.lsp4j.ProgressParams;
|
||||
import org.eclipse.lsp4j.ConfigurationParams;
|
||||
import org.eclipse.lsp4j.RegistrationParams;
|
||||
import org.eclipse.lsp4j.ApplyWorkspaceEditParams;
|
||||
import org.eclipse.lsp4j.MessageActionItem;
|
||||
import org.eclipse.lsp4j.WorkspaceFolder;
|
||||
import org.eclipse.lsp4j.ShowMessageRequestParams;
|
||||
import org.eclipse.lsp4j.UnregistrationParams;
|
||||
|
||||
import io.github.rosemoe.editor.text.Content;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
|
||||
/**
|
||||
* AndroidIDE specific implementation of the LanguageClient
|
||||
@@ -99,13 +101,11 @@ public class IDELanguageClientImpl implements IDELanguageClient {
|
||||
|
||||
protected static final Gson gson = new Gson();
|
||||
protected static final Logger LOG = Logger.instance("AbstractLanguageClient");
|
||||
public static final int DIAGNOSTIC_TRANSITION_DURATION = 80;
|
||||
|
||||
private final Map<File, List<Diagnostic>> diagnostics = new HashMap<>();
|
||||
private static IDELanguageClientImpl mInstance;
|
||||
|
||||
protected EditorActivityProvider activityProvider;
|
||||
private boolean isConnected;
|
||||
|
||||
private IDELanguageClientImpl (EditorActivityProvider provider) {
|
||||
setActivityProvider(provider);
|
||||
@@ -136,27 +136,20 @@ public class IDELanguageClientImpl implements IDELanguageClient {
|
||||
public void setActivityProvider(EditorActivityProvider provider) {
|
||||
this.activityProvider = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Are we connected to the server?
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
return isConnected;
|
||||
}
|
||||
|
||||
protected EditorActivity activity() {
|
||||
if (activityProvider == null) return null;
|
||||
return activityProvider.provide();
|
||||
}
|
||||
|
||||
private EditorFragment findEditorByFile (File file) {
|
||||
private CodeEditorView findEditorByFile (File file) {
|
||||
return activity ().getEditorForFile (file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void semanticHighlights(SemanticHighlight highlights) {
|
||||
final File file = new File(URI.create(highlights.uri));
|
||||
final EditorFragment editor = findEditorByFile(file);
|
||||
final var editor = findEditorByFile(file);
|
||||
|
||||
if(editor != null) {
|
||||
editor.getEditor().setSemanticHighlights(highlights);
|
||||
@@ -200,13 +193,13 @@ public class IDELanguageClientImpl implements IDELanguageClient {
|
||||
return;
|
||||
}
|
||||
|
||||
final EditorFragment frag = findEditorByFile(file);
|
||||
if(frag == null || frag.getBinding() == null) {
|
||||
final var frag = findEditorByFile(file);
|
||||
if(frag == null) {
|
||||
hideBottomDiagnosticView(file);
|
||||
return;
|
||||
}
|
||||
|
||||
final FragmentEditorBinding binding = frag.getBinding();
|
||||
final var binding = frag.getBinding();
|
||||
binding.diagnosticTextContainer.setVisibility(View.VISIBLE);
|
||||
binding.diagnosticText.setVisibility(View.VISIBLE);
|
||||
binding.diagnosticText.setClickable(false);
|
||||
@@ -219,13 +212,12 @@ public class IDELanguageClientImpl implements IDELanguageClient {
|
||||
}
|
||||
|
||||
future.whenComplete((a, b) -> {
|
||||
final Throwable error = b;
|
||||
if(a == null || a.isEmpty()) {
|
||||
hideBottomDiagnosticView(file);
|
||||
return;
|
||||
}
|
||||
final List<CodeAction> actions = a.stream().filter(e -> e.isRight()).map (e -> e.getRight()).collect(Collectors.toList());
|
||||
if(actions == null || actions.isEmpty()) {
|
||||
final List<CodeAction> actions = a.stream().filter(Either::isRight).map (Either::getRight).collect(Collectors.toList());
|
||||
if(actions.isEmpty()) {
|
||||
hideBottomDiagnosticView(file);
|
||||
return;
|
||||
}
|
||||
@@ -236,9 +228,7 @@ public class IDELanguageClientImpl implements IDELanguageClient {
|
||||
sb.append(diagnostic.getMessage());
|
||||
binding.diagnosticText.setText(sb);
|
||||
binding.diagnosticText.setClickable(true);
|
||||
binding.diagnosticText.setOnClickListener(v -> {
|
||||
showAvailableQuickfixes(editor, actions);
|
||||
});
|
||||
binding.diagnosticText.setOnClickListener(v -> showAvailableQuickfixes(editor, actions));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -256,7 +246,7 @@ public class IDELanguageClientImpl implements IDELanguageClient {
|
||||
return;
|
||||
}
|
||||
|
||||
final EditorFragment frag = findEditorByFile(file);
|
||||
final var frag = findEditorByFile(file);
|
||||
if(frag == null || frag.getBinding() == null) {
|
||||
return;
|
||||
}
|
||||
@@ -277,7 +267,7 @@ public class IDELanguageClientImpl implements IDELanguageClient {
|
||||
SignatureInformation info = signatureWithMostParams(signature);
|
||||
if(info == null) return;
|
||||
activity().getBinding().symbolText.setText(formatSignature(info, signature.getActiveParameter()));
|
||||
final EditorFragment frag = findEditorByFile(file);
|
||||
final var frag = findEditorByFile(file);
|
||||
if(frag != null) {
|
||||
final CodeEditor editor = frag.getEditor();
|
||||
final float[] cursor = editor.getCursorPosition();
|
||||
@@ -325,7 +315,8 @@ public class IDELanguageClientImpl implements IDELanguageClient {
|
||||
* @param signature Signature information
|
||||
* @param paramIndex Currently active parameter index
|
||||
*/
|
||||
private CharSequence formatSignature(SignatureInformation signature, int paramIndex) {
|
||||
@NonNull
|
||||
private CharSequence formatSignature(@NonNull SignatureInformation signature, int paramIndex) {
|
||||
String name = signature.getLabel();
|
||||
name = name.substring(0, name.indexOf("("));
|
||||
|
||||
@@ -362,7 +353,7 @@ public class IDELanguageClientImpl implements IDELanguageClient {
|
||||
diagnostics.put(file, params.getDiagnostics());
|
||||
activity().setDiagnosticsAdapter(newDiagnosticsAdapter());
|
||||
|
||||
EditorFragment editor;
|
||||
CodeEditorView editor;
|
||||
if((editor = findEditorByFile(file)) != null) {
|
||||
editor.setDiagnostics(params.getDiagnostics());
|
||||
}
|
||||
@@ -394,13 +385,13 @@ public class IDELanguageClientImpl implements IDELanguageClient {
|
||||
if(loc == null || loc.getUri() == null || loc.getRange() == null) continue;
|
||||
final File file = new File(URI.create(loc.getUri()));
|
||||
if(!file.exists() || !file.isFile()) continue;
|
||||
EditorFragment frag = findEditorByFile(file);
|
||||
var frag = findEditorByFile(file);
|
||||
Content content;
|
||||
if(frag != null && frag.getEditor() != null)
|
||||
content = frag.getEditor().getText();
|
||||
else content = new Content(null, FileIOUtils.readFile2String(file));
|
||||
final List<SearchResult> matches = results.containsKey(file) ? results.get(file) : new ArrayList<>();
|
||||
matches.add(
|
||||
Objects.requireNonNull (matches).add(
|
||||
new SearchResult(
|
||||
loc.getRange(),
|
||||
file,
|
||||
@@ -415,7 +406,7 @@ public class IDELanguageClientImpl implements IDELanguageClient {
|
||||
);
|
||||
results.put(file, matches);
|
||||
} catch (Throwable th) {
|
||||
|
||||
LOG.error ("Failed to show file location", th);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,9 +425,9 @@ public class IDELanguageClientImpl implements IDELanguageClient {
|
||||
|
||||
showLocations(locations
|
||||
.stream()
|
||||
.filter(l -> l != null)
|
||||
.map(l -> asLocation(l))
|
||||
.filter(l -> l != null)
|
||||
.filter(Objects::nonNull)
|
||||
.map(this::asLocation)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
@@ -462,14 +453,9 @@ public class IDELanguageClientImpl implements IDELanguageClient {
|
||||
progress.show(activity().getSupportFragmentManager(), "quick_fix_progress");
|
||||
|
||||
new TaskExecutor().executeAsyncProvideError(() -> performCodeActionAsync(editor, action), (a, b) -> {
|
||||
final Boolean complete = a;
|
||||
final Throwable error = b;
|
||||
|
||||
progress.dismiss();
|
||||
|
||||
if(complete == null || error != null || !complete.booleanValue()) {
|
||||
if(a == null || b != null || !a) {
|
||||
StudioApp.getInstance().toast(R.string.msg_cannot_perform_fix, Toaster.Type.ERROR);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -491,7 +477,7 @@ public class IDELanguageClientImpl implements IDELanguageClient {
|
||||
File file = new File(URI.create(params.getUri()));
|
||||
if(file.exists() && file.isFile() && FileUtils.isUtf8(file)) {
|
||||
final Range range = params.getSelection();
|
||||
EditorFragment frag = activity().getEditorAtIndex (
|
||||
var frag = activity().getEditorAtIndex (
|
||||
activity().getBinding ().tabs.getSelectedTabPosition ());
|
||||
if(frag != null
|
||||
&& frag.getFile() != null
|
||||
@@ -554,13 +540,10 @@ public class IDELanguageClientImpl implements IDELanguageClient {
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder (activity ());
|
||||
builder.setTitle(R.string.msg_code_actions);
|
||||
builder.setItems(asArray(actions), (d, w) -> {
|
||||
final DialogInterface dialog = d;
|
||||
final int which = w;
|
||||
|
||||
dialog.dismiss();
|
||||
d.dismiss();
|
||||
hideDiagnostics();
|
||||
hideBottomDiagnosticView(editor.getFile());
|
||||
performCodeAction(editor, actions.get(which));
|
||||
performCodeAction(editor, actions.get(w));
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
@@ -589,7 +572,7 @@ public class IDELanguageClientImpl implements IDELanguageClient {
|
||||
// Edit is in the same editor which requested the code action
|
||||
editInEditor(editor, edit);
|
||||
} else {
|
||||
EditorFragment openedFrag = findEditorByFile(file);
|
||||
var openedFrag = findEditorByFile(file);
|
||||
|
||||
if(openedFrag != null && openedFrag.getEditor() != null) {
|
||||
// Edit is in another 'opened' file
|
||||
|
||||
@@ -66,13 +66,6 @@ public abstract class LSPClientLauncher extends Thread {
|
||||
public IDELanguageServer getServer() {
|
||||
return this.server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Are we connected to the server?
|
||||
*/
|
||||
public boolean isConnected() {
|
||||
return languageClient != null && languageClient.isConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Actual logic to start the server
|
||||
|
||||
@@ -1,467 +0,0 @@
|
||||
/************************************************************************************
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
*
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
|
||||
package com.itsaky.androidide.lsp;
|
||||
|
||||
import com.itsaky.lsp.SemanticHighlight;
|
||||
import com.itsaky.lsp.SemanticHighlightsParams;
|
||||
import com.itsaky.lsp.services.IDELanguageServer;
|
||||
import com.itsaky.lsp.services.IDETextDocumentService;
|
||||
import com.itsaky.lsp.services.IDEWorkspaceService;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.eclipse.lsp4j.CallHierarchyIncomingCall;
|
||||
import org.eclipse.lsp4j.CallHierarchyIncomingCallsParams;
|
||||
import org.eclipse.lsp4j.CallHierarchyItem;
|
||||
import org.eclipse.lsp4j.CallHierarchyOutgoingCall;
|
||||
import org.eclipse.lsp4j.CallHierarchyOutgoingCallsParams;
|
||||
import org.eclipse.lsp4j.CallHierarchyPrepareParams;
|
||||
import org.eclipse.lsp4j.CodeAction;
|
||||
import org.eclipse.lsp4j.CodeActionParams;
|
||||
import org.eclipse.lsp4j.CodeLens;
|
||||
import org.eclipse.lsp4j.CodeLensParams;
|
||||
import org.eclipse.lsp4j.ColorInformation;
|
||||
import org.eclipse.lsp4j.ColorPresentation;
|
||||
import org.eclipse.lsp4j.ColorPresentationParams;
|
||||
import org.eclipse.lsp4j.Command;
|
||||
import org.eclipse.lsp4j.CompletionItem;
|
||||
import org.eclipse.lsp4j.CompletionList;
|
||||
import org.eclipse.lsp4j.CompletionParams;
|
||||
import org.eclipse.lsp4j.CreateFilesParams;
|
||||
import org.eclipse.lsp4j.DeclarationParams;
|
||||
import org.eclipse.lsp4j.DefinitionParams;
|
||||
import org.eclipse.lsp4j.DeleteFilesParams;
|
||||
import org.eclipse.lsp4j.DidChangeConfigurationParams;
|
||||
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
|
||||
import org.eclipse.lsp4j.DidChangeWatchedFilesParams;
|
||||
import org.eclipse.lsp4j.DidChangeWorkspaceFoldersParams;
|
||||
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
|
||||
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
|
||||
import org.eclipse.lsp4j.DidSaveTextDocumentParams;
|
||||
import org.eclipse.lsp4j.DocumentColorParams;
|
||||
import org.eclipse.lsp4j.DocumentFormattingParams;
|
||||
import org.eclipse.lsp4j.DocumentHighlight;
|
||||
import org.eclipse.lsp4j.DocumentHighlightParams;
|
||||
import org.eclipse.lsp4j.DocumentLink;
|
||||
import org.eclipse.lsp4j.DocumentLinkParams;
|
||||
import org.eclipse.lsp4j.DocumentOnTypeFormattingParams;
|
||||
import org.eclipse.lsp4j.DocumentRangeFormattingParams;
|
||||
import org.eclipse.lsp4j.DocumentSymbol;
|
||||
import org.eclipse.lsp4j.DocumentSymbolParams;
|
||||
import org.eclipse.lsp4j.ExecuteCommandParams;
|
||||
import org.eclipse.lsp4j.FoldingRange;
|
||||
import org.eclipse.lsp4j.FoldingRangeRequestParams;
|
||||
import org.eclipse.lsp4j.Hover;
|
||||
import org.eclipse.lsp4j.HoverParams;
|
||||
import org.eclipse.lsp4j.ImplementationParams;
|
||||
import org.eclipse.lsp4j.InitializeParams;
|
||||
import org.eclipse.lsp4j.InitializeResult;
|
||||
import org.eclipse.lsp4j.InitializedParams;
|
||||
import org.eclipse.lsp4j.LinkedEditingRangeParams;
|
||||
import org.eclipse.lsp4j.LinkedEditingRanges;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.LocationLink;
|
||||
import org.eclipse.lsp4j.Moniker;
|
||||
import org.eclipse.lsp4j.MonikerParams;
|
||||
import org.eclipse.lsp4j.PrepareRenameParams;
|
||||
import org.eclipse.lsp4j.PrepareRenameResult;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.ReferenceParams;
|
||||
import org.eclipse.lsp4j.RenameFilesParams;
|
||||
import org.eclipse.lsp4j.RenameParams;
|
||||
import org.eclipse.lsp4j.ResolveTypeHierarchyItemParams;
|
||||
import org.eclipse.lsp4j.SelectionRange;
|
||||
import org.eclipse.lsp4j.SelectionRangeParams;
|
||||
import org.eclipse.lsp4j.SemanticTokens;
|
||||
import org.eclipse.lsp4j.SemanticTokensDelta;
|
||||
import org.eclipse.lsp4j.SemanticTokensDeltaParams;
|
||||
import org.eclipse.lsp4j.SemanticTokensParams;
|
||||
import org.eclipse.lsp4j.SemanticTokensRangeParams;
|
||||
import org.eclipse.lsp4j.SetTraceParams;
|
||||
import org.eclipse.lsp4j.SignatureHelp;
|
||||
import org.eclipse.lsp4j.SignatureHelpParams;
|
||||
import org.eclipse.lsp4j.SymbolInformation;
|
||||
import org.eclipse.lsp4j.TextEdit;
|
||||
import org.eclipse.lsp4j.TypeDefinitionParams;
|
||||
import org.eclipse.lsp4j.TypeHierarchyItem;
|
||||
import org.eclipse.lsp4j.TypeHierarchyParams;
|
||||
import org.eclipse.lsp4j.WillSaveTextDocumentParams;
|
||||
import org.eclipse.lsp4j.WorkDoneProgressCancelParams;
|
||||
import org.eclipse.lsp4j.WorkspaceEdit;
|
||||
import org.eclipse.lsp4j.WorkspaceSymbolParams;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.eclipse.lsp4j.services.LanguageServer;
|
||||
import org.eclipse.lsp4j.services.TextDocumentService;
|
||||
import org.eclipse.lsp4j.services.WorkspaceService;
|
||||
|
||||
/**
|
||||
* Wraps a {@link LanguageServer} to an {@link IDELanguageServer }
|
||||
*/
|
||||
public class LanguageServerWrapper implements IDELanguageServer {
|
||||
|
||||
private final LanguageServer server;
|
||||
private final IDETextDocumentService textDocumentServiceWrapper;
|
||||
private final IDEWorkspaceService workspaceService;
|
||||
|
||||
public LanguageServerWrapper(LanguageServer server) {
|
||||
this.server = server;
|
||||
this.textDocumentServiceWrapper = new TextDocumentServiceWrapper(server.getTextDocumentService());
|
||||
this.workspaceService = new WorkspaceServiceWrapper(server.getWorkspaceService());
|
||||
}
|
||||
|
||||
public LanguageServer getServer() {
|
||||
return this.server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<InitializeResult> initialize(InitializeParams params) {
|
||||
return server.initialize(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialized(InitializedParams params) {
|
||||
server.initialized(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialized() {
|
||||
server.initialized();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Object> shutdown() {
|
||||
return server.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit() {
|
||||
server.exit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDETextDocumentService getTextDocumentService() {
|
||||
return textDocumentServiceWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDEWorkspaceService getWorkspaceService() {
|
||||
return workspaceService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelProgress(WorkDoneProgressCancelParams params) {
|
||||
server.cancelProgress(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTrace(SetTraceParams params) {
|
||||
}
|
||||
|
||||
class TextDocumentServiceWrapper implements IDETextDocumentService {
|
||||
|
||||
private final TextDocumentService service;
|
||||
|
||||
public TextDocumentServiceWrapper(TextDocumentService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<CallHierarchyIncomingCall>> callHierarchyIncomingCalls(CallHierarchyIncomingCallsParams p1) {
|
||||
return service.callHierarchyIncomingCalls(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<CallHierarchyOutgoingCall>> callHierarchyOutgoingCalls(CallHierarchyOutgoingCallsParams p1) {
|
||||
return service.callHierarchyOutgoingCalls(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<Either<Command, CodeAction>>> codeAction(CodeActionParams p1) {
|
||||
return service.codeAction(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends CodeLens>> codeLens(CodeLensParams p1) {
|
||||
return service.codeLens(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<ColorPresentation>> colorPresentation(ColorPresentationParams p1) {
|
||||
return service.colorPresentation(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completion(CompletionParams p1) {
|
||||
return service.completion(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> declaration(DeclarationParams p1) {
|
||||
return service.declaration(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> definition(DefinitionParams p1) {
|
||||
return service.definition(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didChange(DidChangeTextDocumentParams p1) {
|
||||
service.didChange(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didClose(DidCloseTextDocumentParams p1) {
|
||||
service.didClose(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didOpen(DidOpenTextDocumentParams p1) {
|
||||
service.didOpen(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didSave(DidSaveTextDocumentParams p1) {
|
||||
service.didSave(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<ColorInformation>> documentColor(DocumentColorParams p1) {
|
||||
return service.documentColor(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends DocumentHighlight>> documentHighlight(DocumentHighlightParams p1) {
|
||||
return service.documentHighlight(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<DocumentLink>> documentLink(DocumentLinkParams p1) {
|
||||
return service.documentLink(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<DocumentLink> documentLinkResolve(DocumentLink p1) {
|
||||
return service.documentLinkResolve(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> documentSymbol(DocumentSymbolParams p1) {
|
||||
return service.documentSymbol(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<FoldingRange>> foldingRange(FoldingRangeRequestParams p1) {
|
||||
return service.foldingRange(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends TextEdit>> formatting(DocumentFormattingParams p1) {
|
||||
return service.formatting(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Hover> hover(HoverParams p1) {
|
||||
return service.hover(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> implementation(ImplementationParams p1) {
|
||||
return service.implementation(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<LinkedEditingRanges> linkedEditingRange(LinkedEditingRangeParams p1) {
|
||||
return service.linkedEditingRange(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<Moniker>> moniker(MonikerParams p1) {
|
||||
return service.moniker(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends TextEdit>> onTypeFormatting(DocumentOnTypeFormattingParams p1) {
|
||||
return service.onTypeFormatting(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<CallHierarchyItem>> prepareCallHierarchy(CallHierarchyPrepareParams p1) {
|
||||
return service.prepareCallHierarchy(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Either<Range, PrepareRenameResult>> prepareRename(PrepareRenameParams p1) {
|
||||
return service.prepareRename(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends TextEdit>> rangeFormatting(DocumentRangeFormattingParams p1) {
|
||||
return service.rangeFormatting(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends Location>> references(ReferenceParams p1) {
|
||||
return service.references(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<WorkspaceEdit> rename(RenameParams p1) {
|
||||
return service.rename(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CodeAction> resolveCodeAction(CodeAction p1) {
|
||||
return service.resolveCodeAction(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CodeLens> resolveCodeLens(CodeLens p1) {
|
||||
return service.resolveCodeLens(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CompletionItem> resolveCompletionItem(CompletionItem p1) {
|
||||
return service.resolveCompletionItem(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<TypeHierarchyItem> resolveTypeHierarchy(ResolveTypeHierarchyItemParams p1) {
|
||||
return service.resolveTypeHierarchy(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<SelectionRange>> selectionRange(SelectionRangeParams p1) {
|
||||
return service.selectionRange(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SemanticTokens> semanticTokensFull(SemanticTokensParams p1) {
|
||||
return service.semanticTokensFull(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Either<SemanticTokens, SemanticTokensDelta>> semanticTokensFullDelta(SemanticTokensDeltaParams p1) {
|
||||
return service.semanticTokensFullDelta(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SemanticTokens> semanticTokensRange(SemanticTokensRangeParams p1) {
|
||||
return service.semanticTokensRange(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SignatureHelp> signatureHelp(SignatureHelpParams p1) {
|
||||
return service.signatureHelp(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> typeDefinition(TypeDefinitionParams p1) {
|
||||
return service.typeDefinition(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<TypeHierarchyItem> typeHierarchy(TypeHierarchyParams p1) {
|
||||
return service.typeHierarchy(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void willSave(WillSaveTextDocumentParams p1) {
|
||||
service.willSave(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<TextEdit>> willSaveWaitUntil(WillSaveTextDocumentParams p1) {
|
||||
return service.willSaveWaitUntil(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<SemanticHighlight>> semanticHighlights(SemanticHighlightsParams params) {
|
||||
return CompletableFuture.completedFuture(Collections.emptyList());
|
||||
}
|
||||
}
|
||||
|
||||
class WorkspaceServiceWrapper implements IDEWorkspaceService {
|
||||
|
||||
private final WorkspaceService service;
|
||||
|
||||
public WorkspaceServiceWrapper(WorkspaceService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Object> executeCommand(ExecuteCommandParams params) {
|
||||
return service.executeCommand(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<? extends SymbolInformation>> symbol(WorkspaceSymbolParams params) {
|
||||
return service.symbol(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didChangeConfiguration(DidChangeConfigurationParams params) {
|
||||
service.didChangeConfiguration(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) {
|
||||
service.didChangeWatchedFiles(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams params) {
|
||||
service.didChangeWorkspaceFolders(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<WorkspaceEdit> willCreateFiles(CreateFilesParams params) {
|
||||
return service.willCreateFiles(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didCreateFiles(CreateFilesParams params) {
|
||||
service.didCreateFiles(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<WorkspaceEdit> willRenameFiles(RenameFilesParams params) {
|
||||
return service.willRenameFiles(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didRenameFiles(RenameFilesParams params) {
|
||||
service.didRenameFiles(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<WorkspaceEdit> willDeleteFiles(DeleteFilesParams params) {
|
||||
return service.willDeleteFiles(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didDeleteFiles(DeleteFilesParams params) {
|
||||
service.didDeleteFiles(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/************************************************************************************
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
*
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
|
||||
package com.itsaky.androidide.lsp;
|
||||
|
||||
import org.eclipse.lsp4j.*;
|
||||
|
||||
public enum TokenModifiers {
|
||||
|
||||
PUBLIC("public"),
|
||||
PRIVATE("private"),
|
||||
PROTECTED("protected"),
|
||||
NATIVE("native"),
|
||||
DEFAULT("default"),
|
||||
STRICTFP("strictp"),
|
||||
SYNCHRONIZED("synchronized"),
|
||||
TRANSIENT("transient"),
|
||||
VOLATILE("volatile"),
|
||||
ABSTRACT(SemanticTokenModifiers.Abstract),
|
||||
STATIC(SemanticTokenModifiers.Static),
|
||||
FINAL(SemanticTokenModifiers.Readonly),
|
||||
|
||||
DEPRECATED(SemanticTokenModifiers.Deprecated);
|
||||
|
||||
public final int bitmask = 1 << ordinal();
|
||||
public final int invertBitmask = ~bitmask;
|
||||
|
||||
private String name;
|
||||
|
||||
private TokenModifiers (String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString () {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/************************************************************************************
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
*
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
|
||||
package com.itsaky.androidide.lsp;
|
||||
|
||||
import org.eclipse.lsp4j.SemanticTokenTypes;
|
||||
import io.github.rosemoe.editor.widget.EditorColorScheme;
|
||||
|
||||
public enum TokenType {
|
||||
/**
|
||||
* Order of the member declaration must be preseved!
|
||||
*/
|
||||
NAMESPACE(SemanticTokenTypes.Namespace, EditorColorScheme.PACKAGE_NAME),
|
||||
CLASS(SemanticTokenTypes.Class, EditorColorScheme.TYPE_NAME),
|
||||
INTERFACE(SemanticTokenTypes.Interface, EditorColorScheme.INTERFACE),
|
||||
ENUM(SemanticTokenTypes.Enum, EditorColorScheme.ENUM),
|
||||
ENUM_MEMBER(SemanticTokenTypes.EnumMember, EditorColorScheme.STATIC_FIELD),
|
||||
TYPE(SemanticTokenTypes.Type, EditorColorScheme.TYPE_NAME),
|
||||
TYPE_PARAMETER(SemanticTokenTypes.TypeParameter, EditorColorScheme.TYPE_PARAM),
|
||||
METHOD(SemanticTokenTypes.Method, EditorColorScheme.METHOD_DECLARATION),
|
||||
PROPERTY(SemanticTokenTypes.Property, EditorColorScheme.FIELD),
|
||||
VARIABLE(SemanticTokenTypes.Variable, EditorColorScheme.LOCAL_VARIABLE),
|
||||
PARAMETER(SemanticTokenTypes.Parameter, EditorColorScheme.PARAMETER),
|
||||
MODIFIER(SemanticTokenTypes.Modifier, EditorColorScheme.KEYWORD),
|
||||
KEYWORD(SemanticTokenTypes.Keyword, EditorColorScheme.KEYWORD),
|
||||
|
||||
ANNOTATION("annotation", EditorColorScheme.ANNOTATION),
|
||||
ANNOTATION_TYPE("annotationType", EditorColorScheme.TYPE_NAME),
|
||||
|
||||
UNKNOWN("unknown", EditorColorScheme.TEXT_NORMAL);
|
||||
|
||||
private String name;
|
||||
private int editorType;
|
||||
|
||||
private TokenType (String name, int editorType) {
|
||||
this.name = name;
|
||||
this.editorType = editorType;
|
||||
}
|
||||
|
||||
public int getEditorType() {
|
||||
return editorType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString () {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.models;
|
||||
|
||||
import com.itsaky.androidide.fragments.EditorFragment;
|
||||
import com.itsaky.androidide.project.AndroidProject;
|
||||
|
||||
import org.eclipse.lsp4j.Range;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Represents a file that has been opened in the editor,
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class OpenedFile {
|
||||
|
||||
private final File file;
|
||||
private final Range selection;
|
||||
|
||||
public OpenedFile (File file, Range selection) {
|
||||
this.file = file;
|
||||
this.selection = selection;
|
||||
}
|
||||
|
||||
public File getFile () {
|
||||
return file;
|
||||
}
|
||||
|
||||
public Range getSelection () {
|
||||
return selection;
|
||||
}
|
||||
|
||||
public int hashCode () {
|
||||
return this.file.getAbsolutePath ().hashCode ();
|
||||
}
|
||||
|
||||
public EditorFragment createFragment (final EditorFragment.FileOpenListener fileOpenListener,
|
||||
EditorFragment.ModificationStateListener modificationStateListener) {
|
||||
return EditorFragment.newInstance (this.file, this.selection)
|
||||
.setModificationStateListener (modificationStateListener)
|
||||
.setFileOpenListener (fileOpenListener);
|
||||
}
|
||||
}
|
||||
@@ -1,164 +1,164 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.models;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.attrinfo.models.Attr;
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.impl.UiAttribute;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
/**
|
||||
* A model that is used to show attributes list of a view.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class XMLAttribute extends UiAttribute implements Parcelable {
|
||||
|
||||
/**
|
||||
* Is this attribute applied to any view?
|
||||
*/
|
||||
private boolean isApplied = false;
|
||||
|
||||
/**
|
||||
* Format of this attribute. May be stored here so we don't
|
||||
* have to look in {@link com.itsaky.attrinfo.AttrInfo AttrInfo}
|
||||
* again and again.
|
||||
*/
|
||||
private int format = -1;
|
||||
|
||||
private Attr attr;
|
||||
|
||||
public XMLAttribute (IAttribute attribute) {
|
||||
this (attribute, true);
|
||||
}
|
||||
|
||||
public XMLAttribute (@NonNull IAttribute attribute, boolean isApplied) {
|
||||
this (attribute.getNamespace (), attribute.getAttributeName (), attribute.getValue (), isApplied);
|
||||
}
|
||||
|
||||
public XMLAttribute (String namespace, String name, String value, boolean isApplied) {
|
||||
super (namespace, name, value);
|
||||
this.isApplied = isApplied;
|
||||
}
|
||||
|
||||
private XMLAttribute (@NonNull Parcel in) {
|
||||
this (in.readString (), in.readString (), in.readString (), in.readByte () != 0);
|
||||
}
|
||||
|
||||
public void setFormat (int format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public int getFormat () {
|
||||
return format;
|
||||
}
|
||||
|
||||
public int findFormat () {
|
||||
|
||||
if (getFormat () > 0) {
|
||||
return getFormat ();
|
||||
}
|
||||
|
||||
final var attrs = StudioApp.getInstance ().attrInfo ();
|
||||
this.attr = attrs.getAttribute (getAttributeName ());
|
||||
if (this.attr != null) {
|
||||
this.format = this.attr.format;
|
||||
}
|
||||
|
||||
return getFormat ();
|
||||
}
|
||||
|
||||
public boolean hasFormat (int format) {
|
||||
return (this.format & format) != 0;
|
||||
}
|
||||
|
||||
public Attr getAttr () {
|
||||
return attr;
|
||||
}
|
||||
|
||||
public static final Creator<XMLAttribute> CREATOR = new Creator<XMLAttribute> () {
|
||||
@NonNull
|
||||
@Contract("_ -> new")
|
||||
@Override
|
||||
public XMLAttribute createFromParcel (Parcel in) {
|
||||
return new XMLAttribute (in);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
@Override
|
||||
public XMLAttribute[] newArray (int size) {
|
||||
return new XMLAttribute[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void apply (String value) {
|
||||
super.apply (value);
|
||||
isApplied = true;
|
||||
}
|
||||
|
||||
public boolean isApplied () {
|
||||
return isApplied;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new attribute instance with the android namespace.
|
||||
* {@link #isApplied()} will return true by default.
|
||||
* @param name The name of the attribute.
|
||||
* @param value The value of the attribute.
|
||||
* @return A new instance of this class with the provided data.
|
||||
*/
|
||||
@NonNull
|
||||
@Contract("_, _ -> new")
|
||||
public static XMLAttribute newAndroidAttribute (String name, String value) {
|
||||
return new XMLAttribute ("android", name, value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new attribute instance with the android namespace.
|
||||
* The {@link #isApplied()} will return {@code false} by default.
|
||||
* @param name The name of the attribute.
|
||||
* @return The new attribute instance.
|
||||
*/
|
||||
@NonNull
|
||||
@Contract("_ -> new")
|
||||
public static XMLAttribute newAndroidAttribute (String name) {
|
||||
return new XMLAttribute ("android", name, "", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents () {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel (@NonNull Parcel dest, int flags) {
|
||||
dest.writeString (getNamespace ());
|
||||
dest.writeString (getAttributeName ());
|
||||
dest.writeString (getValue ());
|
||||
dest.writeByte ((byte) (isApplied ? 1 : 0));
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.models;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.attrinfo.models.Attr;
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.impl.UiAttribute;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
/**
|
||||
* A model that is used to show attributes list of a view.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class XMLAttribute extends UiAttribute implements Parcelable {
|
||||
|
||||
/**
|
||||
* Is this attribute applied to any view?
|
||||
*/
|
||||
private boolean isApplied = false;
|
||||
|
||||
/**
|
||||
* Format of this attribute. May be stored here so we don't
|
||||
* have to look in {@link com.itsaky.attrinfo.AttrInfo AttrInfo}
|
||||
* again and again.
|
||||
*/
|
||||
private int format = -1;
|
||||
|
||||
private Attr attr;
|
||||
|
||||
public XMLAttribute (IAttribute attribute) {
|
||||
this (attribute, true);
|
||||
}
|
||||
|
||||
public XMLAttribute (@NonNull IAttribute attribute, boolean isApplied) {
|
||||
this (attribute.getNamespace (), attribute.getAttributeName (), attribute.getValue (), isApplied);
|
||||
}
|
||||
|
||||
public XMLAttribute (String namespace, String name, String value, boolean isApplied) {
|
||||
super (namespace, name, value);
|
||||
this.isApplied = isApplied;
|
||||
}
|
||||
|
||||
private XMLAttribute (@NonNull Parcel in) {
|
||||
this (in.readString (), in.readString (), in.readString (), in.readByte () != 0);
|
||||
}
|
||||
|
||||
public void setFormat (int format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public int getFormat () {
|
||||
return format;
|
||||
}
|
||||
|
||||
public int findFormat () {
|
||||
|
||||
if (getFormat () > 0) {
|
||||
return getFormat ();
|
||||
}
|
||||
|
||||
final var attrs = StudioApp.getInstance ().attrInfo ();
|
||||
this.attr = attrs.getAttribute (getAttributeName ());
|
||||
if (this.attr != null) {
|
||||
this.format = this.attr.format;
|
||||
}
|
||||
|
||||
return getFormat ();
|
||||
}
|
||||
|
||||
public boolean hasFormat (int format) {
|
||||
return (this.format & format) != 0;
|
||||
}
|
||||
|
||||
public Attr getAttr () {
|
||||
return attr;
|
||||
}
|
||||
|
||||
public static final Creator<XMLAttribute> CREATOR = new Creator<XMLAttribute> () {
|
||||
@NonNull
|
||||
@Contract("_ -> new")
|
||||
@Override
|
||||
public XMLAttribute createFromParcel (Parcel in) {
|
||||
return new XMLAttribute (in);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
@Override
|
||||
public XMLAttribute[] newArray (int size) {
|
||||
return new XMLAttribute[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void apply (String value) {
|
||||
super.apply (value);
|
||||
isApplied = true;
|
||||
}
|
||||
|
||||
public boolean isApplied () {
|
||||
return isApplied;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new attribute instance with the android namespace.
|
||||
* {@link #isApplied()} will return true by default.
|
||||
* @param name The name of the attribute.
|
||||
* @param value The value of the attribute.
|
||||
* @return A new instance of this class with the provided data.
|
||||
*/
|
||||
@NonNull
|
||||
@Contract("_, _ -> new")
|
||||
public static XMLAttribute newAndroidAttribute (String name, String value) {
|
||||
return new XMLAttribute ("android", name, value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new attribute instance with the android namespace.
|
||||
* The {@link #isApplied()} will return {@code false} by default.
|
||||
* @param name The name of the attribute.
|
||||
* @return The new attribute instance.
|
||||
*/
|
||||
@NonNull
|
||||
@Contract("_ -> new")
|
||||
public static XMLAttribute newAndroidAttribute (String name) {
|
||||
return new XMLAttribute ("android", name, "", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents () {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel (@NonNull Parcel dest, int flags) {
|
||||
dest.writeString (getNamespace ());
|
||||
dest.writeString (getAttributeName ());
|
||||
dest.writeString (getValue ());
|
||||
dest.writeByte ((byte) (isApplied ? 1 : 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.services;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.models.LogLine;
|
||||
import com.itsaky.androidide.shell.ShellServer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class IDELogService {
|
||||
|
||||
private final ShellServer shell;
|
||||
private final List<LogReceiver.LogListener> logListeners;
|
||||
|
||||
private boolean started;
|
||||
|
||||
public IDELogService () {
|
||||
this.shell = StudioApp.getInstance ().newShell (this::output);
|
||||
this.logListeners = new ArrayList<> ();
|
||||
}
|
||||
|
||||
public void start () {
|
||||
if (started) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.shell.bgAppend ("logcat -v threadtime");
|
||||
started = true;
|
||||
}
|
||||
|
||||
public boolean isStarted () {
|
||||
return started;
|
||||
}
|
||||
|
||||
public void addLogListener (LogReceiver.LogListener listener) {
|
||||
this.logListeners.add (Objects.requireNonNull (listener));
|
||||
}
|
||||
|
||||
public void removeLogListener (LogReceiver.LogListener listener) {
|
||||
this.logListeners.remove (Objects.requireNonNull (listener));
|
||||
}
|
||||
|
||||
private void output (@NonNull String log) {
|
||||
final var line = LogLine.forLogString (log);
|
||||
for (final var listener : this.logListeners) {
|
||||
listener.appendLogLine (line);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,8 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import com.itsaky.androidide.models.LogLine;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -34,6 +36,8 @@ public class LogReceiver extends BroadcastReceiver {
|
||||
|
||||
public static final String APPEND_LOG = "com.itsaky.androidide.logs.APPEND_LOG";
|
||||
public static final String EXTRA_LINE = "log_line";
|
||||
|
||||
private final Logger LOG = Logger.instance ("LogReceiver");
|
||||
|
||||
public LogReceiver setLogListener(LogListener listener) {
|
||||
this.listener = listener;
|
||||
@@ -47,7 +51,9 @@ public class LogReceiver extends BroadcastReceiver {
|
||||
if(line == null) return;
|
||||
try {
|
||||
sendLogLine(line);
|
||||
} catch (Throwable th) {}
|
||||
} catch (Throwable th) {
|
||||
LOG.error ("Unable to parse log line from app.", th);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +64,7 @@ public class LogReceiver extends BroadcastReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
public static interface LogListener {
|
||||
public void appendLogLine(LogLine line);
|
||||
public interface LogListener {
|
||||
void appendLogLine (LogLine line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ public class TaskExecutor {
|
||||
|
||||
private final Executor executor = Executors.newSingleThreadExecutor();
|
||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||
|
||||
private final Logger LOG = Logger.instance ("TaskExecutor");
|
||||
|
||||
public interface Callback<R> {
|
||||
void complete(R result);
|
||||
@@ -49,7 +51,9 @@ public class TaskExecutor {
|
||||
handler.post(() -> {
|
||||
callback.complete(result);
|
||||
});
|
||||
} catch (Throwable th) {}
|
||||
} catch (Throwable th) {
|
||||
LOG.error ("Callable task was not able to finish", th);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -61,6 +65,7 @@ public class TaskExecutor {
|
||||
try {
|
||||
result = callable.call();
|
||||
} catch (Throwable th) {
|
||||
LOG.error ("Callable task was not able to finish", th);
|
||||
error = th;
|
||||
}
|
||||
final R resultCopied = result;
|
||||
@@ -71,4 +76,4 @@ public class TaskExecutor {
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/************************************************************************************
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
*
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
|
||||
package com.itsaky.androidide.tasks.callables;
|
||||
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import java.io.File;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class ReadFileTask implements Callable<String> {
|
||||
|
||||
private final File file;
|
||||
|
||||
public ReadFileTask(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
return FileIOUtils.readFile2String(file);
|
||||
}
|
||||
}
|
||||
@@ -1,111 +1,111 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.ui;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.itsaky.inflater.IView;
|
||||
|
||||
/**
|
||||
* A touch listener for handling touch events of a view in the
|
||||
* UI Designer.
|
||||
*
|
||||
* Some of the views have no effect of setting a click listener
|
||||
* or a long click listener (e.g. SeekBar). In order to fix this,
|
||||
* we use {@link android.view.GestureDetector} and manually handle
|
||||
* click events.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class WidgetTouchListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
|
||||
|
||||
private final GestureDetector mGestureDetector;
|
||||
private final OnClickListener clickListener;
|
||||
private final OnLongClickListener longClickListener;
|
||||
|
||||
private final IView mView;
|
||||
|
||||
public WidgetTouchListener (@NonNull IView view, @Nullable OnClickListener clickListener, @Nullable OnLongClickListener longClickListener) {
|
||||
this.mView = view;
|
||||
this.mGestureDetector = new GestureDetector (view.asView().getContext(), this);
|
||||
this.clickListener = clickListener;
|
||||
this.longClickListener = longClickListener;
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouch(View view, @NonNull MotionEvent 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?
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent e) {
|
||||
if (this.clickListener != null) {
|
||||
this.clickListener.onClick(this.mView);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent e) {
|
||||
|
||||
if (this.mView.getParent () == null) {
|
||||
// This is the root layout so this should not be draggable
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.longClickListener != null &&
|
||||
this.longClickListener.onLongClick(this.mView)) {
|
||||
this.mView.asView().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener which is invoked when we detect a single
|
||||
* click event.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public interface OnClickListener {
|
||||
void onClick (IView view);
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener which is invoked when we detect a long
|
||||
* click event.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public interface OnLongClickListener {
|
||||
boolean onLongClick (IView view);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.ui;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.itsaky.inflater.IView;
|
||||
|
||||
/**
|
||||
* A touch listener for handling touch events of a view in the
|
||||
* UI Designer.
|
||||
*
|
||||
* Some of the views have no effect of setting a click listener
|
||||
* or a long click listener (e.g. SeekBar). In order to fix this,
|
||||
* we use {@link android.view.GestureDetector} and manually handle
|
||||
* click events.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class WidgetTouchListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
|
||||
|
||||
private final GestureDetector mGestureDetector;
|
||||
private final OnClickListener clickListener;
|
||||
private final OnLongClickListener longClickListener;
|
||||
|
||||
private final IView mView;
|
||||
|
||||
public WidgetTouchListener (@NonNull IView view, @Nullable OnClickListener clickListener, @Nullable OnLongClickListener longClickListener) {
|
||||
this.mView = view;
|
||||
this.mGestureDetector = new GestureDetector (view.asView().getContext(), this);
|
||||
this.clickListener = clickListener;
|
||||
this.longClickListener = longClickListener;
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouch(View view, @NonNull MotionEvent 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?
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent e) {
|
||||
if (this.clickListener != null) {
|
||||
this.clickListener.onClick(this.mView);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent e) {
|
||||
|
||||
if (this.mView.getParent () == null) {
|
||||
// This is the root layout so this should not be draggable
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.longClickListener != null &&
|
||||
this.longClickListener.onLongClick(this.mView)) {
|
||||
this.mView.asView().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener which is invoked when we detect a single
|
||||
* click event.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public interface OnClickListener {
|
||||
void onClick (IView view);
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener which is invoked when we detect a long
|
||||
* click event.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public interface OnLongClickListener {
|
||||
boolean onLongClick (IView view);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,293 +1,293 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
import static com.itsaky.androidide.utils.DialogUtils.newMaterialDialogBuilder;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.colorpicker.ColorPickerView;
|
||||
import com.itsaky.androidide.databinding.LayoutDimensionAttrEditorBinding;
|
||||
import com.itsaky.androidide.databinding.LayoutTextAttrEditorBinding;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Helper class for creating dialogs for editing view attributes.
|
||||
* <br>
|
||||
* TODO Should we merge this into {@link DialogUtils}?
|
||||
* TODO An attribute can accept multiple types of values (dimension, enum, reference, etc)
|
||||
* Can we somehow combine the editors according to attribute format?
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class AttributeDialogs {
|
||||
|
||||
private static final String DIMENSION_MATCH = "match_parent";
|
||||
private static final String DIMENSION_WRAP = "wrap_content";
|
||||
|
||||
private static final Logger LOG = Logger.instance ("AttributeDialogs");
|
||||
|
||||
/**
|
||||
* Context that will be used for creating dialogs.
|
||||
* This must be initialized by calling {@link #init(Context)}
|
||||
* and the reference must be released by calling {@link #release()}
|
||||
*/
|
||||
private static Context mContext;
|
||||
|
||||
/**
|
||||
* Initialize this class with the provided context.
|
||||
* @param ctx The context that will be used for creating dialogs.
|
||||
* @see #release()
|
||||
*/
|
||||
public static void init (Context ctx) {
|
||||
mContext = ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the context instance stored in this class.
|
||||
*
|
||||
* @see #init(Context)
|
||||
*/
|
||||
public static void release () {
|
||||
mContext = null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static AlertDialog booleanEditor (OnClickListener listener) {
|
||||
final var items = new String [] {"true", "false"};
|
||||
final var builder = newMaterialDialogBuilder (mContext);
|
||||
builder.setTitle (mContext.getString(R.string.msg_choose_new_val));
|
||||
builder.setItems (items, (dialog, which) -> {
|
||||
dialog.dismiss ();
|
||||
final var val = items [which];
|
||||
if (listener != null) {
|
||||
listener.onClick (dialog, which, val);
|
||||
}
|
||||
});
|
||||
return builder.create ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new dialog for editing an attribute with format STRING.
|
||||
* @param listener The listener that will be invoked when the user clicks the
|
||||
* positive button.
|
||||
* @return The newly created dialog instance.
|
||||
*/
|
||||
@NonNull
|
||||
public static AlertDialog stringEditor (OnClickListener listener) {
|
||||
final var binding = LayoutTextAttrEditorBinding.inflate (LayoutInflater.from (mContext));
|
||||
final var builder = newMaterialDialogBuilder (mContext);
|
||||
builder.setTitle (mContext.getString(R.string.msg_new_str_value));
|
||||
builder.setView (binding.getRoot ());
|
||||
builder.setPositiveButton (android.R.string.ok, (dialog, which) -> {
|
||||
final var newValue = binding.valueField.getEditText ().getText ().toString ().trim ();
|
||||
if (newValue.length () <= 0) {
|
||||
StudioApp.getInstance ().toast (mContext.getString (R.string.msg_invalid_attr_value), Toaster.Type.ERROR);
|
||||
return;
|
||||
}
|
||||
if (listener != null) {
|
||||
listener.onClick (dialog, which, newValue);
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton (android.R.string.cancel, (dialog, which) -> dialog.dismiss ());
|
||||
return builder.create ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new dialog for editing an attribute which accepts dimension values.
|
||||
* @param value The current value of the attribute. Used for determining which item must be selected by default.
|
||||
* @param listener A listener that will be invoked when the user presses the positive button of the dialog.
|
||||
* This listener is invoked only when the user passes a valid dimension value.
|
||||
* @return The newly created dialog instance. Note that this dialog is not shown by default.
|
||||
*/
|
||||
@NonNull
|
||||
public static AlertDialog dimensionEditor (CharSequence value, OnClickListener listener) {
|
||||
final var binding = LayoutDimensionAttrEditorBinding.inflate(LayoutInflater.from (mContext));
|
||||
binding.choices.setOnCheckedChangeListener ((group, checkedId) -> binding.otherValue.setVisibility (binding.other.isChecked () ? View.VISIBLE : View.GONE));
|
||||
|
||||
if (DIMENSION_MATCH.contentEquals (value)) {
|
||||
binding.choices.check (R.id.match);
|
||||
} else if (DIMENSION_WRAP.contentEquals (value)) {
|
||||
binding.choices.check (R.id.wrap);
|
||||
} else {
|
||||
binding.choices.check (R.id.other);
|
||||
}
|
||||
|
||||
final var builder = newMaterialDialogBuilder (mContext);
|
||||
builder.setView (binding.getRoot ());
|
||||
builder.setPositiveButton (android.R.string.ok, ((dialog, which) -> {
|
||||
var val = "";
|
||||
if (binding.choices.getCheckedRadioButtonId () == R.id.match) {
|
||||
val = DIMENSION_MATCH;
|
||||
} else if (binding.choices.getCheckedRadioButtonId () == R.id.wrap) {
|
||||
val = DIMENSION_WRAP;
|
||||
} else {
|
||||
if (binding.otherValue.getEditText () != null) {
|
||||
val = binding.otherValue.getEditText ().getText ().toString ().trim ();
|
||||
} else {
|
||||
val = "";
|
||||
}
|
||||
}
|
||||
|
||||
if (val.length () <= 0) {
|
||||
StudioApp.getInstance ().toast (mContext.getString(R.string.msg_invalid_attr_value), Toaster.Type.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
dialog.dismiss ();
|
||||
|
||||
if (listener != null) {
|
||||
listener.onClick (dialog, which, val);
|
||||
}
|
||||
}));
|
||||
builder.setNegativeButton (android.R.string.cancel, (dialog, which) -> dialog.dismiss ());
|
||||
return builder.create ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #enumEditor(CharSequence[], int, OnClickListener)
|
||||
*/
|
||||
public static AlertDialog enumEditor (CharSequence[] values, CharSequence value, OnClickListener listener) {
|
||||
return enumEditor (values, findIndexOf (values, value), listener);
|
||||
}
|
||||
|
||||
private static int findIndexOf (@NonNull CharSequence[] values, CharSequence value) {
|
||||
for (int i=0;i<values.length;i++) {
|
||||
if (value.equals (values[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IndexOutOfBoundsException ("Cannot find index for value: " + value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new single choice dialog for selecting enum values.
|
||||
* @param values The value entries in the enum declaration.
|
||||
* @param selected The item that will be selected when the dialog is shown.
|
||||
* @param listener The click listener that will be invoked when an item is selected from the list.
|
||||
* @return The newly created dialog instance.
|
||||
*/
|
||||
public static AlertDialog enumEditor (CharSequence[] values, int selected, OnClickListener listener) {
|
||||
final var builder = newMaterialDialogBuilder (mContext);
|
||||
builder.setSingleChoiceItems (values, selected, (dialog, which) -> {
|
||||
dialog.dismiss ();
|
||||
final var val = values [which].toString ();
|
||||
if (listener != null) {
|
||||
listener.onClick (dialog, which, val);
|
||||
}
|
||||
});
|
||||
return builder.show ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new attribute editor dialog to edit flag attributes.
|
||||
* @param values The possible values.
|
||||
* @param value The current value of the attribute.
|
||||
* @param listener The listener that will be invoked when the user presses the positive button.
|
||||
* @return The newly created dialog instance.
|
||||
*/
|
||||
@NonNull
|
||||
public static AlertDialog flagEditor (@NonNull CharSequence[] values, @NonNull CharSequence value, OnClickListener listener) {
|
||||
final var checked = new boolean [values.length];
|
||||
final var vals = value.toString ().split (Pattern.quote ("|"));
|
||||
for (var i=0;i<values.length;i++) {
|
||||
for (var val : vals) {
|
||||
if (val.contentEquals (values[i])) {
|
||||
checked [i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return flagEditor (values, checked, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new attribute editor dialog to edit flag attributes.
|
||||
* @param values The possible values.
|
||||
* @param selected The values that must be selected by default.
|
||||
* @param listener The listener that will be invoked when the user presses the positive button.
|
||||
* @return The newly created dialog instance.
|
||||
*/
|
||||
@NonNull
|
||||
public static AlertDialog flagEditor (@NonNull CharSequence[] values, boolean[] selected, OnClickListener listener) {
|
||||
final var checked = new ArrayList<CharSequence> ();
|
||||
for (var i=0;i<values.length;i++) {
|
||||
if (selected[i]) {
|
||||
checked.add (values[i]);
|
||||
}
|
||||
}
|
||||
final var builder = newMaterialDialogBuilder (mContext);
|
||||
builder.setMultiChoiceItems (values, selected, (dialog, which, isChecked) -> {
|
||||
if (isChecked) {
|
||||
checked.add (values[which]);
|
||||
} else {
|
||||
checked.remove (values[which]);
|
||||
}
|
||||
});
|
||||
builder.setPositiveButton (android.R.string.ok, (dialog, which) -> {
|
||||
if (checked.size () <= 0) {
|
||||
StudioApp.getInstance ().toast (mContext.getString(R.string.msg_select_one_flag), Toaster.Type.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
final var val = TextUtils.join ("|", checked);
|
||||
|
||||
if (listener != null) {
|
||||
listener.onClick (dialog, which, val);
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton (android.R.string.cancel, (dialog, which) -> dialog.dismiss ());
|
||||
return builder.create ();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static AlertDialog colorPicker (final ColorPickerView.OnPickListener listener) {
|
||||
final var view = new ColorPickerView (mContext);
|
||||
final var builder = newMaterialDialogBuilder (mContext);
|
||||
builder.setView (view);
|
||||
|
||||
final var dialog = builder.create ();
|
||||
dialog.getWindow ().setSoftInputMode (WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||
|
||||
view.setOnPickListener ((color, hexCode) -> {
|
||||
dialog.dismiss ();
|
||||
if (listener != null) {
|
||||
listener.onPick (color, hexCode);
|
||||
}
|
||||
});
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public interface OnClickListener {
|
||||
void onClick (DialogInterface dialog, int which, String newValue);
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
import static com.itsaky.androidide.utils.DialogUtils.newMaterialDialogBuilder;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.colorpicker.ColorPickerView;
|
||||
import com.itsaky.androidide.databinding.LayoutDimensionAttrEditorBinding;
|
||||
import com.itsaky.androidide.databinding.LayoutTextAttrEditorBinding;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Helper class for creating dialogs for editing view attributes.
|
||||
* <br>
|
||||
* TODO Should we merge this into {@link DialogUtils}?
|
||||
* TODO An attribute can accept multiple types of values (dimension, enum, reference, etc)
|
||||
* Can we somehow combine the editors according to attribute format?
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class AttributeDialogs {
|
||||
|
||||
private static final String DIMENSION_MATCH = "match_parent";
|
||||
private static final String DIMENSION_WRAP = "wrap_content";
|
||||
|
||||
private static final Logger LOG = Logger.instance ("AttributeDialogs");
|
||||
|
||||
/**
|
||||
* Context that will be used for creating dialogs.
|
||||
* This must be initialized by calling {@link #init(Context)}
|
||||
* and the reference must be released by calling {@link #release()}
|
||||
*/
|
||||
private static Context mContext;
|
||||
|
||||
/**
|
||||
* Initialize this class with the provided context.
|
||||
* @param ctx The context that will be used for creating dialogs.
|
||||
* @see #release()
|
||||
*/
|
||||
public static void init (Context ctx) {
|
||||
mContext = ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the context instance stored in this class.
|
||||
*
|
||||
* @see #init(Context)
|
||||
*/
|
||||
public static void release () {
|
||||
mContext = null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static AlertDialog booleanEditor (OnClickListener listener) {
|
||||
final var items = new String [] {"true", "false"};
|
||||
final var builder = newMaterialDialogBuilder (mContext);
|
||||
builder.setTitle (mContext.getString(R.string.msg_choose_new_val));
|
||||
builder.setItems (items, (dialog, which) -> {
|
||||
dialog.dismiss ();
|
||||
final var val = items [which];
|
||||
if (listener != null) {
|
||||
listener.onClick (dialog, which, val);
|
||||
}
|
||||
});
|
||||
return builder.create ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new dialog for editing an attribute with format STRING.
|
||||
* @param listener The listener that will be invoked when the user clicks the
|
||||
* positive button.
|
||||
* @return The newly created dialog instance.
|
||||
*/
|
||||
@NonNull
|
||||
public static AlertDialog stringEditor (OnClickListener listener) {
|
||||
final var binding = LayoutTextAttrEditorBinding.inflate (LayoutInflater.from (mContext));
|
||||
final var builder = newMaterialDialogBuilder (mContext);
|
||||
builder.setTitle (mContext.getString(R.string.msg_new_str_value));
|
||||
builder.setView (binding.getRoot ());
|
||||
builder.setPositiveButton (android.R.string.ok, (dialog, which) -> {
|
||||
final var newValue = binding.valueField.getEditText ().getText ().toString ().trim ();
|
||||
if (newValue.length () <= 0) {
|
||||
StudioApp.getInstance ().toast (mContext.getString (R.string.msg_invalid_attr_value), Toaster.Type.ERROR);
|
||||
return;
|
||||
}
|
||||
if (listener != null) {
|
||||
listener.onClick (dialog, which, newValue);
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton (android.R.string.cancel, (dialog, which) -> dialog.dismiss ());
|
||||
return builder.create ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new dialog for editing an attribute which accepts dimension values.
|
||||
* @param value The current value of the attribute. Used for determining which item must be selected by default.
|
||||
* @param listener A listener that will be invoked when the user presses the positive button of the dialog.
|
||||
* This listener is invoked only when the user passes a valid dimension value.
|
||||
* @return The newly created dialog instance. Note that this dialog is not shown by default.
|
||||
*/
|
||||
@NonNull
|
||||
public static AlertDialog dimensionEditor (CharSequence value, OnClickListener listener) {
|
||||
final var binding = LayoutDimensionAttrEditorBinding.inflate(LayoutInflater.from (mContext));
|
||||
binding.choices.setOnCheckedChangeListener ((group, checkedId) -> binding.otherValue.setVisibility (binding.other.isChecked () ? View.VISIBLE : View.GONE));
|
||||
|
||||
if (DIMENSION_MATCH.contentEquals (value)) {
|
||||
binding.choices.check (R.id.match);
|
||||
} else if (DIMENSION_WRAP.contentEquals (value)) {
|
||||
binding.choices.check (R.id.wrap);
|
||||
} else {
|
||||
binding.choices.check (R.id.other);
|
||||
}
|
||||
|
||||
final var builder = newMaterialDialogBuilder (mContext);
|
||||
builder.setView (binding.getRoot ());
|
||||
builder.setPositiveButton (android.R.string.ok, ((dialog, which) -> {
|
||||
var val = "";
|
||||
if (binding.choices.getCheckedRadioButtonId () == R.id.match) {
|
||||
val = DIMENSION_MATCH;
|
||||
} else if (binding.choices.getCheckedRadioButtonId () == R.id.wrap) {
|
||||
val = DIMENSION_WRAP;
|
||||
} else {
|
||||
if (binding.otherValue.getEditText () != null) {
|
||||
val = binding.otherValue.getEditText ().getText ().toString ().trim ();
|
||||
} else {
|
||||
val = "";
|
||||
}
|
||||
}
|
||||
|
||||
if (val.length () <= 0) {
|
||||
StudioApp.getInstance ().toast (mContext.getString(R.string.msg_invalid_attr_value), Toaster.Type.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
dialog.dismiss ();
|
||||
|
||||
if (listener != null) {
|
||||
listener.onClick (dialog, which, val);
|
||||
}
|
||||
}));
|
||||
builder.setNegativeButton (android.R.string.cancel, (dialog, which) -> dialog.dismiss ());
|
||||
return builder.create ();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #enumEditor(CharSequence[], int, OnClickListener)
|
||||
*/
|
||||
public static AlertDialog enumEditor (CharSequence[] values, CharSequence value, OnClickListener listener) {
|
||||
return enumEditor (values, findIndexOf (values, value), listener);
|
||||
}
|
||||
|
||||
private static int findIndexOf (@NonNull CharSequence[] values, CharSequence value) {
|
||||
for (int i=0;i<values.length;i++) {
|
||||
if (value.equals (values[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IndexOutOfBoundsException ("Cannot find index for value: " + value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new single choice dialog for selecting enum values.
|
||||
* @param values The value entries in the enum declaration.
|
||||
* @param selected The item that will be selected when the dialog is shown.
|
||||
* @param listener The click listener that will be invoked when an item is selected from the list.
|
||||
* @return The newly created dialog instance.
|
||||
*/
|
||||
public static AlertDialog enumEditor (CharSequence[] values, int selected, OnClickListener listener) {
|
||||
final var builder = newMaterialDialogBuilder (mContext);
|
||||
builder.setSingleChoiceItems (values, selected, (dialog, which) -> {
|
||||
dialog.dismiss ();
|
||||
final var val = values [which].toString ();
|
||||
if (listener != null) {
|
||||
listener.onClick (dialog, which, val);
|
||||
}
|
||||
});
|
||||
return builder.show ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new attribute editor dialog to edit flag attributes.
|
||||
* @param values The possible values.
|
||||
* @param value The current value of the attribute.
|
||||
* @param listener The listener that will be invoked when the user presses the positive button.
|
||||
* @return The newly created dialog instance.
|
||||
*/
|
||||
@NonNull
|
||||
public static AlertDialog flagEditor (@NonNull CharSequence[] values, @NonNull CharSequence value, OnClickListener listener) {
|
||||
final var checked = new boolean [values.length];
|
||||
final var vals = value.toString ().split (Pattern.quote ("|"));
|
||||
for (var i=0;i<values.length;i++) {
|
||||
for (var val : vals) {
|
||||
if (val.contentEquals (values[i])) {
|
||||
checked [i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return flagEditor (values, checked, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new attribute editor dialog to edit flag attributes.
|
||||
* @param values The possible values.
|
||||
* @param selected The values that must be selected by default.
|
||||
* @param listener The listener that will be invoked when the user presses the positive button.
|
||||
* @return The newly created dialog instance.
|
||||
*/
|
||||
@NonNull
|
||||
public static AlertDialog flagEditor (@NonNull CharSequence[] values, boolean[] selected, OnClickListener listener) {
|
||||
final var checked = new ArrayList<CharSequence> ();
|
||||
for (var i=0;i<values.length;i++) {
|
||||
if (selected[i]) {
|
||||
checked.add (values[i]);
|
||||
}
|
||||
}
|
||||
final var builder = newMaterialDialogBuilder (mContext);
|
||||
builder.setMultiChoiceItems (values, selected, (dialog, which, isChecked) -> {
|
||||
if (isChecked) {
|
||||
checked.add (values[which]);
|
||||
} else {
|
||||
checked.remove (values[which]);
|
||||
}
|
||||
});
|
||||
builder.setPositiveButton (android.R.string.ok, (dialog, which) -> {
|
||||
if (checked.size () <= 0) {
|
||||
StudioApp.getInstance ().toast (mContext.getString(R.string.msg_select_one_flag), Toaster.Type.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
final var val = TextUtils.join ("|", checked);
|
||||
|
||||
if (listener != null) {
|
||||
listener.onClick (dialog, which, val);
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton (android.R.string.cancel, (dialog, which) -> dialog.dismiss ());
|
||||
return builder.create ();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static AlertDialog colorPicker (final ColorPickerView.OnPickListener listener) {
|
||||
final var view = new ColorPickerView (mContext);
|
||||
final var builder = newMaterialDialogBuilder (mContext);
|
||||
builder.setView (view);
|
||||
|
||||
final var dialog = builder.create ();
|
||||
dialog.getWindow ().setSoftInputMode (WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||
|
||||
view.setOnPickListener ((color, hexCode) -> {
|
||||
dialog.dismiss ();
|
||||
if (listener != null) {
|
||||
listener.onPick (color, hexCode);
|
||||
}
|
||||
});
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public interface OnClickListener {
|
||||
void onClick (DialogInterface dialog, int which, String newValue);
|
||||
}
|
||||
}
|
||||
@@ -1,93 +1,93 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.itsaky.androidide.R;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
/**
|
||||
* Utility class for creating dialogs.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
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 simply calls {@link #newYesNoDialog(Context, String, String, DialogInterface.OnClickListener, DialogInterface.OnClickListener)}
|
||||
* with default values for title and message.
|
||||
*
|
||||
* @param context The context for the dialog.
|
||||
* @param positiveClickListener A listener that will be invoked on the <span>Yes</span> button click.
|
||||
* @param negativeClickListener A listener that will be invoked on the <span>No</span> button click.
|
||||
* @return The newly created dialog.
|
||||
*/
|
||||
@NonNull
|
||||
public static MaterialAlertDialogBuilder newYesNoDialog (Context context,
|
||||
DialogInterface.OnClickListener positiveClickListener,
|
||||
DialogInterface.OnClickListener negativeClickListener) {
|
||||
return newYesNoDialog (context,
|
||||
context.getString(R.string.msg_yesno_def_title),
|
||||
context.getString(R.string.msg_yesno_def_message),
|
||||
positiveClickListener,
|
||||
negativeClickListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new alert dialog with two buttons: <span>Yes</span> and <span>No</span>.
|
||||
*
|
||||
* @param context The context for the dialog.
|
||||
* @param title The title of the dialog.
|
||||
* @param message The message of the dialog.
|
||||
* @param positiveClickListener A listener that will be invoked on the <span>Yes</span> button click.
|
||||
* @param negativeClickListener A listener that will be invoked on the <span>No</span> button click.
|
||||
* @return The newly created dialog instance.
|
||||
*/
|
||||
@NonNull
|
||||
public static MaterialAlertDialogBuilder newYesNoDialog (Context context,
|
||||
String title,
|
||||
String message,
|
||||
DialogInterface.OnClickListener positiveClickListener,
|
||||
DialogInterface.OnClickListener negativeClickListener) {
|
||||
final var builder = DialogUtils.newMaterialDialogBuilder (context);
|
||||
builder.setTitle (title);
|
||||
builder.setMessage (message);
|
||||
builder.setPositiveButton (android.R.string.yes, positiveClickListener);
|
||||
builder.setNegativeButton (android.R.string.no, negativeClickListener);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.itsaky.androidide.R;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
/**
|
||||
* Utility class for creating dialogs.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
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 simply calls {@link #newYesNoDialog(Context, String, String, DialogInterface.OnClickListener, DialogInterface.OnClickListener)}
|
||||
* with default values for title and message.
|
||||
*
|
||||
* @param context The context for the dialog.
|
||||
* @param positiveClickListener A listener that will be invoked on the <span>Yes</span> button click.
|
||||
* @param negativeClickListener A listener that will be invoked on the <span>No</span> button click.
|
||||
* @return The newly created dialog.
|
||||
*/
|
||||
@NonNull
|
||||
public static MaterialAlertDialogBuilder newYesNoDialog (Context context,
|
||||
DialogInterface.OnClickListener positiveClickListener,
|
||||
DialogInterface.OnClickListener negativeClickListener) {
|
||||
return newYesNoDialog (context,
|
||||
context.getString(R.string.msg_yesno_def_title),
|
||||
context.getString(R.string.msg_yesno_def_message),
|
||||
positiveClickListener,
|
||||
negativeClickListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new alert dialog with two buttons: <span>Yes</span> and <span>No</span>.
|
||||
*
|
||||
* @param context The context for the dialog.
|
||||
* @param title The title of the dialog.
|
||||
* @param message The message of the dialog.
|
||||
* @param positiveClickListener A listener that will be invoked on the <span>Yes</span> button click.
|
||||
* @param negativeClickListener A listener that will be invoked on the <span>No</span> button click.
|
||||
* @return The newly created dialog instance.
|
||||
*/
|
||||
@NonNull
|
||||
public static MaterialAlertDialogBuilder newYesNoDialog (Context context,
|
||||
String title,
|
||||
String message,
|
||||
DialogInterface.OnClickListener positiveClickListener,
|
||||
DialogInterface.OnClickListener negativeClickListener) {
|
||||
final var builder = DialogUtils.newMaterialDialogBuilder (context);
|
||||
builder.setTitle (title);
|
||||
builder.setMessage (message);
|
||||
builder.setPositiveButton (android.R.string.yes, positiveClickListener);
|
||||
builder.setNegativeButton (android.R.string.no, negativeClickListener);
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,66 +1,66 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.itsaky.androidide.databinding.LayoutEditorBottomSheetBinding;
|
||||
|
||||
public class EditorBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {
|
||||
|
||||
private LayoutEditorBottomSheetBinding binding;
|
||||
private static final Logger LOG = Logger.instance ("EditorBottomSheetBehavior");
|
||||
|
||||
public EditorBottomSheetBehavior (Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
}
|
||||
|
||||
public void setBinding (LayoutEditorBottomSheetBinding binding) {
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent (@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull MotionEvent event) {
|
||||
if (binding != null) {
|
||||
final var rect = getPagerRect ();
|
||||
if (rect.contains (event.getX (), event.getY ())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return super.onInterceptTouchEvent (parent, child, event);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private RectF getPagerRect () {
|
||||
final var rect = new RectF ();
|
||||
rect.left = binding.pager.getX ();
|
||||
rect.top = binding.pager.getY ();
|
||||
rect.right = rect.left + binding.pager.getWidth ();
|
||||
rect.bottom = rect.top + binding.pager.getHeight ();
|
||||
return rect;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.itsaky.androidide.databinding.LayoutEditorBottomSheetBinding;
|
||||
|
||||
public class EditorBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {
|
||||
|
||||
private LayoutEditorBottomSheetBinding binding;
|
||||
private static final Logger LOG = Logger.instance ("EditorBottomSheetBehavior");
|
||||
|
||||
public EditorBottomSheetBehavior (Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
}
|
||||
|
||||
public void setBinding (LayoutEditorBottomSheetBinding binding) {
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent (@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull MotionEvent event) {
|
||||
if (binding != null) {
|
||||
final var rect = getPagerRect ();
|
||||
if (rect.contains (event.getX (), event.getY ())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return super.onInterceptTouchEvent (parent, child, event);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private RectF getPagerRect () {
|
||||
final var rect = new RectF ();
|
||||
rect.left = binding.pager.getX ();
|
||||
rect.top = binding.pager.getY ();
|
||||
rect.right = rect.left + binding.pager.getWidth ();
|
||||
rect.bottom = rect.top + binding.pager.getHeight ();
|
||||
return rect;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,276 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
/**
|
||||
* Adapts values whose runtime type may differ from their declaration type. This
|
||||
* is necessary when a field's type is not the same type that GSON should create
|
||||
* when deserializing that field. For example, consider these types:
|
||||
* <pre> {@code
|
||||
* abstract class Shape {
|
||||
* int x;
|
||||
* int y;
|
||||
* }
|
||||
* class Circle extends Shape {
|
||||
* int radius;
|
||||
* }
|
||||
* class Rectangle extends Shape {
|
||||
* int width;
|
||||
* int height;
|
||||
* }
|
||||
* class Diamond extends Shape {
|
||||
* int width;
|
||||
* int height;
|
||||
* }
|
||||
* class Drawing {
|
||||
* Shape bottomShape;
|
||||
* Shape topShape;
|
||||
* }
|
||||
* }</pre>
|
||||
* <p>Without additional type information, the serialized JSON is ambiguous. Is
|
||||
* the bottom shape in this drawing a rectangle or a diamond? <pre> {@code
|
||||
* {
|
||||
* "bottomShape": {
|
||||
* "width": 10,
|
||||
* "height": 5,
|
||||
* "x": 0,
|
||||
* "y": 0
|
||||
* },
|
||||
* "topShape": {
|
||||
* "radius": 2,
|
||||
* "x": 4,
|
||||
* "y": 1
|
||||
* }
|
||||
* }}</pre>
|
||||
* This class addresses this problem by adding type information to the
|
||||
* serialized JSON and honoring that type information when the JSON is
|
||||
* deserialized: <pre> {@code
|
||||
* {
|
||||
* "bottomShape": {
|
||||
* "type": "Diamond",
|
||||
* "width": 10,
|
||||
* "height": 5,
|
||||
* "x": 0,
|
||||
* "y": 0
|
||||
* },
|
||||
* "topShape": {
|
||||
* "type": "Circle",
|
||||
* "radius": 2,
|
||||
* "x": 4,
|
||||
* "y": 1
|
||||
* }
|
||||
* }}</pre>
|
||||
* Both the type field name ({@code "type"}) and the type labels ({@code
|
||||
* "Rectangle"}) are configurable.
|
||||
*
|
||||
* <h3>Registering Types</h3>
|
||||
* Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field
|
||||
* name to the {@link #of} factory method. If you don't supply an explicit type
|
||||
* field name, {@code "type"} will be used. <pre> {@code
|
||||
* RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory
|
||||
* = RuntimeTypeAdapterFactory.of(Shape.class, "type");
|
||||
* }</pre>
|
||||
* Next register all of your subtypes. Every subtype must be explicitly
|
||||
* registered. This protects your application from injection attacks. If you
|
||||
* don't supply an explicit type label, the type's simple name will be used.
|
||||
* <pre> {@code
|
||||
* shapeAdapterFactory.registerSubtype(Rectangle.class, "Rectangle");
|
||||
* shapeAdapterFactory.registerSubtype(Circle.class, "Circle");
|
||||
* shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond");
|
||||
* }</pre>
|
||||
* Finally, register the type adapter factory in your application's GSON builder:
|
||||
* <pre> {@code
|
||||
* Gson gson = new GsonBuilder()
|
||||
* .registerTypeAdapterFactory(shapeAdapterFactory)
|
||||
* .create();
|
||||
* }</pre>
|
||||
* Like {@code GsonBuilder}, this API supports chaining: <pre> {@code
|
||||
* RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
|
||||
* .registerSubtype(Rectangle.class)
|
||||
* .registerSubtype(Circle.class)
|
||||
* .registerSubtype(Diamond.class);
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Serialization and deserialization</h3>
|
||||
* In order to serialize and deserialize a polymorphic object,
|
||||
* you must specify the base type explicitly.
|
||||
* <pre> {@code
|
||||
* Diamond diamond = new Diamond();
|
||||
* String json = gson.toJson(diamond, Shape.class);
|
||||
* }</pre>
|
||||
* And then:
|
||||
* <pre> {@code
|
||||
* Shape shape = gson.fromJson(json, Shape.class);
|
||||
* }</pre>
|
||||
*/
|
||||
public final class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
|
||||
private final Class<?> baseType;
|
||||
private final String typeFieldName;
|
||||
private final Map<String, Class<?>> labelToSubtype = new LinkedHashMap<String, Class<?>>();
|
||||
private final Map<Class<?>, String> subtypeToLabel = new LinkedHashMap<Class<?>, String>();
|
||||
private final boolean maintainType;
|
||||
|
||||
private RuntimeTypeAdapterFactory(Class<?> baseType, String typeFieldName, boolean maintainType) {
|
||||
if (typeFieldName == null || baseType == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.baseType = baseType;
|
||||
this.typeFieldName = typeFieldName;
|
||||
this.maintainType = maintainType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new runtime type adapter using for {@code baseType} using {@code
|
||||
* typeFieldName} as the type field name. Type field names are case sensitive.
|
||||
* {@code maintainType} flag decide if the type will be stored in pojo or not.
|
||||
*/
|
||||
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName, boolean maintainType) {
|
||||
return new RuntimeTypeAdapterFactory<T>(baseType, typeFieldName, maintainType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new runtime type adapter using for {@code baseType} using {@code
|
||||
* typeFieldName} as the type field name. Type field names are case sensitive.
|
||||
*/
|
||||
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName) {
|
||||
return new RuntimeTypeAdapterFactory<T>(baseType, typeFieldName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new runtime type adapter for {@code baseType} using {@code "type"} as
|
||||
* the type field name.
|
||||
*/
|
||||
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType) {
|
||||
return new RuntimeTypeAdapterFactory<T>(baseType, "type", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers {@code type} identified by {@code label}. Labels are case
|
||||
* sensitive.
|
||||
*
|
||||
* @throws IllegalArgumentException if either {@code type} or {@code label}
|
||||
* have already been registered on this type adapter.
|
||||
*/
|
||||
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type, String label) {
|
||||
if (type == null || label == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) {
|
||||
throw new IllegalArgumentException("types and labels must be unique");
|
||||
}
|
||||
labelToSubtype.put(label, type);
|
||||
subtypeToLabel.put(type, label);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers {@code type} identified by its {@link Class#getSimpleName simple
|
||||
* name}. Labels are case sensitive.
|
||||
*
|
||||
* @throws IllegalArgumentException if either {@code type} or its simple name
|
||||
* have already been registered on this type adapter.
|
||||
*/
|
||||
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) {
|
||||
return registerSubtype(type, type.getSimpleName());
|
||||
}
|
||||
|
||||
public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) {
|
||||
if (type.getRawType() != baseType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Map<String, TypeAdapter<?>> labelToDelegate
|
||||
= new LinkedHashMap<String, TypeAdapter<?>>();
|
||||
final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate
|
||||
= new LinkedHashMap<Class<?>, TypeAdapter<?>>();
|
||||
for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
|
||||
TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
|
||||
labelToDelegate.put(entry.getKey(), delegate);
|
||||
subtypeToDelegate.put(entry.getValue(), delegate);
|
||||
}
|
||||
|
||||
return new TypeAdapter<R>() {
|
||||
@Override public R read(JsonReader in) throws IOException {
|
||||
JsonElement jsonElement = Streams.parse(in);
|
||||
JsonElement labelJsonElement;
|
||||
if (maintainType) {
|
||||
labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName);
|
||||
} else {
|
||||
labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);
|
||||
}
|
||||
|
||||
if (labelJsonElement == null) {
|
||||
throw new JsonParseException("cannot deserialize " + baseType
|
||||
+ " because it does not define a field named " + typeFieldName);
|
||||
}
|
||||
String label = labelJsonElement.getAsString();
|
||||
@SuppressWarnings("unchecked") // registration requires that subtype extends T
|
||||
TypeAdapter<R> delegate = (TypeAdapter<R>) labelToDelegate.get(label);
|
||||
if (delegate == null) {
|
||||
throw new JsonParseException("cannot deserialize " + baseType + " subtype named "
|
||||
+ label + "; did you forget to register a subtype?");
|
||||
}
|
||||
return delegate.fromJsonTree(jsonElement);
|
||||
}
|
||||
|
||||
@Override public void write(JsonWriter out, R value) throws IOException {
|
||||
Class<?> srcType = value.getClass();
|
||||
String label = subtypeToLabel.get(srcType);
|
||||
@SuppressWarnings("unchecked") // registration requires that subtype extends T
|
||||
TypeAdapter<R> delegate = (TypeAdapter<R>) subtypeToDelegate.get(srcType);
|
||||
if (delegate == null) {
|
||||
throw new JsonParseException("cannot serialize " + srcType.getName()
|
||||
+ "; did you forget to register a subtype?");
|
||||
}
|
||||
JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
|
||||
|
||||
if (maintainType) {
|
||||
Streams.write(jsonObject, out);
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject clone = new JsonObject();
|
||||
|
||||
if (jsonObject.has(typeFieldName)) {
|
||||
throw new JsonParseException("cannot serialize " + srcType.getName()
|
||||
+ " because it already defines a field named " + typeFieldName);
|
||||
}
|
||||
clone.add(typeFieldName, new JsonPrimitive(label));
|
||||
|
||||
for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) {
|
||||
clone.add(e.getKey(), e.getValue());
|
||||
}
|
||||
Streams.write(clone, out);
|
||||
}
|
||||
}.nullSafe();
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.fragments.EditorFragment;
|
||||
import com.itsaky.androidide.views.SymbolInputView.Symbol;
|
||||
import java.io.File;
|
||||
|
||||
@@ -30,11 +29,11 @@ public class Symbols {
|
||||
|
||||
public static Symbol[] forFile(File file) {
|
||||
if (file.isFile()) {
|
||||
if (file.getName().endsWith(EditorFragment.EXT_JAVA)
|
||||
|| file.getName().endsWith(EditorFragment.EXT_GRADLE))
|
||||
if (file.getName().endsWith(".java")
|
||||
|| file.getName().endsWith(".gradle"))
|
||||
return javaSymbols();
|
||||
|
||||
if (file.getName().endsWith(EditorFragment.EXT_XML))
|
||||
if (file.getName().endsWith(".xml"))
|
||||
return xmlSymbols();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,131 +1,156 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.viewmodel;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.util.Pair;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.itsaky.androidide.models.OpenedFile;
|
||||
import com.itsaky.androidide.project.AndroidProject;
|
||||
import com.itsaky.androidide.project.IDEProject;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* ViewModel for data used in {@link com.itsaky.androidide.EditorActivity}
|
||||
*/
|
||||
public class EditorViewModel extends ViewModel {
|
||||
|
||||
private final MutableLiveData <List<OpenedFile>> mFiles = new MutableLiveData<> (new ArrayList<> ());
|
||||
private final MutableLiveData <AndroidProject> mProject = new MutableLiveData<> (null);
|
||||
private final MutableLiveData <IDEProject> mIDEProject = new MutableLiveData<> (null);
|
||||
|
||||
/**
|
||||
* Holds information about the currently selected editor fragment.
|
||||
* First value in the pair is the index of the editor opened.
|
||||
* Second value is the file that is opened.
|
||||
*/
|
||||
private final MutableLiveData <Pair<Integer, File>> mCurrentFile = new MutableLiveData<> (null);
|
||||
|
||||
/**
|
||||
* Get the current {@link AndroidProject}.
|
||||
* @return The android project.
|
||||
*/
|
||||
@Nullable
|
||||
public AndroidProject getAndroidProject () {
|
||||
return mProject.getValue ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the android project currently opened in the editor activity.
|
||||
* @param project The project that is opened.
|
||||
*/
|
||||
public void setAndroidProject (final AndroidProject project) {
|
||||
this.mProject.setValue (project);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link IDEProject} opened in the editor activity.
|
||||
* @return The ide project.
|
||||
*/
|
||||
@Nullable
|
||||
public IDEProject getIDEProject () {
|
||||
return this.mIDEProject.getValue ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IDEProject that is currently opened in the editor activity.
|
||||
* @param project The project that is opened.
|
||||
*/
|
||||
public void setIDEProject (final IDEProject project) {
|
||||
this.mIDEProject.setValue (project);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given file to the list of opened files.
|
||||
* @param file The file that has been opened.
|
||||
*/
|
||||
public void addFile (final OpenedFile file) {
|
||||
final var files = mFiles.getValue ();
|
||||
Objects.requireNonNull (files).add (file);
|
||||
mFiles.setValue (files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the file at the given index from the list of opened files.
|
||||
* @param index The index of the closed file.
|
||||
*/
|
||||
public void removeFile (int index) {
|
||||
final var files = mFiles.getValue ();
|
||||
Objects.requireNonNull (files).remove (index);
|
||||
mFiles.setValue (files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the opened file at the given index.
|
||||
* @param index The index of the file.
|
||||
* @return The file at the given index.
|
||||
*/
|
||||
public OpenedFile getOpenedFile (final int index) {
|
||||
return Objects.requireNonNull (mFiles.getValue ()).get (index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of files opened.
|
||||
* @return The number of files opened.
|
||||
*/
|
||||
public int getOpenedFileCount () {
|
||||
return Objects.requireNonNull (mFiles.getValue ()).size ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an observer to the list of opened files.
|
||||
* @param lifecycleOwner The lifecycle owner.
|
||||
* @param observer The observer.
|
||||
*/
|
||||
public void observeFiles (LifecycleOwner lifecycleOwner, Observer<List<OpenedFile>> observer) {
|
||||
this.mFiles.observe (lifecycleOwner, observer);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.viewmodel;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.util.Pair;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.itsaky.androidide.project.AndroidProject;
|
||||
import com.itsaky.androidide.project.IDEProject;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* ViewModel for data used in {@link com.itsaky.androidide.EditorActivity}
|
||||
*/
|
||||
public class EditorViewModel extends ViewModel {
|
||||
|
||||
private final MutableLiveData <List<File>> mFiles = new MutableLiveData<> (new ArrayList<> ());
|
||||
private final MutableLiveData <AndroidProject> mProject = new MutableLiveData<> (null);
|
||||
private final MutableLiveData <IDEProject> mIDEProject = new MutableLiveData<> (null);
|
||||
|
||||
/**
|
||||
* Holds information about the currently selected editor fragment.
|
||||
* First value in the pair is the index of the editor opened.
|
||||
* Second value is the file that is opened.
|
||||
*/
|
||||
private final MutableLiveData <Pair<Integer, File>> mCurrentFile = new MutableLiveData<> (null);
|
||||
|
||||
/**
|
||||
* Get the current {@link AndroidProject}.
|
||||
* @return The android project.
|
||||
*/
|
||||
@Nullable
|
||||
public AndroidProject getAndroidProject () {
|
||||
return mProject.getValue ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the android project currently opened in the editor activity.
|
||||
* @param project The project that is opened.
|
||||
*/
|
||||
public void setAndroidProject (final AndroidProject project) {
|
||||
this.mProject.setValue (project);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link IDEProject} opened in the editor activity.
|
||||
* @return The ide project.
|
||||
*/
|
||||
@Nullable
|
||||
public IDEProject getIDEProject () {
|
||||
return this.mIDEProject.getValue ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IDEProject that is currently opened in the editor activity.
|
||||
* @param project The project that is opened.
|
||||
*/
|
||||
public void setIDEProject (final IDEProject project) {
|
||||
this.mIDEProject.setValue (project);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given file to the list of opened files.
|
||||
* @param file The file that has been opened.
|
||||
*/
|
||||
public void addFile (final File file) {
|
||||
final var files = mFiles.getValue ();
|
||||
Objects.requireNonNull (files).add (file);
|
||||
mFiles.setValue (files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the file at the given index from the list of opened files.
|
||||
* @param index The index of the closed file.
|
||||
*/
|
||||
public void removeFile (int index) {
|
||||
final var files = mFiles.getValue ();
|
||||
Objects.requireNonNull (files).remove (index);
|
||||
mFiles.setValue (files);
|
||||
}
|
||||
|
||||
public void removeAllFiles () {
|
||||
mFiles.setValue (new ArrayList<> ());
|
||||
setCurrentFile (-1, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the opened file at the given index.
|
||||
* @param index The index of the file.
|
||||
* @return The file at the given index.
|
||||
*/
|
||||
public File getOpenedFile (final int index) {
|
||||
return Objects.requireNonNull (mFiles.getValue ()).get (index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of files opened.
|
||||
* @return The number of files opened.
|
||||
*/
|
||||
public int getOpenedFileCount () {
|
||||
return Objects.requireNonNull (mFiles.getValue ()).size ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an observer to the list of opened files.
|
||||
* @param lifecycleOwner The lifecycle owner.
|
||||
* @param observer The observer.
|
||||
*/
|
||||
public void observeFiles (LifecycleOwner lifecycleOwner, Observer<List<File>> observer) {
|
||||
this.mFiles.observe (lifecycleOwner, observer);
|
||||
}
|
||||
|
||||
public int getCurrentFileIndex () {
|
||||
if (mCurrentFile.getValue () == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return mCurrentFile.getValue ().first;
|
||||
}
|
||||
|
||||
public File getCurrentFile () {
|
||||
if (mCurrentFile.getValue () == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mCurrentFile.getValue ().second;
|
||||
}
|
||||
|
||||
public void setCurrentFile (final int index, @Nullable final File file) {
|
||||
mCurrentFile.setValue (Pair.create (index, file));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.views;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.blankj.utilcode.util.SizeUtils;
|
||||
import com.itsaky.androidide.adapters.CompletionListAdapter;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.databinding.LayoutCodeEditorBinding;
|
||||
import com.itsaky.androidide.language.groovy.GroovyLanguage;
|
||||
import com.itsaky.androidide.language.java.JavaLanguage;
|
||||
import com.itsaky.androidide.language.xml.XMLLanguage;
|
||||
import com.itsaky.androidide.lexers.xml.XMLLexer;
|
||||
import com.itsaky.androidide.managers.PreferenceManager;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
|
||||
import com.itsaky.androidide.utils.LSPUtils;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.androidide.utils.TypefaceUtils;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import io.github.rosemoe.editor.interfaces.EditorEventListener;
|
||||
import io.github.rosemoe.editor.langs.EmptyLanguage;
|
||||
import io.github.rosemoe.editor.widget.CodeEditor;
|
||||
|
||||
/**
|
||||
* A view that handles opened code editors.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@SuppressLint("ViewConstructor") // This view is always dynamically created.
|
||||
public class CodeEditorView extends FrameLayout {
|
||||
|
||||
private final File file;
|
||||
private final LayoutCodeEditorBinding binding;
|
||||
|
||||
private boolean isModified;
|
||||
private boolean isFirstCreate;
|
||||
|
||||
private static final Logger LOG = Logger.instance ("CodeEditorView");
|
||||
|
||||
private final EditorEventListener mEventListener = new EditorEventListener () {
|
||||
@Override
|
||||
public boolean onRequestFormat (CodeEditor editor, boolean async) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFormatFail (CodeEditor editor, Throwable cause) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFormatSucceed (CodeEditor editor) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewTextSet (CodeEditor editor) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeReplace (CodeEditor editor, CharSequence content) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetSelection (int startLine, int startCol, int endLine, int endCol) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterDelete (CodeEditor editor, CharSequence content, int startLine, int startColumn, int endLine, int endColumn, CharSequence deletedContent) {
|
||||
isModified = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterInsert (CodeEditor editor, CharSequence content, int startLine, int startColumn, int endLine, int endColumn, CharSequence insertedContent) {
|
||||
isModified = true;
|
||||
if (file.getName ().endsWith (".xml")) {
|
||||
boolean isOpen = false;
|
||||
try {
|
||||
isOpen = editor.getText ().charAt (editor.getCursor ().getLeft () - 2) == '<';
|
||||
} catch (Throwable th) {
|
||||
LOG.error (th);
|
||||
}
|
||||
|
||||
if (isOpen && insertedContent.toString ().equals ("/")) {
|
||||
closeCurrentTag (editor.getText ().toString (), endLine, endColumn);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public CodeEditorView (@NonNull Context context, @NonNull File file, final @NonNull Range selection) {
|
||||
super (context);
|
||||
this.file = file;
|
||||
this.isModified = false;
|
||||
this.isFirstCreate = true;
|
||||
|
||||
this.binding = LayoutCodeEditorBinding.inflate (LayoutInflater.from (context));
|
||||
this.binding.editor.setOverScrollEnabled (false);
|
||||
this.binding.editor.setTypefaceText (TypefaceUtils.jetbrainsMono ());
|
||||
this.binding.editor.setTextActionMode (CodeEditor.TextActionMode.ACTION_MODE);
|
||||
this.binding.editor.setHighlightCurrentBlock (true);
|
||||
this.binding.editor.setEventListener (mEventListener);
|
||||
this.binding.editor.setAutoCompletionOnComposing (true);
|
||||
this.binding.editor.setAutoCompletionItemAdapter (new CompletionListAdapter ());
|
||||
this.binding.editor.setLineColorsEnabled (true);
|
||||
this.binding.editor.setDividerWidth (SizeUtils.dp2px (1));
|
||||
this.binding.editor.setColorScheme (new SchemeAndroidIDE ());
|
||||
|
||||
this.binding.diagnosticTextContainer.setVisibility (GONE);
|
||||
|
||||
removeAllViews ();
|
||||
addView (this.binding.getRoot ());
|
||||
|
||||
CompletableFuture.runAsync (() -> {
|
||||
final var contents = FileIOUtils.readFile2String (file);
|
||||
binding.editor.post (() -> {
|
||||
binding.editor.setText (contents);
|
||||
postRead ();
|
||||
if (LSPUtils.isEqual (selection.getStart (), selection.getEnd ())) {
|
||||
getEditor ().setSelection (selection.getStart ().getLine (), selection.getStart ().getCharacter ());
|
||||
} else {
|
||||
getEditor ().setSelectionRegion (
|
||||
selection.getStart ().getLine (),
|
||||
selection.getStart ().getCharacter (),
|
||||
selection.getEnd ().getLine (),
|
||||
selection.getEnd ().getCharacter ()
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public boolean save () {
|
||||
final var text = getText ();
|
||||
final var saved = FileIOUtils.writeFileFromString (getFile (), text);
|
||||
notifySaved ();
|
||||
isModified = false;
|
||||
return saved;
|
||||
}
|
||||
|
||||
public Editor getEditor () {
|
||||
return binding.editor;
|
||||
}
|
||||
|
||||
public LayoutCodeEditorBinding getBinding () {
|
||||
return binding;
|
||||
}
|
||||
|
||||
public File getFile () {
|
||||
return file;
|
||||
}
|
||||
|
||||
public String getText () {
|
||||
return binding.editor.getText ().toString ();
|
||||
}
|
||||
|
||||
public boolean isModified () {
|
||||
return isModified;
|
||||
}
|
||||
|
||||
public void onPause () {
|
||||
CompletableFuture.runAsync (this::save);
|
||||
}
|
||||
|
||||
public void onResume () {
|
||||
configureEditorIfNeeded ();
|
||||
}
|
||||
|
||||
public void undo () {
|
||||
if (binding.editor.canUndo ()) {
|
||||
binding.editor.undo ();
|
||||
}
|
||||
}
|
||||
|
||||
public void redo () {
|
||||
if (binding.editor.canRedo ()) {
|
||||
binding.editor.redo ();
|
||||
}
|
||||
}
|
||||
|
||||
public void findDefinition () {
|
||||
binding.editor.findDefinition ();
|
||||
}
|
||||
|
||||
public void findReferences () {
|
||||
binding.editor.findReferences ();
|
||||
}
|
||||
|
||||
public void commentLine () {
|
||||
binding.editor.commentLine ();
|
||||
}
|
||||
|
||||
public void uncommentLine () {
|
||||
binding.editor.uncommentLine ();
|
||||
}
|
||||
|
||||
public void beginSearch () {
|
||||
binding.editor.beginSearchMode ();
|
||||
}
|
||||
|
||||
public void setDiagnostics (List<Diagnostic> diagnostics) {
|
||||
if (diagnostics == null) {
|
||||
LOG.info ("Clearing diagnostics of code editor", "file:", file);
|
||||
binding.editor.getEditorLanguage ().getAnalyzer ().updateDiagnostics (new HashMap<> ());
|
||||
return;
|
||||
}
|
||||
|
||||
Map<Integer, Map<Integer, Diagnostic>> map = new HashMap<> ();
|
||||
for (int i = 0; i < diagnostics.size (); i++) {
|
||||
final Diagnostic d = diagnostics.get (i);
|
||||
if (d == null) {
|
||||
continue;
|
||||
}
|
||||
final Range range = d.getRange ();
|
||||
final int line = range.getStart ().getLine ();
|
||||
final int column = range.getStart ().getCharacter ();
|
||||
Map<Integer, Diagnostic> mappedByColumn = map.get (line);
|
||||
if (mappedByColumn == null) {
|
||||
mappedByColumn = new HashMap<> ();
|
||||
}
|
||||
mappedByColumn.put (column, d);
|
||||
map.put (line, mappedByColumn);
|
||||
}
|
||||
|
||||
binding.editor.getEditorLanguage ().getAnalyzer ().updateDiagnostics (map);
|
||||
}
|
||||
|
||||
protected void postRead () {
|
||||
if (file.isFile () && file.getName ().endsWith (".java")) {
|
||||
binding.editor.setEditorLanguage (new JavaLanguage (getFile ()));
|
||||
} else if (file.isFile () && file.getName ().endsWith (".xml")) {
|
||||
binding.editor.setEditorLanguage (new XMLLanguage (getFile ()));
|
||||
} else if (file.isFile () && file.getName ().endsWith (".gradle")) {
|
||||
binding.editor.setEditorLanguage (new GroovyLanguage (getFile ()));
|
||||
} else {
|
||||
binding.editor.setEditorLanguage (new EmptyLanguage ());
|
||||
}
|
||||
|
||||
// File must be set only after setting the language
|
||||
// This will make sure that textDocument/didOpen is sent
|
||||
binding.editor.setFile (getFile ());
|
||||
}
|
||||
|
||||
private void configureEditorIfNeeded () {
|
||||
boolean sizeChanged = isFirstCreate || ConstantsBridge.EDITORPREF_SIZE_CHANGED;
|
||||
boolean flagsChanged = isFirstCreate || ConstantsBridge.EDITORPREF_FLAGS_CHANGED;
|
||||
boolean drawHexChanged = isFirstCreate || ConstantsBridge.EDITORPREF_DRAW_HEX_CHANGED;
|
||||
final PreferenceManager prefs = StudioApp.getInstance ().getPrefManager ();
|
||||
|
||||
if (sizeChanged) {
|
||||
float textSize = prefs.getFloat (PreferenceManager.KEY_EDITOR_FONT_SIZE);
|
||||
if (textSize < 6 || textSize > 32) {
|
||||
textSize = 14;
|
||||
}
|
||||
|
||||
binding.editor.setTextSize (textSize);
|
||||
ConstantsBridge.EDITORPREF_SIZE_CHANGED = false;
|
||||
}
|
||||
|
||||
if (flagsChanged) {
|
||||
int flags = 0;
|
||||
if (prefs.getBoolean (PreferenceManager.KEY_EDITORFLAG_WS_LEADING, true)) {
|
||||
flags |= CodeEditor.FLAG_DRAW_WHITESPACE_LEADING;
|
||||
}
|
||||
|
||||
if (prefs.getBoolean (PreferenceManager.KEY_EDITORFLAG_WS_TRAILING, false)) {
|
||||
flags |= CodeEditor.FLAG_DRAW_WHITESPACE_TRAILING;
|
||||
}
|
||||
|
||||
if (prefs.getBoolean (PreferenceManager.KEY_EDITORFLAG_WS_INNER, true)) {
|
||||
flags |= CodeEditor.FLAG_DRAW_WHITESPACE_INNER;
|
||||
}
|
||||
|
||||
if (prefs.getBoolean (PreferenceManager.KEY_EDITORFLAG_WS_EMPTY_LINE, true)) {
|
||||
flags |= CodeEditor.FLAG_DRAW_WHITESPACE_FOR_EMPTY_LINE;
|
||||
}
|
||||
|
||||
if (prefs.getBoolean (PreferenceManager.KEY_EDITORFLAG_LINE_BREAK, true)) {
|
||||
flags |= CodeEditor.FLAG_DRAW_LINE_SEPARATOR;
|
||||
}
|
||||
|
||||
binding.editor.setNonPrintablePaintingFlags (flags);
|
||||
ConstantsBridge.EDITORPREF_FLAGS_CHANGED = false;
|
||||
}
|
||||
|
||||
if (drawHexChanged) {
|
||||
binding.editor.setLineColorsEnabled (prefs.getBoolean (PreferenceManager.KEY_EDITOR_DRAW_HEX, true));
|
||||
ConstantsBridge.EDITORPREF_DRAW_HEX_CHANGED = false;
|
||||
}
|
||||
|
||||
isFirstCreate = false;
|
||||
}
|
||||
|
||||
private void closeCurrentTag (String text, int line, int col) {
|
||||
try {
|
||||
XMLLexer lexer = new XMLLexer (CharStreams.fromReader (new StringReader (text)));
|
||||
Token token;
|
||||
boolean wasSlash = false, wasOpen = false;
|
||||
ArrayList<String> currentNames = new ArrayList<> ();
|
||||
while (((token = lexer.nextToken ()) != null && token.getType () != token.EOF)) {
|
||||
final int type = token.getType ();
|
||||
if (type == XMLLexer.OPEN) {
|
||||
wasOpen = true;
|
||||
} else if (type == XMLLexer.Name) {
|
||||
if (wasOpen && wasSlash && currentNames.size () > 0) {
|
||||
currentNames.remove (0);
|
||||
} else if (wasOpen) {
|
||||
currentNames.add (0, token.getText ());
|
||||
wasOpen = false;
|
||||
}
|
||||
} else if (type == XMLLexer.OPEN_SLASH) {
|
||||
int l = token.getLine () - 1;
|
||||
int c = token.getCharPositionInLine ();
|
||||
if (l == line && c == col) {
|
||||
break;
|
||||
} else if (currentNames.size () > 0) {
|
||||
currentNames.remove (0);
|
||||
}
|
||||
} else if (type == XMLLexer.SLASH_CLOSE
|
||||
|| type == XMLLexer.SPECIAL_CLOSE) {
|
||||
if (currentNames.size () > 0 && token.getText ().trim ().endsWith ("/>")) {
|
||||
currentNames.remove (0);
|
||||
}
|
||||
} else if (type == XMLLexer.SLASH) {
|
||||
wasSlash = true;
|
||||
} else {
|
||||
wasOpen = wasSlash = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentNames.size () > 0) {
|
||||
binding.editor.getText ().insert (line, col + 2, currentNames.get (0));
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
LOG.error ("Unable to close current tag", th);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifySaved () {
|
||||
binding.editor.didSave ();
|
||||
}
|
||||
}
|
||||
@@ -1,88 +1,88 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
|
||||
/**
|
||||
* A container used in UI Designer. This view holds the views inflated from an XML layout.
|
||||
* When there are no children in this layout, it shows a message.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class DesignerLayoutContainer extends LinearLayout {
|
||||
|
||||
private final TextView mMessage;
|
||||
|
||||
public DesignerLayoutContainer (Context context) {
|
||||
this (context, null);
|
||||
}
|
||||
|
||||
public DesignerLayoutContainer (Context context, @Nullable AttributeSet attrs) {
|
||||
this (context, attrs, 0);
|
||||
}
|
||||
|
||||
public DesignerLayoutContainer (Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
this (context, attrs, defStyleAttr, 0);
|
||||
}
|
||||
|
||||
public DesignerLayoutContainer (Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super (context, attrs, defStyleAttr, defStyleRes);
|
||||
|
||||
this.mMessage = new TextView (context);
|
||||
this.mMessage.setText (context.getString(R.string.msg_empty_ui_layout));
|
||||
this.mMessage.setLayoutParams (new LayoutParams (ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
this.mMessage.setGravity (Gravity.CENTER);
|
||||
this.mMessage.setTextSize (TypedValue.COMPLEX_UNIT_SP, 16);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewAdded (View child) {
|
||||
if (child != this.mMessage) {
|
||||
removeView (this.mMessage);
|
||||
} else {
|
||||
super.onViewAdded ( child);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewRemoved (View child) {
|
||||
if (child != this.mMessage && getChildCount () <= 0) {
|
||||
addView (mMessage);
|
||||
} else {
|
||||
super.onViewRemoved (child);
|
||||
}
|
||||
}
|
||||
|
||||
// This is needed by XMLLayoutInflater
|
||||
@Override
|
||||
protected LayoutParams generateDefaultLayoutParams () {
|
||||
return super.generateDefaultLayoutParams ();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
|
||||
/**
|
||||
* A container used in UI Designer. This view holds the views inflated from an XML layout.
|
||||
* When there are no children in this layout, it shows a message.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class DesignerLayoutContainer extends LinearLayout {
|
||||
|
||||
private final TextView mMessage;
|
||||
|
||||
public DesignerLayoutContainer (Context context) {
|
||||
this (context, null);
|
||||
}
|
||||
|
||||
public DesignerLayoutContainer (Context context, @Nullable AttributeSet attrs) {
|
||||
this (context, attrs, 0);
|
||||
}
|
||||
|
||||
public DesignerLayoutContainer (Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
this (context, attrs, defStyleAttr, 0);
|
||||
}
|
||||
|
||||
public DesignerLayoutContainer (Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super (context, attrs, defStyleAttr, defStyleRes);
|
||||
|
||||
this.mMessage = new TextView (context);
|
||||
this.mMessage.setText (context.getString(R.string.msg_empty_ui_layout));
|
||||
this.mMessage.setLayoutParams (new LayoutParams (ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
this.mMessage.setGravity (Gravity.CENTER);
|
||||
this.mMessage.setTextSize (TypedValue.COMPLEX_UNIT_SP, 16);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewAdded (View child) {
|
||||
if (child != this.mMessage) {
|
||||
removeView (this.mMessage);
|
||||
} else {
|
||||
super.onViewAdded ( child);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewRemoved (View child) {
|
||||
if (child != this.mMessage && getChildCount () <= 0) {
|
||||
addView (mMessage);
|
||||
} else {
|
||||
super.onViewRemoved (child);
|
||||
}
|
||||
}
|
||||
|
||||
// This is needed by XMLLayoutInflater
|
||||
@Override
|
||||
protected LayoutParams generateDefaultLayoutParams () {
|
||||
return super.generateDefaultLayoutParams ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
/************************************************************************************
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
*
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
|
||||
package com.itsaky.androidide.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.widget.LinearLayout;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Color;
|
||||
|
||||
public class TooltipLayout extends LinearLayout {
|
||||
|
||||
private final float dp8;
|
||||
private final Path path;
|
||||
private final Paint paint;
|
||||
|
||||
public TooltipLayout (Context ctx) {
|
||||
this(ctx, null);
|
||||
}
|
||||
|
||||
public TooltipLayout (Context ctx, AttributeSet attrs) {
|
||||
this(ctx, attrs, 0);
|
||||
}
|
||||
|
||||
public TooltipLayout (Context ctx, AttributeSet attrs, int style) {
|
||||
super(ctx, attrs, style);
|
||||
|
||||
dp8 = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, getResources().getDisplayMetrics());
|
||||
path = new Path();
|
||||
|
||||
paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
paint.setColor(Color.parseColor("#424242"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
final int width = getWidth();
|
||||
final int height = getHeight();
|
||||
final float midX = width / 2;
|
||||
|
||||
path.moveTo(0, 0);
|
||||
path.lineTo(width, 0);
|
||||
path.lineTo(width, height - dp8);
|
||||
path.lineTo(midX + dp8, height - dp8);
|
||||
path.lineTo(midX, height );
|
||||
path.lineTo(midX - dp8, height - dp8);
|
||||
path.lineTo(0, height - dp8);
|
||||
path.close();
|
||||
|
||||
canvas.drawPath(path, paint);
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/************************************************************************************
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
*
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
|
||||
package com.itsaky.androidide.views;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
public class VerticalViewPager extends ViewPager
|
||||
{
|
||||
public static final String TAG = "VerticalViewPager";
|
||||
private boolean scrollEnabled = true;
|
||||
|
||||
public VerticalViewPager(Context context)
|
||||
{
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public VerticalViewPager(Context context, AttributeSet attrs)
|
||||
{
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public void setScrollEnabled(boolean scrollEnabled)
|
||||
{
|
||||
this.scrollEnabled = scrollEnabled;
|
||||
}
|
||||
|
||||
public boolean isScrollEnabled()
|
||||
{
|
||||
return scrollEnabled;
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setPageTransformer(true, new VerticalPageTransformer());
|
||||
setOverScrollMode(OVER_SCROLL_NEVER);
|
||||
}
|
||||
|
||||
private class VerticalPageTransformer implements ViewPager.PageTransformer
|
||||
{
|
||||
@Override
|
||||
public void transformPage(View view, float position)
|
||||
{
|
||||
if (position < -1)
|
||||
{
|
||||
view.setAlpha(0);
|
||||
} else if (position <= 1)
|
||||
{
|
||||
view.setAlpha(1);
|
||||
view.setTranslationX(view.getWidth() * -position);
|
||||
float yPosition = position * view.getHeight();
|
||||
view.setTranslationY(yPosition);
|
||||
} else
|
||||
{
|
||||
view.setAlpha(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps the X and Y coordinates of your touch event.
|
||||
*/
|
||||
private MotionEvent swapXY(MotionEvent ev)
|
||||
{
|
||||
float width = getWidth();
|
||||
float height = getHeight();
|
||||
float newX = (ev.getY() / height) * width;
|
||||
float newY = (ev.getX() / width) * height;
|
||||
ev.setLocation(newX, newY);
|
||||
return ev;
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev)
|
||||
{
|
||||
boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
|
||||
swapXY(ev); // return touch coordinates to original reference frame for any child views
|
||||
return intercepted;
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev)
|
||||
{
|
||||
return isScrollEnabled() ? super.onTouchEvent(swapXY(ev)) : true;
|
||||
}
|
||||
}
|
||||
@@ -4618,16 +4618,15 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
params.setPosition (getCursorAsLSPPosition ());
|
||||
final CompletableFuture<Either<List<? extends Location>, List<? extends LocationLink>>> future = mLanguageServer.getTextDocumentService ().definition (params);
|
||||
future.whenComplete ((e, t) -> {
|
||||
final Either<List<? extends Location>, List<? extends LocationLink>> either = e;
|
||||
final Throwable th = t;
|
||||
|
||||
if (either == null || mLanguageClient == null || future.isCancelled () || future.isCompletedExceptionally ()) {
|
||||
|
||||
if (e == null || mLanguageClient == null || future.isCancelled () || future.isCompletedExceptionally ()) {
|
||||
LOG.error ("An error occurred while finding definition", t);
|
||||
showDefinitionNotFound (pd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (either.isLeft ()) {
|
||||
final List<? extends Location> locations = either.getLeft ();
|
||||
if (e.isLeft ()) {
|
||||
final List<? extends Location> locations = e.getLeft ();
|
||||
if (locations == null || locations.size () <= 0) {
|
||||
showDefinitionNotFound (pd);
|
||||
return;
|
||||
@@ -4643,8 +4642,8 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
});
|
||||
|
||||
dismissOnUiThread (pd);
|
||||
} else if (either.isRight ()) {
|
||||
final List<? extends LocationLink> locations = either.getRight ();
|
||||
} else if (e.isRight ()) {
|
||||
final List<? extends LocationLink> locations = e.getRight ();
|
||||
if (mLanguageClient == null || locations == null || locations.size () <= 0) {
|
||||
showDefinitionNotFound (pd);
|
||||
return;
|
||||
@@ -4663,6 +4662,7 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
}
|
||||
});
|
||||
} catch (Throwable th) {
|
||||
LOG.error ("An error occurred while finding definition", th);
|
||||
showDefinitionNotFound (pd);
|
||||
}
|
||||
}
|
||||
@@ -4690,26 +4690,26 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
final CompletableFuture<List<? extends Location>> future = mLanguageServer.getTextDocumentService ().references (params);
|
||||
|
||||
future.whenComplete ((l, t) -> {
|
||||
final List<? extends Location> locations = l;
|
||||
final Throwable th = t;
|
||||
|
||||
if (locations == null || mLanguageClient == null || future.isCancelled () || future.isCompletedExceptionally ()) {
|
||||
|
||||
if (l == null || mLanguageClient == null || future.isCancelled () || future.isCompletedExceptionally ()) {
|
||||
LOG.error ("An error occurred while finding references", t);
|
||||
showReferencesNotFound (pd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (locations.isEmpty ()) {
|
||||
if (l.isEmpty ()) {
|
||||
showReferencesNotFound (pd);
|
||||
return;
|
||||
} else {
|
||||
ThreadUtils.runOnUiThread (() -> {
|
||||
mLanguageClient.showLocations (locations);
|
||||
mLanguageClient.showLocations (l);
|
||||
});
|
||||
}
|
||||
|
||||
dismissOnUiThread (pd);
|
||||
});
|
||||
} catch (Throwable th) {
|
||||
LOG.error ("An error occurred while finding references", th);
|
||||
showReferencesNotFound (pd);
|
||||
}
|
||||
}
|
||||
@@ -4741,24 +4741,20 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
|
||||
CompletableFuture<SignatureHelp> future = mLanguageServer.getTextDocumentService ().signatureHelp (params);
|
||||
|
||||
future.whenComplete ((h, t) -> {
|
||||
final SignatureHelp help = h;
|
||||
final Throwable th = t;
|
||||
|
||||
if (help == null || mLanguageClient == null || future.isCancelled () || future.isCompletedExceptionally ()) {
|
||||
if (h == null || mLanguageClient == null || future.isCancelled () || future.isCompletedExceptionally ()) {
|
||||
LOG.error ("An error occurred while finding signature help", t);
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadUtils.runOnUiThread (() -> {
|
||||
mLanguageClient.showSignatureHelp (help, getFile ());
|
||||
mLanguageClient.showSignatureHelp (h, getFile ());
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void dismissOnUiThread (final Dialog dialog) {
|
||||
ThreadUtils.runOnUiThread (() -> {
|
||||
dialog.dismiss ();
|
||||
});
|
||||
ThreadUtils.runOnUiThread (dialog::dismiss);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="@android:color/transparent"/>
|
||||
<stroke android:color="@color/secondaryColor"
|
||||
android:width="4dp"/>
|
||||
<corners android:radius="4dp"/>
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="@android:color/transparent"/>
|
||||
<stroke android:color="@color/secondaryColor"
|
||||
android:width="4dp"/>
|
||||
<corners android:radius="4dp"/>
|
||||
|
||||
</shape>
|
||||
@@ -1,42 +1,42 @@
|
||||
<!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512dp"
|
||||
android:height="512dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<path
|
||||
android:pathData="M50.28,451.72c0,5.52 4.48,10 10,10h391.44c5.52,0 10,-4.48 10,-10V60.28c0,-5.52 -4.48,-10 -10,-10H60.28c-5.52,0 -10,4.48 -10,10V451.72z"
|
||||
android:strokeWidth="20"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M134.9,52.08c1.95,137.82 -1.95,272.71 0,410.53"
|
||||
android:strokeWidth="20"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M255.66,50.91c1.95,137.82 -1.95,272.71 0,410.53"
|
||||
android:strokeWidth="20"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M363.66,50.91c1.95,137.82 -1.95,272.71 0,410.53"
|
||||
android:strokeWidth="20"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"/>
|
||||
</vector>
|
||||
<!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="512dp"
|
||||
android:height="512dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<path
|
||||
android:pathData="M50.28,451.72c0,5.52 4.48,10 10,10h391.44c5.52,0 10,-4.48 10,-10V60.28c0,-5.52 -4.48,-10 -10,-10H60.28c-5.52,0 -10,4.48 -10,10V451.72z"
|
||||
android:strokeWidth="20"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M134.9,52.08c1.95,137.82 -1.95,272.71 0,410.53"
|
||||
android:strokeWidth="20"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M255.66,50.91c1.95,137.82 -1.95,272.71 0,410.53"
|
||||
android:strokeWidth="20"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M363.66,50.91c1.95,137.82 -1.95,272.71 0,410.53"
|
||||
android:strokeWidth="20"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"/>
|
||||
</vector>
|
||||
|
||||
@@ -1,116 +1,119 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.drawerlayout.widget.DrawerLayout
|
||||
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:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/editor_drawerLayout"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/realContainer">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/editor_appBarLayout"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$Behavior">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:background="@color/primaryDarkColor"
|
||||
android:elevation="10dp"
|
||||
android:id="@+id/editor_toolbar"/>
|
||||
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
android:id="@+id/build_progress_indicator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone"
|
||||
app:indicatorColor="@color/secondaryColor"/>
|
||||
|
||||
<com.itsaky.androidide.views.MaterialBanner
|
||||
android:id="@+id/syncBanner"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/file_tab_height"
|
||||
android:background="@color/primaryDarkColor"
|
||||
app:tabIndicatorColor="@color/secondaryColor"
|
||||
app:tabIndicatorGravity="stretch"
|
||||
app:tabIndicatorHeight="@dimen/file_tab_height"
|
||||
app:tabTextAppearance="@style/AppTheme.TabLayout.Title"
|
||||
app:tabSelectedTextColor="@color/primaryTextColor"
|
||||
app:tabMode="scrollable" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/editor_viewPager"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:background="@color/content_background"
|
||||
android:layout_gravity="top"
|
||||
android:layout_marginBottom="@dimen/editor_sheet_peek_height"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"/>
|
||||
|
||||
<include
|
||||
android:id="@+id/bottom_sheet"
|
||||
layout="@layout/layout_editor_bottom_sheet"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/symbolText"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:textColor="@color/primaryTextColor"
|
||||
android:textSize="14sp"
|
||||
android:singleLine="true"
|
||||
android:background="@color/content_background"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp"
|
||||
tools:ignore="MissingConstraints"/>
|
||||
|
||||
<include
|
||||
android:id="@+id/diagnosticInfo"
|
||||
layout="@layout/layout_diagnostic_info"/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
android:id="@+id/startNav"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:background="@color/primaryColor"
|
||||
app:headerLayout="@layout/layout_nav_header"
|
||||
app:menu="@menu/menu_editor_nav">
|
||||
|
||||
</com.google.android.material.navigation.NavigationView>
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:background="@color/primaryColor"
|
||||
android:layout_gravity="end">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/editor_frameLayout"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"/>
|
||||
|
||||
</com.google.android.material.navigation.NavigationView>
|
||||
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.drawerlayout.widget.DrawerLayout
|
||||
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:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/editor_drawerLayout"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/realContainer">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/editor_appBarLayout"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$Behavior">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:background="@color/primaryDarkColor"
|
||||
android:elevation="10dp"
|
||||
android:id="@+id/editor_toolbar"/>
|
||||
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
android:id="@+id/build_progress_indicator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"
|
||||
android:visibility="gone"
|
||||
app:indicatorColor="@color/secondaryColor"/>
|
||||
|
||||
<com.itsaky.androidide.views.MaterialBanner
|
||||
android:id="@+id/syncBanner"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/file_tab_height"
|
||||
android:background="@color/primaryDarkColor"
|
||||
app:tabGravity="start"
|
||||
app:tabIndicatorAnimationMode="elastic"
|
||||
app:tabIndicatorColor="@color/secondaryColor"
|
||||
app:tabIndicatorFullWidth="false"
|
||||
app:tabIndicatorGravity="bottom"
|
||||
app:tabIndicatorHeight="4dp"
|
||||
app:tabMode="scrollable"
|
||||
app:tabSelectedTextColor="@color/secondaryColor"
|
||||
app:tabTextAppearance="@style/AppTheme.TabLayout.Title"
|
||||
app:tabTextColor="@color/secondaryTextColor_light" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<ViewFlipper
|
||||
android:id="@+id/editor_container"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:background="@color/content_background"
|
||||
android:layout_marginBottom="@dimen/editor_sheet_peek_height"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"/>
|
||||
|
||||
<include
|
||||
android:id="@+id/bottom_sheet"
|
||||
layout="@layout/layout_editor_bottom_sheet"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/symbolText"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:textColor="@color/primaryTextColor"
|
||||
android:textSize="14sp"
|
||||
android:singleLine="true"
|
||||
android:background="@color/content_background"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="4dp"
|
||||
tools:ignore="MissingConstraints"/>
|
||||
|
||||
<include
|
||||
android:id="@+id/diagnosticInfo"
|
||||
layout="@layout/layout_diagnostic_info"/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
android:id="@+id/startNav"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:background="@color/primaryColor"
|
||||
app:headerLayout="@layout/layout_nav_header"
|
||||
app:menu="@menu/menu_editor_nav">
|
||||
|
||||
</com.google.android.material.navigation.NavigationView>
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:background="@color/primaryColor"
|
||||
android:layout_gravity="end">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/editor_frameLayout"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"/>
|
||||
|
||||
</com.google.android.material.navigation.NavigationView>
|
||||
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<io.github.rosemoe.editor.widget.CodeEditor
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/editor"
|
||||
android:layout_width="match_parent"
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<io.github.rosemoe.editor.widget.CodeEditor
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/editor"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
@@ -1,81 +1,81 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/widget_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/primaryTextColor"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Button" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/actions_header"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="@string/msg_attr_editor_actions"
|
||||
android:textColor="@color/secondaryTextColor"
|
||||
app:layout_constraintStart_toStartOf="@id/widget_name"
|
||||
app:layout_constraintTop_toBottomOf="@id/widget_name" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/actions_list"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/actions_header" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/attrs_header"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="@string/msg_applicable_attrs"
|
||||
android:textColor="@color/secondaryTextColor"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/actions_list" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/attr_list"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/attrs_header" />
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/widget_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/primaryTextColor"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Button" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/actions_header"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="@string/msg_attr_editor_actions"
|
||||
android:textColor="@color/secondaryTextColor"
|
||||
app:layout_constraintStart_toStartOf="@id/widget_name"
|
||||
app:layout_constraintTop_toBottomOf="@id/widget_name" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/actions_list"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/actions_header" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/attrs_header"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="@string/msg_applicable_attrs"
|
||||
android:textColor="@color/secondaryTextColor"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/actions_list" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/attr_list"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/attrs_header" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,46 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:descendantFocusability="blocksDescendants"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/ic_xml_attribute"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/primaryTextColorLight"
|
||||
android:layout_marginStart="8dp"
|
||||
tools:text="layout_height"/>
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:descendantFocusability="blocksDescendants"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/ic_xml_attribute"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/primaryTextColorLight"
|
||||
android:layout_marginStart="8dp"
|
||||
tools:text="layout_height"/>
|
||||
|
||||
</LinearLayout>
|
||||
Executable → Regular
@@ -1,60 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/choices"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/match"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/attr_val_match_parent" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/wrap"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/attr_val_wrap_content" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/other"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/attr_val_other" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/other_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_new_attr_value">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/choices"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/match"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/attr_val_match_parent" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/wrap"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/attr_val_wrap_content" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/other"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/attr_val_other" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/other_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_new_attr_value">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,75 +1,75 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:elevation="8dp"
|
||||
app:behavior_hideable="false"
|
||||
app:behavior_peekHeight="@dimen/editor_sheet_peek_height"
|
||||
app:layout_behavior="com.itsaky.androidide.utils.EditorBottomSheetBehavior">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/text_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@android:color/black">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/statusText"
|
||||
android:layout_height="20dp"
|
||||
android:layout_width="match_parent"
|
||||
android:gravity="center"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/primaryTextColor"
|
||||
android:singleLine="true" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/swipe_hint"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="18dp"
|
||||
android:text="@string/msg_swipe_up"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/secondaryTextColor"
|
||||
android:textSize="11sp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="36dp"
|
||||
android:background="@android:color/black"
|
||||
app:tabIndicatorColor="@color/secondaryColor"
|
||||
app:tabIndicatorGravity="stretch"
|
||||
app:tabIndicatorHeight="36dp"
|
||||
app:tabTextAppearance="@style/AppTheme.TabLayout.Title"
|
||||
app:tabSelectedTextColor="@color/primaryTextColor"
|
||||
app:tabMode="scrollable"
|
||||
app:tabGravity="fill"/>
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/content_background" />
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:elevation="8dp"
|
||||
app:behavior_hideable="false"
|
||||
app:behavior_peekHeight="@dimen/editor_sheet_peek_height"
|
||||
app:layout_behavior="com.itsaky.androidide.utils.EditorBottomSheetBehavior">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/text_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:orientation="vertical"
|
||||
android:background="@android:color/black">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/statusText"
|
||||
android:layout_height="20dp"
|
||||
android:layout_width="match_parent"
|
||||
android:gravity="center"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/primaryTextColor"
|
||||
android:singleLine="true" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/swipe_hint"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="18dp"
|
||||
android:text="@string/msg_swipe_up"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/secondaryTextColor"
|
||||
android:textSize="11sp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="36dp"
|
||||
android:background="@android:color/black"
|
||||
app:tabIndicatorColor="@color/secondaryColor"
|
||||
app:tabIndicatorGravity="stretch"
|
||||
app:tabIndicatorHeight="36dp"
|
||||
app:tabTextAppearance="@style/AppTheme.TabLayout.Title"
|
||||
app:tabSelectedTextColor="@color/primaryTextColor"
|
||||
app:tabMode="scrollable"
|
||||
app:tabGravity="fill"/>
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/content_background" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,36 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/value_field"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_new_attr_value">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/value_field"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_new_attr_value">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,227 +1,227 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.colorpicker;
|
||||
|
||||
import static android.graphics.Color.alpha;
|
||||
import static android.graphics.Color.argb;
|
||||
import static android.graphics.Color.blue;
|
||||
import static android.graphics.Color.green;
|
||||
import static android.graphics.Color.parseColor;
|
||||
import static android.graphics.Color.red;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.google.android.material.slider.Slider;
|
||||
import com.itsaky.androidide.common.R;
|
||||
import com.itsaky.androidide.common.databinding.LayoutColorPickerBinding;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
/**
|
||||
* A color picker view.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class ColorPickerView extends LinearLayout {
|
||||
|
||||
private int alpha;
|
||||
private int red;
|
||||
private int green;
|
||||
private int blue;
|
||||
|
||||
private OnPickListener mPickListener;
|
||||
private final LayoutColorPickerBinding binding;
|
||||
private final int DEFAULT_COLOR;
|
||||
|
||||
private static final Logger LOG = Logger.instance ("ColorPickerView");
|
||||
|
||||
private final Slider.OnChangeListener mValueChangeListener = (slider, value, fromUser) -> {
|
||||
|
||||
// If it's not from user, then the values have already been updated
|
||||
if (fromUser) {
|
||||
final var id = slider.getId ();
|
||||
final var intVal = (int) value;
|
||||
if (id == R.id.alpha_slider) {
|
||||
alpha = intVal;
|
||||
} else if (id == R.id.red_slider) {
|
||||
red = intVal;
|
||||
} else if (id == R.id.green_slider){
|
||||
green = intVal;
|
||||
} else if (id == R.id.blue_slider) {
|
||||
blue = intVal;
|
||||
}
|
||||
|
||||
updatePreview ();
|
||||
}
|
||||
};
|
||||
|
||||
public ColorPickerView (Context context) {
|
||||
this (context, null);
|
||||
}
|
||||
|
||||
public ColorPickerView (Context context, @Nullable AttributeSet attrs) {
|
||||
this (context, attrs, 0);
|
||||
}
|
||||
|
||||
public ColorPickerView (Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
this (context, attrs, defStyleAttr, 0);
|
||||
}
|
||||
|
||||
public ColorPickerView (Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super (context, attrs, defStyleAttr, defStyleRes);
|
||||
|
||||
this.DEFAULT_COLOR = ContextCompat.getColor (context, R.color.color_picker_default_color);
|
||||
|
||||
this.binding = LayoutColorPickerBinding.inflate (LayoutInflater.from (context));
|
||||
|
||||
removeAllViews ();
|
||||
addView (binding.getRoot (), new LayoutParams (ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
init ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a listener that will be notified when the user picks a color.
|
||||
* @param listener The listener to set.
|
||||
*/
|
||||
public void setOnPickListener (OnPickListener listener) {
|
||||
this.mPickListener = listener;
|
||||
}
|
||||
|
||||
private void init () {
|
||||
|
||||
setColor (DEFAULT_COLOR);
|
||||
|
||||
this.binding.alphaSlider.addOnChangeListener (mValueChangeListener);
|
||||
this.binding.redSlider.addOnChangeListener (mValueChangeListener);
|
||||
this.binding.greenSlider.addOnChangeListener (mValueChangeListener);
|
||||
this.binding.blueSlider.addOnChangeListener (mValueChangeListener);
|
||||
|
||||
this.binding.hexField.getEditText ().addTextChangedListener (new ColorTextWatcher ());
|
||||
|
||||
this.binding.pick.setOnClickListener (v -> {
|
||||
if (mPickListener != null) {
|
||||
mPickListener.onPick (getColor (), getHexColor ());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hex color code of the current color value.
|
||||
* @return The hex color code of the current color value.
|
||||
*/
|
||||
@NonNull
|
||||
public String getHexColor () {
|
||||
return "#" + Integer.toHexString (getColor ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color value of this picker to the given hex color code.
|
||||
* @param hexCode The hex color code.
|
||||
* @see #setColor(int)
|
||||
*/
|
||||
public void setColor (final String hexCode) {
|
||||
setColor (parseColor (hexCode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color value for this color picker.
|
||||
* this will in turn update value of the sliders
|
||||
* and the color preview.
|
||||
* @param color The new color value.
|
||||
*/
|
||||
public void setColor (int color) {
|
||||
this.alpha = alpha (color);
|
||||
this.red = red (color);
|
||||
this.green = green(color);
|
||||
this.blue = blue (color);
|
||||
|
||||
this.binding.alphaSlider.setValue (alpha);
|
||||
this.binding.redSlider.setValue (red);
|
||||
this.binding.greenSlider.setValue (green);
|
||||
this.binding.blueSlider.setValue (blue);
|
||||
|
||||
updatePreview ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the int value of the color from the current
|
||||
* values of alpha, red, green and blue color values.
|
||||
* @return The int color value.
|
||||
*/
|
||||
public int getColor () {
|
||||
return argb (alpha, red, green, blue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to update the preview of the current color.
|
||||
*/
|
||||
private void updatePreview () {
|
||||
this.binding.colorPreview.setBackgroundColor (getColor ());
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener that can be used to get notified when the user picks a color.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public interface OnPickListener {
|
||||
|
||||
/**
|
||||
* Called when the user picks a color using the 'Pick' button.
|
||||
* @param color The integer value of the color.
|
||||
* @param hexCode The hex color code of the color.
|
||||
*/
|
||||
void onPick (int color, String hexCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* A TextWatcher that is used to listen for text updates in the
|
||||
* hex color input field.
|
||||
*/
|
||||
private class ColorTextWatcher implements TextWatcher {
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged (CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged (Editable s) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged (CharSequence s, int start, int before, int count) {
|
||||
try {
|
||||
final var color = parseColor ("#" + s);
|
||||
setColor (color);
|
||||
} catch (Exception e) {
|
||||
// May happen due to invalid color values
|
||||
// Simply ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.colorpicker;
|
||||
|
||||
import static android.graphics.Color.alpha;
|
||||
import static android.graphics.Color.argb;
|
||||
import static android.graphics.Color.blue;
|
||||
import static android.graphics.Color.green;
|
||||
import static android.graphics.Color.parseColor;
|
||||
import static android.graphics.Color.red;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.google.android.material.slider.Slider;
|
||||
import com.itsaky.androidide.common.R;
|
||||
import com.itsaky.androidide.common.databinding.LayoutColorPickerBinding;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
/**
|
||||
* A color picker view.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class ColorPickerView extends LinearLayout {
|
||||
|
||||
private int alpha;
|
||||
private int red;
|
||||
private int green;
|
||||
private int blue;
|
||||
|
||||
private OnPickListener mPickListener;
|
||||
private final LayoutColorPickerBinding binding;
|
||||
private final int DEFAULT_COLOR;
|
||||
|
||||
private static final Logger LOG = Logger.instance ("ColorPickerView");
|
||||
|
||||
private final Slider.OnChangeListener mValueChangeListener = (slider, value, fromUser) -> {
|
||||
|
||||
// If it's not from user, then the values have already been updated
|
||||
if (fromUser) {
|
||||
final var id = slider.getId ();
|
||||
final var intVal = (int) value;
|
||||
if (id == R.id.alpha_slider) {
|
||||
alpha = intVal;
|
||||
} else if (id == R.id.red_slider) {
|
||||
red = intVal;
|
||||
} else if (id == R.id.green_slider){
|
||||
green = intVal;
|
||||
} else if (id == R.id.blue_slider) {
|
||||
blue = intVal;
|
||||
}
|
||||
|
||||
updatePreview ();
|
||||
}
|
||||
};
|
||||
|
||||
public ColorPickerView (Context context) {
|
||||
this (context, null);
|
||||
}
|
||||
|
||||
public ColorPickerView (Context context, @Nullable AttributeSet attrs) {
|
||||
this (context, attrs, 0);
|
||||
}
|
||||
|
||||
public ColorPickerView (Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
this (context, attrs, defStyleAttr, 0);
|
||||
}
|
||||
|
||||
public ColorPickerView (Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super (context, attrs, defStyleAttr, defStyleRes);
|
||||
|
||||
this.DEFAULT_COLOR = ContextCompat.getColor (context, R.color.color_picker_default_color);
|
||||
|
||||
this.binding = LayoutColorPickerBinding.inflate (LayoutInflater.from (context));
|
||||
|
||||
removeAllViews ();
|
||||
addView (binding.getRoot (), new LayoutParams (ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
init ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a listener that will be notified when the user picks a color.
|
||||
* @param listener The listener to set.
|
||||
*/
|
||||
public void setOnPickListener (OnPickListener listener) {
|
||||
this.mPickListener = listener;
|
||||
}
|
||||
|
||||
private void init () {
|
||||
|
||||
setColor (DEFAULT_COLOR);
|
||||
|
||||
this.binding.alphaSlider.addOnChangeListener (mValueChangeListener);
|
||||
this.binding.redSlider.addOnChangeListener (mValueChangeListener);
|
||||
this.binding.greenSlider.addOnChangeListener (mValueChangeListener);
|
||||
this.binding.blueSlider.addOnChangeListener (mValueChangeListener);
|
||||
|
||||
this.binding.hexField.getEditText ().addTextChangedListener (new ColorTextWatcher ());
|
||||
|
||||
this.binding.pick.setOnClickListener (v -> {
|
||||
if (mPickListener != null) {
|
||||
mPickListener.onPick (getColor (), getHexColor ());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hex color code of the current color value.
|
||||
* @return The hex color code of the current color value.
|
||||
*/
|
||||
@NonNull
|
||||
public String getHexColor () {
|
||||
return "#" + Integer.toHexString (getColor ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color value of this picker to the given hex color code.
|
||||
* @param hexCode The hex color code.
|
||||
* @see #setColor(int)
|
||||
*/
|
||||
public void setColor (final String hexCode) {
|
||||
setColor (parseColor (hexCode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color value for this color picker.
|
||||
* this will in turn update value of the sliders
|
||||
* and the color preview.
|
||||
* @param color The new color value.
|
||||
*/
|
||||
public void setColor (int color) {
|
||||
this.alpha = alpha (color);
|
||||
this.red = red (color);
|
||||
this.green = green(color);
|
||||
this.blue = blue (color);
|
||||
|
||||
this.binding.alphaSlider.setValue (alpha);
|
||||
this.binding.redSlider.setValue (red);
|
||||
this.binding.greenSlider.setValue (green);
|
||||
this.binding.blueSlider.setValue (blue);
|
||||
|
||||
updatePreview ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the int value of the color from the current
|
||||
* values of alpha, red, green and blue color values.
|
||||
* @return The int color value.
|
||||
*/
|
||||
public int getColor () {
|
||||
return argb (alpha, red, green, blue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to update the preview of the current color.
|
||||
*/
|
||||
private void updatePreview () {
|
||||
this.binding.colorPreview.setBackgroundColor (getColor ());
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener that can be used to get notified when the user picks a color.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public interface OnPickListener {
|
||||
|
||||
/**
|
||||
* Called when the user picks a color using the 'Pick' button.
|
||||
* @param color The integer value of the color.
|
||||
* @param hexCode The hex color code of the color.
|
||||
*/
|
||||
void onPick (int color, String hexCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* A TextWatcher that is used to listen for text updates in the
|
||||
* hex color input field.
|
||||
*/
|
||||
private class ColorTextWatcher implements TextWatcher {
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged (CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged (Editable s) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged (CharSequence s, int start, int before, int count) {
|
||||
try {
|
||||
final var color = parseColor ("#" + s);
|
||||
setColor (color);
|
||||
} catch (Exception e) {
|
||||
// May happen due to invalid color values
|
||||
// Simply ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#FFFFFF">
|
||||
<group android:scaleX="0.12796001"
|
||||
android:scaleY="0.12796001"
|
||||
android:translateX="7.2014995"
|
||||
android:translateY="1.0909091">
|
||||
<group android:translateY="141.67969">
|
||||
<path android:pathData="M72.359375,-72L58.34375,-72L52.578125,-38L67.296875,-38L67.296875,-30L51.21875,-30L46,-0L37,-0L42.34375,-30L25.078125,-30L20,-0L11,-0L16.15625,-30L3.640625,-30L3.640625,-38L17.609375,-38L23.390625,-72L8.703125,-72L8.703125,-80L24.765625,-80L30,-110L39,-110L33.65625,-80L50.921875,-80L56,-110L65,-110L59.78125,-80L72.359375,-80L72.359375,-72ZM49.484375,-72L32.28125,-72L26.515625,-38L43.578125,-38L49.484375,-72Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.6"/>
|
||||
</group>
|
||||
</group>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#FFFFFF">
|
||||
<group android:scaleX="0.12796001"
|
||||
android:scaleY="0.12796001"
|
||||
android:translateX="7.2014995"
|
||||
android:translateY="1.0909091">
|
||||
<group android:translateY="141.67969">
|
||||
<path android:pathData="M72.359375,-72L58.34375,-72L52.578125,-38L67.296875,-38L67.296875,-30L51.21875,-30L46,-0L37,-0L42.34375,-30L25.078125,-30L20,-0L11,-0L16.15625,-30L3.640625,-30L3.640625,-38L17.609375,-38L23.390625,-72L8.703125,-72L8.703125,-80L24.765625,-80L30,-110L39,-110L33.65625,-80L50.921875,-80L56,-110L65,-110L59.78125,-80L72.359375,-80L72.359375,-72ZM49.484375,-72L32.28125,-72L26.515625,-38L43.578125,-38L49.484375,-72Z"
|
||||
android:fillColor="#000000"
|
||||
android:fillAlpha="0.6"/>
|
||||
</group>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -1,135 +1,135 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
<View
|
||||
android:id="@+id/color_preview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/alpha_slider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="255"
|
||||
android:stepSize="1"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
app:thumbColor="@android:color/white"
|
||||
app:trackColorActive="@android:color/white"
|
||||
app:tickVisible="false"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/color_preview"/>
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/red_slider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="255"
|
||||
android:stepSize="1"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:thumbColor="@android:color/holo_red_light"
|
||||
app:trackColorActive="@android:color/holo_red_light"
|
||||
app:tickVisible="false"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/alpha_slider"/>
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/green_slider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="255"
|
||||
android:stepSize="1"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:thumbColor="@android:color/holo_green_light"
|
||||
app:trackColorActive="@android:color/holo_green_light"
|
||||
app:tickVisible="false"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/red_slider"/>
|
||||
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/blue_slider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="255"
|
||||
android:stepSize="1"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:thumbColor="@android:color/holo_blue_dark"
|
||||
app:trackColorActive="@android:color/holo_blue_dark"
|
||||
app:tickVisible="false"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/green_slider"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/pick"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/msg_color_picker_pick"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:textColor="@android:color/white"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/blue_slider"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/hex_field"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/msg_hex_color_code"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:minWidth="100dp"
|
||||
app:startIconDrawable="@drawable/ic_hex_icon"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/pick"
|
||||
app:layout_constraintTop_toBottomOf="@id/blue_slider">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:digits="abcdef0123456789"
|
||||
android:maxLength="8"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
<View
|
||||
android:id="@+id/color_preview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/alpha_slider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="255"
|
||||
android:stepSize="1"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
app:thumbColor="@android:color/white"
|
||||
app:trackColorActive="@android:color/white"
|
||||
app:tickVisible="false"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/color_preview"/>
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/red_slider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="255"
|
||||
android:stepSize="1"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:thumbColor="@android:color/holo_red_light"
|
||||
app:trackColorActive="@android:color/holo_red_light"
|
||||
app:tickVisible="false"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/alpha_slider"/>
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/green_slider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="255"
|
||||
android:stepSize="1"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:thumbColor="@android:color/holo_green_light"
|
||||
app:trackColorActive="@android:color/holo_green_light"
|
||||
app:tickVisible="false"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/red_slider"/>
|
||||
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/blue_slider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="255"
|
||||
android:stepSize="1"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:thumbColor="@android:color/holo_blue_dark"
|
||||
app:trackColorActive="@android:color/holo_blue_dark"
|
||||
app:tickVisible="false"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/green_slider"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/pick"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/msg_color_picker_pick"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:textColor="@android:color/white"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/blue_slider"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/hex_field"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/msg_hex_color_code"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:minWidth="100dp"
|
||||
app:startIconDrawable="@drawable/ic_hex_icon"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/pick"
|
||||
app:layout_constraintTop_toBottomOf="@id/blue_slider">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:digits="abcdef0123456789"
|
||||
android:maxLength="8"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,111 +1,111 @@
|
||||
/************************************************************************************
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
*
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
package com.itsaky.inflater;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Attribute that can be applied to a {@link IView}
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public interface IAttribute {
|
||||
|
||||
/**
|
||||
* The android namespace
|
||||
*/
|
||||
public static final String NS_ANDROID = "android";
|
||||
|
||||
@NonNull
|
||||
String getNamespace ();
|
||||
|
||||
@NonNull
|
||||
String getAttributeName ();
|
||||
|
||||
@NonNull
|
||||
String getValue ();
|
||||
|
||||
/**
|
||||
* Set the given string as a value to this attribute.
|
||||
* This will set {@link #isApplied()} to return {@code true}.
|
||||
*
|
||||
* @param value The value to set.
|
||||
*/
|
||||
void apply (String value);
|
||||
|
||||
public static final Comparator <IAttribute> COMPARATOR = (first, second) -> {
|
||||
var result = compareNull(first, second);
|
||||
if (result != IAttribute.DO_COMPARE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = compareNull(first.getNamespace(), second.getNamespace());
|
||||
if (result != IAttribute.DO_COMPARE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// If we reach here, then it means that the attributes and their namespaces are not null
|
||||
// First, the attributes must be compared by their namespaces
|
||||
|
||||
final var firstNs = first.getNamespace();
|
||||
final var secondNs = second.getNamespace();
|
||||
|
||||
if (firstNs.equals(secondNs)) {
|
||||
|
||||
// Compare by attribute names if the namespaces are same
|
||||
return first.getAttributeName().compareTo(second.getAttributeName());
|
||||
}
|
||||
|
||||
// xmlns declarations must be on top
|
||||
if (firstNs.equals("xmlns") && !secondNs.equals("xmlns")) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!firstNs.equals("xmlns") && secondNs.equals("xmlns")) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returned by {@link #compareNull(Object, Object)} to indicate
|
||||
* that the comparison must be done further.
|
||||
*/
|
||||
static final int DO_COMPARE = 2;
|
||||
|
||||
private static int compareNull (Object first, Object second) {
|
||||
if (first == null && second == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (first != null && second == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (first == null && second != null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return DO_COMPARE;
|
||||
}
|
||||
}
|
||||
/************************************************************************************
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
*
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
package com.itsaky.inflater;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Attribute that can be applied to a {@link IView}
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public interface IAttribute {
|
||||
|
||||
/**
|
||||
* The android namespace
|
||||
*/
|
||||
public static final String NS_ANDROID = "android";
|
||||
|
||||
@NonNull
|
||||
String getNamespace ();
|
||||
|
||||
@NonNull
|
||||
String getAttributeName ();
|
||||
|
||||
@NonNull
|
||||
String getValue ();
|
||||
|
||||
/**
|
||||
* Set the given string as a value to this attribute.
|
||||
* This will set {@link #isApplied()} to return {@code true}.
|
||||
*
|
||||
* @param value The value to set.
|
||||
*/
|
||||
void apply (String value);
|
||||
|
||||
public static final Comparator <IAttribute> COMPARATOR = (first, second) -> {
|
||||
var result = compareNull(first, second);
|
||||
if (result != IAttribute.DO_COMPARE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = compareNull(first.getNamespace(), second.getNamespace());
|
||||
if (result != IAttribute.DO_COMPARE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// If we reach here, then it means that the attributes and their namespaces are not null
|
||||
// First, the attributes must be compared by their namespaces
|
||||
|
||||
final var firstNs = first.getNamespace();
|
||||
final var secondNs = second.getNamespace();
|
||||
|
||||
if (firstNs.equals(secondNs)) {
|
||||
|
||||
// Compare by attribute names if the namespaces are same
|
||||
return first.getAttributeName().compareTo(second.getAttributeName());
|
||||
}
|
||||
|
||||
// xmlns declarations must be on top
|
||||
if (firstNs.equals("xmlns") && !secondNs.equals("xmlns")) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!firstNs.equals("xmlns") && secondNs.equals("xmlns")) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returned by {@link #compareNull(Object, Object)} to indicate
|
||||
* that the comparison must be done further.
|
||||
*/
|
||||
static final int DO_COMPARE = 2;
|
||||
|
||||
private static int compareNull (Object first, Object second) {
|
||||
if (first == null && second == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (first != null && second == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (first == null && second != null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return DO_COMPARE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,86 +1,86 @@
|
||||
/************************************************************************************
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
package com.itsaky.inflater;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Finds values in resources of a project
|
||||
*/
|
||||
public interface IResourceFinder {
|
||||
|
||||
/**
|
||||
* Find the path of the drawable with the provided name
|
||||
* <p>
|
||||
* This method will be called if the value of attribute starts with '@drawable/'
|
||||
*
|
||||
* @param name The name of the drawable
|
||||
* @return The drawable resource.
|
||||
*/
|
||||
File findDrawable (@NonNull String name);
|
||||
|
||||
/**
|
||||
* Find the layout file with the specified name.
|
||||
*
|
||||
* @param name The name of the layout file
|
||||
* @return The found layout resource file
|
||||
*/
|
||||
File inflateLayout (@NonNull String name);
|
||||
|
||||
/**
|
||||
* Find a string resource. Must be a valid string or {@code null}
|
||||
*
|
||||
* @param name The name of the resource
|
||||
* @return Value of the resource
|
||||
*/
|
||||
String findString (@NonNull String name);
|
||||
|
||||
/**
|
||||
* Find a color resource. The returned value must be a valid color value or {@code -1}
|
||||
*
|
||||
* @param name The name of the resource
|
||||
* @return Value of the resource
|
||||
*/
|
||||
String findColor (@NonNull String name);
|
||||
|
||||
/**
|
||||
* Find an array resource. Must be a valid array or {@code null}
|
||||
*
|
||||
* @param name The name of the resource
|
||||
* @return Value of the resource
|
||||
*/
|
||||
String[] findArray (@NonNull String name);
|
||||
|
||||
/**
|
||||
* Find the dimension value with the given name.
|
||||
*
|
||||
* @param name The name of the dimension resource
|
||||
* @return The String representation of the dimension. It could one of the following format :
|
||||
* 1dp, 12sp, 123px, etc.
|
||||
*/
|
||||
String findDimension (@NonNull String name);
|
||||
|
||||
/**
|
||||
* Set the file which is currently being inflated.
|
||||
*
|
||||
* @param file The file.
|
||||
*/
|
||||
void setInflatingFile (@NonNull File file);
|
||||
}
|
||||
/************************************************************************************
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
package com.itsaky.inflater;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Finds values in resources of a project
|
||||
*/
|
||||
public interface IResourceFinder {
|
||||
|
||||
/**
|
||||
* Find the path of the drawable with the provided name
|
||||
* <p>
|
||||
* This method will be called if the value of attribute starts with '@drawable/'
|
||||
*
|
||||
* @param name The name of the drawable
|
||||
* @return The drawable resource.
|
||||
*/
|
||||
File findDrawable (@NonNull String name);
|
||||
|
||||
/**
|
||||
* Find the layout file with the specified name.
|
||||
*
|
||||
* @param name The name of the layout file
|
||||
* @return The found layout resource file
|
||||
*/
|
||||
File inflateLayout (@NonNull String name);
|
||||
|
||||
/**
|
||||
* Find a string resource. Must be a valid string or {@code null}
|
||||
*
|
||||
* @param name The name of the resource
|
||||
* @return Value of the resource
|
||||
*/
|
||||
String findString (@NonNull String name);
|
||||
|
||||
/**
|
||||
* Find a color resource. The returned value must be a valid color value or {@code -1}
|
||||
*
|
||||
* @param name The name of the resource
|
||||
* @return Value of the resource
|
||||
*/
|
||||
String findColor (@NonNull String name);
|
||||
|
||||
/**
|
||||
* Find an array resource. Must be a valid array or {@code null}
|
||||
*
|
||||
* @param name The name of the resource
|
||||
* @return Value of the resource
|
||||
*/
|
||||
String[] findArray (@NonNull String name);
|
||||
|
||||
/**
|
||||
* Find the dimension value with the given name.
|
||||
*
|
||||
* @param name The name of the dimension resource
|
||||
* @return The String representation of the dimension. It could one of the following format :
|
||||
* 1dp, 12sp, 123px, etc.
|
||||
*/
|
||||
String findDimension (@NonNull String name);
|
||||
|
||||
/**
|
||||
* Set the file which is currently being inflated.
|
||||
*
|
||||
* @param file The file.
|
||||
*/
|
||||
void setInflatingFile (@NonNull File file);
|
||||
}
|
||||
|
||||
+81
-81
@@ -1,81 +1,81 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.AbsSeekBar;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Attribute adapter for handling attributes related to
|
||||
* AbsSeekBar.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public abstract class AbsSeekBarAttrAdapter extends ProgressBarAttrAdapter {
|
||||
|
||||
public AbsSeekBarAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof AbsSeekBar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(@NonNull IAttribute attribute, View view) {
|
||||
final var seek = (AbsSeekBar) view;
|
||||
final var namespace = attribute.getNamespace();
|
||||
final var name = attribute.getAttributeName();
|
||||
final var value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "thumbTint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "thumbTintMode" :
|
||||
seek.setThumbTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
case "tickMarkTint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "tickMarkTintMode":
|
||||
seek.setTickMarkTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
default:
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.AbsSeekBar;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Attribute adapter for handling attributes related to
|
||||
* AbsSeekBar.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public abstract class AbsSeekBarAttrAdapter extends ProgressBarAttrAdapter {
|
||||
|
||||
public AbsSeekBarAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof AbsSeekBar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(@NonNull IAttribute attribute, View view) {
|
||||
final var seek = (AbsSeekBar) view;
|
||||
final var namespace = attribute.getNamespace();
|
||||
final var name = attribute.getAttributeName();
|
||||
final var value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "thumbTint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "thumbTintMode" :
|
||||
seek.setThumbTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
case "tickMarkTint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "tickMarkTintMode":
|
||||
seek.setTickMarkTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
default:
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
|
||||
+71
-71
@@ -1,71 +1,71 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.AbsSpinner;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
public class AbsSpinnerAttrAdapter extends AdapterViewAttrAdapter {
|
||||
|
||||
public AbsSpinnerAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof AbsSpinner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
final var spinner = (AbsSpinner) view;
|
||||
final var context = spinner.getContext();
|
||||
final var namespace = attribute.getNamespace();
|
||||
final var name = attribute.getAttributeName();
|
||||
final var value = attribute.getValue();
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (name) {
|
||||
case "entries" :
|
||||
final var adapter = newSimpleAdapter(context, resourceFinder.findArray(value));
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
spinner.setAdapter(adapter);
|
||||
break;
|
||||
default:
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled){
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.AbsSpinner;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
public class AbsSpinnerAttrAdapter extends AdapterViewAttrAdapter {
|
||||
|
||||
public AbsSpinnerAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof AbsSpinner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
final var spinner = (AbsSpinner) view;
|
||||
final var context = spinner.getContext();
|
||||
final var namespace = attribute.getNamespace();
|
||||
final var name = attribute.getAttributeName();
|
||||
final var value = attribute.getValue();
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (name) {
|
||||
case "entries" :
|
||||
final var adapter = newSimpleAdapter(context, resourceFinder.findArray(value));
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
spinner.setAdapter(adapter);
|
||||
break;
|
||||
default:
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled){
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
|
||||
+70
-70
@@ -1,70 +1,70 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter;
|
||||
|
||||
/**
|
||||
* Adapter for handling attributes related to
|
||||
* AdapterView.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public abstract class AdapterViewAttrAdapter extends ViewGroupAttrAdapter {
|
||||
|
||||
public AdapterViewAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof AdapterView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
// No special attributes for AdapterView
|
||||
return super.apply(attribute, view);
|
||||
}
|
||||
|
||||
protected ArrayAdapter <String> newSimpleAdapter (Context ctx) {
|
||||
return newSimpleAdapter(ctx, newAdapterItems(4));
|
||||
}
|
||||
|
||||
protected ArrayAdapter <String> newSimpleAdapter (Context ctx, String[] items) {
|
||||
return new ArrayAdapter<String>(ctx, android.R.layout.simple_list_item_1, items);
|
||||
}
|
||||
|
||||
protected String[] newAdapterItems (int size) {
|
||||
final var items = new String [size];
|
||||
for (var i = 0;i<size;i++) {
|
||||
items[i] = "Item " + i;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter;
|
||||
|
||||
/**
|
||||
* Adapter for handling attributes related to
|
||||
* AdapterView.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public abstract class AdapterViewAttrAdapter extends ViewGroupAttrAdapter {
|
||||
|
||||
public AdapterViewAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof AdapterView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
// No special attributes for AdapterView
|
||||
return super.apply(attribute, view);
|
||||
}
|
||||
|
||||
protected ArrayAdapter <String> newSimpleAdapter (Context ctx) {
|
||||
return newSimpleAdapter(ctx, newAdapterItems(4));
|
||||
}
|
||||
|
||||
protected ArrayAdapter <String> newSimpleAdapter (Context ctx, String[] items) {
|
||||
return new ArrayAdapter<String>(ctx, android.R.layout.simple_list_item_1, items);
|
||||
}
|
||||
|
||||
protected String[] newAdapterItems (int size) {
|
||||
final var items = new String [size];
|
||||
for (var i = 0;i<size;i++) {
|
||||
items[i] = "Item " + i;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
+53
-53
@@ -1,53 +1,53 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Adapter for handling attributes related to Button.
|
||||
*
|
||||
* However, {@code android.widget.Button} has no special attributes.
|
||||
* This adpater simply inherits from {@link TextViewAttrAdapter}.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class ButtonAttrAdapter extends TextViewAttrAdapter {
|
||||
|
||||
public ButtonAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof Button;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
// No special attributes for Button
|
||||
return super.apply(attribute, view);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Adapter for handling attributes related to Button.
|
||||
*
|
||||
* However, {@code android.widget.Button} has no special attributes.
|
||||
* This adpater simply inherits from {@link TextViewAttrAdapter}.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class ButtonAttrAdapter extends TextViewAttrAdapter {
|
||||
|
||||
public ButtonAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof Button;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
// No special attributes for Button
|
||||
return super.apply(attribute, view);
|
||||
}
|
||||
}
|
||||
|
||||
+48
-48
@@ -1,48 +1,48 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Attribute handler for handling attributes related to CheckBox
|
||||
*/
|
||||
public class CheckBoxAttrAdapter extends CompondButtonAttrAdapter {
|
||||
|
||||
public CheckBoxAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof CheckBox;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
|
||||
// No special attributes for CheckBox
|
||||
return super.apply(attribute, view);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Attribute handler for handling attributes related to CheckBox
|
||||
*/
|
||||
public class CheckBoxAttrAdapter extends CompondButtonAttrAdapter {
|
||||
|
||||
public CheckBoxAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof CheckBox;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
|
||||
// No special attributes for CheckBox
|
||||
return super.apply(attribute, view);
|
||||
}
|
||||
}
|
||||
|
||||
+80
-80
@@ -1,80 +1,80 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.CheckedTextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Attribute handler for handling attributes related to
|
||||
* CheckedTextView.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class CheckedTextViewAttrAdapter extends TextViewAttrAdapter {
|
||||
|
||||
public CheckedTextViewAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof CheckedTextView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
|
||||
final CheckedTextView text = (CheckedTextView) view;
|
||||
final String namespace = attribute.getNamespace();
|
||||
final String name = attribute.getAttributeName();
|
||||
final String value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "checkMarkTintMode" :
|
||||
text.setCheckMarkTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
case "checkMarkTint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "checkMark" :
|
||||
// Ignored...
|
||||
break;
|
||||
default :
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.CheckedTextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Attribute handler for handling attributes related to
|
||||
* CheckedTextView.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class CheckedTextViewAttrAdapter extends TextViewAttrAdapter {
|
||||
|
||||
public CheckedTextViewAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof CheckedTextView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
|
||||
final CheckedTextView text = (CheckedTextView) view;
|
||||
final String namespace = attribute.getNamespace();
|
||||
final String name = attribute.getAttributeName();
|
||||
final String value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "checkMarkTintMode" :
|
||||
text.setCheckMarkTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
case "checkMarkTint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "checkMark" :
|
||||
// Ignored...
|
||||
break;
|
||||
default :
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
|
||||
+64
-64
@@ -1,64 +1,64 @@
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.CompoundButton;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Attribute handler for handling attibutes to CompoundButton
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class CompondButtonAttrAdapter extends ButtonAttrAdapter {
|
||||
|
||||
public CompondButtonAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof CompoundButton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
final CompoundButton button = (CompoundButton) view;
|
||||
final Context context = button.getContext();
|
||||
final String namespace = attribute.getNamespace();
|
||||
final String name = attribute.getAttributeName();
|
||||
final String value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "buttonTint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "button" :
|
||||
button.setButtonDrawable(parseDrawable(value, context));
|
||||
break;
|
||||
case "buttonTintMode" :
|
||||
button.setButtonTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
default :
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.CompoundButton;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Attribute handler for handling attibutes to CompoundButton
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class CompondButtonAttrAdapter extends ButtonAttrAdapter {
|
||||
|
||||
public CompondButtonAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof CompoundButton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
final CompoundButton button = (CompoundButton) view;
|
||||
final Context context = button.getContext();
|
||||
final String namespace = attribute.getNamespace();
|
||||
final String name = attribute.getAttributeName();
|
||||
final String value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "buttonTint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "button" :
|
||||
button.setButtonDrawable(parseDrawable(value, context));
|
||||
break;
|
||||
case "buttonTintMode" :
|
||||
button.setButtonTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
default :
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
|
||||
+46
-46
@@ -1,46 +1,46 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
public class EditTextAttrAdapter extends TextViewAttrAdapter {
|
||||
|
||||
public EditTextAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof EditText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
// No special attributes for EditText
|
||||
return super.apply(attribute, view);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
public class EditTextAttrAdapter extends TextViewAttrAdapter {
|
||||
|
||||
public EditTextAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof EditText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
// No special attributes for EditText
|
||||
return super.apply(attribute, view);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+50
-50
@@ -1,50 +1,50 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Attribute handler for handling attributes related to
|
||||
* ImageButton.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class ImageButtonAttrAdapter extends ImageViewAttrAdapter {
|
||||
|
||||
public ImageButtonAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof ImageButton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
// No special attributes for ImageButton
|
||||
return super.apply(attribute, view);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Attribute handler for handling attributes related to
|
||||
* ImageButton.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class ImageButtonAttrAdapter extends ImageViewAttrAdapter {
|
||||
|
||||
public ImageButtonAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof ImageButton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
// No special attributes for ImageButton
|
||||
return super.apply(attribute, view);
|
||||
}
|
||||
}
|
||||
|
||||
+135
-135
@@ -1,135 +1,135 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.adapters.android.view.ViewAttrAdapter;
|
||||
|
||||
/**
|
||||
* Attribute handler for handling attributes related to
|
||||
* ImageButton.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class ImageViewAttrAdapter extends ViewAttrAdapter {
|
||||
|
||||
public ImageViewAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof ImageView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
final ImageView image = (ImageView) view;
|
||||
final Context context = image.getContext();
|
||||
final DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||
final String namespace = attribute.getNamespace();
|
||||
final String name = attribute.getAttributeName();
|
||||
final String value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "adjustViewBounds" :
|
||||
image.setAdjustViewBounds(parseBoolean(value));
|
||||
break;
|
||||
case "baseline" :
|
||||
image.setBaseline(parseDimension(value, 0, dm));
|
||||
break;
|
||||
case "baselineAlignBottom" :
|
||||
image.setBaselineAlignBottom(parseBoolean(value));
|
||||
break;
|
||||
case "cropToPadding" :
|
||||
image.setCropToPadding(parseBoolean(value));
|
||||
break;
|
||||
case "maxHeight" :
|
||||
image.setMaxHeight(parseDimension(value, 0, dm));
|
||||
break;
|
||||
case "maxWidth" :
|
||||
image.setMaxWidth(parseDimension(value, 0, dm));
|
||||
break;
|
||||
case "scaleType" :
|
||||
image.setScaleType(parseScaleType(value));
|
||||
break;
|
||||
case "src" :
|
||||
image.setImageDrawable(parseImageResource(value, context));
|
||||
break;
|
||||
case "tint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "tintMode" :
|
||||
image.setImageTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
default :
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
private Drawable parseImageResource(String value, Context context) {
|
||||
if (value.startsWith("@drawable/") || value.startsWith("@mipmap/")) {
|
||||
return parseDrawable(value, context);
|
||||
}
|
||||
return ContextCompat.getDrawable(context, android.R.drawable.ic_delete);
|
||||
}
|
||||
|
||||
private ImageView.ScaleType parseScaleType(String value) {
|
||||
switch (value) {
|
||||
case "center" :
|
||||
return ImageView.ScaleType.CENTER;
|
||||
case "centerCrop" :
|
||||
return ImageView.ScaleType.CENTER_CROP;
|
||||
case "centerInside" :
|
||||
return ImageView.ScaleType.CENTER_INSIDE;
|
||||
case "fitEnd" :
|
||||
return ImageView.ScaleType.FIT_END;
|
||||
case "fitStart" :
|
||||
return ImageView.ScaleType.FIT_START;
|
||||
case "fitXY" :
|
||||
return ImageView.ScaleType.FIT_XY;
|
||||
case "matrix" :
|
||||
return ImageView.ScaleType.MATRIX;
|
||||
case "fitCenter" :
|
||||
default :
|
||||
return ImageView.ScaleType.FIT_CENTER;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.adapters.android.view.ViewAttrAdapter;
|
||||
|
||||
/**
|
||||
* Attribute handler for handling attributes related to
|
||||
* ImageButton.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class ImageViewAttrAdapter extends ViewAttrAdapter {
|
||||
|
||||
public ImageViewAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof ImageView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
final ImageView image = (ImageView) view;
|
||||
final Context context = image.getContext();
|
||||
final DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||
final String namespace = attribute.getNamespace();
|
||||
final String name = attribute.getAttributeName();
|
||||
final String value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "adjustViewBounds" :
|
||||
image.setAdjustViewBounds(parseBoolean(value));
|
||||
break;
|
||||
case "baseline" :
|
||||
image.setBaseline(parseDimension(value, 0, dm));
|
||||
break;
|
||||
case "baselineAlignBottom" :
|
||||
image.setBaselineAlignBottom(parseBoolean(value));
|
||||
break;
|
||||
case "cropToPadding" :
|
||||
image.setCropToPadding(parseBoolean(value));
|
||||
break;
|
||||
case "maxHeight" :
|
||||
image.setMaxHeight(parseDimension(value, 0, dm));
|
||||
break;
|
||||
case "maxWidth" :
|
||||
image.setMaxWidth(parseDimension(value, 0, dm));
|
||||
break;
|
||||
case "scaleType" :
|
||||
image.setScaleType(parseScaleType(value));
|
||||
break;
|
||||
case "src" :
|
||||
image.setImageDrawable(parseImageResource(value, context));
|
||||
break;
|
||||
case "tint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "tintMode" :
|
||||
image.setImageTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
default :
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
private Drawable parseImageResource(String value, Context context) {
|
||||
if (value.startsWith("@drawable/") || value.startsWith("@mipmap/")) {
|
||||
return parseDrawable(value, context);
|
||||
}
|
||||
return ContextCompat.getDrawable(context, android.R.drawable.ic_delete);
|
||||
}
|
||||
|
||||
private ImageView.ScaleType parseScaleType(String value) {
|
||||
switch (value) {
|
||||
case "center" :
|
||||
return ImageView.ScaleType.CENTER;
|
||||
case "centerCrop" :
|
||||
return ImageView.ScaleType.CENTER_CROP;
|
||||
case "centerInside" :
|
||||
return ImageView.ScaleType.CENTER_INSIDE;
|
||||
case "fitEnd" :
|
||||
return ImageView.ScaleType.FIT_END;
|
||||
case "fitStart" :
|
||||
return ImageView.ScaleType.FIT_START;
|
||||
case "fitXY" :
|
||||
return ImageView.ScaleType.FIT_XY;
|
||||
case "matrix" :
|
||||
return ImageView.ScaleType.MATRIX;
|
||||
case "fitCenter" :
|
||||
default :
|
||||
return ImageView.ScaleType.FIT_CENTER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+98
-98
@@ -1,98 +1,98 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter;
|
||||
|
||||
/**
|
||||
* Adapter for handling attributes related to LinearLayout.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class LinearLayoutAttrAdapter extends ViewGroupAttrAdapter {
|
||||
|
||||
public LinearLayoutAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof LinearLayout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
final LinearLayout linear = (LinearLayout) view;
|
||||
final String namespace = attribute.getNamespace();
|
||||
final String name = attribute.getAttributeName();
|
||||
final String value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "baselineAligned" :
|
||||
linear.setBaselineAligned(parseBoolean(value));
|
||||
break;
|
||||
case "baselineAlignedChildIndex" :
|
||||
linear.setBaselineAlignedChildIndex(parseInteger(value, linear.getChildCount()));
|
||||
break;
|
||||
case "gravity" :
|
||||
linear.setGravity(parseGravity(value));
|
||||
break;
|
||||
case "measureWithLargestChild" :
|
||||
linear.setMeasureWithLargestChildEnabled(parseBoolean(value));
|
||||
break;
|
||||
case "orientation" :
|
||||
linear.setOrientation(parseOrientation(value));
|
||||
break;
|
||||
case "weightSum" :
|
||||
linear.setWeightSum(parseFloat(value));
|
||||
break;
|
||||
default :
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
protected int parseOrientation(@NonNull String value) {
|
||||
switch (value) {
|
||||
case "vertical" :
|
||||
return LinearLayout.VERTICAL;
|
||||
case "horizontal" :
|
||||
default :
|
||||
return LinearLayout.HORIZONTAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter;
|
||||
|
||||
/**
|
||||
* Adapter for handling attributes related to LinearLayout.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class LinearLayoutAttrAdapter extends ViewGroupAttrAdapter {
|
||||
|
||||
public LinearLayoutAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof LinearLayout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
final LinearLayout linear = (LinearLayout) view;
|
||||
final String namespace = attribute.getNamespace();
|
||||
final String name = attribute.getAttributeName();
|
||||
final String value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "baselineAligned" :
|
||||
linear.setBaselineAligned(parseBoolean(value));
|
||||
break;
|
||||
case "baselineAlignedChildIndex" :
|
||||
linear.setBaselineAlignedChildIndex(parseInteger(value, linear.getChildCount()));
|
||||
break;
|
||||
case "gravity" :
|
||||
linear.setGravity(parseGravity(value));
|
||||
break;
|
||||
case "measureWithLargestChild" :
|
||||
linear.setMeasureWithLargestChildEnabled(parseBoolean(value));
|
||||
break;
|
||||
case "orientation" :
|
||||
linear.setOrientation(parseOrientation(value));
|
||||
break;
|
||||
case "weightSum" :
|
||||
linear.setWeightSum(parseFloat(value));
|
||||
break;
|
||||
default :
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
protected int parseOrientation(@NonNull String value) {
|
||||
switch (value) {
|
||||
case "vertical" :
|
||||
return LinearLayout.VERTICAL;
|
||||
case "horizontal" :
|
||||
default :
|
||||
return LinearLayout.HORIZONTAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+142
-142
@@ -1,142 +1,142 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.adapters.android.view.ViewAttrAdapter;
|
||||
|
||||
/**
|
||||
* Attribute adapter for handling attributes related to
|
||||
* ProgressBar.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class ProgressBarAttrAdapter extends ViewAttrAdapter {
|
||||
|
||||
public ProgressBarAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof ProgressBar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
final ProgressBar pb = (ProgressBar) view;
|
||||
final Context context = pb.getContext();
|
||||
final DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||
final String namespace = attribute.getNamespace();
|
||||
final String name = attribute.getAttributeName();
|
||||
final String value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "indeterminate" :
|
||||
pb.setIndeterminate(parseBoolean(value));
|
||||
break;
|
||||
case "indeterminateDrawable" :
|
||||
pb.setIndeterminateDrawable(parseDrawable(value, context));
|
||||
break;
|
||||
case "indeterminateTint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "indeterminateTintMode" :
|
||||
pb.setIndeterminateTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
case "max" :
|
||||
pb.setMax(parseInteger(value, 100));
|
||||
break;
|
||||
case "maxHeight" :
|
||||
if (isApi29 ()) {
|
||||
pb.setMaxHeight(parseDimension(value, Integer.MAX_VALUE, dm));
|
||||
}
|
||||
break;
|
||||
case "maxWidth" :
|
||||
if (isApi29 ()) {
|
||||
pb.setMaxWidth(parseDimension(value, Integer.MAX_VALUE, dm));
|
||||
}
|
||||
break;
|
||||
case "min" :
|
||||
if (isApi26()) {
|
||||
pb.setMin(parseInteger(value, 0));
|
||||
}
|
||||
break;
|
||||
case "minHeight" :
|
||||
if (isApi29()) {
|
||||
pb.setMinHeight(parseDimension(value, 0, dm));
|
||||
}
|
||||
break;
|
||||
case "minWidth" :
|
||||
if (isApi29()) {
|
||||
pb.setMinWidth(parseDimension(value, 0, dm));
|
||||
}
|
||||
break;
|
||||
case "progress" :
|
||||
pb.setProgress(parseInteger(value, 50));
|
||||
break;
|
||||
case "progressBackgroundTint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "progressBackgroundTintMode" :
|
||||
pb.setProgressBackgroundTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
case "progressDrawable" :
|
||||
pb.setProgressDrawable(parseDrawable(value, context));
|
||||
break;
|
||||
case "progressTint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "progressTintMode" :
|
||||
pb.setProgressTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
case "secondaryProgress" :
|
||||
pb.setSecondaryProgress(parseInteger(value, 0));
|
||||
break;
|
||||
case "secondaryProgressTint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "secondaryProgressTintMode" :
|
||||
pb.setSecondaryProgressTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
default :
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.adapters.android.view.ViewAttrAdapter;
|
||||
|
||||
/**
|
||||
* Attribute adapter for handling attributes related to
|
||||
* ProgressBar.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class ProgressBarAttrAdapter extends ViewAttrAdapter {
|
||||
|
||||
public ProgressBarAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof ProgressBar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
final ProgressBar pb = (ProgressBar) view;
|
||||
final Context context = pb.getContext();
|
||||
final DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||
final String namespace = attribute.getNamespace();
|
||||
final String name = attribute.getAttributeName();
|
||||
final String value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "indeterminate" :
|
||||
pb.setIndeterminate(parseBoolean(value));
|
||||
break;
|
||||
case "indeterminateDrawable" :
|
||||
pb.setIndeterminateDrawable(parseDrawable(value, context));
|
||||
break;
|
||||
case "indeterminateTint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "indeterminateTintMode" :
|
||||
pb.setIndeterminateTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
case "max" :
|
||||
pb.setMax(parseInteger(value, 100));
|
||||
break;
|
||||
case "maxHeight" :
|
||||
if (isApi29 ()) {
|
||||
pb.setMaxHeight(parseDimension(value, Integer.MAX_VALUE, dm));
|
||||
}
|
||||
break;
|
||||
case "maxWidth" :
|
||||
if (isApi29 ()) {
|
||||
pb.setMaxWidth(parseDimension(value, Integer.MAX_VALUE, dm));
|
||||
}
|
||||
break;
|
||||
case "min" :
|
||||
if (isApi26()) {
|
||||
pb.setMin(parseInteger(value, 0));
|
||||
}
|
||||
break;
|
||||
case "minHeight" :
|
||||
if (isApi29()) {
|
||||
pb.setMinHeight(parseDimension(value, 0, dm));
|
||||
}
|
||||
break;
|
||||
case "minWidth" :
|
||||
if (isApi29()) {
|
||||
pb.setMinWidth(parseDimension(value, 0, dm));
|
||||
}
|
||||
break;
|
||||
case "progress" :
|
||||
pb.setProgress(parseInteger(value, 50));
|
||||
break;
|
||||
case "progressBackgroundTint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "progressBackgroundTintMode" :
|
||||
pb.setProgressBackgroundTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
case "progressDrawable" :
|
||||
pb.setProgressDrawable(parseDrawable(value, context));
|
||||
break;
|
||||
case "progressTint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "progressTintMode" :
|
||||
pb.setProgressTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
case "secondaryProgress" :
|
||||
pb.setSecondaryProgress(parseInteger(value, 0));
|
||||
break;
|
||||
case "secondaryProgressTint" :
|
||||
// TODO Parse color state list
|
||||
break;
|
||||
case "secondaryProgressTintMode" :
|
||||
pb.setSecondaryProgressTintMode(parsePorterDuffMode(value));
|
||||
break;
|
||||
default :
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
|
||||
+51
-51
@@ -1,51 +1,51 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.RadioButton;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Attribute adapter for handling attributes related to
|
||||
* RadioButton.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class RadioButtonAttrAdapter extends CompondButtonAttrAdapter {
|
||||
|
||||
public RadioButtonAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof RadioButton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
// No special attributes for RadioButton
|
||||
return super.apply(attribute, view);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.RadioButton;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Attribute adapter for handling attributes related to
|
||||
* RadioButton.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class RadioButtonAttrAdapter extends CompondButtonAttrAdapter {
|
||||
|
||||
public RadioButtonAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof RadioButton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
// No special attributes for RadioButton
|
||||
return super.apply(attribute, view);
|
||||
}
|
||||
}
|
||||
|
||||
+78
-78
@@ -1,78 +1,78 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter;
|
||||
|
||||
/**
|
||||
* Attribute adapter for handling attributes related to
|
||||
* RelativeLayout.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class RelativeLayoutAttrAdapter extends ViewGroupAttrAdapter {
|
||||
|
||||
public RelativeLayoutAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof RelativeLayout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
|
||||
final RelativeLayout relative = (RelativeLayout) view;
|
||||
final String namespace = attribute.getNamespace();
|
||||
final String name = attribute.getAttributeName();
|
||||
final String value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "gravity" :
|
||||
relative.setGravity(parseGravity(value));
|
||||
break;
|
||||
case "ignoreGravity" :
|
||||
relative.setIgnoreGravity(parseId(value));
|
||||
break;
|
||||
default :
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter;
|
||||
|
||||
/**
|
||||
* Attribute adapter for handling attributes related to
|
||||
* RelativeLayout.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class RelativeLayoutAttrAdapter extends ViewGroupAttrAdapter {
|
||||
|
||||
public RelativeLayoutAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof RelativeLayout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
|
||||
final RelativeLayout relative = (RelativeLayout) view;
|
||||
final String namespace = attribute.getNamespace();
|
||||
final String name = attribute.getAttributeName();
|
||||
final String value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "gravity" :
|
||||
relative.setGravity(parseGravity(value));
|
||||
break;
|
||||
case "ignoreGravity" :
|
||||
relative.setIgnoreGravity(parseId(value));
|
||||
break;
|
||||
default :
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
|
||||
+71
-71
@@ -1,71 +1,71 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.SeekBar;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Attribute adapter for handling attributes related to
|
||||
* SeekBar.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class SeekBarAttrAdapter extends AbsSeekBarAttrAdapter {
|
||||
|
||||
public SeekBarAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof SeekBar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(@NonNull IAttribute attribute, View view) {
|
||||
final var seek = (SeekBar) view;
|
||||
final var context = seek.getContext();
|
||||
final var namespace = attribute.getNamespace();
|
||||
final var name = attribute.getAttributeName();
|
||||
final var value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "thumb":
|
||||
seek.setThumb(parseDrawable(value, context));
|
||||
break;
|
||||
default:
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return handled;
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.SeekBar;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Attribute adapter for handling attributes related to
|
||||
* SeekBar.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class SeekBarAttrAdapter extends AbsSeekBarAttrAdapter {
|
||||
|
||||
public SeekBarAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof SeekBar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(@NonNull IAttribute attribute, View view) {
|
||||
final var seek = (SeekBar) view;
|
||||
final var context = seek.getContext();
|
||||
final var namespace = attribute.getNamespace();
|
||||
final var name = attribute.getAttributeName();
|
||||
final var value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "thumb":
|
||||
seek.setThumb(parseDrawable(value, context));
|
||||
break;
|
||||
default:
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return handled;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+88
-88
@@ -1,88 +1,88 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Attribute adapter for handling attributes related to
|
||||
* Spinner.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class SpinnerAttrAdapter extends AbsSpinnerAttrAdapter {
|
||||
|
||||
public SpinnerAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof Spinner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(@NonNull IAttribute attribute, View view) {
|
||||
final var spinner = (Spinner) view;
|
||||
final var context = spinner.getContext();
|
||||
final var dm = context.getResources().getDisplayMetrics();
|
||||
final var namespace = attribute.getNamespace();
|
||||
final var name = attribute.getAttributeName();
|
||||
final var value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "dropDownHorizontalOffset":
|
||||
spinner.setDropDownHorizontalOffset(parseDimension(value, 0, dm));
|
||||
break;
|
||||
case "dropDownVerticalOffset":
|
||||
spinner.setDropDownVerticalOffset(parseDimension(value, 0, dm));
|
||||
break;
|
||||
case "dropDownWidth":
|
||||
spinner.setDropDownWidth(parseDimension(value, 0, dm));
|
||||
break;
|
||||
case "gravity" :
|
||||
spinner.setGravity(parseGravity(value));
|
||||
break;
|
||||
case "popupBackground" :
|
||||
spinner.setPopupBackgroundDrawable(parseDrawable(value, context));
|
||||
break;
|
||||
default:
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
/**
|
||||
* Attribute adapter for handling attributes related to
|
||||
* Spinner.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class SpinnerAttrAdapter extends AbsSpinnerAttrAdapter {
|
||||
|
||||
public SpinnerAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof Spinner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(@NonNull IAttribute attribute, View view) {
|
||||
final var spinner = (Spinner) view;
|
||||
final var context = spinner.getContext();
|
||||
final var dm = context.getResources().getDisplayMetrics();
|
||||
final var namespace = attribute.getNamespace();
|
||||
final var name = attribute.getAttributeName();
|
||||
final var value = attribute.getValue();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
|
||||
switch (name) {
|
||||
case "dropDownHorizontalOffset":
|
||||
spinner.setDropDownHorizontalOffset(parseDimension(value, 0, dm));
|
||||
break;
|
||||
case "dropDownVerticalOffset":
|
||||
spinner.setDropDownVerticalOffset(parseDimension(value, 0, dm));
|
||||
break;
|
||||
case "dropDownWidth":
|
||||
spinner.setDropDownWidth(parseDimension(value, 0, dm));
|
||||
break;
|
||||
case "gravity" :
|
||||
spinner.setGravity(parseGravity(value));
|
||||
break;
|
||||
case "popupBackground" :
|
||||
spinner.setPopupBackgroundDrawable(parseDrawable(value, context));
|
||||
break;
|
||||
default:
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
|
||||
+239
-239
@@ -1,239 +1,239 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.util.Linkify;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.blankj.utilcode.util.SizeUtils;
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.adapters.android.view.ViewAttrAdapter;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Adapter for handling attributes related to TextView.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class TextViewAttrAdapter extends ViewAttrAdapter {
|
||||
|
||||
public TextViewAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof TextView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
final TextView text = (TextView) view;
|
||||
final String namespace = attribute.getNamespace();
|
||||
final String name = attribute.getAttributeName();
|
||||
final String value = attribute.getValue();
|
||||
final ViewGroup.LayoutParams params = view.getLayoutParams();
|
||||
final Resources res = view.getResources();
|
||||
final DisplayMetrics dm = res.getDisplayMetrics();
|
||||
final Context ctx = view.getContext();
|
||||
final Drawable[] drawables = text.getCompoundDrawables();
|
||||
final Drawable[] drawablesRelative = text.getCompoundDrawablesRelative();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
switch (name) {
|
||||
case "autoLink" :
|
||||
text.setAutoLinkMask(parseAutoLinkMask(value));
|
||||
break;
|
||||
case "drawableLeft" :
|
||||
text.setCompoundDrawables(parseDrawable(value, ctx), drawables[1], drawables[2], drawables[3]);
|
||||
break;
|
||||
case "drawableTop" :
|
||||
text.setCompoundDrawables(drawables[0], parseDrawable(value, ctx), drawables[2], drawables[3]);
|
||||
break;
|
||||
case "drawableRight" :
|
||||
text.setCompoundDrawables(drawables[0], drawables[1], parseDrawable(value, ctx), drawables[3]);
|
||||
break;
|
||||
case "drawableBottom" :
|
||||
text.setCompoundDrawables(drawables[0], drawables[1], drawables[2], parseDrawable(value, ctx));
|
||||
break;
|
||||
case "drawableStart" :
|
||||
text.setCompoundDrawables(parseDrawable(value, ctx), drawablesRelative[1], drawablesRelative[2], drawablesRelative[3]);
|
||||
break;
|
||||
case "drawableEnd" :
|
||||
text.setCompoundDrawables(drawablesRelative[0], drawablesRelative[1], parseDrawable(value, ctx), drawablesRelative[3]);
|
||||
break;
|
||||
case "drawablePadding" :
|
||||
text.setCompoundDrawablePadding(parseDimension(value, 0, dm));
|
||||
break;
|
||||
case "ellipsize" :
|
||||
text.setEllipsize(parseEllipsize(value));
|
||||
break;
|
||||
case "gravity" :
|
||||
text.setGravity(parseGravity(value));
|
||||
break;
|
||||
case "hint" :
|
||||
text.setHint(parseString(value));
|
||||
break;
|
||||
case "letterSpacing" :
|
||||
text.setLetterSpacing(parseFloat(value));
|
||||
break;
|
||||
case "lineHeight" :
|
||||
text.setLines(parseInteger(value, Integer.MAX_VALUE));
|
||||
break;
|
||||
case "linksClickable" :
|
||||
text.setLinksClickable(parseBoolean(value));
|
||||
break;
|
||||
case "marqueeRepeatLimit" :
|
||||
text.setMarqueeRepeatLimit(parseInteger(value, Integer.MAX_VALUE));
|
||||
break;
|
||||
case "maxLines" :
|
||||
text.setMaxLines(parseInteger(value, Integer.MAX_VALUE));
|
||||
break;
|
||||
case "minLines" :
|
||||
text.setMinLines(parseInteger(value, 1));
|
||||
break;
|
||||
case "singleLine" :
|
||||
text.setSingleLine(parseBoolean(value));
|
||||
break;
|
||||
case "text" :
|
||||
text.setText(parseString(value));
|
||||
break;
|
||||
case "textAllCaps" :
|
||||
text.setAllCaps(parseBoolean(value));
|
||||
break;
|
||||
case "textColor" :
|
||||
text.setTextColor(parseColor(value, ctx));
|
||||
break;
|
||||
case "textColorHint" :
|
||||
text.setHintTextColor(parseColor(value, ctx));
|
||||
break;
|
||||
case "textSize" :
|
||||
text.setTextSize(TypedValue.COMPLEX_UNIT_PX, parseDimension(value, SizeUtils.sp2px(14), dm));
|
||||
break;
|
||||
case "textStyle" :
|
||||
text.setTypeface(text.getTypeface(), parseTextStyle(value));
|
||||
break;
|
||||
case "typeface" :
|
||||
text.setTypeface(parseTypeface(value));
|
||||
break;
|
||||
default :
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
protected int parseTextStyle(@NonNull String value) {
|
||||
final String[] splits = value.split(Pattern.quote("|"));
|
||||
int mask = 0;
|
||||
for (String split : splits) {
|
||||
mask |= textStyleFor(split);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
protected int textStyleFor(@NonNull String split) {
|
||||
switch (split) {
|
||||
case "bold" :
|
||||
return Typeface.BOLD;
|
||||
case "italic" :
|
||||
return Typeface.ITALIC;
|
||||
case "normal" :
|
||||
default :
|
||||
return Typeface.NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
protected Typeface parseTypeface(@NonNull String value) {
|
||||
switch (value) {
|
||||
case "sans" :
|
||||
return Typeface.SANS_SERIF;
|
||||
case "serif" :
|
||||
return Typeface.SERIF;
|
||||
case "monospace" :
|
||||
return Typeface.MONOSPACE;
|
||||
case "normal" :
|
||||
default :
|
||||
return Typeface.DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
protected TextUtils.TruncateAt parseEllipsize(@NonNull String value) {
|
||||
switch (value) {
|
||||
case "end" :
|
||||
return TextUtils.TruncateAt.END;
|
||||
case "start" :
|
||||
return TextUtils.TruncateAt.START;
|
||||
case "marquee" :
|
||||
return TextUtils.TruncateAt.MARQUEE;
|
||||
case "middle" :
|
||||
return TextUtils.TruncateAt.MIDDLE;
|
||||
case "none" :
|
||||
default :
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected int parseAutoLinkMask(@NonNull String value) {
|
||||
final String[] splits = value.split(Pattern.quote("|"));
|
||||
int mask = 0;
|
||||
for (String split : splits) {
|
||||
mask |= autoLinkMaskFor (split);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
protected int autoLinkMaskFor(@NonNull String mask) {
|
||||
switch (mask) {
|
||||
case "all" :
|
||||
return Linkify.ALL;
|
||||
case "web" :
|
||||
return Linkify.WEB_URLS;
|
||||
case "phone" :
|
||||
return Linkify.PHONE_NUMBERS;
|
||||
case "map" :
|
||||
return Linkify.MAP_ADDRESSES;
|
||||
case "email" :
|
||||
return Linkify.EMAIL_ADDRESSES;
|
||||
case "none" :
|
||||
default :
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.adapters.android.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.util.Linkify;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.blankj.utilcode.util.SizeUtils;
|
||||
import com.itsaky.inflater.IAttribute;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.adapters.android.view.ViewAttrAdapter;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Adapter for handling attributes related to TextView.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class TextViewAttrAdapter extends ViewAttrAdapter {
|
||||
|
||||
public TextViewAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicableTo(View view) {
|
||||
return view instanceof TextView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(IAttribute attribute, View view) {
|
||||
final TextView text = (TextView) view;
|
||||
final String namespace = attribute.getNamespace();
|
||||
final String name = attribute.getAttributeName();
|
||||
final String value = attribute.getValue();
|
||||
final ViewGroup.LayoutParams params = view.getLayoutParams();
|
||||
final Resources res = view.getResources();
|
||||
final DisplayMetrics dm = res.getDisplayMetrics();
|
||||
final Context ctx = view.getContext();
|
||||
final Drawable[] drawables = text.getCompoundDrawables();
|
||||
final Drawable[] drawablesRelative = text.getCompoundDrawablesRelative();
|
||||
|
||||
if (!canHandleNamespace(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean handled = true;
|
||||
switch (name) {
|
||||
case "autoLink" :
|
||||
text.setAutoLinkMask(parseAutoLinkMask(value));
|
||||
break;
|
||||
case "drawableLeft" :
|
||||
text.setCompoundDrawables(parseDrawable(value, ctx), drawables[1], drawables[2], drawables[3]);
|
||||
break;
|
||||
case "drawableTop" :
|
||||
text.setCompoundDrawables(drawables[0], parseDrawable(value, ctx), drawables[2], drawables[3]);
|
||||
break;
|
||||
case "drawableRight" :
|
||||
text.setCompoundDrawables(drawables[0], drawables[1], parseDrawable(value, ctx), drawables[3]);
|
||||
break;
|
||||
case "drawableBottom" :
|
||||
text.setCompoundDrawables(drawables[0], drawables[1], drawables[2], parseDrawable(value, ctx));
|
||||
break;
|
||||
case "drawableStart" :
|
||||
text.setCompoundDrawables(parseDrawable(value, ctx), drawablesRelative[1], drawablesRelative[2], drawablesRelative[3]);
|
||||
break;
|
||||
case "drawableEnd" :
|
||||
text.setCompoundDrawables(drawablesRelative[0], drawablesRelative[1], parseDrawable(value, ctx), drawablesRelative[3]);
|
||||
break;
|
||||
case "drawablePadding" :
|
||||
text.setCompoundDrawablePadding(parseDimension(value, 0, dm));
|
||||
break;
|
||||
case "ellipsize" :
|
||||
text.setEllipsize(parseEllipsize(value));
|
||||
break;
|
||||
case "gravity" :
|
||||
text.setGravity(parseGravity(value));
|
||||
break;
|
||||
case "hint" :
|
||||
text.setHint(parseString(value));
|
||||
break;
|
||||
case "letterSpacing" :
|
||||
text.setLetterSpacing(parseFloat(value));
|
||||
break;
|
||||
case "lineHeight" :
|
||||
text.setLines(parseInteger(value, Integer.MAX_VALUE));
|
||||
break;
|
||||
case "linksClickable" :
|
||||
text.setLinksClickable(parseBoolean(value));
|
||||
break;
|
||||
case "marqueeRepeatLimit" :
|
||||
text.setMarqueeRepeatLimit(parseInteger(value, Integer.MAX_VALUE));
|
||||
break;
|
||||
case "maxLines" :
|
||||
text.setMaxLines(parseInteger(value, Integer.MAX_VALUE));
|
||||
break;
|
||||
case "minLines" :
|
||||
text.setMinLines(parseInteger(value, 1));
|
||||
break;
|
||||
case "singleLine" :
|
||||
text.setSingleLine(parseBoolean(value));
|
||||
break;
|
||||
case "text" :
|
||||
text.setText(parseString(value));
|
||||
break;
|
||||
case "textAllCaps" :
|
||||
text.setAllCaps(parseBoolean(value));
|
||||
break;
|
||||
case "textColor" :
|
||||
text.setTextColor(parseColor(value, ctx));
|
||||
break;
|
||||
case "textColorHint" :
|
||||
text.setHintTextColor(parseColor(value, ctx));
|
||||
break;
|
||||
case "textSize" :
|
||||
text.setTextSize(TypedValue.COMPLEX_UNIT_PX, parseDimension(value, SizeUtils.sp2px(14), dm));
|
||||
break;
|
||||
case "textStyle" :
|
||||
text.setTypeface(text.getTypeface(), parseTextStyle(value));
|
||||
break;
|
||||
case "typeface" :
|
||||
text.setTypeface(parseTypeface(value));
|
||||
break;
|
||||
default :
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
handled = super.apply(attribute, view);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
protected int parseTextStyle(@NonNull String value) {
|
||||
final String[] splits = value.split(Pattern.quote("|"));
|
||||
int mask = 0;
|
||||
for (String split : splits) {
|
||||
mask |= textStyleFor(split);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
protected int textStyleFor(@NonNull String split) {
|
||||
switch (split) {
|
||||
case "bold" :
|
||||
return Typeface.BOLD;
|
||||
case "italic" :
|
||||
return Typeface.ITALIC;
|
||||
case "normal" :
|
||||
default :
|
||||
return Typeface.NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
protected Typeface parseTypeface(@NonNull String value) {
|
||||
switch (value) {
|
||||
case "sans" :
|
||||
return Typeface.SANS_SERIF;
|
||||
case "serif" :
|
||||
return Typeface.SERIF;
|
||||
case "monospace" :
|
||||
return Typeface.MONOSPACE;
|
||||
case "normal" :
|
||||
default :
|
||||
return Typeface.DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
protected TextUtils.TruncateAt parseEllipsize(@NonNull String value) {
|
||||
switch (value) {
|
||||
case "end" :
|
||||
return TextUtils.TruncateAt.END;
|
||||
case "start" :
|
||||
return TextUtils.TruncateAt.START;
|
||||
case "marquee" :
|
||||
return TextUtils.TruncateAt.MARQUEE;
|
||||
case "middle" :
|
||||
return TextUtils.TruncateAt.MIDDLE;
|
||||
case "none" :
|
||||
default :
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected int parseAutoLinkMask(@NonNull String value) {
|
||||
final String[] splits = value.split(Pattern.quote("|"));
|
||||
int mask = 0;
|
||||
for (String split : splits) {
|
||||
mask |= autoLinkMaskFor (split);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
protected int autoLinkMaskFor(@NonNull String mask) {
|
||||
switch (mask) {
|
||||
case "all" :
|
||||
return Linkify.ALL;
|
||||
case "web" :
|
||||
return Linkify.WEB_URLS;
|
||||
case "phone" :
|
||||
return Linkify.PHONE_NUMBERS;
|
||||
case "map" :
|
||||
return Linkify.MAP_ADDRESSES;
|
||||
case "email" :
|
||||
return Linkify.EMAIL_ADDRESSES;
|
||||
case "none" :
|
||||
default :
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+153
-153
@@ -1,153 +1,153 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.drawable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.NinePatch;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.NinePatchDrawable;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.blankj.utilcode.util.ImageUtils;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.sdsmdg.harjot.vectormaster.VectorMasterDrawable;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlPullParserFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* Utility methods for creating instances of suitable drawable parsers for a file
|
||||
* or an XML code.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public abstract class DrawableParserFactory {
|
||||
|
||||
/**
|
||||
* Create a new drawable parser for the given file. If the given file is not an XML Document,
|
||||
* then a no-op parser is returned which simply returns the drawable for that file. If the file
|
||||
* is an XML document, then a suitable parser is determined by looking at the root tag of the
|
||||
* drawable.
|
||||
*
|
||||
* @param context The context. This will be used for obtaining app resources.
|
||||
* @param file The file to parse.
|
||||
* @param resourceFinder The resource finder. Used for finding references to another resources.
|
||||
* @return A new {@link IDrawableParser} instance if the inputs are valid, or {@code null} if there were any
|
||||
* issues creating the parser.
|
||||
* @throws XmlPullParserException Thrown by the {@link XmlPullParser#setInput(Reader)}.
|
||||
* @throws IOException Thrown by {@link XmlPullParser#next()}
|
||||
*/
|
||||
@Nullable
|
||||
public static IDrawableParser newParser (@NonNull final Context context, @NonNull final File file, final IResourceFinder resourceFinder) throws XmlPullParserException, IOException {
|
||||
if (file.getName ().endsWith (".xml")) {
|
||||
final var code = FileIOUtils.readFile2String (file);
|
||||
return DrawableParserFactory.newXmlDrawableParser (code, resourceFinder, context.getResources ().getDisplayMetrics ());
|
||||
} else {
|
||||
final var bitmap = ImageUtils.getBitmap (file);
|
||||
if (bitmap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final var chunk = bitmap.getNinePatchChunk ();
|
||||
if (NinePatch.isNinePatchChunk (chunk)) {
|
||||
return new NoParser (new NinePatchDrawable (context.getResources (), new NinePatch (bitmap, chunk)));
|
||||
}
|
||||
|
||||
return new NoParser (new BitmapDrawable (context.getResources (), bitmap));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new drawable parser for the given xml code.
|
||||
*
|
||||
* @param xmlDrawable The XML code. This must be valid.
|
||||
* @param resourceFinder The resource finder. Used for finding references to another resources.
|
||||
* @param displayMetrics This will be used by generated parsers to parse dimension vlaues.
|
||||
* @return A new {@link IDrawableParser} instance if the inputs are valid, or {@code null} if there were any
|
||||
* issues creating the parser.
|
||||
* @throws XmlPullParserException Thrown by the {@link XmlPullParser#setInput(Reader)}.
|
||||
* @throws IOException Thrown by {@link XmlPullParser#next()}
|
||||
*/
|
||||
@Nullable
|
||||
public static IDrawableParser newXmlDrawableParser (String xmlDrawable, IResourceFinder resourceFinder, DisplayMetrics displayMetrics) throws XmlPullParserException, IOException {
|
||||
final var factory = XmlPullParserFactory.newInstance ();
|
||||
factory.setNamespaceAware (true);
|
||||
|
||||
final var parser = factory.newPullParser ();
|
||||
parser.setInput (new StringReader (xmlDrawable));
|
||||
|
||||
var event = parser.getEventType ();
|
||||
while (event != XmlPullParser.END_DOCUMENT) {
|
||||
if (event == XmlPullParser.START_TAG) {
|
||||
final var name = parser.getName ();
|
||||
switch (name) {
|
||||
case "shape" :
|
||||
return new ShapeDrawableParser (parser, resourceFinder, displayMetrics);
|
||||
case "vector" :
|
||||
return new NoParser (VectorMasterDrawable.fromXML (xmlDrawable));
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
event = parser.next ();
|
||||
}
|
||||
|
||||
// TODO Implement parsers for these root tags if possible
|
||||
// 1. <bitmap>
|
||||
// 2. <nine-patch>
|
||||
// 3. <layer-list>
|
||||
// 4. <selector>
|
||||
// 5. <level-list>
|
||||
// 6. <transition>
|
||||
// 7. <inset>
|
||||
// 8. <clip>
|
||||
// 9. <scale>
|
||||
|
||||
// We don't support parsing of this type of drawable
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instead of parsing anything, this returns the provided drawable.
|
||||
*/
|
||||
private static class NoParser extends IDrawableParser {
|
||||
|
||||
private final Drawable parsed;
|
||||
|
||||
protected NoParser (final Drawable parsed) {
|
||||
super (null, null, null);
|
||||
this.parsed = parsed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable parse () {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.drawable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.NinePatch;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.NinePatchDrawable;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.blankj.utilcode.util.ImageUtils;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.sdsmdg.harjot.vectormaster.VectorMasterDrawable;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlPullParserFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* Utility methods for creating instances of suitable drawable parsers for a file
|
||||
* or an XML code.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public abstract class DrawableParserFactory {
|
||||
|
||||
/**
|
||||
* Create a new drawable parser for the given file. If the given file is not an XML Document,
|
||||
* then a no-op parser is returned which simply returns the drawable for that file. If the file
|
||||
* is an XML document, then a suitable parser is determined by looking at the root tag of the
|
||||
* drawable.
|
||||
*
|
||||
* @param context The context. This will be used for obtaining app resources.
|
||||
* @param file The file to parse.
|
||||
* @param resourceFinder The resource finder. Used for finding references to another resources.
|
||||
* @return A new {@link IDrawableParser} instance if the inputs are valid, or {@code null} if there were any
|
||||
* issues creating the parser.
|
||||
* @throws XmlPullParserException Thrown by the {@link XmlPullParser#setInput(Reader)}.
|
||||
* @throws IOException Thrown by {@link XmlPullParser#next()}
|
||||
*/
|
||||
@Nullable
|
||||
public static IDrawableParser newParser (@NonNull final Context context, @NonNull final File file, final IResourceFinder resourceFinder) throws XmlPullParserException, IOException {
|
||||
if (file.getName ().endsWith (".xml")) {
|
||||
final var code = FileIOUtils.readFile2String (file);
|
||||
return DrawableParserFactory.newXmlDrawableParser (code, resourceFinder, context.getResources ().getDisplayMetrics ());
|
||||
} else {
|
||||
final var bitmap = ImageUtils.getBitmap (file);
|
||||
if (bitmap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final var chunk = bitmap.getNinePatchChunk ();
|
||||
if (NinePatch.isNinePatchChunk (chunk)) {
|
||||
return new NoParser (new NinePatchDrawable (context.getResources (), new NinePatch (bitmap, chunk)));
|
||||
}
|
||||
|
||||
return new NoParser (new BitmapDrawable (context.getResources (), bitmap));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new drawable parser for the given xml code.
|
||||
*
|
||||
* @param xmlDrawable The XML code. This must be valid.
|
||||
* @param resourceFinder The resource finder. Used for finding references to another resources.
|
||||
* @param displayMetrics This will be used by generated parsers to parse dimension vlaues.
|
||||
* @return A new {@link IDrawableParser} instance if the inputs are valid, or {@code null} if there were any
|
||||
* issues creating the parser.
|
||||
* @throws XmlPullParserException Thrown by the {@link XmlPullParser#setInput(Reader)}.
|
||||
* @throws IOException Thrown by {@link XmlPullParser#next()}
|
||||
*/
|
||||
@Nullable
|
||||
public static IDrawableParser newXmlDrawableParser (String xmlDrawable, IResourceFinder resourceFinder, DisplayMetrics displayMetrics) throws XmlPullParserException, IOException {
|
||||
final var factory = XmlPullParserFactory.newInstance ();
|
||||
factory.setNamespaceAware (true);
|
||||
|
||||
final var parser = factory.newPullParser ();
|
||||
parser.setInput (new StringReader (xmlDrawable));
|
||||
|
||||
var event = parser.getEventType ();
|
||||
while (event != XmlPullParser.END_DOCUMENT) {
|
||||
if (event == XmlPullParser.START_TAG) {
|
||||
final var name = parser.getName ();
|
||||
switch (name) {
|
||||
case "shape" :
|
||||
return new ShapeDrawableParser (parser, resourceFinder, displayMetrics);
|
||||
case "vector" :
|
||||
return new NoParser (VectorMasterDrawable.fromXML (xmlDrawable));
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
event = parser.next ();
|
||||
}
|
||||
|
||||
// TODO Implement parsers for these root tags if possible
|
||||
// 1. <bitmap>
|
||||
// 2. <nine-patch>
|
||||
// 3. <layer-list>
|
||||
// 4. <selector>
|
||||
// 5. <level-list>
|
||||
// 6. <transition>
|
||||
// 7. <inset>
|
||||
// 8. <clip>
|
||||
// 9. <scale>
|
||||
|
||||
// We don't support parsing of this type of drawable
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instead of parsing anything, this returns the provided drawable.
|
||||
*/
|
||||
private static class NoParser extends IDrawableParser {
|
||||
|
||||
private final Drawable parsed;
|
||||
|
||||
protected NoParser (final Drawable parsed) {
|
||||
super (null, null, null);
|
||||
this.parsed = parsed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable parse () {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,74 +1,74 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.drawable;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.util.CommonParseUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
/**
|
||||
* Base class for drawable parsers.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public abstract class IDrawableParser extends CommonParseUtils {
|
||||
|
||||
protected final XmlPullParser parser;
|
||||
|
||||
protected IDrawableParser (XmlPullParser parser, IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super(resourceFinder, displayMetrics);
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
public abstract Drawable parse () throws Exception;
|
||||
|
||||
protected int attrIndex (final String name) {
|
||||
for (int i = 0; i < this.parser.getAttributeCount (); i++) {
|
||||
if (this.parser.getAttributeName (i).equals (name)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected String value (int index) {
|
||||
return parser.getAttributeValue (index);
|
||||
}
|
||||
|
||||
protected boolean isApi26() {
|
||||
return Build.VERSION.SDK_INT >= 26;
|
||||
}
|
||||
|
||||
protected boolean isApi28() {
|
||||
return Build.VERSION.SDK_INT >= 28;
|
||||
}
|
||||
|
||||
protected boolean isApi29() {
|
||||
return Build.VERSION.SDK_INT >= 29;
|
||||
}
|
||||
|
||||
protected boolean isApi30() {
|
||||
return Build.VERSION.SDK_INT >= 30;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.drawable;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.util.CommonParseUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
/**
|
||||
* Base class for drawable parsers.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public abstract class IDrawableParser extends CommonParseUtils {
|
||||
|
||||
protected final XmlPullParser parser;
|
||||
|
||||
protected IDrawableParser (XmlPullParser parser, IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super(resourceFinder, displayMetrics);
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
public abstract Drawable parse () throws Exception;
|
||||
|
||||
protected int attrIndex (final String name) {
|
||||
for (int i = 0; i < this.parser.getAttributeCount (); i++) {
|
||||
if (this.parser.getAttributeName (i).equals (name)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected String value (int index) {
|
||||
return parser.getAttributeValue (index);
|
||||
}
|
||||
|
||||
protected boolean isApi26() {
|
||||
return Build.VERSION.SDK_INT >= 26;
|
||||
}
|
||||
|
||||
protected boolean isApi28() {
|
||||
return Build.VERSION.SDK_INT >= 28;
|
||||
}
|
||||
|
||||
protected boolean isApi29() {
|
||||
return Build.VERSION.SDK_INT >= 29;
|
||||
}
|
||||
|
||||
protected boolean isApi30() {
|
||||
return Build.VERSION.SDK_INT >= 30;
|
||||
}
|
||||
}
|
||||
|
||||
+349
-349
@@ -1,349 +1,349 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.drawable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.itsaky.androidide.app.BaseApplication;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Parses a drawable whose root element is
|
||||
* {@code <shape>}. If the parse was successful, returns a {@link GradientDrawable},
|
||||
* otherwise {@code null}.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class ShapeDrawableParser extends IDrawableParser {
|
||||
|
||||
protected ShapeDrawableParser (XmlPullParser parser, IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (parser, resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Drawable parse () throws Exception {
|
||||
|
||||
final var drawable = new GradientDrawable ();
|
||||
|
||||
var index = attrIndex ("shape");
|
||||
drawable.setShape (index == -1 ? GradientDrawable.RECTANGLE : parseShape (this.parser.getAttributeValue (index)));
|
||||
|
||||
if (isApi29 ()) {
|
||||
index = attrIndex ("innerRadius");
|
||||
drawable.setInnerRadius (index == -1 ? 0 : parseDimension (value (index), 0));
|
||||
|
||||
index = attrIndex ("innerRadiusRatio");
|
||||
drawable.setInnerRadiusRatio (index == -1 ? 1f : Float.parseFloat (value (index)));
|
||||
|
||||
index = attrIndex ("thickness");
|
||||
drawable.setThickness (index == -1 ? 0 : parseDimension (value (index), 0));
|
||||
|
||||
index = attrIndex ("thicknessRatio");
|
||||
drawable.setThicknessRatio (index == -1 ? 1f : Float.parseFloat (value (index)));
|
||||
}
|
||||
|
||||
index = attrIndex ("useLevel");
|
||||
drawable.setUseLevel (index != -1 && parseBoolean (value (index)));
|
||||
|
||||
var event = parser.getEventType ();
|
||||
while (event != XmlPullParser.END_DOCUMENT) {
|
||||
final var name = parser.getName ();
|
||||
if (event == XmlPullParser.START_TAG) {
|
||||
switch (name) {
|
||||
case "corners":
|
||||
parseCorners (drawable);
|
||||
break;
|
||||
case "gradient":
|
||||
parseGradient (drawable);
|
||||
break;
|
||||
case "padding" :
|
||||
parsePadding (drawable);
|
||||
break;
|
||||
case "size" :
|
||||
parseSize (drawable);
|
||||
break;
|
||||
case "solid" :
|
||||
parseSolid (drawable);
|
||||
break;
|
||||
case "stroke" :
|
||||
parseStroke (drawable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
event = parser.next ();
|
||||
}
|
||||
|
||||
return drawable;
|
||||
}
|
||||
|
||||
private void parseStroke (@NonNull final GradientDrawable drawable) {
|
||||
var index = attrIndex ("width");
|
||||
var strokeWidth = 0;
|
||||
if (index != - 1) {
|
||||
drawable.setStroke (strokeWidth = parseDimension (value (index), 0), Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
index = attrIndex ("color");
|
||||
var strokeColor = Color.TRANSPARENT;
|
||||
if (index != -1) {
|
||||
drawable.setStroke (strokeWidth, strokeColor = parseColor (value (index), appContext ()));
|
||||
}
|
||||
|
||||
index = attrIndex ("dashWidth");
|
||||
var dashWidth = 0;
|
||||
if (index != -1) {
|
||||
drawable.setStroke (strokeWidth, strokeColor, dashWidth = parseDimension (value (index), 0), 0);
|
||||
}
|
||||
|
||||
index = attrIndex ("dashGap");
|
||||
if (index != -1) {
|
||||
drawable.setStroke (strokeWidth, strokeColor, dashWidth, parseDimension (value (index), 0));
|
||||
}
|
||||
}
|
||||
|
||||
private void parseSolid (@NonNull final GradientDrawable drawable) {
|
||||
var index = attrIndex ("color");
|
||||
if (index != -1) {
|
||||
drawable.setColor (parseColor (value (index), appContext ()));
|
||||
}
|
||||
}
|
||||
|
||||
private void parseSize (@NonNull final GradientDrawable drawable) {
|
||||
var index = attrIndex ("width");
|
||||
if (index != -1) {
|
||||
drawable.setSize (parseDimension (value (index), 0), drawable.getIntrinsicHeight ());
|
||||
}
|
||||
|
||||
index = attrIndex ("height");
|
||||
if (index != -1) {
|
||||
drawable.setSize (drawable.getIntrinsicWidth (), parseDimension (value (index), 0));
|
||||
}
|
||||
}
|
||||
|
||||
private void parsePadding (@NonNull final GradientDrawable drawable) {
|
||||
|
||||
// Padding is available from API 29 only
|
||||
if (!isApi29 ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final var rect = new Rect ();
|
||||
if (!drawable.getPadding (rect)) {
|
||||
rect.set (0,0,0,0);
|
||||
}
|
||||
|
||||
var changed = false;
|
||||
var index = attrIndex ("left");
|
||||
if (index != -1) {
|
||||
rect.left = parseDimension (value (index), 0);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
index = attrIndex ("top");
|
||||
if (index != -1) {
|
||||
rect.top = parseDimension (value (index), 0);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
index = attrIndex ("right");
|
||||
if (index != -1) {
|
||||
rect.right = parseDimension (value (index), 0);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
index = attrIndex ("bottom");
|
||||
if (index != -1) {
|
||||
rect.bottom = parseDimension (value (index), 0);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
drawable.setPadding (rect.left, rect.top, rect.right, rect.bottom);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseGradient (@NonNull final GradientDrawable drawable) {
|
||||
var index = attrIndex ("angle");
|
||||
drawable.setOrientation (index == -1 ? GradientDrawable.Orientation.LEFT_RIGHT : parseGradientOrientation (value (index)));
|
||||
|
||||
index = attrIndex ("centerX");
|
||||
var center = index == - 1 ? drawable.getGradientCenterX () : Float.parseFloat (value (index));
|
||||
drawable.setGradientCenter (center, drawable.getGradientCenterY ());
|
||||
|
||||
index = attrIndex ("centerY");
|
||||
center = index == - 1 ? drawable.getGradientCenterX () : Float.parseFloat (value (index));
|
||||
drawable.setGradientCenter (drawable.getGradientCenterX (), center);
|
||||
|
||||
final var colors = new int [3];
|
||||
var changed = false;
|
||||
Arrays.fill (colors, -1);
|
||||
|
||||
index = attrIndex ("centerColor");
|
||||
if (index != -1) {
|
||||
colors [1] = parseColor (value (index), appContext ());
|
||||
changed = true;
|
||||
}
|
||||
|
||||
index = attrIndex ("endColor");
|
||||
if (index != -1) {
|
||||
colors [2] = parseColor (value (index), appContext ());
|
||||
changed = true;
|
||||
}
|
||||
|
||||
index = attrIndex ("gradientRadius");
|
||||
drawable.setGradientRadius (index == -1 ? drawable.getGradientRadius () : Float.parseFloat (value (index)));
|
||||
|
||||
index = attrIndex ("startColor");
|
||||
if (index != -1) {
|
||||
colors[0] = parseColor (value (index), appContext ());
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
drawable.setColors (colors);
|
||||
}
|
||||
|
||||
index = attrIndex ("type");
|
||||
drawable.setGradientType (index == -1 ? GradientDrawable.LINEAR_GRADIENT : parseGradientType (value (index)));
|
||||
|
||||
index = attrIndex ("useLevel");
|
||||
drawable.setUseLevel (index != -1 && parseBoolean (value (index)));
|
||||
}
|
||||
|
||||
private int parseGradientType (String value) {
|
||||
switch (value) {
|
||||
case "radial" :
|
||||
return GradientDrawable.RADIAL_GRADIENT;
|
||||
case "sweep" :
|
||||
return GradientDrawable.SWEEP_GRADIENT;
|
||||
case "linear" :
|
||||
default:
|
||||
return GradientDrawable.LINEAR_GRADIENT;
|
||||
}
|
||||
}
|
||||
|
||||
private GradientDrawable.Orientation parseGradientOrientation (String value) {
|
||||
final var angle = Integer.parseInt (value);
|
||||
|
||||
// Angle must be between 0-315 and a multiple of 45
|
||||
// Angle moves in anti-clockwise direction
|
||||
if (angle == 45) {
|
||||
return GradientDrawable.Orientation.BL_TR;
|
||||
}
|
||||
|
||||
if (angle == 90) {
|
||||
return GradientDrawable.Orientation.BOTTOM_TOP;
|
||||
}
|
||||
|
||||
if (angle == 135) {
|
||||
return GradientDrawable.Orientation.BR_TL;
|
||||
}
|
||||
|
||||
if (angle == 180) {
|
||||
return GradientDrawable.Orientation.RIGHT_LEFT;
|
||||
}
|
||||
|
||||
if (angle == 225) {
|
||||
return GradientDrawable.Orientation.TR_BL;
|
||||
}
|
||||
|
||||
if (angle == 270) {
|
||||
return GradientDrawable.Orientation.TOP_BOTTOM;
|
||||
}
|
||||
|
||||
if (angle == 315) {
|
||||
return GradientDrawable.Orientation.TL_BR;
|
||||
}
|
||||
|
||||
// Angle 0 or any invalid valid should make the orientation left to right
|
||||
return GradientDrawable.Orientation.LEFT_RIGHT;
|
||||
}
|
||||
|
||||
private void parseCorners (@NonNull GradientDrawable drawable) {
|
||||
int index;
|
||||
index = attrIndex ("radius");
|
||||
drawable.setCornerRadius (index == -1 ? 0f : parseDimension (value (index), 0));
|
||||
|
||||
var changed = false;
|
||||
final var radii = new float[8];
|
||||
Arrays.fill (radii, 0f);
|
||||
|
||||
index = attrIndex ("topLeftRadius");
|
||||
if (index != -1) {
|
||||
radii[0] = radii[1] = parseDimension (value (index), 0);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
index = attrIndex ("topRightRadius");
|
||||
if (index != -1) {
|
||||
radii[2] = radii[3] = parseDimension (value (index), 0);
|
||||
changed = true;
|
||||
|
||||
}
|
||||
|
||||
index = attrIndex ("bottomLeftRadius");
|
||||
if (index != -1) {
|
||||
radii[4] = radii[5] = parseDimension (value (index), 0);
|
||||
changed = true;
|
||||
|
||||
}
|
||||
|
||||
index = attrIndex ("bottomRightRadius");
|
||||
if (index != -1) {
|
||||
radii[6] = radii[7] = parseDimension (value (index), 0);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
drawable.setCornerRadii (radii);
|
||||
}
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
private int parseShape (@NonNull String value) {
|
||||
switch (value) {
|
||||
case "oval" :
|
||||
return GradientDrawable.OVAL;
|
||||
case "line":
|
||||
return GradientDrawable.LINE;
|
||||
case "ring" :
|
||||
return GradientDrawable.RING;
|
||||
case "rectangle" :
|
||||
default:
|
||||
return GradientDrawable.RECTANGLE;
|
||||
}
|
||||
}
|
||||
|
||||
protected Context appContext () {
|
||||
return BaseApplication.getBaseInstance ();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.drawable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.itsaky.androidide.app.BaseApplication;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Parses a drawable whose root element is
|
||||
* {@code <shape>}. If the parse was successful, returns a {@link GradientDrawable},
|
||||
* otherwise {@code null}.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class ShapeDrawableParser extends IDrawableParser {
|
||||
|
||||
protected ShapeDrawableParser (XmlPullParser parser, IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
super (parser, resourceFinder, displayMetrics);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Drawable parse () throws Exception {
|
||||
|
||||
final var drawable = new GradientDrawable ();
|
||||
|
||||
var index = attrIndex ("shape");
|
||||
drawable.setShape (index == -1 ? GradientDrawable.RECTANGLE : parseShape (this.parser.getAttributeValue (index)));
|
||||
|
||||
if (isApi29 ()) {
|
||||
index = attrIndex ("innerRadius");
|
||||
drawable.setInnerRadius (index == -1 ? 0 : parseDimension (value (index), 0));
|
||||
|
||||
index = attrIndex ("innerRadiusRatio");
|
||||
drawable.setInnerRadiusRatio (index == -1 ? 1f : Float.parseFloat (value (index)));
|
||||
|
||||
index = attrIndex ("thickness");
|
||||
drawable.setThickness (index == -1 ? 0 : parseDimension (value (index), 0));
|
||||
|
||||
index = attrIndex ("thicknessRatio");
|
||||
drawable.setThicknessRatio (index == -1 ? 1f : Float.parseFloat (value (index)));
|
||||
}
|
||||
|
||||
index = attrIndex ("useLevel");
|
||||
drawable.setUseLevel (index != -1 && parseBoolean (value (index)));
|
||||
|
||||
var event = parser.getEventType ();
|
||||
while (event != XmlPullParser.END_DOCUMENT) {
|
||||
final var name = parser.getName ();
|
||||
if (event == XmlPullParser.START_TAG) {
|
||||
switch (name) {
|
||||
case "corners":
|
||||
parseCorners (drawable);
|
||||
break;
|
||||
case "gradient":
|
||||
parseGradient (drawable);
|
||||
break;
|
||||
case "padding" :
|
||||
parsePadding (drawable);
|
||||
break;
|
||||
case "size" :
|
||||
parseSize (drawable);
|
||||
break;
|
||||
case "solid" :
|
||||
parseSolid (drawable);
|
||||
break;
|
||||
case "stroke" :
|
||||
parseStroke (drawable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
event = parser.next ();
|
||||
}
|
||||
|
||||
return drawable;
|
||||
}
|
||||
|
||||
private void parseStroke (@NonNull final GradientDrawable drawable) {
|
||||
var index = attrIndex ("width");
|
||||
var strokeWidth = 0;
|
||||
if (index != - 1) {
|
||||
drawable.setStroke (strokeWidth = parseDimension (value (index), 0), Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
index = attrIndex ("color");
|
||||
var strokeColor = Color.TRANSPARENT;
|
||||
if (index != -1) {
|
||||
drawable.setStroke (strokeWidth, strokeColor = parseColor (value (index), appContext ()));
|
||||
}
|
||||
|
||||
index = attrIndex ("dashWidth");
|
||||
var dashWidth = 0;
|
||||
if (index != -1) {
|
||||
drawable.setStroke (strokeWidth, strokeColor, dashWidth = parseDimension (value (index), 0), 0);
|
||||
}
|
||||
|
||||
index = attrIndex ("dashGap");
|
||||
if (index != -1) {
|
||||
drawable.setStroke (strokeWidth, strokeColor, dashWidth, parseDimension (value (index), 0));
|
||||
}
|
||||
}
|
||||
|
||||
private void parseSolid (@NonNull final GradientDrawable drawable) {
|
||||
var index = attrIndex ("color");
|
||||
if (index != -1) {
|
||||
drawable.setColor (parseColor (value (index), appContext ()));
|
||||
}
|
||||
}
|
||||
|
||||
private void parseSize (@NonNull final GradientDrawable drawable) {
|
||||
var index = attrIndex ("width");
|
||||
if (index != -1) {
|
||||
drawable.setSize (parseDimension (value (index), 0), drawable.getIntrinsicHeight ());
|
||||
}
|
||||
|
||||
index = attrIndex ("height");
|
||||
if (index != -1) {
|
||||
drawable.setSize (drawable.getIntrinsicWidth (), parseDimension (value (index), 0));
|
||||
}
|
||||
}
|
||||
|
||||
private void parsePadding (@NonNull final GradientDrawable drawable) {
|
||||
|
||||
// Padding is available from API 29 only
|
||||
if (!isApi29 ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final var rect = new Rect ();
|
||||
if (!drawable.getPadding (rect)) {
|
||||
rect.set (0,0,0,0);
|
||||
}
|
||||
|
||||
var changed = false;
|
||||
var index = attrIndex ("left");
|
||||
if (index != -1) {
|
||||
rect.left = parseDimension (value (index), 0);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
index = attrIndex ("top");
|
||||
if (index != -1) {
|
||||
rect.top = parseDimension (value (index), 0);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
index = attrIndex ("right");
|
||||
if (index != -1) {
|
||||
rect.right = parseDimension (value (index), 0);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
index = attrIndex ("bottom");
|
||||
if (index != -1) {
|
||||
rect.bottom = parseDimension (value (index), 0);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
drawable.setPadding (rect.left, rect.top, rect.right, rect.bottom);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseGradient (@NonNull final GradientDrawable drawable) {
|
||||
var index = attrIndex ("angle");
|
||||
drawable.setOrientation (index == -1 ? GradientDrawable.Orientation.LEFT_RIGHT : parseGradientOrientation (value (index)));
|
||||
|
||||
index = attrIndex ("centerX");
|
||||
var center = index == - 1 ? drawable.getGradientCenterX () : Float.parseFloat (value (index));
|
||||
drawable.setGradientCenter (center, drawable.getGradientCenterY ());
|
||||
|
||||
index = attrIndex ("centerY");
|
||||
center = index == - 1 ? drawable.getGradientCenterX () : Float.parseFloat (value (index));
|
||||
drawable.setGradientCenter (drawable.getGradientCenterX (), center);
|
||||
|
||||
final var colors = new int [3];
|
||||
var changed = false;
|
||||
Arrays.fill (colors, -1);
|
||||
|
||||
index = attrIndex ("centerColor");
|
||||
if (index != -1) {
|
||||
colors [1] = parseColor (value (index), appContext ());
|
||||
changed = true;
|
||||
}
|
||||
|
||||
index = attrIndex ("endColor");
|
||||
if (index != -1) {
|
||||
colors [2] = parseColor (value (index), appContext ());
|
||||
changed = true;
|
||||
}
|
||||
|
||||
index = attrIndex ("gradientRadius");
|
||||
drawable.setGradientRadius (index == -1 ? drawable.getGradientRadius () : Float.parseFloat (value (index)));
|
||||
|
||||
index = attrIndex ("startColor");
|
||||
if (index != -1) {
|
||||
colors[0] = parseColor (value (index), appContext ());
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
drawable.setColors (colors);
|
||||
}
|
||||
|
||||
index = attrIndex ("type");
|
||||
drawable.setGradientType (index == -1 ? GradientDrawable.LINEAR_GRADIENT : parseGradientType (value (index)));
|
||||
|
||||
index = attrIndex ("useLevel");
|
||||
drawable.setUseLevel (index != -1 && parseBoolean (value (index)));
|
||||
}
|
||||
|
||||
private int parseGradientType (String value) {
|
||||
switch (value) {
|
||||
case "radial" :
|
||||
return GradientDrawable.RADIAL_GRADIENT;
|
||||
case "sweep" :
|
||||
return GradientDrawable.SWEEP_GRADIENT;
|
||||
case "linear" :
|
||||
default:
|
||||
return GradientDrawable.LINEAR_GRADIENT;
|
||||
}
|
||||
}
|
||||
|
||||
private GradientDrawable.Orientation parseGradientOrientation (String value) {
|
||||
final var angle = Integer.parseInt (value);
|
||||
|
||||
// Angle must be between 0-315 and a multiple of 45
|
||||
// Angle moves in anti-clockwise direction
|
||||
if (angle == 45) {
|
||||
return GradientDrawable.Orientation.BL_TR;
|
||||
}
|
||||
|
||||
if (angle == 90) {
|
||||
return GradientDrawable.Orientation.BOTTOM_TOP;
|
||||
}
|
||||
|
||||
if (angle == 135) {
|
||||
return GradientDrawable.Orientation.BR_TL;
|
||||
}
|
||||
|
||||
if (angle == 180) {
|
||||
return GradientDrawable.Orientation.RIGHT_LEFT;
|
||||
}
|
||||
|
||||
if (angle == 225) {
|
||||
return GradientDrawable.Orientation.TR_BL;
|
||||
}
|
||||
|
||||
if (angle == 270) {
|
||||
return GradientDrawable.Orientation.TOP_BOTTOM;
|
||||
}
|
||||
|
||||
if (angle == 315) {
|
||||
return GradientDrawable.Orientation.TL_BR;
|
||||
}
|
||||
|
||||
// Angle 0 or any invalid valid should make the orientation left to right
|
||||
return GradientDrawable.Orientation.LEFT_RIGHT;
|
||||
}
|
||||
|
||||
private void parseCorners (@NonNull GradientDrawable drawable) {
|
||||
int index;
|
||||
index = attrIndex ("radius");
|
||||
drawable.setCornerRadius (index == -1 ? 0f : parseDimension (value (index), 0));
|
||||
|
||||
var changed = false;
|
||||
final var radii = new float[8];
|
||||
Arrays.fill (radii, 0f);
|
||||
|
||||
index = attrIndex ("topLeftRadius");
|
||||
if (index != -1) {
|
||||
radii[0] = radii[1] = parseDimension (value (index), 0);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
index = attrIndex ("topRightRadius");
|
||||
if (index != -1) {
|
||||
radii[2] = radii[3] = parseDimension (value (index), 0);
|
||||
changed = true;
|
||||
|
||||
}
|
||||
|
||||
index = attrIndex ("bottomLeftRadius");
|
||||
if (index != -1) {
|
||||
radii[4] = radii[5] = parseDimension (value (index), 0);
|
||||
changed = true;
|
||||
|
||||
}
|
||||
|
||||
index = attrIndex ("bottomRightRadius");
|
||||
if (index != -1) {
|
||||
radii[6] = radii[7] = parseDimension (value (index), 0);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
drawable.setCornerRadii (radii);
|
||||
}
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
private int parseShape (@NonNull String value) {
|
||||
switch (value) {
|
||||
case "oval" :
|
||||
return GradientDrawable.OVAL;
|
||||
case "line":
|
||||
return GradientDrawable.LINE;
|
||||
case "ring" :
|
||||
return GradientDrawable.RING;
|
||||
case "rectangle" :
|
||||
default:
|
||||
return GradientDrawable.RECTANGLE;
|
||||
}
|
||||
}
|
||||
|
||||
protected Context appContext () {
|
||||
return BaseApplication.getBaseInstance ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,276 +1,276 @@
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.NinePatch;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.NinePatchDrawable;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.view.GravityCompat;
|
||||
|
||||
import com.blankj.utilcode.util.ImageUtils;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.drawable.DrawableParserFactory;
|
||||
import com.sdsmdg.harjot.vectormaster.VectorMasterDrawable;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Contains methods which are commonly used by layout inflater,
|
||||
* attribute adapters and drawable parsers.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class CommonParseUtils {
|
||||
|
||||
private static final Pattern HEX_COLOR = Pattern.compile("#[a-fA-F0-9]{6,8}");
|
||||
|
||||
protected IResourceFinder resourceFinder;
|
||||
protected final DisplayMetrics displayMetrics;
|
||||
|
||||
private static final Logger LOG = Logger.instance ("CommonParseUtils");
|
||||
|
||||
public CommonParseUtils (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
this.resourceFinder = resourceFinder;
|
||||
this.displayMetrics = displayMetrics;
|
||||
}
|
||||
|
||||
protected boolean parseBoolean(@NonNull String value) {
|
||||
if (!value.startsWith("@")) {
|
||||
if ("true".equals(value)) {
|
||||
return true;
|
||||
} else if ("false".equals(value)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// TODO Find resource value in booleans.xml
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected int parseDimension(final String value, int defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
char c = value.charAt(0);
|
||||
if (Character.isDigit(c)) {
|
||||
// A dimension value which starts with a digit. E.g.: 1dp, 12sp, 123px, etc.
|
||||
String dimensionVal = "";
|
||||
int index = 0;
|
||||
while (Character.isDigit(c = value.charAt(index))) {
|
||||
dimensionVal += c;
|
||||
index ++;
|
||||
}
|
||||
|
||||
final int dimen = Integer.parseInt(dimensionVal);
|
||||
final String dimensionType = value.substring(index);
|
||||
return (int) TypedValue.applyDimension(getUnitForDimensionType(dimensionType), dimen, displayMetrics);
|
||||
} else if (c == '@') {
|
||||
String name = value.substring("@dimen/".length());
|
||||
return parseDimension(resourceFinder.findDimension(name), defaultValue);
|
||||
} else if (Character.isLetter(c)) {
|
||||
// This could be one of the following :
|
||||
// 1. match_parent
|
||||
// 2. wrap_content
|
||||
// 3. fill_parent
|
||||
switch (value) {
|
||||
case "match_parent" :
|
||||
case "fill_parent" :
|
||||
return ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
case "wrap_content" :
|
||||
default :
|
||||
return ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
protected int getUnitForDimensionType(@NonNull String dimensionType) {
|
||||
switch (dimensionType) {
|
||||
case "dp" :
|
||||
return TypedValue.COMPLEX_UNIT_DIP;
|
||||
case "sp" :
|
||||
return TypedValue.COMPLEX_UNIT_SP;
|
||||
case "px" :
|
||||
return TypedValue.COMPLEX_UNIT_PX;
|
||||
case "pt" :
|
||||
return TypedValue.COMPLEX_UNIT_PT;
|
||||
case "in" :
|
||||
return TypedValue.COMPLEX_UNIT_IN;
|
||||
case "mm" :
|
||||
return TypedValue.COMPLEX_UNIT_MM;
|
||||
}
|
||||
return TypedValue.COMPLEX_UNIT_DIP;
|
||||
}
|
||||
|
||||
protected int parseColor (String color, final Context ctx) {
|
||||
if (HEX_COLOR.matcher(color).matches()) {
|
||||
try {
|
||||
return Color.parseColor(color);
|
||||
} catch (Throwable th) {
|
||||
// Ignored
|
||||
}
|
||||
} else if (color.startsWith("@color/")) {
|
||||
return parseColor(resourceFinder.findColor(color.substring("@color/".length())), ctx);
|
||||
} else if (color.startsWith("@android:color/")) {
|
||||
final int id = findAndroidResId("color", color.substring("@android:color/".length()));
|
||||
return ContextCompat.getColor(ctx, id);
|
||||
}
|
||||
|
||||
return Color.parseColor("#00ffffff");
|
||||
}
|
||||
|
||||
protected Drawable parseDrawable (String value, final Context ctx) {
|
||||
if (HEX_COLOR.matcher(value).matches()) {
|
||||
return drawableForColor(value);
|
||||
} else if (value.startsWith("@")) {
|
||||
|
||||
// First check if this is a reference to an android resource
|
||||
if (value.startsWith("@android:")) {
|
||||
final String typeAndValue = value.substring("@android:".length());
|
||||
final String[] split = typeAndValue.split(Pattern.quote("/")); // For @android:color/white, it will be ["color", "white"]
|
||||
final String type = split[0];
|
||||
final String typeVal = split[1];
|
||||
final int id = findAndroidResId (type, typeVal);
|
||||
|
||||
if (id != -1) {
|
||||
switch (type) {
|
||||
case "color" :
|
||||
return drawableForColor(ContextCompat.getColor(ctx, id));
|
||||
case "drawable" :
|
||||
return ContextCompat.getDrawable(ctx, id);
|
||||
default :
|
||||
return newTransparentDrawable();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// We found a reference to another resource
|
||||
if (value.startsWith("@drawable/")) {
|
||||
final File drawable = resourceFinder.findDrawable (value.substring("@drawable/".length()));
|
||||
|
||||
if (drawable == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
final var parser = DrawableParserFactory.newParser (ctx, drawable, resourceFinder);
|
||||
if (parser == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parser.parse();
|
||||
} catch (Exception e) {
|
||||
LOG.error ("Error parsing drawable", e);
|
||||
return null;
|
||||
}
|
||||
} else if (value.startsWith("@color/")) {
|
||||
final String color = resourceFinder.findColor(value.substring("@color/".length()));
|
||||
// TODO Check if this color resource is a selector
|
||||
return parseDrawable(color, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
return newTransparentDrawable();
|
||||
}
|
||||
|
||||
private int findAndroidResId(String type, String name) {
|
||||
try {
|
||||
final Class<?> typeClass = android.R.class.getClassLoader().loadClass("android.R$" + type);
|
||||
final Field typeField = typeClass.getDeclaredField(name);
|
||||
typeField.setAccessible(true);
|
||||
return (int) typeField.get(null);
|
||||
} catch (Throwable th) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
protected Drawable drawableForColor (String color) {
|
||||
try {
|
||||
return drawableForColor(Color.parseColor(color));
|
||||
} catch (Throwable th) {
|
||||
return newTransparentDrawable();
|
||||
}
|
||||
}
|
||||
|
||||
protected Drawable drawableForColor (int color) {
|
||||
return new ColorDrawable (color);
|
||||
}
|
||||
|
||||
protected Drawable newTransparentDrawable () {
|
||||
return new ColorDrawable (Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
protected int parseGravity (@NonNull String value) {
|
||||
final String[] splits = value.split(Pattern.quote("|"));
|
||||
int result = -1;
|
||||
for (String split : splits) {
|
||||
final int gravity = gravityFor(split);
|
||||
if (result == -1) {
|
||||
result = gravity;
|
||||
} else {
|
||||
result |= gravity;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected int gravityFor(@NonNull String gravity) {
|
||||
switch (gravity) {
|
||||
case "center" :
|
||||
return Gravity.CENTER;
|
||||
case "center_vertical" :
|
||||
return Gravity.CENTER_VERTICAL;
|
||||
case "center_horizontal" :
|
||||
return Gravity.CENTER_HORIZONTAL;
|
||||
case "left" :
|
||||
return Gravity.LEFT;
|
||||
case "right" :
|
||||
return Gravity.RIGHT;
|
||||
case "top" :
|
||||
return Gravity.TOP;
|
||||
case "bottom" :
|
||||
return Gravity.BOTTOM;
|
||||
case "start" :
|
||||
return GravityCompat.START;
|
||||
case "end" :
|
||||
return GravityCompat.END;
|
||||
default :
|
||||
return Gravity.TOP|Gravity.START;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of AndroidIDE.
|
||||
*
|
||||
* AndroidIDE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* AndroidIDE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.inflater.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.NinePatch;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.NinePatchDrawable;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.view.GravityCompat;
|
||||
|
||||
import com.blankj.utilcode.util.ImageUtils;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
import com.itsaky.inflater.IResourceFinder;
|
||||
import com.itsaky.inflater.drawable.DrawableParserFactory;
|
||||
import com.sdsmdg.harjot.vectormaster.VectorMasterDrawable;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Contains methods which are commonly used by layout inflater,
|
||||
* attribute adapters and drawable parsers.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class CommonParseUtils {
|
||||
|
||||
private static final Pattern HEX_COLOR = Pattern.compile("#[a-fA-F0-9]{6,8}");
|
||||
|
||||
protected IResourceFinder resourceFinder;
|
||||
protected final DisplayMetrics displayMetrics;
|
||||
|
||||
private static final Logger LOG = Logger.instance ("CommonParseUtils");
|
||||
|
||||
public CommonParseUtils (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
|
||||
this.resourceFinder = resourceFinder;
|
||||
this.displayMetrics = displayMetrics;
|
||||
}
|
||||
|
||||
protected boolean parseBoolean(@NonNull String value) {
|
||||
if (!value.startsWith("@")) {
|
||||
if ("true".equals(value)) {
|
||||
return true;
|
||||
} else if ("false".equals(value)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// TODO Find resource value in booleans.xml
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected int parseDimension(final String value, int defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
char c = value.charAt(0);
|
||||
if (Character.isDigit(c)) {
|
||||
// A dimension value which starts with a digit. E.g.: 1dp, 12sp, 123px, etc.
|
||||
String dimensionVal = "";
|
||||
int index = 0;
|
||||
while (Character.isDigit(c = value.charAt(index))) {
|
||||
dimensionVal += c;
|
||||
index ++;
|
||||
}
|
||||
|
||||
final int dimen = Integer.parseInt(dimensionVal);
|
||||
final String dimensionType = value.substring(index);
|
||||
return (int) TypedValue.applyDimension(getUnitForDimensionType(dimensionType), dimen, displayMetrics);
|
||||
} else if (c == '@') {
|
||||
String name = value.substring("@dimen/".length());
|
||||
return parseDimension(resourceFinder.findDimension(name), defaultValue);
|
||||
} else if (Character.isLetter(c)) {
|
||||
// This could be one of the following :
|
||||
// 1. match_parent
|
||||
// 2. wrap_content
|
||||
// 3. fill_parent
|
||||
switch (value) {
|
||||
case "match_parent" :
|
||||
case "fill_parent" :
|
||||
return ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
case "wrap_content" :
|
||||
default :
|
||||
return ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
protected int getUnitForDimensionType(@NonNull String dimensionType) {
|
||||
switch (dimensionType) {
|
||||
case "dp" :
|
||||
return TypedValue.COMPLEX_UNIT_DIP;
|
||||
case "sp" :
|
||||
return TypedValue.COMPLEX_UNIT_SP;
|
||||
case "px" :
|
||||
return TypedValue.COMPLEX_UNIT_PX;
|
||||
case "pt" :
|
||||
return TypedValue.COMPLEX_UNIT_PT;
|
||||
case "in" :
|
||||
return TypedValue.COMPLEX_UNIT_IN;
|
||||
case "mm" :
|
||||
return TypedValue.COMPLEX_UNIT_MM;
|
||||
}
|
||||
return TypedValue.COMPLEX_UNIT_DIP;
|
||||
}
|
||||
|
||||
protected int parseColor (String color, final Context ctx) {
|
||||
if (HEX_COLOR.matcher(color).matches()) {
|
||||
try {
|
||||
return Color.parseColor(color);
|
||||
} catch (Throwable th) {
|
||||
// Ignored
|
||||
}
|
||||
} else if (color.startsWith("@color/")) {
|
||||
return parseColor(resourceFinder.findColor(color.substring("@color/".length())), ctx);
|
||||
} else if (color.startsWith("@android:color/")) {
|
||||
final int id = findAndroidResId("color", color.substring("@android:color/".length()));
|
||||
return ContextCompat.getColor(ctx, id);
|
||||
}
|
||||
|
||||
return Color.parseColor("#00ffffff");
|
||||
}
|
||||
|
||||
protected Drawable parseDrawable (String value, final Context ctx) {
|
||||
if (HEX_COLOR.matcher(value).matches()) {
|
||||
return drawableForColor(value);
|
||||
} else if (value.startsWith("@")) {
|
||||
|
||||
// First check if this is a reference to an android resource
|
||||
if (value.startsWith("@android:")) {
|
||||
final String typeAndValue = value.substring("@android:".length());
|
||||
final String[] split = typeAndValue.split(Pattern.quote("/")); // For @android:color/white, it will be ["color", "white"]
|
||||
final String type = split[0];
|
||||
final String typeVal = split[1];
|
||||
final int id = findAndroidResId (type, typeVal);
|
||||
|
||||
if (id != -1) {
|
||||
switch (type) {
|
||||
case "color" :
|
||||
return drawableForColor(ContextCompat.getColor(ctx, id));
|
||||
case "drawable" :
|
||||
return ContextCompat.getDrawable(ctx, id);
|
||||
default :
|
||||
return newTransparentDrawable();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// We found a reference to another resource
|
||||
if (value.startsWith("@drawable/")) {
|
||||
final File drawable = resourceFinder.findDrawable (value.substring("@drawable/".length()));
|
||||
|
||||
if (drawable == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
final var parser = DrawableParserFactory.newParser (ctx, drawable, resourceFinder);
|
||||
if (parser == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parser.parse();
|
||||
} catch (Exception e) {
|
||||
LOG.error ("Error parsing drawable", e);
|
||||
return null;
|
||||
}
|
||||
} else if (value.startsWith("@color/")) {
|
||||
final String color = resourceFinder.findColor(value.substring("@color/".length()));
|
||||
// TODO Check if this color resource is a selector
|
||||
return parseDrawable(color, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
return newTransparentDrawable();
|
||||
}
|
||||
|
||||
private int findAndroidResId(String type, String name) {
|
||||
try {
|
||||
final Class<?> typeClass = android.R.class.getClassLoader().loadClass("android.R$" + type);
|
||||
final Field typeField = typeClass.getDeclaredField(name);
|
||||
typeField.setAccessible(true);
|
||||
return (int) typeField.get(null);
|
||||
} catch (Throwable th) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
protected Drawable drawableForColor (String color) {
|
||||
try {
|
||||
return drawableForColor(Color.parseColor(color));
|
||||
} catch (Throwable th) {
|
||||
return newTransparentDrawable();
|
||||
}
|
||||
}
|
||||
|
||||
protected Drawable drawableForColor (int color) {
|
||||
return new ColorDrawable (color);
|
||||
}
|
||||
|
||||
protected Drawable newTransparentDrawable () {
|
||||
return new ColorDrawable (Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
protected int parseGravity (@NonNull String value) {
|
||||
final String[] splits = value.split(Pattern.quote("|"));
|
||||
int result = -1;
|
||||
for (String split : splits) {
|
||||
final int gravity = gravityFor(split);
|
||||
if (result == -1) {
|
||||
result = gravity;
|
||||
} else {
|
||||
result |= gravity;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected int gravityFor(@NonNull String gravity) {
|
||||
switch (gravity) {
|
||||
case "center" :
|
||||
return Gravity.CENTER;
|
||||
case "center_vertical" :
|
||||
return Gravity.CENTER_VERTICAL;
|
||||
case "center_horizontal" :
|
||||
return Gravity.CENTER_HORIZONTAL;
|
||||
case "left" :
|
||||
return Gravity.LEFT;
|
||||
case "right" :
|
||||
return Gravity.RIGHT;
|
||||
case "top" :
|
||||
return Gravity.TOP;
|
||||
case "bottom" :
|
||||
return Gravity.BOTTOM;
|
||||
case "start" :
|
||||
return GravityCompat.START;
|
||||
case "end" :
|
||||
return GravityCompat.END;
|
||||
default :
|
||||
return Gravity.TOP|Gravity.START;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<declare-styleable name="VectorMasterView">
|
||||
<attr name="vector_src" format="reference" />
|
||||
<attr name="use_legacy_parser" format="boolean" />
|
||||
</declare-styleable>
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of AndroidIDE.
|
||||
~
|
||||
~ AndroidIDE is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ AndroidIDE is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<declare-styleable name="VectorMasterView">
|
||||
<attr name="vector_src" format="reference" />
|
||||
<attr name="use_legacy_parser" format="boolean" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user