mirror of
https://github.com/Dark98/SliceBeam.git
synced 2026-07-03 16:49:03 +00:00
Compare commits
46 Commits
0.2.0
...
Development
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f4ae6e38f | |||
| 7e723fce4e | |||
| 2b8c182516 | |||
| 213e723276 | |||
| bb07740d43 | |||
| d37c099060 | |||
| 197e46eabc | |||
| 3b67c4cdac | |||
| 254ab77439 | |||
| 1e02fbf071 | |||
| aab6387f26 | |||
| d692d3e061 | |||
| 5f2f0829dd | |||
| a5e0d8a9d8 | |||
| 20010bfbd5 | |||
| f352a02b9f | |||
| 261ba81e06 | |||
| 6dbb3a8f1d | |||
| 76996225df | |||
| 12b370ce30 | |||
| dd7a6ddf1d | |||
| 9b32fe68d7 | |||
| 33487afdc9 | |||
| bc1007aa59 | |||
| 4c4469b1fd | |||
| e4320be0ce | |||
| 0933adf1b9 | |||
| 70f4d08a8c | |||
| a27a8c1d5d | |||
| abf53f1c43 | |||
| 5f13961d05 | |||
| 264e742d3a | |||
| 462f0a4c55 | |||
| 6bb8926fb7 | |||
| e1fe683154 | |||
| 8529d94899 | |||
| df09f8ef39 | |||
| 8b200e689c | |||
| 838cedbf1a | |||
| e52a1ad949 | |||
| 2c1d45153b | |||
| 6ede5931e8 | |||
| 1976ac4899 | |||
| 58b24d5137 | |||
| 662018834e | |||
| 6092f123b5 |
@@ -0,0 +1,93 @@
|
|||||||
|
<svg width="270" height="80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#a)">
|
||||||
|
<mask id="b" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
|
||||||
|
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#b)">
|
||||||
|
<path d="M260 80H10A10 10 0 0 1 0 70V10A10 10 0 0 1 10 0h250a10 10 0 0 1 10 10v60a10 10 0 0 1-10 10Z" fill="#000"/>
|
||||||
|
</g>
|
||||||
|
<mask id="c" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
|
||||||
|
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#c)">
|
||||||
|
<path d="M94.8 20.5c0 1.7-.5 3-1.5 4a5.8 5.8 0 0 1-4.4 1.8 6 6 0 0 1-4.4-1.8 6 6 0 0 1-1.8-4.5 6 6 0 0 1 1.8-4.5 6 6 0 0 1 4.4-1.8 7 7 0 0 1 2.5.5 5 5 0 0 1 1.9 1.4l-1 1a4 4 0 0 0-3.4-1.4 4.6 4.6 0 0 0-4.7 4.8c0 1.4.5 2.6 1.5 3.5.9.9 2 1.3 3.2 1.3a5 5 0 0 0 3.4-1.3c.6-.6.9-1.4 1-2.5h-4.4v-1.4h5.9v.9Z" fill="#fff" stroke="#fff" stroke-width=".3" stroke-miterlimit="10"/>
|
||||||
|
</g>
|
||||||
|
<mask id="d" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
|
||||||
|
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#d)">
|
||||||
|
<path d="M104 15.5h-5.4v3.8h5v1.4h-5v3.8h5.5V26h-7V14h7v1.5Z" fill="#fff" stroke="#fff" stroke-width=".3" stroke-miterlimit="10"/>
|
||||||
|
</g>
|
||||||
|
<mask id="e" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
|
||||||
|
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#e)">
|
||||||
|
<path d="M110.6 26H109V15.5h-3.3V14h8.2v1.5h-3.3V26Z" fill="#fff" stroke="#fff" stroke-width=".3" stroke-miterlimit="10"/>
|
||||||
|
</g>
|
||||||
|
<mask id="f" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
|
||||||
|
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#f)">
|
||||||
|
<path d="M119.9 14h1.5v12H120V14Z" fill="#fff" stroke="#fff" stroke-width=".3" stroke-miterlimit="10"/>
|
||||||
|
</g>
|
||||||
|
<mask id="g" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
|
||||||
|
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#g)">
|
||||||
|
<path d="M128.3 26h-1.6V15.5h-3.3V14h8.2v1.5h-3.3V26Z" fill="#fff" stroke="#fff" stroke-width=".3" stroke-miterlimit="10"/>
|
||||||
|
</g>
|
||||||
|
<mask id="h" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
|
||||||
|
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#h)">
|
||||||
|
<path d="M139.6 23.4a4 4 0 0 0 3.2 1.4 4.7 4.7 0 0 0 4.6-4.8c0-1.4-.4-2.5-1.3-3.4-1-1-2-1.4-3.3-1.4-1.3 0-2.4.5-3.2 1.4-1 .9-1.4 2-1.4 3.4s.5 2.5 1.4 3.4Zm7.6 1a5.9 5.9 0 0 1-4.4 1.9c-1.7 0-3.2-.6-4.4-1.9a6.1 6.1 0 0 1-1.7-4.4 6.1 6.1 0 0 1 6.1-6.3c1.8 0 3.2.6 4.4 1.9A6.1 6.1 0 0 1 149 20c0 1.8-.6 3.2-1.8 4.4Z" fill="#fff"/>
|
||||||
|
</g>
|
||||||
|
<mask id="i" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
|
||||||
|
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#i)">
|
||||||
|
<path d="M139.6 23.4a4 4 0 0 0 3.2 1.4 4.7 4.7 0 0 0 4.6-4.8c0-1.4-.4-2.5-1.3-3.4-1-1-2-1.4-3.3-1.4-1.3 0-2.4.5-3.2 1.4-1 .9-1.4 2-1.4 3.4s.5 2.5 1.4 3.4Zm7.6 1a5.9 5.9 0 0 1-4.4 1.9c-1.7 0-3.2-.6-4.4-1.9a6.1 6.1 0 0 1-1.7-4.4 6.1 6.1 0 0 1 6.1-6.3c1.8 0 3.2.6 4.4 1.9A6.1 6.1 0 0 1 149 20c0 1.8-.6 3.2-1.8 4.4Z" stroke="#fff" stroke-width=".3" stroke-miterlimit="10"/>
|
||||||
|
</g>
|
||||||
|
<mask id="j" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
|
||||||
|
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#j)">
|
||||||
|
<path d="M151.2 26V14h1.8l5.9 9.3V14h1.5v12h-1.6l-6.1-9.8V26h-1.5Z" fill="#fff" stroke="#fff" stroke-width=".3" stroke-miterlimit="10"/>
|
||||||
|
</g>
|
||||||
|
<mask id="k" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
|
||||||
|
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#k)">
|
||||||
|
<path d="M213.9 60h3.7V35h-3.7v25Zm33.6-16-4.3 10.8h-.1L238.6 44h-4l6.7 15.2-3.8 8.4h3.9L251.6 44h-4.1Zm-21.2 13.2c-1.2 0-3-.6-3-2.2 0-1.9 2.2-2.6 4-2.6 1.7 0 2.5.3 3.5.8-.3 2.3-2.3 4-4.5 4Zm.5-13.7c-2.7 0-5.5 1.1-6.7 3.8l3.3 1.4c.7-1.4 2-1.9 3.4-1.9 2 0 4 1.2 4 3.3v.2c-.7-.4-2.2-1-4-1-3.5 0-7.1 2-7.1 5.7 0 3.3 2.9 5.5 6.2 5.5 2.5 0 3.9-1.2 4.7-2.5h.2v2h3.6v-9.6c0-4.5-3.3-7-7.6-7Zm-23 3.5h-5.4v-8.5h5.3c2.8 0 4.4 2.3 4.4 4.3 0 1.9-1.6 4.2-4.4 4.2Zm-.2-12h-9v25h3.8v-9.5h5.2c4.1 0 8.2-3 8.2-7.7 0-4.8-4-7.8-8.2-7.8Zm-48.8 22.2c-2.5 0-4.7-2.2-4.7-5.2s2.2-5.1 4.8-5.1c2.5 0 4.5 2.1 4.5 5.1 0 3-2 5.2-4.6 5.2Zm4.3-11.8c-1-1-2.5-1.9-4.6-1.9a8.5 8.5 0 0 0-8.1 8.5c0 4.8 3.9 8.5 8.1 8.5 2 0 3.7-.9 4.5-2h.1v1.3c0 3.3-1.7 5-4.5 5a4.7 4.7 0 0 1-4.3-3l-3.2 1.3a8 8 0 0 0 7.5 5c4.4 0 8-2.5 8-8.8V44h-3.5v1.4Zm6.2 14.6h3.7V35h-3.7v25Zm9.2-8.2c0-3.3 2.6-5 4.4-5 1.5 0 2.8.7 3.2 1.8l-7.6 3.2Zm11.6-2.9c-.7-1.9-2.9-5.4-7.3-5.4s-8 3.5-8 8.5c0 4.8 3.6 8.5 8.4 8.5 4 0 6.2-2.4 7.1-3.8l-2.9-1.9a4.9 4.9 0 0 1-4.2 2.4c-1.9 0-3.2-1-4-2.6l11.3-4.7-.4-1Zm-90.6-2.8v3.6h8.6c-.2 2-1 3.5-2 4.6a8.8 8.8 0 0 1-6.6 2.6 9.5 9.5 0 0 1-9.5-9.6 9.5 9.5 0 0 1 16-7l2.5-2.6A13.4 13.4 0 0 0 82 47.3c0 7.3 6.1 13.2 13.4 13.2 4 0 6.9-1.3 9.2-3.7a12 12 0 0 0 3.1-8.4c0-.9 0-1.6-.2-2.3h-12Zm22.1 11c-2.5 0-4.8-2-4.8-5 0-3.2 2.3-5.2 4.8-5.2 2.6 0 4.8 2 4.8 5.1 0 3-2.2 5.2-4.8 5.2Zm0-13.6a8.4 8.4 0 0 0-8.5 8.5c0 5 3.8 8.5 8.5 8.5 4.8 0 8.6-3.6 8.6-8.5 0-5-3.9-8.5-8.6-8.5Zm18.7 13.7c-2.6 0-4.8-2.2-4.8-5.2s2.2-5.1 4.8-5.1c2.5 0 4.8 2 4.8 5.1 0 3-2.3 5.2-4.8 5.2Zm0-13.7a8.4 8.4 0 0 0-8.6 8.5c0 5 3.9 8.5 8.6 8.5 4.7 0 8.5-3.6 8.5-8.5 0-5-3.8-8.5-8.5-8.5Z" fill="#fff"/>
|
||||||
|
</g>
|
||||||
|
<mask id="l" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
|
||||||
|
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#l)">
|
||||||
|
<path d="M41.4 38.8 20.1 61.4a5.7 5.7 0 0 0 8.5 3.5l24-13.8-11.2-12.3Z" fill="#EA4335"/>
|
||||||
|
</g>
|
||||||
|
<mask id="m" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
|
||||||
|
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#m)">
|
||||||
|
<path d="m63 35-10.4-6L41 39.4 52.7 51l10.2-6a5.8 5.8 0 0 0 .1-10Z" fill="#FBBC04"/>
|
||||||
|
</g>
|
||||||
|
<mask id="n" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
|
||||||
|
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#n)">
|
||||||
|
<path d="M20.1 18.6 20 20v40l.2 1.4 22-22-22-20.8Z" fill="#4285F4"/>
|
||||||
|
</g>
|
||||||
|
<mask id="o" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="-21" y="-20" width="311" height="121">
|
||||||
|
<path d="M-20-20h310v120H-20V-20Z" fill="#fff"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#o)">
|
||||||
|
<path d="m41.6 40 11-11-24-13.9a5.8 5.8 0 0 0-8.5 3.4L41.6 40Z" fill="#34A853"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="a">
|
||||||
|
<path fill="#fff" transform="matrix(1 0 0 -1 0 80)" d="M0 0h270v80H0z"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 6.9 KiB |
@@ -0,0 +1,50 @@
|
|||||||
|
name: Android CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: set up JDK 21
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
java-version: '21'
|
||||||
|
distribution: 'temurin'
|
||||||
|
cache: gradle
|
||||||
|
|
||||||
|
- name: Install NDK 23.1.7779620
|
||||||
|
run: |
|
||||||
|
echo "y" | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --install "ndk;23.1.7779620"
|
||||||
|
|
||||||
|
- name: Cache Build Deps
|
||||||
|
id: native-dirs-cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
app/src/main/jniImports/boost/
|
||||||
|
app/src/main/jniImports/oneTBB/
|
||||||
|
app/src/main/occt/
|
||||||
|
key: ${{ runner.os }}-native-dirs-${{ hashFiles('**/*.gradle*', '**/gradle.properties', '**/gradle-wrapper.properties', 'gradle/libs.versions.toml', 'settings.gradle*') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-native-dirs-
|
||||||
|
|
||||||
|
- name: Native cache status
|
||||||
|
run: echo "Native cache hit? ${{ steps.native-dirs-cache.outputs.cache-hit }}"
|
||||||
|
|
||||||
|
- name: Grant execute permission for gradlew
|
||||||
|
run: chmod +x gradlew
|
||||||
|
|
||||||
|
- name: Build with Gradle
|
||||||
|
run: ./gradlew build
|
||||||
|
|
||||||
|
- name: Upload Debug Build Artifact
|
||||||
|
uses: actions/upload-artifact@v6.0.0
|
||||||
|
with:
|
||||||
|
name: Build-${{ github.run_id }}
|
||||||
|
path: app/build/outputs/apk/debug/*.apk
|
||||||
@@ -30,6 +30,9 @@ app/src/main/jni/occt_wrapper/occtwrapper_export.h
|
|||||||
app/release/
|
app/release/
|
||||||
|
|
||||||
.idea/
|
.idea/
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.settings
|
||||||
|
|
||||||
app/src/main/jniImports/boost/
|
app/src/main/jniImports/boost/
|
||||||
app/src/main/jniImports/oneTBB/
|
app/src/main/jniImports/oneTBB/
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
[submodule "EventBus"]
|
[submodule "EventBus"]
|
||||||
path = EventBus
|
path = EventBus
|
||||||
url = https://github.com/utkabobr/EventBus
|
url = https://github.com/utkabobr/EventBus
|
||||||
|
[submodule "SAPIL"]
|
||||||
|
path = SAPIL
|
||||||
|
url = https://github.com/utkabobr/SAPIL
|
||||||
|
|||||||
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"java.configuration.updateBuildConfiguration": "automatic",
|
||||||
|
"java.jdt.ls.java.home": "C:\\Program Files\\Android\\Android Studio\\jbr",
|
||||||
|
"java.configuration.runtimes": [
|
||||||
|
{
|
||||||
|
"name": "JavaSE-21",
|
||||||
|
"path": "C:\\Program Files\\Android\\Android Studio\\jbr",
|
||||||
|
"default": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"terminal.integrated.env.windows": {
|
||||||
|
"JAVA_HOME": "C:\\Program Files\\Android\\Android Studio\\jbr",
|
||||||
|
"PATH": "C:\\Program Files\\Android\\Android Studio\\jbr\\bin;${env:PATH}"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,9 @@ It is based on PrusaSlicer's core and well optimized for Android touchscreen int
|
|||||||
- K3D Chat for discussion & support (Russian language only): https://t.me/K_3_D
|
- K3D Chat for discussion & support (Russian language only): https://t.me/K_3_D
|
||||||
|
|
||||||
# Quick Start
|
# Quick Start
|
||||||
Just download APK from [Releases tab](https://github.com/utkabobr/SliceBeam/releases/latest) and follow setup instructions. Google Play builds will be available later.
|
[<img src="/.github/img/getongp.svg">](https://play.google.com/store/apps/details?id=ru.ytkab0bp.slicebeam)
|
||||||
|
|
||||||
|
Or download APK from [Releases tab](https://github.com/utkabobr/SliceBeam/releases/latest) and follow setup instructions.
|
||||||
|
|
||||||
# Where to get printer profiles?
|
# Where to get printer profiles?
|
||||||
|
|
||||||
@@ -66,4 +68,4 @@ Copy .so libs to app/src/main/occt/jniLibs; Include files to app/src/main/occt/i
|
|||||||
You can also reduce size of libraries by removing unnecessary modules (Check app/src/main/occt/jniLibs/clean.py)
|
You can also reduce size of libraries by removing unnecessary modules (Check app/src/main/occt/jniLibs/clean.py)
|
||||||
|
|
||||||
## Why not to include all of them in repo?
|
## Why not to include all of them in repo?
|
||||||
They're HUGE (More than 2 Gb) binary files, so you must build them yourself.
|
They're HUGE (More than 2 Gb) binary files, so you must build them yourself.
|
||||||
|
|||||||
Submodule
+1
Submodule SAPIL added at d0c6422d79
+18
-11
@@ -8,7 +8,7 @@ include(CheckLibraryExists)
|
|||||||
include(GenerateExportHeader)
|
include(GenerateExportHeader)
|
||||||
include(CheckCSourceCompiles)
|
include(CheckCSourceCompiles)
|
||||||
|
|
||||||
project(SliceBeam)
|
project(Santoku)
|
||||||
cmake_minimum_required(VERSION 3.4.1)
|
cmake_minimum_required(VERSION 3.4.1)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
@@ -17,12 +17,13 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
set(IS_CLANG_CL TRUE)
|
set(IS_CLANG_CL TRUE)
|
||||||
set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-imsvc")
|
set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-imsvc")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-old-style-cast -Wno-reserved-id-macro -Wno-c++98-compat-pedantic -funwind-tables -fopenmp -static-openmp -Wl,--no-merge-exidx-entries")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-old-style-cast -Wno-reserved-id-macro -Wno-c++98-compat-pedantic -funwind-tables -fopenmp -static-openmp -Wl,--no-merge-exidx-entries")
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,common-page-size=16384 -Wl,-z,max-page-size=16384")
|
||||||
add_compile_options(-fsigned-char)
|
add_compile_options(-fsigned-char)
|
||||||
|
|
||||||
# Suppress all warnings
|
# Suppress all warnings
|
||||||
add_definitions(-w)
|
add_definitions(-w)
|
||||||
add_definitions(-DNDEBUG)
|
add_definitions(-DNDEBUG)
|
||||||
add_definitions(-DSLIC3R_VERSION=${SLIC3R_VERSION})
|
add_definitions(-DSLIC3R_VERSION="${SLIC3R_VERSION}")
|
||||||
add_definitions(-DSLIC3R_BUILD_ID=${SLIC3R_BUILD_ID})
|
add_definitions(-DSLIC3R_BUILD_ID=${SLIC3R_BUILD_ID})
|
||||||
|
|
||||||
set(jni_imports ${CMAKE_SOURCE_DIR}/src/main/jniImports)
|
set(jni_imports ${CMAKE_SOURCE_DIR}/src/main/jniImports)
|
||||||
@@ -40,10 +41,10 @@ elseif (${ANDROID_ABI} STREQUAL "x86")
|
|||||||
set(BOOST_ARCH "x32")
|
set(BOOST_ARCH "x32")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(BOOST_LIBS atomic charconv chrono container context contract coroutine date_time exception fiber
|
set(BOOST_LIBS atomic charconv chrono container contract date_time exception
|
||||||
filesystem graph iostreams json log log_setup math_c99 math_c99f math_c99l math_tr1 math_tr1f
|
filesystem graph iostreams json log log_setup math_c99 math_c99f math_c99l math_tr1 math_tr1f
|
||||||
math_tr1l nowide prg_exec_monitor program_options random regex serialization stacktrace_basic
|
math_tr1l nowide prg_exec_monitor program_options random regex serialization stacktrace_basic
|
||||||
stacktrace_noop system test_exec_moinotr thread timer type_erasure unit_test_framework url wave
|
stacktrace_noop system test_exec_monitor thread timer type_erasure unit_test_framework url wave
|
||||||
wserialization)
|
wserialization)
|
||||||
|
|
||||||
foreach (NAME IN LISTS BOOST_LIBS)
|
foreach (NAME IN LISTS BOOST_LIBS)
|
||||||
@@ -782,6 +783,8 @@ add_library(slic3r
|
|||||||
src/main/jni/libslic3r/Fill/FillBase.hpp
|
src/main/jni/libslic3r/Fill/FillBase.hpp
|
||||||
src/main/jni/libslic3r/Fill/FillConcentric.cpp
|
src/main/jni/libslic3r/Fill/FillConcentric.cpp
|
||||||
src/main/jni/libslic3r/Fill/FillConcentric.hpp
|
src/main/jni/libslic3r/Fill/FillConcentric.hpp
|
||||||
|
src/main/jni/libslic3r/Fill/FillCrossHatch.cpp
|
||||||
|
src/main/jni/libslic3r/Fill/FillCrossHatch.hpp
|
||||||
src/main/jni/libslic3r/Fill/FillEnsuring.cpp
|
src/main/jni/libslic3r/Fill/FillEnsuring.cpp
|
||||||
src/main/jni/libslic3r/Fill/FillEnsuring.hpp
|
src/main/jni/libslic3r/Fill/FillEnsuring.hpp
|
||||||
src/main/jni/libslic3r/Fill/FillHoneycomb.cpp
|
src/main/jni/libslic3r/Fill/FillHoneycomb.cpp
|
||||||
@@ -1241,9 +1244,9 @@ add_library(slic3r
|
|||||||
|
|
||||||
src/main/jni/bbl/Orient.cpp
|
src/main/jni/bbl/Orient.cpp
|
||||||
|
|
||||||
src/main/jni/slicebeam/beam_native.cpp
|
src/main/jni/santoku/beam_native.cpp
|
||||||
src/main/jni/slicebeam/GLModel.cpp
|
src/main/jni/santoku/GLModel.cpp
|
||||||
src/main/jni/slicebeam/GLShader.cpp
|
src/main/jni/santoku/GLShader.cpp
|
||||||
|
|
||||||
# $<TARGET_OBJECTS:simd>
|
# $<TARGET_OBJECTS:simd>
|
||||||
)
|
)
|
||||||
@@ -1320,12 +1323,9 @@ target_link_libraries(slic3r PRIVATE
|
|||||||
boost_charconv
|
boost_charconv
|
||||||
boost_chrono
|
boost_chrono
|
||||||
boost_container
|
boost_container
|
||||||
boost_context
|
|
||||||
boost_contract
|
boost_contract
|
||||||
boost_coroutine
|
|
||||||
boost_date_time
|
boost_date_time
|
||||||
boost_exception
|
boost_exception
|
||||||
boost_fiber
|
|
||||||
boost_filesystem
|
boost_filesystem
|
||||||
boost_graph
|
boost_graph
|
||||||
boost_iostreams
|
boost_iostreams
|
||||||
@@ -1352,4 +1352,11 @@ target_link_libraries(slic3r PRIVATE
|
|||||||
boost_wave
|
boost_wave
|
||||||
boost_wserialization
|
boost_wserialization
|
||||||
${OCCT_LIBS}
|
${OCCT_LIBS}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(ANDROID)
|
||||||
|
target_link_options(slic3r PRIVATE
|
||||||
|
"-Wl,-z,common-page-size=16384"
|
||||||
|
"-Wl,-z,max-page-size=16384"
|
||||||
|
"-Wl,--rosegment")
|
||||||
|
endif()
|
||||||
|
|||||||
+634
-21
@@ -1,28 +1,35 @@
|
|||||||
|
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||||
|
import org.gradle.jvm.toolchain.JavaToolchainService
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
}
|
}
|
||||||
|
|
||||||
def commit = getGitCommitHash(file('.'))
|
def commit = getGitCommitHash(file('.'))
|
||||||
|
def prodCloudBaseUrl = "https://santoku.dark98.co.uk/v1/"
|
||||||
|
def prodBeamBaseUrl = "https://santoku.dark98.co.uk"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace 'ru.ytkab0bp.slicebeam'
|
namespace 'com.dark98.santoku'
|
||||||
compileSdk 34
|
compileSdk 35
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "ru.ytkab0bp.slicebeam"
|
applicationId "com.dark98.santoku"
|
||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 34
|
targetSdk 35
|
||||||
versionCode 6
|
versionCode 1
|
||||||
versionName "0.2.0"
|
versionName "0.0.1"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
buildConfigField "String", "CLOUD_BASE_URL_PROD", "\"" + prodCloudBaseUrl + "\""
|
||||||
|
buildConfigField "String", "BEAM_BASE_URL_PROD", "\"" + prodBeamBaseUrl + "\""
|
||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
arguments '-DANDROID_STL=c++_shared', '-DANDROID_PLATFORM=21',
|
arguments '-DANDROID_STL=c++_shared', '-DANDROID_PLATFORM=21',
|
||||||
'-DCMAKE_BUILD_TYPE=Release', // Disabling this results in drastically degradation of slicing times on debug builds
|
'-DCMAKE_BUILD_TYPE=Release', // Disabling this results in drastically degradation of slicing times on debug builds
|
||||||
"-DSLIC3R_VERSION=\"${defaultConfig.versionName}\"",
|
"-DSLIC3R_VERSION=${defaultConfig.versionName}",
|
||||||
"-DSLIC3R_BUILD_ID=\"${defaultConfig.versionCode}\""
|
"-DSLIC3R_BUILD_ID=${defaultConfig.versionCode}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,7 +43,6 @@ android {
|
|||||||
release {
|
release {
|
||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
buildConfigField "boolean", "IS_GOOGLE_PLAY", "false"
|
|
||||||
buildConfigField "String", "COMMIT", "\"" + commit + "\""
|
buildConfigField "String", "COMMIT", "\"" + commit + "\""
|
||||||
ndk {
|
ndk {
|
||||||
//noinspection ChromeOsAbiSupport
|
//noinspection ChromeOsAbiSupport
|
||||||
@@ -44,7 +50,8 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug {
|
debug {
|
||||||
buildConfigField "boolean", "IS_GOOGLE_PLAY", "false"
|
applicationIdSuffix ".debug"
|
||||||
|
resValue "string", "AppName", "Santoku (Debug)"
|
||||||
buildConfigField "String", "COMMIT", "\"" + commit + "\""
|
buildConfigField "String", "COMMIT", "\"" + commit + "\""
|
||||||
ndk {
|
ndk {
|
||||||
debugSymbolLevel 'NONE'
|
debugSymbolLevel 'NONE'
|
||||||
@@ -54,15 +61,15 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_21
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_21
|
||||||
}
|
}
|
||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
path file('CMakeLists.txt')
|
path file('CMakeLists.txt')
|
||||||
}
|
}
|
||||||
ndkVersion "23.1.7779620"
|
ndkVersion "29.0.14206865"
|
||||||
}
|
}
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
@@ -71,18 +78,78 @@ android {
|
|||||||
|
|
||||||
applicationVariants.all { variant ->
|
applicationVariants.all { variant ->
|
||||||
variant.outputs.all {
|
variant.outputs.all {
|
||||||
outputFileName = "SliceBeam_" + commit + ".apk"
|
def isDebug = variant.buildType.name == "debug"
|
||||||
|
def suffix = isDebug ? "_debug" : ""
|
||||||
|
outputFileName = "Santoku_" + commit + suffix + ".apk"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def javaToolchainService = extensions.getByType(JavaToolchainService)
|
||||||
|
tasks.withType(JavaCompile).configureEach {
|
||||||
|
javaCompiler = javaToolchainService.compilerFor {
|
||||||
|
languageVersion = JavaLanguageVersion.of(21)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static String getGitCommitHash(File dir) {
|
static String getGitCommitHash(File dir) {
|
||||||
try {
|
try {
|
||||||
return Runtime.getRuntime().exec("git rev-parse HEAD", null, dir).inputStream.readLines().get(0).substring(0, 10)
|
File gitDir = new File(dir, ".git")
|
||||||
} catch (Exception e){
|
if (!gitDir.exists()) {
|
||||||
println("Unable to get git commit hash:")
|
return "non-git"
|
||||||
e.printStackTrace()
|
}
|
||||||
return "non-git build"
|
|
||||||
|
// Worktree/submodule support: .git may be a file with "gitdir: <path>".
|
||||||
|
if (gitDir.isFile()) {
|
||||||
|
String pointer = gitDir.text.trim()
|
||||||
|
if (pointer.startsWith("gitdir:")) {
|
||||||
|
gitDir = new File(dir, pointer.substring("gitdir:".length()).trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gitDir.exists() || !gitDir.isDirectory()) {
|
||||||
|
return "non-git"
|
||||||
|
}
|
||||||
|
|
||||||
|
File headFile = new File(gitDir, "HEAD")
|
||||||
|
if (!headFile.exists()) {
|
||||||
|
return "non-git"
|
||||||
|
}
|
||||||
|
|
||||||
|
String head = headFile.text.trim()
|
||||||
|
String fullHash = null
|
||||||
|
if (head.startsWith("ref:")) {
|
||||||
|
String refPath = head.substring("ref:".length()).trim()
|
||||||
|
File refFile = new File(gitDir, refPath)
|
||||||
|
if (refFile.exists()) {
|
||||||
|
fullHash = refFile.text.trim()
|
||||||
|
} else {
|
||||||
|
File packedRefs = new File(gitDir, "packed-refs")
|
||||||
|
if (packedRefs.exists()) {
|
||||||
|
String prefix = refPath + " "
|
||||||
|
for (String line : packedRefs.readLines()) {
|
||||||
|
if (line.startsWith("#") || line.startsWith("^") || line.trim().isEmpty()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
int space = line.indexOf(' ')
|
||||||
|
if (space > 0 && line.substring(space + 1).trim() == refPath) {
|
||||||
|
fullHash = line.substring(0, space).trim()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Detached HEAD.
|
||||||
|
fullHash = head
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fullHash || fullHash.length() < 10) {
|
||||||
|
return "non-git"
|
||||||
|
}
|
||||||
|
return fullHash.substring(0, 10)
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return "non-git"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,11 +157,557 @@ dependencies {
|
|||||||
implementation project(":eventbus")
|
implementation project(":eventbus")
|
||||||
implementation project(":eventbus_api")
|
implementation project(":eventbus_api")
|
||||||
annotationProcessor project(":eventbus_processor")
|
annotationProcessor project(":eventbus_processor")
|
||||||
|
implementation project(":sapil")
|
||||||
|
|
||||||
|
implementation 'com.google.code.gson:gson:2.11.0'
|
||||||
|
implementation 'com.github.instacart:truetime-android:4.0.0.alpha'
|
||||||
implementation 'com.github.mrudultora:Colorpicker:1.2.0'
|
implementation 'com.github.mrudultora:Colorpicker:1.2.0'
|
||||||
implementation 'com.github.bumptech.glide:glide:4.16.0'
|
implementation 'com.github.bumptech.glide:glide:4.16.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||||
implementation 'com.google.android.material:material:1.12.0'
|
implementation 'com.google.android.material:material:1.12.0'
|
||||||
implementation 'com.loopj.android:android-async-http:1.4.11'
|
implementation 'com.loopj.android:android-async-http:1.4.11'
|
||||||
implementation 'androidx.activity:activity:1.9.1'
|
implementation 'androidx.activity:activity:1.10.1'
|
||||||
}
|
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
def loadLocalProperties() {
|
||||||
|
def props = new Properties()
|
||||||
|
def propsFile = rootProject.file("local.properties")
|
||||||
|
if (propsFile.exists()) {
|
||||||
|
propsFile.withInputStream { props.load(it) }
|
||||||
|
}
|
||||||
|
return props
|
||||||
|
}
|
||||||
|
|
||||||
|
def toWslPath(String winPath) {
|
||||||
|
return winPath.replaceAll('^([A-Za-z]):') { m -> "/mnt/${m[1].toLowerCase()}" }.replace('\\', '/')
|
||||||
|
}
|
||||||
|
|
||||||
|
def localProps = loadLocalProperties()
|
||||||
|
def sdkDir = localProps.getProperty("sdk.dir") ?: System.getenv("ANDROID_SDK_ROOT") ?: System.getenv("ANDROID_HOME")
|
||||||
|
if (!sdkDir) {
|
||||||
|
throw new GradleException("Missing sdk.dir in local.properties and ANDROID_SDK_ROOT/ANDROID_HOME is not set")
|
||||||
|
}
|
||||||
|
def ndkDir = "${sdkDir}/ndk/${android.ndkVersion}"
|
||||||
|
if (!file(ndkDir).exists()) {
|
||||||
|
throw new GradleException("NDK not found at ${ndkDir} (ndkVersion=${android.ndkVersion})")
|
||||||
|
}
|
||||||
|
def cmakeRoot = file("${sdkDir}/cmake")
|
||||||
|
def cmakeDir = cmakeRoot.listFiles()?.findAll { it.isDirectory() }?.sort { a, b -> b.name <=> a.name }?.first()
|
||||||
|
if (!cmakeDir) {
|
||||||
|
throw new GradleException("No CMake found under ${sdkDir}/cmake")
|
||||||
|
}
|
||||||
|
def osName = System.getProperty("os.name").toLowerCase()
|
||||||
|
def isWindows = osName.contains("windows")
|
||||||
|
def isMac = osName.contains("mac")
|
||||||
|
def cmakeExe = "${cmakeDir}/bin/cmake" + (isWindows ? ".exe" : "")
|
||||||
|
def ninjaExe = "${cmakeDir}/bin/ninja" + (isWindows ? ".exe" : "")
|
||||||
|
def toolchainFile = "${ndkDir}/build/cmake/android.toolchain.cmake"
|
||||||
|
def wslExe = "${System.getenv("SystemRoot") ?: "C:/Windows"}/System32/wsl.exe"
|
||||||
|
def prebuiltTag = isWindows ? "windows-x86_64" : (isMac ? "darwin-x86_64" : "linux-x86_64")
|
||||||
|
def llvmBinDir = "${ndkDir}/toolchains/llvm/prebuilt/${prebuiltTag}/bin"
|
||||||
|
def pageSizeLinkerFlags = "-Wl,-z,common-page-size=16384 -Wl,-z,max-page-size=16384"
|
||||||
|
def forceNativeRebuild = project.hasProperty("forceNativeRebuild") && project.property("forceNativeRebuild").toString().toLowerCase() == "true"
|
||||||
|
|
||||||
|
def tbbSrc = "${rootDir}/third_party/openvdb-android/tbb-aarch64"
|
||||||
|
def tbbBuildArm64 = "${rootDir}/third_party/openvdb-android/build-tbb-android-21-arm64"
|
||||||
|
def tbbInstallArm64 = "${rootDir}/third_party/openvdb-android/dist-android-21-arm64"
|
||||||
|
def tbbBuildArmv7 = "${rootDir}/third_party/openvdb-android/build-tbb-android-21-armv7"
|
||||||
|
def tbbInstallArmv7 = "${rootDir}/third_party/openvdb-android/dist-android-21-armv7"
|
||||||
|
|
||||||
|
def occtSrc = "${rootDir}/third_party/occt"
|
||||||
|
def occtBuildArm64 = "${rootDir}/third_party/occt/build-android-arm64-v8a"
|
||||||
|
def occtDistArm64 = "${rootDir}/third_party/occt/dist/android-arm64-v8a"
|
||||||
|
def occtBuildArmv7 = "${rootDir}/third_party/occt/build-android-armeabi-v7a"
|
||||||
|
def occtDistArmv7 = "${rootDir}/third_party/occt/dist/android-armeabi-v7a"
|
||||||
|
|
||||||
|
def boostDir = "${rootDir}/third_party/Boost-for-Android"
|
||||||
|
def boostOutArm64 = "${boostDir}/build/out/arm64-v8a"
|
||||||
|
def boostOutArmv7 = "${boostDir}/build/out/armeabi-v7a"
|
||||||
|
def rootPathForTasks = rootProject.projectDir.absolutePath
|
||||||
|
def appProjectPath = project.projectDir.absolutePath
|
||||||
|
def boostArm64OutLib = "${appProjectPath}/src/main/jniImports/boost/lib/arm64-v8a/lib/libboost_atomic-clang-mt-a64-1_85.a"
|
||||||
|
def boostArmv7OutLib = "${appProjectPath}/src/main/jniImports/boost/lib/armeabi-v7a/lib/libboost_atomic-clang-mt-a32-1_85.a"
|
||||||
|
def boostHeadersOut = "${appProjectPath}/src/main/jniImports/boost/include/boost/variant.hpp"
|
||||||
|
def tbbArm64LibA = "${appProjectPath}/src/main/jniImports/oneTBB/lib/arm64-v8a/libtbb.a"
|
||||||
|
def tbbArm64MallocA = "${appProjectPath}/src/main/jniImports/oneTBB/lib/arm64-v8a/libtbbmalloc.a"
|
||||||
|
def tbbArm64So = "${appProjectPath}/src/main/jniLibs/arm64-v8a/libtbb.so"
|
||||||
|
def tbbArm64MallocSo = "${appProjectPath}/src/main/jniLibs/arm64-v8a/libtbbmalloc.so"
|
||||||
|
def tbbArmv7LibA = "${appProjectPath}/src/main/jniImports/oneTBB/lib/armeabi-v7a/libtbb.a"
|
||||||
|
def tbbArmv7MallocA = "${appProjectPath}/src/main/jniImports/oneTBB/lib/armeabi-v7a/libtbbmalloc.a"
|
||||||
|
def tbbArmv7So = "${appProjectPath}/src/main/jniLibs/armeabi-v7a/libtbb.so"
|
||||||
|
def tbbArmv7MallocSo = "${appProjectPath}/src/main/jniLibs/armeabi-v7a/libtbbmalloc.so"
|
||||||
|
def tbbHeaderOut = "${appProjectPath}/src/main/jniImports/oneTBB/include/tbb/tbb.h"
|
||||||
|
def occtArm64So = "${appProjectPath}/src/main/occt/jniLibs/arm64-v8a/libTKDESTEP.so"
|
||||||
|
def occtArmv7So = "${appProjectPath}/src/main/occt/jniLibs/armeabi-v7a/libTKDESTEP.so"
|
||||||
|
def occtHeaderArm64Out = "${appProjectPath}/src/main/occt/include/arm64-v8a/STEPCAFControl_Reader.hxx"
|
||||||
|
def occtHeaderArmv7Out = "${appProjectPath}/src/main/occt/include/armeabi-v7a/STEPCAFControl_Reader.hxx"
|
||||||
|
|
||||||
|
tasks.register("ensureThirdParty") {
|
||||||
|
doLast {
|
||||||
|
def rootPath = rootPathForTasks
|
||||||
|
def thirdPartyDir = new File(rootPath, "third_party")
|
||||||
|
if (!thirdPartyDir.exists()) {
|
||||||
|
thirdPartyDir.mkdirs()
|
||||||
|
}
|
||||||
|
def repos = [
|
||||||
|
[path: "${rootPath}/third_party/Boost-for-Android", url: "https://github.com/moritz-wundke/Boost-for-Android.git"],
|
||||||
|
[path: "${rootPath}/third_party/openvdb-android", url: "https://github.com/syoyo/openvdb-android.git"],
|
||||||
|
[path: "${rootPath}/third_party/occt", url: "https://github.com/Open-Cascade-SAS/OCCT.git"]
|
||||||
|
]
|
||||||
|
def runCommand = { List<String> cmd, File workDir ->
|
||||||
|
Process process = new ProcessBuilder(cmd)
|
||||||
|
.directory(workDir)
|
||||||
|
.inheritIO()
|
||||||
|
.start()
|
||||||
|
int exitCode = process.waitFor()
|
||||||
|
if (exitCode != 0) {
|
||||||
|
throw new GradleException("Command failed (${exitCode}): ${cmd.join(' ')}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (def repo : repos) {
|
||||||
|
if (!new File(repo.path).exists()) {
|
||||||
|
runCommand(["git", "clone", "--depth", "1", repo.url, repo.path], new File(rootPath))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def vdbRoot = new File(rootPath, "third_party/openvdb-android")
|
||||||
|
if (new File(vdbRoot, ".gitmodules").exists() && !new File(vdbRoot, "tbb-aarch64/CMakeLists.txt").exists()) {
|
||||||
|
runCommand(["git", "submodule", "update", "--init", "--recursive"], vdbRoot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("patchBoostForAndroid") {
|
||||||
|
dependsOn("ensureThirdParty")
|
||||||
|
doLast {
|
||||||
|
def boostRoot = new File(rootPathForTasks, "third_party/Boost-for-Android")
|
||||||
|
def scriptFile = new File(boostRoot, "build-android.sh")
|
||||||
|
if (scriptFile.exists()) {
|
||||||
|
def scriptText = scriptFile.getText("UTF-8")
|
||||||
|
def changed = false
|
||||||
|
|
||||||
|
if (!scriptText.contains("FORCE_PLATFORM_OS")) {
|
||||||
|
def marker = "# Check platform patch"
|
||||||
|
def markerIdx = scriptText.indexOf(marker)
|
||||||
|
if (markerIdx >= 0) {
|
||||||
|
def esacIdx = scriptText.indexOf("esac", markerIdx)
|
||||||
|
if (esacIdx >= 0) {
|
||||||
|
def insertPos = scriptText.indexOf("\n", esacIdx)
|
||||||
|
if (insertPos >= 0) {
|
||||||
|
insertPos += 1
|
||||||
|
def insert = 'if [ -n "${FORCE_PLATFORM_OS}" ]; then\n' +
|
||||||
|
' PlatformOS="${FORCE_PLATFORM_OS}"\n' +
|
||||||
|
'fi\n\n'
|
||||||
|
scriptText = scriptText.substring(0, insertPos) + insert + scriptText.substring(insertPos)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!scriptText.contains("CXXPATH.exe")) {
|
||||||
|
def marker = 'if [ -n "${AndroidSourcesDetected}"'
|
||||||
|
if (scriptText.contains(marker)) {
|
||||||
|
def insert = 'if [ ! -f "$CXXPATH" ] && [ -f "$CXXPATH.exe" ]; then\n' +
|
||||||
|
' CXXPATH="$CXXPATH.exe"\n' +
|
||||||
|
'fi\n\n'
|
||||||
|
scriptText = scriptText.replace(marker, insert + marker)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
scriptFile.write(scriptText, "UTF-8")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def commonFile = new File(boostRoot, "configs/user-config-ndk23-1_85_0-common.jam")
|
||||||
|
if (commonFile.exists()) {
|
||||||
|
def commonText = commonFile.getText("UTF-8")
|
||||||
|
def commonChanged = false
|
||||||
|
if (!commonText.contains("AndroidToolSuffix")) {
|
||||||
|
def header = 'import os ;\n' +
|
||||||
|
'local AndroidToolSuffix = [ os.environ AndroidToolSuffix ] ;\n' +
|
||||||
|
'if ! $(AndroidToolSuffix) { AndroidToolSuffix = "" ; }\n\n'
|
||||||
|
commonText = header + commonText.trim() + "\n"
|
||||||
|
commonChanged = true
|
||||||
|
}
|
||||||
|
if (!commonText.contains('llvm-ar$(AndroidToolSuffix)')) {
|
||||||
|
commonText = commonText.replace('<archiver>$(AndroidBinariesPath)/llvm-ar', '<archiver>$(AndroidBinariesPath)/llvm-ar$(AndroidToolSuffix)')
|
||||||
|
commonChanged = true
|
||||||
|
}
|
||||||
|
if (!commonText.contains('llvm-ranlib$(AndroidToolSuffix)')) {
|
||||||
|
commonText = commonText.replace('<ranlib>$(AndroidBinariesPath)/llvm-ranlib', '<ranlib>$(AndroidBinariesPath)/llvm-ranlib$(AndroidToolSuffix)')
|
||||||
|
commonChanged = true
|
||||||
|
}
|
||||||
|
if (commonChanged) {
|
||||||
|
commonFile.write(commonText, "UTF-8")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def arm64File = new File(boostRoot, "configs/user-config-ndk23-1_85_0-arm64-v8a.jam")
|
||||||
|
if (arm64File.exists()) {
|
||||||
|
def text = arm64File.getText("UTF-8")
|
||||||
|
if (!text.contains("<arch>")) {
|
||||||
|
text = "<arch>arm\n<address-model>64\n<binary-format>elf\n<abi>aapcs\n" + text
|
||||||
|
arm64File.write(text, "UTF-8")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def armv7File = new File(boostRoot, "configs/user-config-ndk23-1_85_0-armeabi-v7a.jam")
|
||||||
|
if (armv7File.exists()) {
|
||||||
|
def text = armv7File.getText("UTF-8")
|
||||||
|
if (!text.contains("<arch>")) {
|
||||||
|
text = "<arch>arm\n<address-model>32\n<binary-format>elf\n<abi>aapcs\n" + text
|
||||||
|
armv7File.write(text, "UTF-8")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("buildTbbArm64") {
|
||||||
|
dependsOn("ensureThirdParty")
|
||||||
|
onlyIf {
|
||||||
|
forceNativeRebuild ||
|
||||||
|
!new File(tbbArm64LibA).exists() ||
|
||||||
|
!new File(tbbArm64MallocA).exists() ||
|
||||||
|
!new File(tbbArm64So).exists() ||
|
||||||
|
!new File(tbbArm64MallocSo).exists()
|
||||||
|
}
|
||||||
|
doLast {
|
||||||
|
exec {
|
||||||
|
commandLine cmakeExe, "-G", "Ninja",
|
||||||
|
"-S", tbbSrc,
|
||||||
|
"-B", tbbBuildArm64,
|
||||||
|
"-DCMAKE_MAKE_PROGRAM=${ninjaExe}",
|
||||||
|
"-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
|
||||||
|
"-DCMAKE_TOOLCHAIN_FILE=${toolchainFile}",
|
||||||
|
"-DANDROID_ABI=arm64-v8a",
|
||||||
|
"-DANDROID_PLATFORM=android-21",
|
||||||
|
"-DANDROID_STL=c++_shared",
|
||||||
|
"-DCMAKE_BUILD_TYPE=Release",
|
||||||
|
"-DCMAKE_SHARED_LINKER_FLAGS=${pageSizeLinkerFlags}",
|
||||||
|
"-DCMAKE_INSTALL_PREFIX=${tbbInstallArm64}",
|
||||||
|
"-DTBB_BUILD_TESTS=Off"
|
||||||
|
}
|
||||||
|
exec {
|
||||||
|
commandLine cmakeExe, "--build", tbbBuildArm64, "--target", "install"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("buildTbbArmv7") {
|
||||||
|
dependsOn("ensureThirdParty")
|
||||||
|
onlyIf {
|
||||||
|
forceNativeRebuild ||
|
||||||
|
!new File(tbbArmv7LibA).exists() ||
|
||||||
|
!new File(tbbArmv7MallocA).exists() ||
|
||||||
|
!new File(tbbArmv7So).exists() ||
|
||||||
|
!new File(tbbArmv7MallocSo).exists()
|
||||||
|
}
|
||||||
|
doLast {
|
||||||
|
exec {
|
||||||
|
commandLine cmakeExe, "-G", "Ninja",
|
||||||
|
"-S", tbbSrc,
|
||||||
|
"-B", tbbBuildArmv7,
|
||||||
|
"-DCMAKE_MAKE_PROGRAM=${ninjaExe}",
|
||||||
|
"-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
|
||||||
|
"-DCMAKE_TOOLCHAIN_FILE=${toolchainFile}",
|
||||||
|
"-DANDROID_ABI=armeabi-v7a",
|
||||||
|
"-DANDROID_PLATFORM=android-21",
|
||||||
|
"-DANDROID_STL=c++_shared",
|
||||||
|
"-DCMAKE_BUILD_TYPE=Release",
|
||||||
|
"-DCMAKE_SHARED_LINKER_FLAGS=${pageSizeLinkerFlags}",
|
||||||
|
"-DCMAKE_INSTALL_PREFIX=${tbbInstallArmv7}",
|
||||||
|
"-DTBB_BUILD_TESTS=Off"
|
||||||
|
}
|
||||||
|
exec {
|
||||||
|
commandLine cmakeExe, "--build", tbbBuildArmv7, "--target", "install"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("copyTbbArm64", Copy) {
|
||||||
|
dependsOn("buildTbbArm64")
|
||||||
|
onlyIf {
|
||||||
|
forceNativeRebuild ||
|
||||||
|
!new File(tbbArm64LibA).exists() ||
|
||||||
|
!new File(tbbArm64MallocA).exists() ||
|
||||||
|
!new File(tbbArm64So).exists() ||
|
||||||
|
!new File(tbbArm64MallocSo).exists()
|
||||||
|
}
|
||||||
|
from("${tbbInstallArm64}/lib") {
|
||||||
|
include "libtbb_static.a"
|
||||||
|
include "libtbbmalloc_static.a"
|
||||||
|
include "libtbb.so"
|
||||||
|
include "libtbbmalloc.so"
|
||||||
|
}
|
||||||
|
into("${projectDir}/src/main/jniImports/oneTBB/tmp-arm64")
|
||||||
|
doLast {
|
||||||
|
copy {
|
||||||
|
from "${projectDir}/src/main/jniImports/oneTBB/tmp-arm64/libtbb_static.a"
|
||||||
|
into "${projectDir}/src/main/jniImports/oneTBB/lib/arm64-v8a"
|
||||||
|
rename { "libtbb.a" }
|
||||||
|
}
|
||||||
|
copy {
|
||||||
|
from "${projectDir}/src/main/jniImports/oneTBB/tmp-arm64/libtbbmalloc_static.a"
|
||||||
|
into "${projectDir}/src/main/jniImports/oneTBB/lib/arm64-v8a"
|
||||||
|
rename { "libtbbmalloc.a" }
|
||||||
|
}
|
||||||
|
copy {
|
||||||
|
from "${projectDir}/src/main/jniImports/oneTBB/tmp-arm64/libtbb.so"
|
||||||
|
into "${projectDir}/src/main/jniLibs/arm64-v8a"
|
||||||
|
}
|
||||||
|
copy {
|
||||||
|
from "${projectDir}/src/main/jniImports/oneTBB/tmp-arm64/libtbbmalloc.so"
|
||||||
|
into "${projectDir}/src/main/jniLibs/arm64-v8a"
|
||||||
|
}
|
||||||
|
delete("${projectDir}/src/main/jniImports/oneTBB/tmp-arm64")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("copyTbbArmv7", Copy) {
|
||||||
|
dependsOn("buildTbbArmv7")
|
||||||
|
onlyIf {
|
||||||
|
forceNativeRebuild ||
|
||||||
|
!new File(tbbArmv7LibA).exists() ||
|
||||||
|
!new File(tbbArmv7MallocA).exists() ||
|
||||||
|
!new File(tbbArmv7So).exists() ||
|
||||||
|
!new File(tbbArmv7MallocSo).exists()
|
||||||
|
}
|
||||||
|
from("${tbbInstallArmv7}/lib") {
|
||||||
|
include "libtbb_static.a"
|
||||||
|
include "libtbbmalloc_static.a"
|
||||||
|
include "libtbb.so"
|
||||||
|
include "libtbbmalloc.so"
|
||||||
|
}
|
||||||
|
into("${projectDir}/src/main/jniImports/oneTBB/tmp-armv7")
|
||||||
|
doLast {
|
||||||
|
copy {
|
||||||
|
from "${projectDir}/src/main/jniImports/oneTBB/tmp-armv7/libtbb_static.a"
|
||||||
|
into "${projectDir}/src/main/jniImports/oneTBB/lib/armeabi-v7a"
|
||||||
|
rename { "libtbb.a" }
|
||||||
|
}
|
||||||
|
copy {
|
||||||
|
from "${projectDir}/src/main/jniImports/oneTBB/tmp-armv7/libtbbmalloc_static.a"
|
||||||
|
into "${projectDir}/src/main/jniImports/oneTBB/lib/armeabi-v7a"
|
||||||
|
rename { "libtbbmalloc.a" }
|
||||||
|
}
|
||||||
|
copy {
|
||||||
|
from "${projectDir}/src/main/jniImports/oneTBB/tmp-armv7/libtbb.so"
|
||||||
|
into "${projectDir}/src/main/jniLibs/armeabi-v7a"
|
||||||
|
}
|
||||||
|
copy {
|
||||||
|
from "${projectDir}/src/main/jniImports/oneTBB/tmp-armv7/libtbbmalloc.so"
|
||||||
|
into "${projectDir}/src/main/jniLibs/armeabi-v7a"
|
||||||
|
}
|
||||||
|
delete("${projectDir}/src/main/jniImports/oneTBB/tmp-armv7")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("copyTbbHeaders") {
|
||||||
|
dependsOn("buildTbbArm64")
|
||||||
|
onlyIf { !new File(tbbHeaderOut).exists() }
|
||||||
|
doLast {
|
||||||
|
copy {
|
||||||
|
from "${tbbInstallArm64}/include/tbb"
|
||||||
|
into "${projectDir}/src/main/jniImports/oneTBB/include/tbb"
|
||||||
|
}
|
||||||
|
copy {
|
||||||
|
from "${projectDir}/src/main/jniImports/oneTBB/include/tbb"
|
||||||
|
into "${projectDir}/src/main/jniImports/oneTBB/include/oneapi/tbb"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("buildBoostArm64") {
|
||||||
|
dependsOn("patchBoostForAndroid")
|
||||||
|
onlyIf { !new File(boostArm64OutLib).exists() }
|
||||||
|
doLast {
|
||||||
|
if (isWindows) {
|
||||||
|
if (!new File(wslExe).exists()) {
|
||||||
|
throw new GradleException("WSL is required to build Boost on Windows. Install WSL or provide prebuilt Boost libs.")
|
||||||
|
}
|
||||||
|
def ndkWsl = toWslPath(ndkDir)
|
||||||
|
def boostWsl = toWslPath(boostDir)
|
||||||
|
def binWsl = toWslPath(llvmBinDir)
|
||||||
|
def cmd = "set -euo pipefail; cd ${boostWsl}; chmod +x ${binWsl}/aarch64-linux-android21-clang++ ${binWsl}/llvm-ar.exe ${binWsl}/llvm-ranlib.exe || true; FORCE_PLATFORM_OS=windows AndroidCompilerSuffix= AndroidToolSuffix=.exe ./build-android.sh ${ndkWsl} --boost=1.85.0 --arch=arm64-v8a --target-version=21 --without-libraries=context,coroutine,fiber,python"
|
||||||
|
Process p = new ProcessBuilder([wslExe, "bash", "-lc", cmd]).inheritIO().start()
|
||||||
|
if (p.waitFor() != 0) {
|
||||||
|
throw new GradleException("Boost arm64 build failed")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
def cmd = "set -euo pipefail; cd ${boostDir}; chmod +x ${llvmBinDir}/aarch64-linux-android21-clang++ ${llvmBinDir}/llvm-ar ${llvmBinDir}/llvm-ranlib || true; ./build-android.sh ${ndkDir} --boost=1.85.0 --arch=arm64-v8a --target-version=21 --without-libraries=context,coroutine,fiber,python"
|
||||||
|
Process p = new ProcessBuilder(["bash", "-lc", cmd]).inheritIO().start()
|
||||||
|
if (p.waitFor() != 0) {
|
||||||
|
throw new GradleException("Boost arm64 build failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("buildBoostArmv7") {
|
||||||
|
dependsOn("patchBoostForAndroid")
|
||||||
|
onlyIf { !new File(boostArmv7OutLib).exists() }
|
||||||
|
doLast {
|
||||||
|
if (isWindows) {
|
||||||
|
if (!new File(wslExe).exists()) {
|
||||||
|
throw new GradleException("WSL is required to build Boost on Windows. Install WSL or provide prebuilt Boost libs.")
|
||||||
|
}
|
||||||
|
def ndkWsl = toWslPath(ndkDir)
|
||||||
|
def boostWsl = toWslPath(boostDir)
|
||||||
|
def binWsl = toWslPath(llvmBinDir)
|
||||||
|
def cmd = "set -euo pipefail; cd ${boostWsl}; chmod +x ${binWsl}/armv7a-linux-androideabi21-clang++ ${binWsl}/llvm-ar.exe ${binWsl}/llvm-ranlib.exe || true; FORCE_PLATFORM_OS=windows AndroidCompilerSuffix= AndroidToolSuffix=.exe ./build-android.sh ${ndkWsl} --boost=1.85.0 --arch=armeabi-v7a --target-version=21 --without-libraries=context,coroutine,fiber,python"
|
||||||
|
Process p = new ProcessBuilder([wslExe, "bash", "-lc", cmd]).inheritIO().start()
|
||||||
|
if (p.waitFor() != 0) {
|
||||||
|
throw new GradleException("Boost armv7 build failed")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
def cmd = "set -euo pipefail; cd ${boostDir}; chmod +x ${llvmBinDir}/armv7a-linux-androideabi21-clang++ ${llvmBinDir}/llvm-ar ${llvmBinDir}/llvm-ranlib || true; ./build-android.sh ${ndkDir} --boost=1.85.0 --arch=armeabi-v7a --target-version=21 --without-libraries=context,coroutine,fiber,python"
|
||||||
|
Process p = new ProcessBuilder(["bash", "-lc", cmd]).inheritIO().start()
|
||||||
|
if (p.waitFor() != 0) {
|
||||||
|
throw new GradleException("Boost armv7 build failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("copyBoostArm64", Copy) {
|
||||||
|
dependsOn("buildBoostArm64")
|
||||||
|
onlyIf { !new File(boostArm64OutLib).exists() }
|
||||||
|
from("${boostOutArm64}/lib") {
|
||||||
|
include "libboost_*.a"
|
||||||
|
}
|
||||||
|
into("${projectDir}/src/main/jniImports/boost/lib/arm64-v8a/lib")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("copyBoostArmv7", Copy) {
|
||||||
|
dependsOn("buildBoostArmv7")
|
||||||
|
onlyIf { !new File(boostArmv7OutLib).exists() }
|
||||||
|
from("${boostOutArmv7}/lib") {
|
||||||
|
include "libboost_*.a"
|
||||||
|
}
|
||||||
|
into("${projectDir}/src/main/jniImports/boost/lib/armeabi-v7a/lib")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("copyBoostHeaders", Copy) {
|
||||||
|
dependsOn("ensureThirdParty")
|
||||||
|
onlyIf { !new File(boostHeadersOut).exists() }
|
||||||
|
from("${boostDir}/boost_1_85_0/boost")
|
||||||
|
into("${projectDir}/src/main/jniImports/boost/include/boost")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("buildOcctArm64") {
|
||||||
|
dependsOn("ensureThirdParty")
|
||||||
|
onlyIf { forceNativeRebuild || !new File(occtArm64So).exists() }
|
||||||
|
doLast {
|
||||||
|
List<String> cfgCmd = [
|
||||||
|
cmakeExe, "-G", "Ninja",
|
||||||
|
"-S", occtSrc,
|
||||||
|
"-B", occtBuildArm64,
|
||||||
|
"-DCMAKE_MAKE_PROGRAM=${ninjaExe}",
|
||||||
|
"-DCMAKE_TOOLCHAIN_FILE=${toolchainFile}",
|
||||||
|
"-DANDROID_ABI=arm64-v8a",
|
||||||
|
"-DANDROID_PLATFORM=android-21",
|
||||||
|
"-DCMAKE_BUILD_TYPE=Release",
|
||||||
|
"-DBUILD_LIBRARY_TYPE=Shared",
|
||||||
|
"-DCMAKE_SHARED_LINKER_FLAGS=${pageSizeLinkerFlags}",
|
||||||
|
"-DINSTALL_DIR_LIB=libs/arm64-v8a",
|
||||||
|
"-DINSTALL_DIR_INCLUDE=inc",
|
||||||
|
"-DBUILD_DOC_Overview=OFF",
|
||||||
|
"-DBUILD_DOC_RefMan=OFF",
|
||||||
|
"-DUSE_FREETYPE=OFF",
|
||||||
|
"-DUSE_RAPIDJSON=OFF",
|
||||||
|
"-DUSE_DRACO=OFF",
|
||||||
|
"-DCMAKE_INSTALL_PREFIX=${occtDistArm64}"
|
||||||
|
]
|
||||||
|
Process p1 = new ProcessBuilder(cfgCmd).inheritIO().start()
|
||||||
|
if (p1.waitFor() != 0) throw new GradleException("OCCT arm64 configure failed")
|
||||||
|
Process p2 = new ProcessBuilder([cmakeExe, "--build", occtBuildArm64, "--target", "install", "--config", "Release", "--", "-j2"]).inheritIO().start()
|
||||||
|
if (p2.waitFor() != 0) throw new GradleException("OCCT arm64 build failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("buildOcctArmv7") {
|
||||||
|
dependsOn("ensureThirdParty")
|
||||||
|
onlyIf { forceNativeRebuild || !new File(occtArmv7So).exists() }
|
||||||
|
doLast {
|
||||||
|
List<String> cfgCmd = [
|
||||||
|
cmakeExe, "-G", "Ninja",
|
||||||
|
"-S", occtSrc,
|
||||||
|
"-B", occtBuildArmv7,
|
||||||
|
"-DCMAKE_MAKE_PROGRAM=${ninjaExe}",
|
||||||
|
"-DCMAKE_TOOLCHAIN_FILE=${toolchainFile}",
|
||||||
|
"-DANDROID_ABI=armeabi-v7a",
|
||||||
|
"-DANDROID_PLATFORM=android-21",
|
||||||
|
"-DCMAKE_BUILD_TYPE=Release",
|
||||||
|
"-DBUILD_LIBRARY_TYPE=Shared",
|
||||||
|
"-DCMAKE_SHARED_LINKER_FLAGS=${pageSizeLinkerFlags}",
|
||||||
|
"-DINSTALL_DIR_LIB=libs/armeabi-v7a",
|
||||||
|
"-DINSTALL_DIR_INCLUDE=inc",
|
||||||
|
"-DBUILD_DOC_Overview=OFF",
|
||||||
|
"-DBUILD_DOC_RefMan=OFF",
|
||||||
|
"-DUSE_FREETYPE=OFF",
|
||||||
|
"-DUSE_RAPIDJSON=OFF",
|
||||||
|
"-DUSE_DRACO=OFF",
|
||||||
|
"-DCMAKE_INSTALL_PREFIX=${occtDistArmv7}"
|
||||||
|
]
|
||||||
|
Process p1 = new ProcessBuilder(cfgCmd).inheritIO().start()
|
||||||
|
if (p1.waitFor() != 0) throw new GradleException("OCCT armv7 configure failed")
|
||||||
|
Process p2 = new ProcessBuilder([cmakeExe, "--build", occtBuildArmv7, "--target", "install", "--config", "Release", "--", "-j2"]).inheritIO().start()
|
||||||
|
if (p2.waitFor() != 0) throw new GradleException("OCCT armv7 build failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("copyOcctArm64", Copy) {
|
||||||
|
dependsOn("buildOcctArm64")
|
||||||
|
onlyIf { forceNativeRebuild || !new File(occtArm64So).exists() }
|
||||||
|
from("${occtDistArm64}/libs/arm64-v8a") {
|
||||||
|
include "*.so"
|
||||||
|
}
|
||||||
|
into("${projectDir}/src/main/occt/jniLibs/arm64-v8a")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("copyOcctArmv7", Copy) {
|
||||||
|
dependsOn("buildOcctArmv7")
|
||||||
|
onlyIf { forceNativeRebuild || !new File(occtArmv7So).exists() }
|
||||||
|
from("${occtDistArmv7}/libs/armeabi-v7a") {
|
||||||
|
include "*.so"
|
||||||
|
}
|
||||||
|
into("${projectDir}/src/main/occt/jniLibs/armeabi-v7a")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("copyOcctHeadersArm64", Copy) {
|
||||||
|
dependsOn("buildOcctArm64")
|
||||||
|
onlyIf { !new File(occtHeaderArm64Out).exists() }
|
||||||
|
from("${occtDistArm64}/inc")
|
||||||
|
into("${projectDir}/src/main/occt/include/arm64-v8a")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("copyOcctHeadersArmv7", Copy) {
|
||||||
|
dependsOn("buildOcctArmv7")
|
||||||
|
onlyIf { !new File(occtHeaderArmv7Out).exists() }
|
||||||
|
from("${occtDistArmv7}/inc")
|
||||||
|
into("${projectDir}/src/main/occt/include/armeabi-v7a")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("prepareNativeDeps") {
|
||||||
|
dependsOn(
|
||||||
|
"copyTbbArm64",
|
||||||
|
"copyTbbArmv7",
|
||||||
|
"copyTbbHeaders",
|
||||||
|
"copyBoostArm64",
|
||||||
|
"copyBoostArmv7",
|
||||||
|
"copyBoostHeaders",
|
||||||
|
"copyOcctArm64",
|
||||||
|
"copyOcctArmv7",
|
||||||
|
"copyOcctHeadersArm64",
|
||||||
|
"copyOcctHeadersArmv7"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
afterEvaluate {
|
||||||
|
tasks.matching { it.name.startsWith("buildCMake") }.configureEach {
|
||||||
|
dependsOn("prepareNativeDeps")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+5
-1
@@ -18,4 +18,8 @@
|
|||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
# If you keep the line number information, uncomment this to
|
||||||
# hide the original source file name.
|
# hide the original source file name.
|
||||||
#-renamesourcefileattribute SourceFile
|
#-renamesourcefileattribute SourceFile
|
||||||
|
|
||||||
|
# Keep cloud API and SAPIL runtime proxies.
|
||||||
|
-keep class com.dark98.santoku.cloud.** { *; }
|
||||||
|
-keep class ru.ytkab0bp.sapil.** { *; }
|
||||||
|
|||||||
@@ -5,17 +5,25 @@
|
|||||||
<uses-feature android:glEsVersion="0x00030000" android:required="true"/>
|
<uses-feature android:glEsVersion="0x00030000" android:required="true"/>
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
|
||||||
|
|
||||||
|
<queries>
|
||||||
|
<!-- WebView fails sometime if not queried, idk why -->
|
||||||
|
<package android:name="com.google.android.webview"/>
|
||||||
|
</queries>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
android:icon="@mipmap/icon"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/AppName"
|
android:label="@string/AppName"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.SliceBeam"
|
android:theme="@style/Theme.Santoku"
|
||||||
android:name=".SliceBeam"
|
android:name=".Santoku"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
|
android:largeHeap="true"
|
||||||
|
android:isGame="false"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
@@ -24,7 +32,7 @@
|
|||||||
android:windowSoftInputMode="adjustPan"
|
android:windowSoftInputMode="adjustPan"
|
||||||
android:configChanges="uiMode"
|
android:configChanges="uiMode"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/Theme.SliceBeam">
|
android:theme="@style/Theme.Santoku">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
@@ -35,6 +43,13 @@
|
|||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="santoku" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
<data android:scheme="file" />
|
<data android:scheme="file" />
|
||||||
<data android:scheme="content" />
|
<data android:scheme="content" />
|
||||||
<data android:mimeType="*/*" />
|
<data android:mimeType="*/*" />
|
||||||
@@ -44,7 +59,7 @@
|
|||||||
<activity android:name=".SetupActivity" android:exported="false"/>
|
<activity android:name=".SetupActivity" android:exported="false"/>
|
||||||
<activity android:name=".SafeStartActivity" android:exported="false"/>
|
<activity android:name=".SafeStartActivity" android:exported="false"/>
|
||||||
<provider
|
<provider
|
||||||
android:authorities="ru.ytkab0bp.slicebeam.provider"
|
android:authorities="${applicationId}.provider"
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:grantUriPermissions="true"
|
android:grantUriPermissions="true"
|
||||||
android:exported="false">
|
android:exported="false">
|
||||||
@@ -53,6 +68,17 @@
|
|||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/provider_paths" />
|
android:resource="@xml/provider_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name=".providers.AppDocumentsProvider"
|
||||||
|
android:authorities="${applicationId}.documents"
|
||||||
|
android:exported="true"
|
||||||
|
android:grantUriPermissions="true"
|
||||||
|
android:permission="android.permission.MANAGE_DOCUMENTS">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
|
||||||
|
</intent-filter>
|
||||||
|
</provider>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -47,13 +47,13 @@ var dark = $['is_dark_theme'];
|
|||||||
document.getElementsByClassName('md-option')[dark ? 1 : 0].click();
|
document.getElementsByClassName('md-option')[dark ? 1 : 0].click();
|
||||||
|
|
||||||
beginSaveFile = function(filename) {
|
beginSaveFile = function(filename) {
|
||||||
SliceBeam.beginDownload(filename);
|
Santoku.beginDownload(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeToFile = function(data) {
|
writeToFile = function(data) {
|
||||||
SliceBeam.writeData(btoa(unescape(encodeURIComponent(data))));
|
Santoku.writeData(btoa(unescape(encodeURIComponent(data))));
|
||||||
}
|
}
|
||||||
|
|
||||||
finishFile = function() {
|
finishFile = function() {
|
||||||
SliceBeam.finishDownload();
|
Santoku.finishDownload();
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ var dark = $['is_dark_theme'];
|
|||||||
document.getElementsByClassName('md-option')[dark ? 1 : 0].click();
|
document.getElementsByClassName('md-option')[dark ? 1 : 0].click();
|
||||||
|
|
||||||
saveTextAsFile = function(filename, text) {
|
saveTextAsFile = function(filename, text) {
|
||||||
SliceBeam.beginDownload(filename);
|
Santoku.beginDownload(filename);
|
||||||
SliceBeam.writeData(btoa(unescape(encodeURIComponent(text))));
|
Santoku.writeData(btoa(unescape(encodeURIComponent(text))));
|
||||||
SliceBeam.finishDownload();
|
Santoku.finishDownload();
|
||||||
}
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,771 @@
|
|||||||
|
package com.dark98.santoku;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Process;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.ColorUtils;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
import com.dark98.santoku.cloud.CloudController;
|
||||||
|
import com.dark98.santoku.components.BeamAlertDialogBuilder;
|
||||||
|
import com.dark98.santoku.components.ChangeLogBottomSheet;
|
||||||
|
import com.dark98.santoku.components.UnfoldMenu;
|
||||||
|
import com.dark98.santoku.config.ConfigObject;
|
||||||
|
import com.dark98.santoku.events.NeedDismissSnackbarEvent;
|
||||||
|
import com.dark98.santoku.events.NeedSnackbarEvent;
|
||||||
|
import com.dark98.santoku.events.ObjectsListChangedEvent;
|
||||||
|
import com.dark98.santoku.fragment.BedFragment;
|
||||||
|
import com.dark98.santoku.navigation.Fragment;
|
||||||
|
import com.dark98.santoku.navigation.MobileNavigationDelegate;
|
||||||
|
import com.dark98.santoku.navigation.NavigationDelegate;
|
||||||
|
import com.dark98.santoku.slic3r.Model;
|
||||||
|
import com.dark98.santoku.slic3r.Slic3rConfigWrapper;
|
||||||
|
import com.dark98.santoku.slic3r.Slic3rRuntimeError;
|
||||||
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
|
import com.dark98.santoku.utils.IOUtils;
|
||||||
|
import com.dark98.santoku.utils.Prefs;
|
||||||
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
import com.dark98.santoku.view.SnackbarsLayout;
|
||||||
|
|
||||||
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
// Activity result
|
||||||
|
public final static int REQUEST_CODE_OPEN_FILE = 1, REQUEST_CODE_EXPORT_GCODE = 2,
|
||||||
|
REQUEST_CODE_IMPORT_PROFILES = 3, REQUEST_CODE_EXPORT_PROFILES = 4,
|
||||||
|
REQUEST_CODE_EXPORT_3MF = 5;
|
||||||
|
|
||||||
|
private static MainActivity activeInstance;
|
||||||
|
|
||||||
|
public static List<ConfigObject> EXPORTING_PRINTS;
|
||||||
|
public static List<ConfigObject> EXPORTING_FILAMENTS;
|
||||||
|
public static List<ConfigObject> EXPORTING_PRINTERS;
|
||||||
|
|
||||||
|
|
||||||
|
private static SparseArray<NavigationDelegate> liveDelegate = new SparseArray<>();
|
||||||
|
private static int lastId;
|
||||||
|
|
||||||
|
private int id;
|
||||||
|
private NavigationDelegate delegate;
|
||||||
|
private boolean landscape;
|
||||||
|
private UnfoldMenu unfoldMenu;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (Prefs.getPrefs().contains("crash")) {
|
||||||
|
startActivity(new Intent(this, SafeStartActivity.class));
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Santoku.CONFIG == null) {
|
||||||
|
Prefs.setLastCommit();
|
||||||
|
startActivity(new Intent(this, SetupActivity.class));
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeInstance == null) {
|
||||||
|
activeInstance = this;
|
||||||
|
} else {
|
||||||
|
Intent i = new Intent(this, MainActivity.class);
|
||||||
|
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
if (getIntent() != null) {
|
||||||
|
i.setAction(getIntent().getAction());
|
||||||
|
i.putExtras(getIntent());
|
||||||
|
i.setDataAndType(getIntent().getData(), getIntent().getType());
|
||||||
|
}
|
||||||
|
startActivity(i);
|
||||||
|
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = savedInstanceState == null ? lastId++ : savedInstanceState.getInt("id");
|
||||||
|
|
||||||
|
if (delegate == null) {
|
||||||
|
NavigationDelegate saved = liveDelegate.get(id);
|
||||||
|
liveDelegate.remove(id);
|
||||||
|
if (saved != null && isCompatible(saved)) {
|
||||||
|
delegate = saved;
|
||||||
|
} else {
|
||||||
|
delegate = onCreateDelegate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delegate.setContext(this);
|
||||||
|
|
||||||
|
delegate.onCreate();
|
||||||
|
View v = delegate.onCreateView(this);
|
||||||
|
if (delegate.getContainerView() == null || delegate.getContainerView().getParent() == null) {
|
||||||
|
throw new IllegalArgumentException("Delegate hasn't created container view!");
|
||||||
|
}
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(v, (v2, insets) -> {
|
||||||
|
Insets systemBars = insets.getSystemWindowInsets();
|
||||||
|
v2.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets.consumeSystemWindowInsets();
|
||||||
|
});
|
||||||
|
setContentView(v);
|
||||||
|
|
||||||
|
if (getIntent() != null && getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_VIEW)) {
|
||||||
|
Uri data = getIntent().getData();
|
||||||
|
if (data != null && "santoku".equalsIgnoreCase(data.getScheme())) {
|
||||||
|
setIntent(null);
|
||||||
|
} else {
|
||||||
|
loadFile(data);
|
||||||
|
setIntent(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DisplayMetrics dm = getResources().getDisplayMetrics();
|
||||||
|
landscape = dm.widthPixels > dm.heightPixels;
|
||||||
|
View decorView = getWindow().getDecorView();
|
||||||
|
decorView.setBackgroundColor(ThemesRepo.getColor(android.R.attr.windowBackground));
|
||||||
|
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
|
||||||
|
getWindow().setStatusBarColor(Color.TRANSPARENT);
|
||||||
|
getWindow().setNavigationBarColor(Color.TRANSPARENT);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
getWindow().getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (landscape) {
|
||||||
|
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
|
||||||
|
decorView.setSystemUiVisibility(uiOptions);
|
||||||
|
|
||||||
|
decorView.setOnSystemUiVisibilityChangeListener(visibility -> {
|
||||||
|
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
|
||||||
|
visibility |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
|
||||||
|
int finalVisibility = visibility;
|
||||||
|
ViewUtils.postOnMainThread(() -> decorView.setSystemUiVisibility(finalVisibility), 500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
getWindow().setStatusBarContrastEnforced(false);
|
||||||
|
getWindow().setNavigationBarContrastEnforced(false);
|
||||||
|
}
|
||||||
|
if (ColorUtils.calculateLuminance(ThemesRepo.getColor(android.R.attr.windowBackground)) >= 0.9f) {
|
||||||
|
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
||||||
|
} else {
|
||||||
|
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Objects.equals(Prefs.getLastCommit(), BuildConfig.COMMIT) && Santoku.hasUpdateInfo) {
|
||||||
|
Prefs.setLastCommit();
|
||||||
|
new ChangeLogBottomSheet(this).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public NavigationDelegate getNavigationDelegate() {
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNewIntent(@NonNull Intent intent) {
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
Uri data = intent.getData();
|
||||||
|
if (data != null && "santoku".equalsIgnoreCase(data.getScheme())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loadFile(data);
|
||||||
|
setIntent(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @noinspection ResultOfMethodCallIgnored*/
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
if (requestCode == MainActivity.REQUEST_CODE_EXPORT_3MF) {
|
||||||
|
Fragment fragment = getNavigationDelegate().getCurrentFragment();
|
||||||
|
if (fragment instanceof BedFragment) {
|
||||||
|
try {
|
||||||
|
OutputStream out = getContentResolver().openOutputStream(data.getData());
|
||||||
|
Model model = ((BedFragment) fragment).getGlView().getRenderer().getModel();
|
||||||
|
File tempFile = File.createTempFile("temp_project", ".3mf");
|
||||||
|
Santoku.genCurrentConfig();
|
||||||
|
File cfg = Santoku.getCurrentConfigFile();
|
||||||
|
model.export3mf(cfg.getAbsolutePath(), tempFile.getAbsolutePath());
|
||||||
|
|
||||||
|
InputStream in = new FileInputStream(tempFile);
|
||||||
|
byte[] buffer = new byte[10240];
|
||||||
|
int c;
|
||||||
|
while ((c = in.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, c);
|
||||||
|
}
|
||||||
|
in.close();
|
||||||
|
out.close();
|
||||||
|
tempFile.delete();
|
||||||
|
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileExport3mfSuccess));
|
||||||
|
} catch (IOException | Slic3rRuntimeError e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (requestCode == MainActivity.REQUEST_CODE_EXPORT_GCODE) {
|
||||||
|
try {
|
||||||
|
OutputStream out = getContentResolver().openOutputStream(data.getData());
|
||||||
|
InputStream in = new FileInputStream(BedFragment.getTempGCodePath());
|
||||||
|
byte[] buffer = new byte[10240];
|
||||||
|
int c;
|
||||||
|
while ((c = in.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, c);
|
||||||
|
}
|
||||||
|
in.close();
|
||||||
|
out.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
} else if (requestCode == MainActivity.REQUEST_CODE_OPEN_FILE) {
|
||||||
|
loadFile(data.getData());
|
||||||
|
} else if (requestCode == MainActivity.REQUEST_CODE_EXPORT_PROFILES) {
|
||||||
|
try {
|
||||||
|
Slic3rConfigWrapper w = new Slic3rConfigWrapper();
|
||||||
|
w.printConfigs.addAll(EXPORTING_PRINTS);
|
||||||
|
w.filamentConfigs.addAll(EXPORTING_FILAMENTS);
|
||||||
|
w.printerConfigs.addAll(EXPORTING_PRINTERS);
|
||||||
|
|
||||||
|
EXPORTING_PRINTS = null;
|
||||||
|
EXPORTING_FILAMENTS = null;
|
||||||
|
EXPORTING_PRINTERS = null;
|
||||||
|
|
||||||
|
w.presets = new ConfigObject();
|
||||||
|
if (w.findPrint(Santoku.CONFIG.presets.get("print")) != null) {
|
||||||
|
w.presets.put("print", Santoku.CONFIG.presets.get("print"));
|
||||||
|
}
|
||||||
|
if (w.findFilament(Santoku.CONFIG.presets.get("filament")) != null) {
|
||||||
|
w.presets.put("filament", Santoku.CONFIG.presets.get("filament"));
|
||||||
|
}
|
||||||
|
if (w.findPrinter(Santoku.CONFIG.presets.get("printer")) != null) {
|
||||||
|
w.presets.put("printer", Santoku.CONFIG.presets.get("printer"));
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputStream out = getContentResolver().openOutputStream(data.getData());
|
||||||
|
out.write(w.serialize().getBytes(StandardCharsets.UTF_8));
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileExportProfilesSuccess));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
} else if (requestCode == MainActivity.REQUEST_CODE_IMPORT_PROFILES) {
|
||||||
|
Uri uri = data.getData();
|
||||||
|
String fileName = IOUtils.getDisplayName(uri);
|
||||||
|
|
||||||
|
if (fileName == null) {
|
||||||
|
new BeamAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.MenuFileImportProfilesFailed)
|
||||||
|
.setMessage(R.string.MenuFileOpenFileFailedNullName)
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileName.endsWith(".orca_printer")) {
|
||||||
|
loadConvertedProfile(uri);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fileName.endsWith(".ini")) {
|
||||||
|
new BeamAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.MenuFileImportProfilesFailed)
|
||||||
|
.setMessage(R.string.MenuFileImportProfilesFailedNotIni)
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
loadIniForImport(getContentResolver().openInputStream(uri));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
new BeamAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.MenuFileImportProfilesFailed)
|
||||||
|
.setMessage(e.toString())
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadConvertedProfile(Uri uri) {
|
||||||
|
String tag = UUID.randomUUID().toString();
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.OrcaConversionPleaseWait).tag(tag));
|
||||||
|
File f = new File(Santoku.getModelCacheDir(), "orca_conv.zip");
|
||||||
|
IOUtils.IO_POOL.submit(()->{
|
||||||
|
try {
|
||||||
|
InputStream in = getContentResolver().openInputStream(uri);
|
||||||
|
FileOutputStream fos = new FileOutputStream(f);
|
||||||
|
byte[] buffer = new byte[10240];
|
||||||
|
int c;
|
||||||
|
while ((c = in.read(buffer)) != -1) {
|
||||||
|
fos.write(buffer, 0, c);
|
||||||
|
}
|
||||||
|
fos.close();
|
||||||
|
in.close();
|
||||||
|
|
||||||
|
ZipFile zf = new ZipFile(f);
|
||||||
|
JSONObject bundle = new JSONObject(IOUtils.readString(zf.getInputStream(zf.getEntry("bundle_structure.json"))));
|
||||||
|
if (!bundle.get("bundle_type").equals("printer config bundle")) {
|
||||||
|
zf.close();
|
||||||
|
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
|
||||||
|
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.MenuFileImportProfilesFailed)
|
||||||
|
.setMessage(R.string.OrcaConversionNotAConfigBundle)
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Slic3rConfigWrapper w = new Slic3rConfigWrapper();
|
||||||
|
if (bundle.has("process_config")) {
|
||||||
|
JSONArray arr = bundle.getJSONArray("process_config");
|
||||||
|
List<String> names = new ArrayList<>();
|
||||||
|
List<String> stripped = new ArrayList<>();
|
||||||
|
for (int i = 0; i < arr.length(); i++) {
|
||||||
|
String v = arr.getString(i);
|
||||||
|
names.add(v);
|
||||||
|
stripped.add(v.substring(v.indexOf('/') + 1, v.length() - 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String name : names) {
|
||||||
|
w.printConfigs.add(IOUtils.configJsonToIni(new JSONObject(IOUtils.readString(zf.getInputStream(zf.getEntry(name)))), "process", Slic3rConfigWrapper.PRINT_CONFIG_KEYS, stripped));
|
||||||
|
}
|
||||||
|
for (ConfigObject obj : w.printConfigs) {
|
||||||
|
String inherit = obj.get("inherits");
|
||||||
|
while (inherit != null) {
|
||||||
|
ConfigObject _obj = w.findPrint(inherit);
|
||||||
|
if (_obj == null) throw new IOUtils.MissingProfileException(inherit);
|
||||||
|
|
||||||
|
obj.values.remove("inherits");
|
||||||
|
HashMap<String, String> newMap = new HashMap<>();
|
||||||
|
newMap.putAll(_obj.values);
|
||||||
|
newMap.putAll(obj.values);
|
||||||
|
obj.values = newMap;
|
||||||
|
|
||||||
|
inherit = obj.values.get("inherits");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bundle.has("filament_config")) {
|
||||||
|
JSONArray arr = bundle.getJSONArray("filament_config");
|
||||||
|
List<String> names = new ArrayList<>();
|
||||||
|
List<String> stripped = new ArrayList<>();
|
||||||
|
for (int i = 0; i < arr.length(); i++) {
|
||||||
|
String v = arr.getString(i);
|
||||||
|
names.add(v);
|
||||||
|
stripped.add(v.substring(v.indexOf('/') + 1, v.length() - 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String name : names) {
|
||||||
|
w.filamentConfigs.add(IOUtils.configJsonToIni(new JSONObject(IOUtils.readString(zf.getInputStream(zf.getEntry(name)))), "filament", Slic3rConfigWrapper.FILAMENT_CONFIG_KEYS, stripped));
|
||||||
|
}
|
||||||
|
for (ConfigObject obj : w.filamentConfigs) {
|
||||||
|
String inherit = obj.get("inherits");
|
||||||
|
while (inherit != null) {
|
||||||
|
ConfigObject _obj = w.findFilament(inherit);
|
||||||
|
if (_obj == null) throw new IOUtils.MissingProfileException(inherit);
|
||||||
|
|
||||||
|
obj.values.remove("inherits");
|
||||||
|
HashMap<String, String> newMap = new HashMap<>();
|
||||||
|
newMap.putAll(_obj.values);
|
||||||
|
newMap.putAll(obj.values);
|
||||||
|
obj.values = newMap;
|
||||||
|
|
||||||
|
inherit = obj.values.get("inherits");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bundle.has("printer_config")) {
|
||||||
|
JSONArray arr = bundle.getJSONArray("printer_config");
|
||||||
|
List<String> names = new ArrayList<>();
|
||||||
|
List<String> stripped = new ArrayList<>();
|
||||||
|
for (int i = 0; i < arr.length(); i++) {
|
||||||
|
String v = arr.getString(i);
|
||||||
|
names.add(v);
|
||||||
|
stripped.add(v.substring(v.indexOf('/') + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String name : names) {
|
||||||
|
w.printerConfigs.add(IOUtils.configJsonToIni(new JSONObject(IOUtils.readString(zf.getInputStream(zf.getEntry(name)))), "machine", Slic3rConfigWrapper.PRINTER_CONFIG_KEYS, stripped));
|
||||||
|
}
|
||||||
|
for (ConfigObject obj : w.printerConfigs) {
|
||||||
|
String inherit = obj.get("inherits");
|
||||||
|
while (inherit != null) {
|
||||||
|
ConfigObject _obj = w.findPrinter(inherit);
|
||||||
|
if (_obj == null) throw new IOUtils.MissingProfileException(inherit);
|
||||||
|
|
||||||
|
obj.values.remove("inherits");
|
||||||
|
HashMap<String, String> newMap = new HashMap<>();
|
||||||
|
newMap.putAll(_obj.values);
|
||||||
|
newMap.putAll(obj.values);
|
||||||
|
obj.values = newMap;
|
||||||
|
|
||||||
|
inherit = obj.values.get("inherits");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zf.close();
|
||||||
|
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
|
||||||
|
loadIniForImport(new ByteArrayInputStream(w.serialize().getBytes(StandardCharsets.UTF_8)));
|
||||||
|
} catch (IOUtils.MissingProfileException ep) {
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
|
||||||
|
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.MenuFileImportProfilesFailed)
|
||||||
|
.setMessage(getString(R.string.MenuFileImportProfilesFailedBaseProfileNotFound, ep.profile))
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show());
|
||||||
|
} catch (Exception e) {
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
|
||||||
|
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.MenuFileImportProfilesFailed)
|
||||||
|
.setMessage(e.toString())
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadIniForImport(InputStream in) {
|
||||||
|
IOUtils.IO_POOL.submit(()->{
|
||||||
|
try {
|
||||||
|
Slic3rConfigWrapper w = new Slic3rConfigWrapper(in);
|
||||||
|
|
||||||
|
ViewUtils.postOnMainThread(() -> {
|
||||||
|
CharSequence[] prints = new CharSequence[w.printConfigs.size()];
|
||||||
|
boolean[] enabledPrints = new boolean[prints.length];
|
||||||
|
for (int i = 0; i < prints.length; i++) {
|
||||||
|
prints[i] = w.printConfigs.get(i).getTitle();
|
||||||
|
enabledPrints[i] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CharSequence[] filaments = new CharSequence[w.filamentConfigs.size()];
|
||||||
|
boolean[] enabledFilaments = new boolean[filaments.length];
|
||||||
|
for (int i = 0; i < filaments.length; i++) {
|
||||||
|
filaments[i] = w.filamentConfigs.get(i).getTitle();
|
||||||
|
enabledFilaments[i] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CharSequence[] printers = new CharSequence[w.printerConfigs.size()];
|
||||||
|
boolean[] enabledPrinters = new boolean[printers.length];
|
||||||
|
for (int i = 0; i < printers.length; i++) {
|
||||||
|
printers[i] = w.printerConfigs.get(i).getTitle();
|
||||||
|
enabledPrinters[i] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prints.length == 0 && filaments.length == 0 && printers.length == 0) {
|
||||||
|
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.MenuFileImportProfilesFailed)
|
||||||
|
.setMessage(R.string.MenuFileImportProfilesFailedEmpty)
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Runnable finish = () -> {
|
||||||
|
for (int i = 0; i < enabledPrints.length; i++) {
|
||||||
|
if (enabledPrints[i]) {
|
||||||
|
Santoku.CONFIG.importPrint(w.printConfigs.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < enabledFilaments.length; i++) {
|
||||||
|
if (enabledFilaments[i]) {
|
||||||
|
Santoku.CONFIG.importFilament(w.filamentConfigs.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < enabledPrinters.length; i++) {
|
||||||
|
if (enabledPrinters[i]) {
|
||||||
|
Santoku.CONFIG.importPrinter(w.printerConfigs.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Santoku.saveConfig();
|
||||||
|
};
|
||||||
|
Runnable printersRun = () -> {
|
||||||
|
if (printers.length == 0) {
|
||||||
|
finish.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new BeamAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.MenuFileExportProfilesPrinters)
|
||||||
|
.setMultiChoiceItems(printers, enabledPrinters, (dialog, which, isChecked) -> enabledPrinters[which] = isChecked)
|
||||||
|
.setPositiveButton(android.R.string.ok, (d3, w3) -> finish.run())
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.show();
|
||||||
|
};
|
||||||
|
Runnable filamentsRun = () -> {
|
||||||
|
if (filaments.length == 0) {
|
||||||
|
printersRun.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new BeamAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.MenuFileExportProfilesFilaments)
|
||||||
|
.setMultiChoiceItems(filaments, enabledFilaments, (dialog, which, isChecked) -> enabledFilaments[which] = isChecked)
|
||||||
|
.setPositiveButton(android.R.string.ok, (d2, w2) -> printersRun.run())
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.show();
|
||||||
|
};
|
||||||
|
if (prints.length == 0) {
|
||||||
|
filamentsRun.run();
|
||||||
|
} else {
|
||||||
|
new BeamAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.MenuFileExportProfilesPrints)
|
||||||
|
.setMultiChoiceItems(prints, enabledPrints, (dialog, which, isChecked) -> enabledPrints[which] = isChecked)
|
||||||
|
.setPositiveButton(android.R.string.ok, (d1, w1) -> filamentsRun.run())
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e("MainActivity", "Failed to read file", e);
|
||||||
|
|
||||||
|
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.MenuFileImportProfilesFailed)
|
||||||
|
.setMessage(e.toString())
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadFile(File f, boolean autoorient) {
|
||||||
|
String tag = UUID.randomUUID().toString();
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileOpenFileLoading).tag(tag));
|
||||||
|
IOUtils.IO_POOL.submit(() -> {
|
||||||
|
Process.setThreadPriority(-20);
|
||||||
|
if (delegate.getCurrentFragment() instanceof BedFragment) {
|
||||||
|
BedFragment fragment = (BedFragment) delegate.getCurrentFragment();
|
||||||
|
try {
|
||||||
|
boolean gcode = f.getName().endsWith(".gcode");
|
||||||
|
if (gcode) {
|
||||||
|
fragment.loadGCode(f);
|
||||||
|
} else {
|
||||||
|
fragment.loadModel(f);
|
||||||
|
}
|
||||||
|
fragment.getGlView().queueEvent(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Model model = fragment.getGlView().getRenderer().getModel();
|
||||||
|
if (model == null || fragment.getGlView().getRenderer().getBed() == null) {
|
||||||
|
fragment.getGlView().queueEvent(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gcode) {
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
|
||||||
|
}
|
||||||
|
int i = model.getObjectsCount() - 1;
|
||||||
|
if (autoorient) {
|
||||||
|
model.autoOrient(i);
|
||||||
|
fragment.getGlView().getRenderer().invalidateGlModel(i);
|
||||||
|
fragment.getGlView().requestRender();
|
||||||
|
}
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileOpenFileLoaded));
|
||||||
|
if (model.isBigObject(i)) {
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.WARNING, R.string.MenuFileOpenFileBigObject));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Slic3rRuntimeError e) {
|
||||||
|
Log.e("MainActivity", "Failed to load model", e);
|
||||||
|
f.delete();
|
||||||
|
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
|
||||||
|
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.MenuFileOpenFileFailed)
|
||||||
|
.setMessage(e.toString())
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadFile(Uri uri) {
|
||||||
|
if (uri == null) return;
|
||||||
|
|
||||||
|
ContentResolver resolver = getContentResolver();
|
||||||
|
String fileName = IOUtils.getDisplayName(uri);
|
||||||
|
if (fileName == null) {
|
||||||
|
new BeamAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.MenuFileOpenFileFailed)
|
||||||
|
.setMessage(R.string.MenuFileOpenFileFailedNullName)
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fileName.endsWith(".orca_printer")) {
|
||||||
|
loadConvertedProfile(uri);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fileName.endsWith(".ini")) {
|
||||||
|
try {
|
||||||
|
loadIniForImport(resolver.openInputStream(uri));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
new BeamAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.MenuFileImportProfilesFailed)
|
||||||
|
.setMessage(e.toString())
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File f = new File(Santoku.getModelCacheDir(), fileName);
|
||||||
|
// TODO: Check if file already exists
|
||||||
|
IOUtils.IO_POOL.submit(()->{
|
||||||
|
try {
|
||||||
|
InputStream in = resolver.openInputStream(uri);
|
||||||
|
FileOutputStream fos = new FileOutputStream(f);
|
||||||
|
byte[] buffer = new byte[10240]; int c;
|
||||||
|
while ((c = in.read(buffer)) != -1) {
|
||||||
|
fos.write(buffer, 0, c);
|
||||||
|
}
|
||||||
|
fos.close();
|
||||||
|
in.close();
|
||||||
|
loadFile(f, false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e("MainActivity", "Failed to write cache file", e);
|
||||||
|
|
||||||
|
f.delete();
|
||||||
|
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
|
||||||
|
.setTitle(R.string.MenuFileOpenFileFailed)
|
||||||
|
.setMessage(e.toString())
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||||
|
super.onConfigurationChanged(newConfig);
|
||||||
|
|
||||||
|
if ((newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) != Configuration.UI_MODE_NIGHT_UNDEFINED) {
|
||||||
|
ThemesRepo.resetSystemResolvedTheme();
|
||||||
|
ThemesRepo.invalidate(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onApplyTheme() {
|
||||||
|
delegate.onApplyTheme();
|
||||||
|
|
||||||
|
View decorView = getWindow().getDecorView();
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
if (ColorUtils.calculateLuminance(ThemesRepo.getColor(android.R.attr.windowBackground)) >= 0.9f) {
|
||||||
|
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
||||||
|
} else {
|
||||||
|
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
decorView.setBackgroundColor(ThemesRepo.getColor(android.R.attr.windowBackground));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
if (isChangingConfigurations()) {
|
||||||
|
outState.putInt("id", id);
|
||||||
|
liveDelegate.put(id, delegate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCompatible(NavigationDelegate delegate) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private NavigationDelegate onCreateDelegate() {
|
||||||
|
return new MobileNavigationDelegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showUnfoldMenu(UnfoldMenu menu, View v) {
|
||||||
|
if (unfoldMenu != null) return;
|
||||||
|
menu.setOnDismiss(() -> unfoldMenu = null);
|
||||||
|
menu.show(v, delegate.getOverlayView());
|
||||||
|
unfoldMenu = menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
if (unfoldMenu != null) {
|
||||||
|
unfoldMenu.dismiss();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (delegate.onBackPressed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
delegate.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
delegate.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
Santoku.saveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (activeInstance == this) {
|
||||||
|
activeInstance = null;
|
||||||
|
}
|
||||||
|
if (delegate != null) {
|
||||||
|
delegate.onDestroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-5
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam;
|
package com.dark98.santoku;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -17,10 +17,10 @@ import android.widget.TextView;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.graphics.ColorUtils;
|
import androidx.core.graphics.ColorUtils;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Prefs;
|
import com.dark98.santoku.utils.Prefs;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
import ru.ytkab0bp.slicebeam.view.BeamButton;
|
import com.dark98.santoku.view.BeamButton;
|
||||||
|
|
||||||
public class SafeStartActivity extends Activity {
|
public class SafeStartActivity extends Activity {
|
||||||
@Override
|
@Override
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
package com.dark98.santoku;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Application;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.instacart.truetime.time.TrueTimeImpl;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ru.ytkab0bp.eventbus.EventBus;
|
||||||
|
import com.dark98.santoku.boot.AppBoot;
|
||||||
|
import com.dark98.santoku.boot.CheckUpdateJsonTask;
|
||||||
|
import com.dark98.santoku.boot.ClearModelCacheTask;
|
||||||
|
import com.dark98.santoku.boot.CloudInitTask;
|
||||||
|
import com.dark98.santoku.boot.EventBusTask;
|
||||||
|
import com.dark98.santoku.boot.LoadSlic3rConfigTask;
|
||||||
|
import com.dark98.santoku.boot.PrefsTask;
|
||||||
|
import com.dark98.santoku.boot.PrintConfigWarmupTask;
|
||||||
|
import com.dark98.santoku.boot.TrueTimeTask;
|
||||||
|
import com.dark98.santoku.boot.VibrationUtilsTask;
|
||||||
|
import com.dark98.santoku.cloud.CloudController;
|
||||||
|
import com.dark98.santoku.config.ConfigObject;
|
||||||
|
import com.dark98.santoku.slic3r.ConfigOptionDef;
|
||||||
|
import com.dark98.santoku.slic3r.PrintConfigDef;
|
||||||
|
import com.dark98.santoku.slic3r.Slic3rConfigWrapper;
|
||||||
|
import com.dark98.santoku.utils.Prefs;
|
||||||
|
|
||||||
|
public class Santoku extends Application {
|
||||||
|
public static Santoku INSTANCE;
|
||||||
|
public static EventBus EVENT_BUS = EventBus.newBus("main");
|
||||||
|
public static TrueTimeImpl TRUE_TIME;
|
||||||
|
public static Slic3rConfigWrapper CONFIG;
|
||||||
|
public static int CONFIG_UID = 0;
|
||||||
|
public static boolean hasUpdateInfo;
|
||||||
|
|
||||||
|
@SuppressLint("ApplySharedPref")
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
INSTANCE = this;
|
||||||
|
AppBoot.run(Arrays.asList(
|
||||||
|
new EventBusTask(),
|
||||||
|
new PrefsTask(),
|
||||||
|
new VibrationUtilsTask(),
|
||||||
|
new TrueTimeTask(),
|
||||||
|
new PrintConfigWarmupTask(),
|
||||||
|
new CheckUpdateJsonTask(),
|
||||||
|
new ClearModelCacheTask(),
|
||||||
|
new LoadSlic3rConfigTask(),
|
||||||
|
new CloudInitTask()
|
||||||
|
));
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
|
||||||
|
StringWriter sw = new StringWriter();
|
||||||
|
PrintWriter pw = new PrintWriter(sw);
|
||||||
|
e.printStackTrace(pw);
|
||||||
|
|
||||||
|
Prefs.getPrefs().edit().putString("crash", sw.toString()).commit();
|
||||||
|
Intent intent = new Intent(this, SafeStartActivity.class);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
|
startActivity(intent);
|
||||||
|
Runtime.getRuntime().exit(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void saveConfig() {
|
||||||
|
if (CONFIG == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Santoku.CONFIG_UID++;
|
||||||
|
File f = getConfigFile();
|
||||||
|
File dir = f.getParentFile();
|
||||||
|
if (dir != null && !dir.exists()) {
|
||||||
|
// Best effort: ensure app files dir exists before saving.
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
dir.mkdirs();
|
||||||
|
}
|
||||||
|
String serialized = CONFIG.serialize();
|
||||||
|
try {
|
||||||
|
File tmp = new File(f.getParentFile(), f.getName() + ".tmp");
|
||||||
|
FileOutputStream fos = new FileOutputStream(tmp);
|
||||||
|
fos.write(serialized.getBytes(StandardCharsets.UTF_8));
|
||||||
|
fos.getFD().sync();
|
||||||
|
fos.close();
|
||||||
|
|
||||||
|
if (f.exists() && !f.delete()) {
|
||||||
|
Log.w("Config", "Failed to delete old config before rename: " + f.getAbsolutePath());
|
||||||
|
}
|
||||||
|
if (!tmp.renameTo(f)) {
|
||||||
|
// Fallback to direct write if rename fails.
|
||||||
|
FileOutputStream direct = new FileOutputStream(f);
|
||||||
|
direct.write(serialized.getBytes(StandardCharsets.UTF_8));
|
||||||
|
direct.getFD().sync();
|
||||||
|
direct.close();
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
tmp.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current config should be regenerated on next slice/export.
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
getCurrentConfigFile().delete();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e("Config", "Failed to save config", e);
|
||||||
|
}
|
||||||
|
CloudController.notifyDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File getModelCacheDir() {
|
||||||
|
File f = new File(INSTANCE.getCacheDir(), "model");
|
||||||
|
if (!f.exists()) f.mkdirs();
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File getConfigFile() {
|
||||||
|
return new File(INSTANCE.getFilesDir(), "slic3r.ini");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConfigObject buildCurrentConfigObject() {
|
||||||
|
ConfigObject singleObject = new ConfigObject();
|
||||||
|
ConfigObject printerConfig = Santoku.CONFIG.findPrinter(Santoku.CONFIG.presets.get("printer"));
|
||||||
|
if (printerConfig != null) {
|
||||||
|
singleObject.values.putAll(printerConfig.values);
|
||||||
|
}
|
||||||
|
ConfigObject printConfig = Santoku.CONFIG.findPrint(Santoku.CONFIG.presets.get("print"));
|
||||||
|
if (printConfig != null) {
|
||||||
|
for (Map.Entry<String, String> en : printConfig.values.entrySet()) {
|
||||||
|
if (!Slic3rConfigWrapper.PRINTER_CONFIG_KEYS.contains(en.getKey())) {
|
||||||
|
singleObject.values.put(en.getKey(), en.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: MMU. Detect by printerConfig#getExtruderCount()
|
||||||
|
ConfigObject filamentConfig = Santoku.CONFIG.findFilament(Santoku.CONFIG.presets.get("filament"));
|
||||||
|
if (filamentConfig != null) {
|
||||||
|
for (Map.Entry<String, String> en : filamentConfig.values.entrySet()) {
|
||||||
|
if (!Slic3rConfigWrapper.PRINTER_CONFIG_KEYS.contains(en.getKey())) {
|
||||||
|
singleObject.values.put(en.getKey(), en.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintConfigDef def = PrintConfigDef.getInstance();
|
||||||
|
for (Map.Entry<String, ConfigOptionDef> en : def.options.entrySet()) {
|
||||||
|
if (singleObject.get(en.getKey()) == null && !PrintConfigDef.SKIP_DEFAULT_OPTIONS.contains(en.getKey()) && en.getValue().defaultValue != null) {
|
||||||
|
singleObject.put(en.getKey(), en.getValue().defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return singleObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void genCurrentConfig() throws IOException {
|
||||||
|
File cfg = getCurrentConfigFile();
|
||||||
|
FileOutputStream fos = new FileOutputStream(cfg);
|
||||||
|
ConfigObject singleObject = buildCurrentConfigObject();
|
||||||
|
fos.write(singleObject.serialize().getBytes(StandardCharsets.UTF_8));
|
||||||
|
fos.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File getCurrentConfigFile() {
|
||||||
|
return new File(INSTANCE.getFilesDir(), "slic3r_current.ini");
|
||||||
|
}
|
||||||
|
}
|
||||||
+464
-146
@@ -1,26 +1,43 @@
|
|||||||
package ru.ytkab0bp.slicebeam;
|
package com.dark98.santoku;
|
||||||
|
|
||||||
import static android.opengl.GLES30.*;
|
import static android.opengl.GLES30.GL_COLOR_BUFFER_BIT;
|
||||||
|
import static android.opengl.GLES30.GL_DEPTH_BUFFER_BIT;
|
||||||
|
import static android.opengl.GLES30.GL_DEPTH_TEST;
|
||||||
|
import static android.opengl.GLES30.glClear;
|
||||||
|
import static android.opengl.GLES30.glClearColor;
|
||||||
|
import static android.opengl.GLES30.glDisable;
|
||||||
|
import static android.opengl.GLES30.glEnable;
|
||||||
|
import static android.opengl.GLES30.glViewport;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.opengl.GLSurfaceView;
|
import android.opengl.GLSurfaceView;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.text.InputType;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
|
import android.view.MotionEvent;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.CheckBox;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -30,12 +47,14 @@ import androidx.activity.EdgeToEdge;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.core.graphics.ColorUtils;
|
import androidx.core.graphics.ColorUtils;
|
||||||
import androidx.core.graphics.Insets;
|
import androidx.core.graphics.Insets;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
import androidx.dynamicanimation.animation.FloatValueHolder;
|
import androidx.dynamicanimation.animation.FloatValueHolder;
|
||||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||||
import androidx.dynamicanimation.animation.SpringForce;
|
import androidx.dynamicanimation.animation.SpringForce;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.viewpager2.widget.ViewPager2;
|
import androidx.viewpager2.widget.ViewPager2;
|
||||||
|
|
||||||
@@ -44,6 +63,7 @@ import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
|
|||||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
|
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
|
||||||
import com.loopj.android.http.AsyncHttpClient;
|
import com.loopj.android.http.AsyncHttpClient;
|
||||||
import com.loopj.android.http.AsyncHttpResponseHandler;
|
import com.loopj.android.http.AsyncHttpResponseHandler;
|
||||||
|
import ru.ytkab0bp.sapil.APICallback;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
@@ -73,41 +93,47 @@ import javax.microedition.khronos.opengles.GL10;
|
|||||||
|
|
||||||
import cz.msebera.android.httpclient.Header;
|
import cz.msebera.android.httpclient.Header;
|
||||||
import ru.ytkab0bp.eventbus.EventHandler;
|
import ru.ytkab0bp.eventbus.EventHandler;
|
||||||
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
|
import com.dark98.santoku.cloud.CloudAPI;
|
||||||
import ru.ytkab0bp.slicebeam.config.ConfigObject;
|
import com.dark98.santoku.cloud.CloudController;
|
||||||
import ru.ytkab0bp.slicebeam.events.BeamServerDataUpdatedEvent;
|
import com.dark98.santoku.components.BeamAlertDialogBuilder;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.BigHeaderItem;
|
import com.dark98.santoku.components.CloudManageBottomSheet;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerAdapter;
|
import com.dark98.santoku.config.ConfigObject;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
|
import com.dark98.santoku.events.CloudLoginStateUpdatedEvent;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.TextHintRecyclerItem;
|
import com.dark98.santoku.events.CloudSyncFinishedEvent;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.GLModel;
|
import com.dark98.santoku.recycler.BigHeaderItem;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.GLShaderProgram;
|
import com.dark98.santoku.recycler.PreferenceItem;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.GLShadersManager;
|
import com.dark98.santoku.recycler.SimpleRecyclerAdapter;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
|
import com.dark98.santoku.recycler.SimpleRecyclerItem;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Slic3rUtils;
|
import com.dark98.santoku.recycler.TextHintRecyclerItem;
|
||||||
import ru.ytkab0bp.slicebeam.theme.BeamTheme;
|
import com.dark98.santoku.slic3r.GLModel;
|
||||||
import ru.ytkab0bp.slicebeam.theme.IThemeView;
|
import com.dark98.santoku.slic3r.GLShaderProgram;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.slic3r.GLShadersManager;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Prefs;
|
import com.dark98.santoku.slic3r.Slic3rConfigWrapper;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.slic3r.Slic3rUtils;
|
||||||
import ru.ytkab0bp.slicebeam.view.BeamSwitch;
|
import com.dark98.santoku.theme.BeamTheme;
|
||||||
import ru.ytkab0bp.slicebeam.view.BoostySubsView;
|
import com.dark98.santoku.theme.IThemeView;
|
||||||
import ru.ytkab0bp.slicebeam.view.FadeRecyclerView;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.view.MiniColorView;
|
import com.dark98.santoku.utils.Prefs;
|
||||||
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
import com.dark98.santoku.view.BeamSwitch;
|
||||||
|
import com.dark98.santoku.view.FadeRecyclerView;
|
||||||
|
import com.dark98.santoku.view.MiniColorView;
|
||||||
|
import com.dark98.santoku.view.TextColorImageSpan;
|
||||||
|
|
||||||
public class SetupActivity extends AppCompatActivity {
|
public class SetupActivity extends AppCompatActivity {
|
||||||
public final static String EXTRA_ABOUT = "about";
|
public final static String EXTRA_ABOUT = "about";
|
||||||
|
public final static String EXTRA_CLOUD_PROFILE = "cloud_profile";
|
||||||
|
public final static String EXTRA_CLOUD_IMPORT_FROM_SETUP = "cloud_import_from_setup";
|
||||||
|
|
||||||
private final static String TAG = "SetupActivity";
|
private final static String TAG = "SetupActivity";
|
||||||
|
|
||||||
private final static List<String> REPOS_URLS = Arrays.asList(
|
private final static List<String> REPOS_URLS = Arrays.asList(
|
||||||
"https://preset-repo-api.prusa3d.com/v1/repos",
|
"https://preset-repo-api.prusa3d.com/v1/repos",
|
||||||
"https://raw.githubusercontent.com/utkabobr/SliceBeam/refs/heads/master/.profiledumpsrepo/manifest.json"
|
"https://raw.githubusercontent.com/Dark98/SliceBeam/refs/heads/master/.profiledumpsrepo/manifest.json"
|
||||||
);
|
);
|
||||||
|
|
||||||
private final static int REPOS_INDEX = 1;
|
private final static int REPOS_INDEX = 1;
|
||||||
private final static int PROFILES_INDEX = 2;
|
private final static int PROFILES_INDEX = 2;
|
||||||
private static int BOOSTY_INDEX = 3;
|
|
||||||
|
|
||||||
private final static int TYPE_PRINTER = 0, TYPE_PRINT_CONFIG = 1, TYPE_FILAMENT = 2;
|
private final static int TYPE_PRINTER = 0, TYPE_PRINT_CONFIG = 1, TYPE_FILAMENT = 2;
|
||||||
|
|
||||||
@@ -117,10 +143,10 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
private GLSurfaceView backgroundView;
|
private GLSurfaceView backgroundView;
|
||||||
private GLModel backgroundModel;
|
private GLModel backgroundModel;
|
||||||
|
private GLShadersManager shadersManager;
|
||||||
|
|
||||||
private int titleY;
|
private int titleY;
|
||||||
private float backgroundProgress;
|
private float backgroundProgress;
|
||||||
private float boostyProgress;
|
|
||||||
|
|
||||||
private SpringAnimation fakeScroller;
|
private SpringAnimation fakeScroller;
|
||||||
|
|
||||||
@@ -129,6 +155,7 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
private List<ProfilesRepo> repos = new ArrayList<>();
|
private List<ProfilesRepo> repos = new ArrayList<>();
|
||||||
private ReposItem reposItem;
|
private ReposItem reposItem;
|
||||||
private ProfilesItem profilesItem;
|
private ProfilesItem profilesItem;
|
||||||
|
private CloudProfileItem cloudItem;
|
||||||
private boolean isReposLoaded;
|
private boolean isReposLoaded;
|
||||||
private boolean limitRepoFragmentCount = true;
|
private boolean limitRepoFragmentCount = true;
|
||||||
private boolean limitProfileFragmentCount = true;
|
private boolean limitProfileFragmentCount = true;
|
||||||
@@ -137,11 +164,13 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
private Map<ProfilesRepo, List<Slic3rConfigWrapper>> profilesMap = new HashMap<>();
|
private Map<ProfilesRepo, List<Slic3rConfigWrapper>> profilesMap = new HashMap<>();
|
||||||
private boolean isProfilesLoaded;
|
private boolean isProfilesLoaded;
|
||||||
private boolean about;
|
private boolean about;
|
||||||
|
private boolean cloudProfile;
|
||||||
|
private boolean cloudImport;
|
||||||
|
|
||||||
private List<ConfigObject> enabledPrinters = new ArrayList<>();
|
private List<ConfigObject> enabledPrinters = new ArrayList<>();
|
||||||
|
|
||||||
{
|
{
|
||||||
client.setUserAgent(String.format(Locale.ROOT, "SliceBeam/%s-%d", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
|
client.setUserAgent(String.format(Locale.ROOT, "Santoku/%s-%d", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
|
||||||
client.setEnableRedirects(true);
|
client.setEnableRedirects(true);
|
||||||
client.setLoggingEnabled(false);
|
client.setLoggingEnabled(false);
|
||||||
}
|
}
|
||||||
@@ -150,11 +179,13 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
EdgeToEdge.enable(this);
|
EdgeToEdge.enable(this);
|
||||||
SliceBeam.EVENT_BUS.registerListener(this);
|
Santoku.EVENT_BUS.registerListener(this);
|
||||||
|
|
||||||
about = getIntent().getBooleanExtra(EXTRA_ABOUT, false);
|
about = getIntent().getBooleanExtra(EXTRA_ABOUT, false);
|
||||||
|
cloudProfile = getIntent().getBooleanExtra(EXTRA_CLOUD_PROFILE, false);
|
||||||
|
cloudImport = getIntent().getBooleanExtra(EXTRA_CLOUD_IMPORT_FROM_SETUP, false);
|
||||||
|
|
||||||
if (!about) {
|
if (!about && !cloudProfile) {
|
||||||
new BeamAlertDialogBuilder(this)
|
new BeamAlertDialogBuilder(this)
|
||||||
.setTitle(R.string.IntroEarlyAccess)
|
.setTitle(R.string.IntroEarlyAccess)
|
||||||
.setMessage(R.string.IntroEarlyAccessMessage)
|
.setMessage(R.string.IntroEarlyAccessMessage)
|
||||||
@@ -162,11 +193,15 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cloudProfile) {
|
||||||
|
backgroundProgress = 1f;
|
||||||
|
}
|
||||||
|
|
||||||
pager = new ViewPager2(this);
|
pager = new ViewPager2(this);
|
||||||
adapter = new SimpleRecyclerAdapter() {
|
adapter = new SimpleRecyclerAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
return about ? 1 : limitRepoFragmentCount ? REPOS_INDEX + 1 : limitProfileFragmentCount ? PROFILES_INDEX + 1 : super.getItemCount();
|
return about || cloudProfile ? 1 : limitRepoFragmentCount ? REPOS_INDEX + 1 : limitProfileFragmentCount ? PROFILES_INDEX + 1 : super.getItemCount();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
setItems();
|
setItems();
|
||||||
@@ -197,23 +232,12 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||||
if (position == 0) {
|
if (position == 0 && !cloudProfile) {
|
||||||
backgroundProgress = positionOffset;
|
backgroundProgress = positionOffset;
|
||||||
} else {
|
} else {
|
||||||
backgroundProgress = 1f;
|
backgroundProgress = 1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (position == BOOSTY_INDEX) {
|
|
||||||
boostyProgress = 1f - positionOffset;
|
|
||||||
} else if (position == BOOSTY_INDEX - 1) {
|
|
||||||
boostyProgress = positionOffset;
|
|
||||||
} else {
|
|
||||||
boostyProgress = 0f;
|
|
||||||
}
|
|
||||||
if (profilesItem != null && profilesItem.recyclerView != null) {
|
|
||||||
profilesItem.recyclerView.setOverlayAlpha(1f - boostyProgress);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (position == REPOS_INDEX) {
|
if (position == REPOS_INDEX) {
|
||||||
if (!isReposLoaded && !isLoading) {
|
if (!isReposLoaded && !isLoading) {
|
||||||
loadRepos(true);
|
loadRepos(true);
|
||||||
@@ -293,7 +317,7 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
Log.e(TAG, "Failed to load vendor file " + iniUrl, error);
|
Log.e(TAG, "Failed to load vendor file " + iniUrl, error);
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
ViewUtils.postOnMainThread(() -> {
|
ViewUtils.postOnMainThread(() -> {
|
||||||
Toast.makeText(SliceBeam.INSTANCE, R.string.IntroFailedToLoadRepos, Toast.LENGTH_SHORT).show();
|
Toast.makeText(Santoku.INSTANCE, R.string.IntroFailedToLoadRepos, Toast.LENGTH_SHORT).show();
|
||||||
fakeScroll(-1);
|
fakeScroll(-1);
|
||||||
pager.setUserInputEnabled(true);
|
pager.setUserInputEnabled(true);
|
||||||
});
|
});
|
||||||
@@ -320,7 +344,7 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
isLoading = false;
|
isLoading = false;
|
||||||
Log.e(TAG, "Failed to load repo", error);
|
Log.e(TAG, "Failed to load repo", error);
|
||||||
ViewUtils.postOnMainThread(() -> {
|
ViewUtils.postOnMainThread(() -> {
|
||||||
Toast.makeText(SliceBeam.INSTANCE, R.string.IntroFailedToLoadRepos, Toast.LENGTH_SHORT).show();
|
Toast.makeText(Santoku.INSTANCE, R.string.IntroFailedToLoadRepos, Toast.LENGTH_SHORT).show();
|
||||||
fakeScroll(-1);
|
fakeScroll(-1);
|
||||||
pager.setUserInputEnabled(true);
|
pager.setUserInputEnabled(true);
|
||||||
});
|
});
|
||||||
@@ -333,14 +357,7 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
title.setTranslationY(ViewUtils.lerp(titleY, (ViewUtils.dp(52) - title.getHeight() * title.getScaleY()) / 2f, backgroundProgress));
|
invalidateTitleY();
|
||||||
float sc = ViewUtils.lerp(1, 22 / 32f, backgroundProgress);
|
|
||||||
title.setPivotX(title.getWidth() / 2f);
|
|
||||||
title.setPivotY(0);
|
|
||||||
title.setScaleX(sc);
|
|
||||||
title.setScaleY(sc);
|
|
||||||
int color = ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.textColorOnAccent), ThemesRepo.getColor(android.R.attr.colorAccent), backgroundProgress - boostyProgress);
|
|
||||||
title.setTextColor(color);
|
|
||||||
backgroundView.requestRender();
|
backgroundView.requestRender();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -353,7 +370,7 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
super.onSizeChanged(w, h, oldw, oldh);
|
super.onSizeChanged(w, h, oldw, oldh);
|
||||||
|
|
||||||
titleY = h / 4;
|
titleY = h / 4;
|
||||||
title.setTranslationY(ViewUtils.lerp(titleY, title.getPaddingTop(), backgroundProgress));
|
invalidateTitleY();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fl.setClipChildren(false);
|
fl.setClipChildren(false);
|
||||||
@@ -364,20 +381,23 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
super.surfaceDestroyed(holder);
|
super.surfaceDestroyed(holder);
|
||||||
backgroundModel.release();
|
backgroundModel.release();
|
||||||
backgroundModel = null;
|
backgroundModel = null;
|
||||||
GLShadersManager.clearShaders();
|
shadersManager.clearShaders();
|
||||||
|
shadersManager = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
backgroundView.setEGLContextClientVersion(3);
|
backgroundView.setEGLContextClientVersion(3);
|
||||||
backgroundView.setRenderer(new GLSurfaceView.Renderer() {
|
backgroundView.setRenderer(new GLSurfaceView.Renderer() {
|
||||||
@Override
|
@Override
|
||||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
public void onSurfaceCreated(GL10 gl, EGLConfig config) {}
|
||||||
backgroundModel = new GLModel();
|
|
||||||
backgroundModel.initBackgroundTriangles();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSurfaceChanged(GL10 gl, int width, int height) {
|
public void onSurfaceChanged(GL10 gl, int width, int height) {
|
||||||
glViewport(0, 0, width, height);
|
glViewport(0, 0, width, height);
|
||||||
|
if (backgroundModel == null) {
|
||||||
|
backgroundModel = new GLModel();
|
||||||
|
backgroundModel.initBackgroundTriangles();
|
||||||
|
shadersManager = new GLShadersManager();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private float time;
|
private float time;
|
||||||
@@ -393,18 +413,17 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
if (backgroundModel != null) {
|
if (backgroundModel != null) {
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
GLShaderProgram shader = GLShadersManager.get(GLShadersManager.SHADER_BEAM_INTRO);
|
GLShaderProgram shader = shadersManager.get(GLShadersManager.SHADER_BEAM_INTRO);
|
||||||
shader.startUsing();
|
shader.startUsing();
|
||||||
int topColor = ThemesRepo.getColor(android.R.attr.colorAccent);
|
int topColor = ThemesRepo.getColor(android.R.attr.colorAccent);
|
||||||
int bottomColor = ThemesRepo.getColor(android.R.attr.windowBackground);
|
int bottomColor = ThemesRepo.getColor(android.R.attr.windowBackground);
|
||||||
if (boostyProgress != 0f) {
|
if (cloudProfile) {
|
||||||
topColor = ColorUtils.blendARGB(bottomColor, ThemesRepo.getColor(R.attr.boostyColorTop), boostyProgress);
|
bottomColor = ColorUtils.blendARGB(bottomColor, topColor, 0.5f);
|
||||||
bottomColor = ColorUtils.blendARGB(bottomColor, ThemesRepo.getColor(R.attr.boostyColorBottom), boostyProgress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shader.setUniformColor("top_color", topColor);
|
shader.setUniformColor("top_color", topColor);
|
||||||
shader.setUniformColor("bottom_color", bottomColor);
|
shader.setUniformColor("bottom_color", bottomColor);
|
||||||
shader.setUniform("progress", backgroundProgress - (boostyProgress != 0 ? 1.2f : 0));
|
shader.setUniform("progress", backgroundProgress - (cloudProfile ? 1.4f : 0));
|
||||||
shader.setUniform("time", time);
|
shader.setUniform("time", time);
|
||||||
backgroundModel.render();
|
backgroundModel.render();
|
||||||
shader.stopUsing();
|
shader.stopUsing();
|
||||||
@@ -418,7 +437,7 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
title = new TextView(this);
|
title = new TextView(this);
|
||||||
title.setGravity(Gravity.CENTER);
|
title.setGravity(Gravity.CENTER);
|
||||||
title.setTypeface(Typeface.DEFAULT_BOLD);
|
title.setTypeface(Typeface.DEFAULT_BOLD);
|
||||||
title.setText(R.string.AppName);
|
title.setText(cloudProfile ? R.string.SettingsCloudManageTitle : R.string.AppName);
|
||||||
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 32);
|
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 32);
|
||||||
title.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
|
title.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
|
||||||
title.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL));
|
title.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL));
|
||||||
@@ -443,25 +462,54 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void invalidateTitleY() {
|
||||||
|
float sc = ViewUtils.lerp(1, 22 / 32f, backgroundProgress);
|
||||||
|
title.setPivotX(title.getWidth() / 2f);
|
||||||
|
title.setPivotY(0);
|
||||||
|
title.setScaleX(sc);
|
||||||
|
title.setScaleY(sc);
|
||||||
|
int color = ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.textColorOnAccent), ThemesRepo.getColor(android.R.attr.colorAccent), cloudProfile ? 0f : backgroundProgress);
|
||||||
|
title.setTextColor(color);
|
||||||
|
title.setTranslationY(ViewUtils.lerp(titleY, (ViewUtils.dp(52) - title.getHeight() * title.getScaleY()) / 2f, backgroundProgress));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|
||||||
SliceBeam.EVENT_BUS.unregisterListener(this);
|
Santoku.EVENT_BUS.unregisterListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
@EventHandler(runOnMainThread = true)
|
@EventHandler(runOnMainThread = true)
|
||||||
public void onDataUpdated(BeamServerDataUpdatedEvent e) {
|
public void onCloudSyncFinished(CloudSyncFinishedEvent e) {
|
||||||
if (!about) {
|
if (cloudProfile && Prefs.getCloudAPIToken() != null && cloudImport) {
|
||||||
boolean wasBoosty = BOOSTY_INDEX != -1;
|
finish();
|
||||||
if (wasBoosty != BeamServerData.isBoostyAvailable()) {
|
}
|
||||||
setItems();
|
if (!about && !cloudProfile) {
|
||||||
|
if (Prefs.getCloudAPIToken() != null) {
|
||||||
|
limitRepoFragmentCount = false;
|
||||||
|
limitProfileFragmentCount = false;
|
||||||
|
pager.getAdapter().notifyDataSetChanged();
|
||||||
|
pager.setCurrentItem(pager.getAdapter().getItemCount() - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventHandler(runOnMainThread = true)
|
||||||
|
public void onCloudAuthStateUpdated(CloudLoginStateUpdatedEvent e) {
|
||||||
|
if (cloudProfile) {
|
||||||
|
cloudItem.bindLoginButton(true);
|
||||||
|
}
|
||||||
|
if (!about && !cloudProfile && reposItem != null) {
|
||||||
|
reposItem.onCloudInfoUpdated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setItems() {
|
private void setItems() {
|
||||||
if (about) {
|
if (cloudProfile){
|
||||||
|
adapter.setItems(Collections.singletonList(cloudItem = new CloudProfileItem()));
|
||||||
|
} else if (about) {
|
||||||
adapter.setItems(Collections.singletonList(new AboutItem()));
|
adapter.setItems(Collections.singletonList(new AboutItem()));
|
||||||
} else {
|
} else {
|
||||||
List<SimpleRecyclerItem> items = new ArrayList<>(Arrays.asList(
|
List<SimpleRecyclerItem> items = new ArrayList<>(Arrays.asList(
|
||||||
@@ -469,13 +517,6 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
reposItem = new ReposItem(),
|
reposItem = new ReposItem(),
|
||||||
profilesItem = new ProfilesItem()));
|
profilesItem = new ProfilesItem()));
|
||||||
|
|
||||||
if (BeamServerData.isBoostyAvailable()) {
|
|
||||||
BOOSTY_INDEX = items.size();
|
|
||||||
items.add(new BoostyItem());
|
|
||||||
} else {
|
|
||||||
BOOSTY_INDEX = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
items.add(new FinishItem());
|
items.add(new FinishItem());
|
||||||
adapter.setItems(items);
|
adapter.setItems(items);
|
||||||
}
|
}
|
||||||
@@ -548,7 +589,7 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
Log.e(TAG, "Failed to load repos", error);
|
Log.e(TAG, "Failed to load repos", error);
|
||||||
if (fromPage) {
|
if (fromPage) {
|
||||||
ViewUtils.postOnMainThread(() -> {
|
ViewUtils.postOnMainThread(() -> {
|
||||||
Toast.makeText(SliceBeam.INSTANCE, R.string.IntroFailedToLoadRepos, Toast.LENGTH_SHORT).show();
|
Toast.makeText(Santoku.INSTANCE, R.string.IntroFailedToLoadRepos, Toast.LENGTH_SHORT).show();
|
||||||
fakeScroll(-1);
|
fakeScroll(-1);
|
||||||
pager.setUserInputEnabled(true);
|
pager.setUserInputEnabled(true);
|
||||||
});
|
});
|
||||||
@@ -595,6 +636,303 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
fakeScroller.start();
|
fakeScroller.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class CloudProfileItem extends SimpleRecyclerItem<View> {
|
||||||
|
private FrameLayout buttonView;
|
||||||
|
private TextView buttonText;
|
||||||
|
private ProgressBar buttonProgress;
|
||||||
|
private TextView titleView;
|
||||||
|
private TextView signUpButton;
|
||||||
|
private boolean signUpInProgress;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(Context ctx) {
|
||||||
|
LinearLayout ll = new LinearLayout(ctx);
|
||||||
|
ll.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
ll.setPadding(0, ViewUtils.dp(42), 0, 0);
|
||||||
|
|
||||||
|
titleView = new TextView(ctx);
|
||||||
|
titleView.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
|
||||||
|
titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||||
|
titleView.setGravity(Gravity.CENTER);
|
||||||
|
titleView.setPadding(ViewUtils.dp(12), 0, ViewUtils.dp(12), 0);
|
||||||
|
ll.addView(titleView);
|
||||||
|
bindHeader();
|
||||||
|
|
||||||
|
View spacer = new View(ctx);
|
||||||
|
ll.addView(spacer, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
|
||||||
|
|
||||||
|
TextView tosButton = new TextView(ctx);
|
||||||
|
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(ctx.getString(R.string.SettingsCloudManageTermsOfService)).append(" ");
|
||||||
|
Drawable dr = ContextCompat.getDrawable(ctx, R.drawable.external_link_outline_24);
|
||||||
|
int size = ViewUtils.dp(16);
|
||||||
|
dr.setBounds(0, 0, size, size);
|
||||||
|
sb.append("d", new TextColorImageSpan(dr, ViewUtils.dp(2f)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
tosButton.setText(sb);
|
||||||
|
tosButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
|
||||||
|
tosButton.setTextColor(Color.WHITE);
|
||||||
|
tosButton.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||||
|
tosButton.setGravity(Gravity.CENTER);
|
||||||
|
tosButton.setPadding(ViewUtils.dp(12), ViewUtils.dp(8), ViewUtils.dp(12), ViewUtils.dp(8));
|
||||||
|
tosButton.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
|
||||||
|
tosButton.setOnClickListener(v -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(BuildConfig.BEAM_BASE_URL_PROD + "/tos"))));
|
||||||
|
ll.addView(tosButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{
|
||||||
|
leftMargin = rightMargin = ViewUtils.dp(16);
|
||||||
|
bottomMargin = ViewUtils.dp(8);
|
||||||
|
}});
|
||||||
|
|
||||||
|
buttonView = new FrameLayout(ctx);
|
||||||
|
buttonView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), ThemesRepo.getColor(android.R.attr.colorAccent), 16));
|
||||||
|
|
||||||
|
buttonText = new TextView(ctx);
|
||||||
|
buttonText.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
|
||||||
|
buttonText.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||||
|
buttonText.setGravity(Gravity.CENTER);
|
||||||
|
buttonText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||||
|
buttonView.addView(buttonText, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
|
||||||
|
|
||||||
|
buttonProgress = new ProgressBar(ctx);
|
||||||
|
buttonProgress.setIndeterminateTintList(ColorStateList.valueOf(ThemesRepo.getColor(R.attr.textColorOnAccent)));
|
||||||
|
buttonView.addView(buttonProgress, new FrameLayout.LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28), Gravity.CENTER));
|
||||||
|
|
||||||
|
ll.addView(buttonView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{
|
||||||
|
leftMargin = rightMargin = ViewUtils.dp(16);
|
||||||
|
}});
|
||||||
|
|
||||||
|
signUpButton = new TextView(ctx);
|
||||||
|
signUpButton.setText(R.string.SettingsCloudManageButtonSignUp);
|
||||||
|
signUpButton.setTextColor(ThemesRepo.getColor(android.R.attr.colorAccent));
|
||||||
|
signUpButton.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||||
|
signUpButton.setGravity(Gravity.CENTER);
|
||||||
|
signUpButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||||
|
signUpButton.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
|
||||||
|
signUpButton.setOnClickListener(v -> showSignUpDialog(v.getContext()));
|
||||||
|
ll.addView(signUpButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{
|
||||||
|
leftMargin = rightMargin = ViewUtils.dp(16);
|
||||||
|
topMargin = ViewUtils.dp(8);
|
||||||
|
bottomMargin = ViewUtils.dp(16);
|
||||||
|
}});
|
||||||
|
|
||||||
|
bindLoginButton(false);
|
||||||
|
|
||||||
|
ll.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
return ll;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindLoginButton(boolean animate) {
|
||||||
|
boolean loggedIn = Prefs.getCloudAPIToken() != null;
|
||||||
|
boolean loading = !loggedIn && CloudController.isLoggingIn();
|
||||||
|
boolean wasLoading = buttonProgress.getTag() != null;
|
||||||
|
if (animate) {
|
||||||
|
if (wasLoading != loading) {
|
||||||
|
buttonProgress.setTag(loading ? 1 : null);
|
||||||
|
|
||||||
|
buttonProgress.animate().cancel();
|
||||||
|
buttonProgress.animate().scaleX(loading ? 1f : 0.4f).scaleY(loading ? 1f : 0.4f).alpha(loading ? 1f : 0f).setDuration(150).setInterpolator(ViewUtils.CUBIC_INTERPOLATOR).setListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationStart(Animator animation) {
|
||||||
|
if (loading) {
|
||||||
|
buttonProgress.setVisibility(View.VISIBLE);
|
||||||
|
buttonProgress.setAlpha(0f);
|
||||||
|
buttonProgress.setScaleX(0.4f);
|
||||||
|
buttonProgress.setScaleY(0.4f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
if (!loading) {
|
||||||
|
buttonProgress.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
buttonText.animate().cancel();
|
||||||
|
buttonText.animate().scaleX(!loading ? 1f : 0.4f).scaleY(!loading ? 1f : 0.4f).alpha(!loading ? 1f : 0f).setDuration(150).setInterpolator(ViewUtils.CUBIC_INTERPOLATOR).setListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationStart(Animator animation) {
|
||||||
|
if (!loading) {
|
||||||
|
buttonText.setVisibility(View.VISIBLE);
|
||||||
|
buttonText.setAlpha(0f);
|
||||||
|
buttonText.setScaleX(0.4f);
|
||||||
|
buttonText.setScaleY(0.4f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
if (loading) {
|
||||||
|
buttonText.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buttonProgress.setTag(loading ? 1 : null);
|
||||||
|
buttonProgress.setVisibility(loading ? View.VISIBLE : View.GONE);
|
||||||
|
buttonText.setVisibility(loading ? View.GONE : View.VISIBLE);
|
||||||
|
}
|
||||||
|
buttonText.setText(loggedIn ? R.string.SettingsCloudManageButtonManage : R.string.SettingsCloudManageButtonLogIn);
|
||||||
|
bindHeader();
|
||||||
|
buttonView.setOnClickListener(v-> {
|
||||||
|
if (loading) {
|
||||||
|
new BeamAlertDialogBuilder(v.getContext())
|
||||||
|
.setTitle(R.string.SettingsCloudManageButtonLogInCancelTitle)
|
||||||
|
.setMessage(R.string.SettingsCloudManageButtonLogInCancel)
|
||||||
|
.setNegativeButton(R.string.No, null)
|
||||||
|
.setPositiveButton(R.string.Yes, (dialog, which) -> CloudController.cancelLogin())
|
||||||
|
.show();
|
||||||
|
} else if (Prefs.getCloudAPIToken() != null) {
|
||||||
|
new CloudManageBottomSheet(v.getContext()).show();
|
||||||
|
} else {
|
||||||
|
CloudController.beginLogin();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (signUpButton != null) {
|
||||||
|
boolean showSignUp = !loggedIn && !loading;
|
||||||
|
signUpButton.setVisibility(showSignUp ? View.VISIBLE : View.GONE);
|
||||||
|
signUpButton.setEnabled(showSignUp && !signUpInProgress);
|
||||||
|
signUpButton.setAlpha(signUpButton.isEnabled() ? 1f : 0.6f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindHeader() {
|
||||||
|
if (titleView == null) return;
|
||||||
|
CloudAPI.UserInfo info = CloudController.getUserInfo();
|
||||||
|
if (Prefs.getCloudAPIToken() != null && info != null && info.displayName != null) {
|
||||||
|
titleView.setText(titleView.getContext().getString(R.string.SettingsCloudManageLoggedInAs, info.displayName));
|
||||||
|
} else {
|
||||||
|
titleView.setText(R.string.SettingsCloudManageDescription);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showSignUpDialog(Context ctx) {
|
||||||
|
if (signUpInProgress) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LinearLayout ll = new LinearLayout(ctx);
|
||||||
|
ll.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
ll.setPadding(ViewUtils.dp(16), ViewUtils.dp(8), ViewUtils.dp(16), 0);
|
||||||
|
|
||||||
|
TextView emailLabel = new TextView(ctx);
|
||||||
|
emailLabel.setText(R.string.SettingsCloudManageSignUpEmail);
|
||||||
|
emailLabel.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
|
||||||
|
emailLabel.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||||
|
ll.addView(emailLabel, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
EditText emailInput = new EditText(ctx);
|
||||||
|
emailInput.setHint(R.string.SettingsCloudManageSignUpEmail);
|
||||||
|
emailInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
|
||||||
|
emailInput.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||||
|
emailInput.setHintTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||||
|
ll.addView(emailInput, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
TextView displayNameLabel = new TextView(ctx);
|
||||||
|
displayNameLabel.setText(R.string.SettingsCloudManageSignUpDisplayName);
|
||||||
|
displayNameLabel.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
|
||||||
|
displayNameLabel.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||||
|
ll.addView(displayNameLabel, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||||
|
topMargin = ViewUtils.dp(12);
|
||||||
|
}});
|
||||||
|
|
||||||
|
EditText displayNameInput = new EditText(ctx);
|
||||||
|
displayNameInput.setHint(R.string.SettingsCloudManageSignUpDisplayName);
|
||||||
|
displayNameInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PERSON_NAME);
|
||||||
|
displayNameInput.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||||
|
displayNameInput.setHintTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||||
|
ll.addView(displayNameInput, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||||
|
topMargin = ViewUtils.dp(8);
|
||||||
|
}});
|
||||||
|
|
||||||
|
TextView passwordLabel = new TextView(ctx);
|
||||||
|
passwordLabel.setText(R.string.SettingsCloudManageSignUpPassword);
|
||||||
|
passwordLabel.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
|
||||||
|
passwordLabel.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||||
|
ll.addView(passwordLabel, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||||
|
topMargin = ViewUtils.dp(12);
|
||||||
|
}});
|
||||||
|
|
||||||
|
EditText passwordInput = new EditText(ctx);
|
||||||
|
passwordInput.setHint(R.string.SettingsCloudManageSignUpPassword);
|
||||||
|
passwordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||||
|
passwordInput.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||||
|
passwordInput.setHintTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||||
|
ll.addView(passwordInput, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||||
|
topMargin = ViewUtils.dp(8);
|
||||||
|
}});
|
||||||
|
|
||||||
|
CheckBox showPassword = new CheckBox(ctx);
|
||||||
|
showPassword.setText(R.string.SettingsCloudManageSignUpShowPassword);
|
||||||
|
showPassword.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||||
|
showPassword.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
|
int selection = passwordInput.getSelectionEnd();
|
||||||
|
if (isChecked) {
|
||||||
|
passwordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
|
||||||
|
} else {
|
||||||
|
passwordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||||
|
}
|
||||||
|
passwordInput.setSelection(Math.max(selection, 0));
|
||||||
|
});
|
||||||
|
ll.addView(showPassword, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||||
|
topMargin = ViewUtils.dp(4);
|
||||||
|
}});
|
||||||
|
|
||||||
|
TextView dialogTitle = new TextView(ctx);
|
||||||
|
dialogTitle.setText(R.string.SettingsCloudManageButtonSignUp);
|
||||||
|
dialogTitle.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||||
|
dialogTitle.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 28);
|
||||||
|
dialogTitle.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||||
|
dialogTitle.setGravity(Gravity.CENTER);
|
||||||
|
dialogTitle.setPadding(0, ViewUtils.dp(4), 0, ViewUtils.dp(8));
|
||||||
|
ll.addView(dialogTitle, 0, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
new BeamAlertDialogBuilder(ctx)
|
||||||
|
.setView(ll)
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.setPositiveButton(R.string.SettingsCloudManageButtonSignUp, (dialog, which) -> {
|
||||||
|
String email = emailInput.getText().toString().trim();
|
||||||
|
String displayName = displayNameInput.getText().toString().trim();
|
||||||
|
String password = passwordInput.getText().toString();
|
||||||
|
if (TextUtils.isEmpty(email) || TextUtils.isEmpty(displayName) || TextUtils.isEmpty(password)) {
|
||||||
|
Toast.makeText(ctx, R.string.SettingsCloudManageSignUpMissingFields, Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CloudAPI api = CloudController.getApiSafe();
|
||||||
|
if (api == null) {
|
||||||
|
Toast.makeText(ctx, R.string.SettingsCloudManageSignUpFailed, Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
signUpInProgress = true;
|
||||||
|
bindLoginButton(true);
|
||||||
|
api.signup(email, password, displayName, new APICallback<CloudAPI.AuthToken>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(CloudAPI.AuthToken response) {
|
||||||
|
Prefs.setCloudAPIToken(response.bearer);
|
||||||
|
signUpInProgress = false;
|
||||||
|
CloudController.init();
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onException(Exception e) {
|
||||||
|
signUpInProgress = false;
|
||||||
|
ViewUtils.postOnMainThread(() -> {
|
||||||
|
new BeamAlertDialogBuilder(ctx)
|
||||||
|
.setTitle(R.string.SettingsCloudManageSignUpFailed)
|
||||||
|
.setMessage(e.toString())
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show();
|
||||||
|
bindLoginButton(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private final class AboutItem extends SimpleRecyclerItem<View> {
|
private final class AboutItem extends SimpleRecyclerItem<View> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -724,6 +1062,8 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
private FrameLayout loadedLayout;
|
private FrameLayout loadedLayout;
|
||||||
private SimpleRecyclerAdapter adapter;
|
private SimpleRecyclerAdapter adapter;
|
||||||
|
private TextView cloudImportView;
|
||||||
|
private TextView cloudOrView;
|
||||||
private TextView customProfileView;
|
private TextView customProfileView;
|
||||||
private TextView buttonView;
|
private TextView buttonView;
|
||||||
|
|
||||||
@@ -740,10 +1080,29 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
LinearLayout ll = new LinearLayout(ctx);
|
LinearLayout ll = new LinearLayout(ctx);
|
||||||
ll.setOrientation(LinearLayout.VERTICAL);
|
ll.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
cloudImportView = new TextView(ctx);
|
||||||
|
cloudImportView.setVisibility(View.GONE);
|
||||||
|
cloudImportView.setText(R.string.IntroImportFromCloud);
|
||||||
|
cloudImportView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
|
||||||
|
cloudImportView.setGravity(Gravity.CENTER);
|
||||||
|
cloudImportView.setPadding(ViewUtils.dp(12), ViewUtils.dp(8), ViewUtils.dp(12), ViewUtils.dp(8));
|
||||||
|
cloudImportView.setOnClickListener(v -> startActivity(new Intent(v.getContext(), SetupActivity.class).putExtra(SetupActivity.EXTRA_CLOUD_PROFILE, true).putExtra(SetupActivity.EXTRA_CLOUD_IMPORT_FROM_SETUP, true)));
|
||||||
|
ll.addView(cloudImportView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(48)) {{
|
||||||
|
leftMargin = rightMargin = ViewUtils.dp(16);
|
||||||
|
}});
|
||||||
|
|
||||||
|
cloudOrView = new TextView(ctx);
|
||||||
|
cloudOrView.setVisibility(View.GONE);
|
||||||
|
cloudOrView.setText(R.string.IntroImportOr);
|
||||||
|
cloudOrView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||||
|
cloudOrView.setGravity(Gravity.CENTER);
|
||||||
|
ll.addView(cloudOrView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||||
|
leftMargin = rightMargin = ViewUtils.dp(16);
|
||||||
|
}});
|
||||||
|
|
||||||
customProfileView = new TextView(ctx);
|
customProfileView = new TextView(ctx);
|
||||||
customProfileView.setText(R.string.IntroCustomProfile);
|
customProfileView.setText(R.string.IntroCustomProfile);
|
||||||
customProfileView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
|
customProfileView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
|
||||||
customProfileView.setTextColor(ThemesRepo.getColor(android.R.attr.colorAccent));
|
|
||||||
customProfileView.setGravity(Gravity.CENTER);
|
customProfileView.setGravity(Gravity.CENTER);
|
||||||
customProfileView.setPadding(ViewUtils.dp(12), ViewUtils.dp(8), ViewUtils.dp(12), ViewUtils.dp(8));
|
customProfileView.setPadding(ViewUtils.dp(12), ViewUtils.dp(8), ViewUtils.dp(12), ViewUtils.dp(8));
|
||||||
customProfileView.setOnClickListener(v -> {
|
customProfileView.setOnClickListener(v -> {
|
||||||
@@ -752,8 +1111,9 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
SetupActivity.this.adapter.notifyItemRangeInserted(REPOS_INDEX + 1, SetupActivity.this.adapter.getItemCount() - REPOS_INDEX - 1);
|
SetupActivity.this.adapter.notifyItemRangeInserted(REPOS_INDEX + 1, SetupActivity.this.adapter.getItemCount() - REPOS_INDEX - 1);
|
||||||
scrollToNext();
|
scrollToNext();
|
||||||
});
|
});
|
||||||
ll.addView(customProfileView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
ll.addView(customProfileView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(48)) {{
|
||||||
bottomMargin = ViewUtils.dp(12);
|
leftMargin = rightMargin = ViewUtils.dp(16);
|
||||||
|
bottomMargin = ViewUtils.dp(6);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
buttonView = new TextView(ctx);
|
buttonView = new TextView(ctx);
|
||||||
@@ -800,12 +1160,18 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void onBindView(View view) {
|
public void onBindView(View view) {
|
||||||
progressBar.setIndeterminateTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.colorAccent)));
|
progressBar.setIndeterminateTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.colorAccent)));
|
||||||
|
cloudImportView.setTextColor(ThemesRepo.getColor(android.R.attr.colorAccent));
|
||||||
|
cloudImportView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
|
||||||
|
cloudImportView.setVisibility(Prefs.getCloudAPIToken() != null ? View.VISIBLE : View.GONE);
|
||||||
|
cloudOrView.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||||
|
cloudOrView.setVisibility(Prefs.getCloudAPIToken() != null ? View.VISIBLE : View.GONE);
|
||||||
customProfileView.setTextColor(ThemesRepo.getColor(android.R.attr.colorAccent));
|
customProfileView.setTextColor(ThemesRepo.getColor(android.R.attr.colorAccent));
|
||||||
|
customProfileView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
|
||||||
buttonView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), ThemesRepo.getColor(android.R.attr.colorAccent), 16));
|
buttonView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), ThemesRepo.getColor(android.R.attr.colorAccent), 16));
|
||||||
|
|
||||||
if (adapter.getItemCount() == 0 && isReposLoaded) {
|
if (adapter.getItemCount() == 0 && isReposLoaded) {
|
||||||
List<SimpleRecyclerItem> items = new ArrayList<>(repos);
|
List<SimpleRecyclerItem> items = new ArrayList<>(repos);
|
||||||
items.add(new TextHintRecyclerItem(SliceBeam.INSTANCE.getString(R.string.IntroSelectRepos)));
|
items.add(new TextHintRecyclerItem(Santoku.INSTANCE.getString(R.string.IntroSelectRepos)));
|
||||||
adapter.setItems(items);
|
adapter.setItems(items);
|
||||||
} else {
|
} else {
|
||||||
adapter.notifyDataSetChanged();
|
adapter.notifyDataSetChanged();
|
||||||
@@ -823,9 +1189,16 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onCloudInfoUpdated() {
|
||||||
|
if (cloudImportView != null) {
|
||||||
|
cloudImportView.setVisibility(Prefs.getCloudAPIToken() != null ? View.VISIBLE : View.GONE);
|
||||||
|
cloudOrView.setVisibility(Prefs.getCloudAPIToken() != null ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void onReposLoaded() {
|
public void onReposLoaded() {
|
||||||
List<SimpleRecyclerItem> items = new ArrayList<>(repos);
|
List<SimpleRecyclerItem> items = new ArrayList<>(repos);
|
||||||
items.add(new TextHintRecyclerItem(SliceBeam.INSTANCE.getString(R.string.IntroSelectRepos)));
|
items.add(new TextHintRecyclerItem(Santoku.INSTANCE.getString(R.string.IntroSelectRepos)));
|
||||||
adapter.setItems(items);
|
adapter.setItems(items);
|
||||||
new SpringAnimation(new FloatValueHolder(0))
|
new SpringAnimation(new FloatValueHolder(0))
|
||||||
.setMinimumVisibleChange(1 / 256f)
|
.setMinimumVisibleChange(1 / 256f)
|
||||||
@@ -979,63 +1352,6 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class BoostyItem extends SimpleRecyclerItem<View> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(Context ctx) {
|
|
||||||
LinearLayout ll = new LinearLayout(ctx);
|
|
||||||
ll.setOrientation(LinearLayout.VERTICAL);
|
|
||||||
ll.setPadding(0, ViewUtils.dp(42), 0, 0);
|
|
||||||
|
|
||||||
TextView title = new TextView(ctx);
|
|
||||||
title.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
|
|
||||||
title.setText(R.string.IntroBoostyTitle);
|
|
||||||
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
|
||||||
title.setGravity(Gravity.CENTER);
|
|
||||||
title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
|
||||||
title.setPadding(ViewUtils.dp(12), 0, ViewUtils.dp(12), 0);
|
|
||||||
ll.addView(title);
|
|
||||||
|
|
||||||
BoostySubsView subsView = new BoostySubsView(ctx);
|
|
||||||
if (SliceBeam.SERVER_DATA != null) {
|
|
||||||
List<String> list = new ArrayList<>(SliceBeam.SERVER_DATA.boostySubscribers);
|
|
||||||
Collections.shuffle(list);
|
|
||||||
subsView.setStrings(list);
|
|
||||||
}
|
|
||||||
ll.addView(subsView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f) {{
|
|
||||||
bottomMargin = ViewUtils.dp(64);
|
|
||||||
}});
|
|
||||||
|
|
||||||
TextView subscribeButton = new TextView(ctx);
|
|
||||||
subscribeButton.setText(R.string.IntroBoostySupport);
|
|
||||||
subscribeButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
|
|
||||||
subscribeButton.setTextColor(ThemesRepo.getColor(R.attr.boostyColorTop));
|
|
||||||
subscribeButton.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
|
||||||
subscribeButton.setGravity(Gravity.CENTER);
|
|
||||||
subscribeButton.setPadding(ViewUtils.dp(12), ViewUtils.dp(8), ViewUtils.dp(12), ViewUtils.dp(8));
|
|
||||||
subscribeButton.setOnClickListener(v -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://boosty.to/ytkab0bp"))));
|
|
||||||
ll.addView(subscribeButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
|
||||||
bottomMargin = ViewUtils.dp(12);
|
|
||||||
}});
|
|
||||||
|
|
||||||
TextView buttonView = new TextView(ctx);
|
|
||||||
buttonView.setText(R.string.IntroNext);
|
|
||||||
buttonView.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
|
|
||||||
buttonView.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
|
||||||
buttonView.setGravity(Gravity.CENTER);
|
|
||||||
buttonView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
|
||||||
buttonView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), ThemesRepo.getColor(R.attr.boostyColorTop), 16));
|
|
||||||
buttonView.setOnClickListener(v-> scrollToNext());
|
|
||||||
ll.addView(buttonView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{
|
|
||||||
leftMargin = rightMargin = ViewUtils.dp(16);
|
|
||||||
bottomMargin = ViewUtils.dp(16);
|
|
||||||
}});
|
|
||||||
|
|
||||||
ll.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
|
||||||
return ll;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class FinishItem extends SimpleRecyclerItem<View> {
|
private final class FinishItem extends SimpleRecyclerItem<View> {
|
||||||
private TextView buttonView;
|
private TextView buttonView;
|
||||||
|
|
||||||
@@ -1166,11 +1482,13 @@ public class SetupActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
SliceBeam.getCurrentConfigFile().delete();
|
if (Prefs.getCloudAPIToken() == null || Santoku.CONFIG == null) {
|
||||||
SliceBeam.CONFIG = cfg;
|
Santoku.getCurrentConfigFile().delete();
|
||||||
FileOutputStream fos = new FileOutputStream(SliceBeam.getConfigFile());
|
Santoku.CONFIG = cfg;
|
||||||
fos.write(cfg.serialize().getBytes(StandardCharsets.UTF_8));
|
FileOutputStream fos = new FileOutputStream(Santoku.getConfigFile());
|
||||||
fos.close();
|
fos.write(cfg.serialize().getBytes(StandardCharsets.UTF_8));
|
||||||
|
fos.close();
|
||||||
|
}
|
||||||
|
|
||||||
startActivity(new Intent(SetupActivity.this, MainActivity.class));
|
startActivity(new Intent(SetupActivity.this, MainActivity.class));
|
||||||
finish();
|
finish();
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
package com.dark98.santoku.boot;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.SparseBooleanArray;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import com.dark98.santoku.BuildConfig;
|
||||||
|
|
||||||
|
public class AppBoot {
|
||||||
|
private final static String TAG = "boot";
|
||||||
|
|
||||||
|
static ExecutorService executor = Executors.newCachedThreadPool();
|
||||||
|
static List<BootTask> tasks;
|
||||||
|
static List<Runnable> pendingMain = new ArrayList<>();
|
||||||
|
static List<BootTask> pendingTasks = new ArrayList<>();
|
||||||
|
static SparseBooleanArray completed = new SparseBooleanArray();
|
||||||
|
static CountDownLatch latch;
|
||||||
|
|
||||||
|
public static void run(List<BootTask> tasks) {
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
AppBoot.tasks = tasks;
|
||||||
|
int size = tasks.size();
|
||||||
|
for (int i = 0, s = tasks.size(); i < s; i++) {
|
||||||
|
BootTask task = tasks.get(i);
|
||||||
|
if (task.nonCritical) {
|
||||||
|
if (!task.workerThread) {
|
||||||
|
throw new IllegalArgumentException("Can't schedule non-critical task on main thread");
|
||||||
|
}
|
||||||
|
size--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AppBoot.latch = new CountDownLatch(size);
|
||||||
|
|
||||||
|
for (int i = 0, s = tasks.size(); i < s; i++) {
|
||||||
|
BootTask task = tasks.get(i);
|
||||||
|
task.index = i;
|
||||||
|
tryRunTask(task, true, false);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
while (!latch.await(10, TimeUnit.MILLISECONDS)) {
|
||||||
|
if (!pendingMain.isEmpty()) {
|
||||||
|
List<Runnable> clone = new ArrayList<>(pendingMain);
|
||||||
|
for (Runnable r : clone) {
|
||||||
|
r.run();
|
||||||
|
}
|
||||||
|
pendingMain.removeAll(clone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
Log.d(TAG, "Boot in " + (System.currentTimeMillis() - start) + "ms");
|
||||||
|
}
|
||||||
|
tryShutdown();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void tryShutdown() {
|
||||||
|
if (completed.size() == tasks.size()) {
|
||||||
|
executor.shutdown();
|
||||||
|
executor = null;
|
||||||
|
tasks = null;
|
||||||
|
pendingMain = null;
|
||||||
|
pendingTasks = null;
|
||||||
|
completed = null;
|
||||||
|
latch = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void tryRunTask(BootTask task, boolean fromMain, boolean isContinue) {
|
||||||
|
if (checkDependencies(task.dependencies)) {
|
||||||
|
Runnable r = () -> {
|
||||||
|
try {
|
||||||
|
task.run.run();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error while executing boot task", e);
|
||||||
|
}
|
||||||
|
completed.put(task.index, true);
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
Log.d(TAG, "Finish " + task);
|
||||||
|
}
|
||||||
|
if (!task.nonCritical) {
|
||||||
|
latch.countDown();
|
||||||
|
} else {
|
||||||
|
tryShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isContinue) {
|
||||||
|
continueTasks(fromMain);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (task.workerThread) {
|
||||||
|
executor.submit(r);
|
||||||
|
} else {
|
||||||
|
if (fromMain) {
|
||||||
|
r.run();
|
||||||
|
} else {
|
||||||
|
pendingMain.add(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pendingTasks.add(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void continueTasks(boolean fromMain) {
|
||||||
|
for (Iterator<BootTask> it = pendingTasks.iterator(); it.hasNext();) {
|
||||||
|
BootTask task = it.next();
|
||||||
|
if (checkDependencies(task.dependencies)) {
|
||||||
|
tryRunTask(task, fromMain, true);
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkDependencies(List<Class<?>> clzs) {
|
||||||
|
if (clzs.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0, s = tasks.size(); i < s; i++) {
|
||||||
|
if (clzs.contains(tasks.get(i).getClass())) {
|
||||||
|
if (!completed.get(i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.dark98.santoku.boot;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BootTask {
|
||||||
|
public final List<Class<?>> dependencies;
|
||||||
|
public final Runnable run;
|
||||||
|
public boolean workerThread;
|
||||||
|
public int priority;
|
||||||
|
public boolean nonCritical;
|
||||||
|
|
||||||
|
/* package */ int index;
|
||||||
|
|
||||||
|
public BootTask(Runnable run) {
|
||||||
|
this.dependencies = Collections.emptyList();
|
||||||
|
this.run = run;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BootTask(List<Class<?>> dependencies, Runnable run) {
|
||||||
|
this.dependencies = dependencies;
|
||||||
|
this.run = run;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BootTask onWorker() {
|
||||||
|
return onWorker(-20);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BootTask onWorker(int priority) {
|
||||||
|
this.workerThread = true;
|
||||||
|
this.priority = priority;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.dark98.santoku.boot;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.dark98.santoku.Santoku;
|
||||||
|
|
||||||
|
public class CheckUpdateJsonTask extends BootTask {
|
||||||
|
public CheckUpdateJsonTask() {
|
||||||
|
super(() -> {
|
||||||
|
try {
|
||||||
|
Santoku.INSTANCE.getAssets().open("update.json").close();
|
||||||
|
Santoku.hasUpdateInfo = true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Santoku.hasUpdateInfo = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
onWorker();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.dark98.santoku.boot;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import com.dark98.santoku.Santoku;
|
||||||
|
|
||||||
|
public class ClearModelCacheTask extends BootTask {
|
||||||
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
|
public ClearModelCacheTask() {
|
||||||
|
super(()->{
|
||||||
|
File cache = Santoku.getModelCacheDir();
|
||||||
|
if (cache.exists()) {
|
||||||
|
for (File f : cache.listFiles()) {
|
||||||
|
f.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
nonCritical = true;
|
||||||
|
onWorker();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.dark98.santoku.boot;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import com.dark98.santoku.cloud.CloudController;
|
||||||
|
|
||||||
|
public class CloudCachedInitTask extends BootTask {
|
||||||
|
public CloudCachedInitTask() {
|
||||||
|
super(Collections.singletonList(PrefsTask.class), CloudController::initCached);
|
||||||
|
onWorker();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.dark98.santoku.boot;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import com.dark98.santoku.cloud.CloudController;
|
||||||
|
|
||||||
|
public class CloudInitTask extends BootTask {
|
||||||
|
public CloudInitTask() {
|
||||||
|
super(Arrays.asList(PrefsTask.class, TrueTimeTask.class, LoadSlic3rConfigTask.class, CloudCachedInitTask.class), CloudController::init);
|
||||||
|
onWorker();
|
||||||
|
nonCritical = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.dark98.santoku.boot;
|
||||||
|
|
||||||
|
import ru.ytkab0bp.eventbus.EventBus;
|
||||||
|
import com.dark98.santoku.BuildConfig;
|
||||||
|
|
||||||
|
public class EventBusTask extends BootTask {
|
||||||
|
|
||||||
|
public EventBusTask() {
|
||||||
|
super(() -> {
|
||||||
|
String appId = BuildConfig.APPLICATION_ID;
|
||||||
|
if (BuildConfig.DEBUG && appId.endsWith(".debug")) {
|
||||||
|
appId = appId.substring(0, appId.length() - ".debug".length());
|
||||||
|
}
|
||||||
|
EventBus.registerImpl(appId);
|
||||||
|
});
|
||||||
|
onWorker();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.dark98.santoku.boot;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.dark98.santoku.Santoku;
|
||||||
|
import com.dark98.santoku.slic3r.Slic3rConfigWrapper;
|
||||||
|
|
||||||
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
|
public class LoadSlic3rConfigTask extends BootTask {
|
||||||
|
public LoadSlic3rConfigTask() {
|
||||||
|
super(() -> {
|
||||||
|
File cfgFile = Santoku.getConfigFile();
|
||||||
|
Santoku.getCurrentConfigFile().delete();
|
||||||
|
if (cfgFile.exists()) {
|
||||||
|
try {
|
||||||
|
Santoku.CONFIG = new Slic3rConfigWrapper(cfgFile);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
onWorker();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.dark98.santoku.boot;
|
||||||
|
|
||||||
|
import com.dark98.santoku.Santoku;
|
||||||
|
import com.dark98.santoku.utils.Prefs;
|
||||||
|
|
||||||
|
public class PrefsTask extends BootTask {
|
||||||
|
public PrefsTask() {
|
||||||
|
super(()->Prefs.init(Santoku.INSTANCE));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.dark98.santoku.boot;
|
||||||
|
|
||||||
|
import com.dark98.santoku.slic3r.PrintConfigDef;
|
||||||
|
|
||||||
|
public class PrintConfigWarmupTask extends BootTask {
|
||||||
|
public PrintConfigWarmupTask() {
|
||||||
|
super(PrintConfigDef::getInstance);
|
||||||
|
onWorker();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package com.dark98.santoku.boot;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.instacart.truetime.TrueTimeEventListener;
|
||||||
|
import com.instacart.truetime.time.TrueTimeImpl;
|
||||||
|
import com.instacart.truetime.time.TrueTimeParameters;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers;
|
||||||
|
import com.dark98.santoku.Santoku;
|
||||||
|
|
||||||
|
public class TrueTimeTask extends BootTask {
|
||||||
|
public TrueTimeTask() {
|
||||||
|
super(() -> {
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
Santoku.TRUE_TIME = new TrueTimeImpl(new TrueTimeParameters.Builder().buildParams(), Dispatchers.getIO(), new TrueTimeEventListener() {
|
||||||
|
@Override
|
||||||
|
public void initialize(@NonNull TrueTimeParameters trueTimeParameters) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeSuccess(@NonNull long[] longs) {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeFailed(@NonNull Exception e) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void nextInitializeIn(long l) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resolvedNtpHostToIPs(@NonNull String s, @NonNull List<? extends InetAddress> list) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void lastSntpRequestAttempt(@NonNull InetAddress inetAddress) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sntpRequestFailed(@NonNull Exception e) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void syncDispatcherException(@NonNull Throwable throwable) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sntpRequest(@NonNull InetAddress inetAddress) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sntpRequestSuccessful(@NonNull InetAddress inetAddress) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sntpRequestFailed(@NonNull InetAddress inetAddress, @NonNull Exception e) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void storingTrueTime(@NonNull long[] longs) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void returningTrueTime(@NonNull Date date) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void returningDeviceTime() {}
|
||||||
|
});
|
||||||
|
Santoku.TRUE_TIME.sync();
|
||||||
|
try {
|
||||||
|
latch.await(300, TimeUnit.MILLISECONDS);
|
||||||
|
} catch (InterruptedException ignored) {}
|
||||||
|
});
|
||||||
|
onWorker();
|
||||||
|
nonCritical = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.dark98.santoku.boot;
|
||||||
|
|
||||||
|
import com.dark98.santoku.Santoku;
|
||||||
|
import com.dark98.santoku.utils.VibrationUtils;
|
||||||
|
|
||||||
|
public class VibrationUtilsTask extends BootTask {
|
||||||
|
|
||||||
|
public VibrationUtilsTask() {
|
||||||
|
super(() -> VibrationUtils.init(Santoku.INSTANCE));
|
||||||
|
onWorker();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
package com.dark98.santoku.cloud;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ru.ytkab0bp.sapil.APICallback;
|
||||||
|
import ru.ytkab0bp.sapil.APILibrary;
|
||||||
|
import ru.ytkab0bp.sapil.APIRequestHandle;
|
||||||
|
import ru.ytkab0bp.sapil.APIRunner;
|
||||||
|
import ru.ytkab0bp.sapil.Arg;
|
||||||
|
import ru.ytkab0bp.sapil.Header;
|
||||||
|
import ru.ytkab0bp.sapil.Method;
|
||||||
|
import ru.ytkab0bp.sapil.RequestType;
|
||||||
|
import com.dark98.santoku.BuildConfig;
|
||||||
|
import com.dark98.santoku.utils.Prefs;
|
||||||
|
|
||||||
|
public interface CloudAPI extends APIRunner {
|
||||||
|
CloudAPI INSTANCE = APILibrary.newRunner(CloudAPI.class, new RunnerConfig() {
|
||||||
|
private final Map<String, String> headers = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getBaseURL() {
|
||||||
|
return BuildConfig.CLOUD_BASE_URL_PROD;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDefaultUserAgent() {
|
||||||
|
return "Santoku v" + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_CODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getDefaultHeaders() {
|
||||||
|
headers.clear();
|
||||||
|
if (Prefs.getCloudAPIToken() != null) {
|
||||||
|
headers.put("Authorization", "Bearer " + Prefs.getCloudAPIToken());
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ru.ytkab0bp.sapil.util.Pair<String, String> getNamingTransformPolicy() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begins login flow, returns auth link
|
||||||
|
*/
|
||||||
|
@Method("login/begin")
|
||||||
|
APIRequestHandle loginBegin(APICallback<LoginData> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks new login state by session id
|
||||||
|
*/
|
||||||
|
@Method("login/check")
|
||||||
|
void loginCheck(@Arg("sessionId") String sessionId, APICallback<LoginState> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels login flow
|
||||||
|
*/
|
||||||
|
@Method("login/cancel")
|
||||||
|
void loginCancel(@Arg("sessionId") String sessionId, APICallback<Boolean> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets current user info
|
||||||
|
* <p>
|
||||||
|
* Requires authorization
|
||||||
|
*/
|
||||||
|
@Method("user/getInfo")
|
||||||
|
void userGetInfo(APICallback<UserInfo> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new account (email/password)
|
||||||
|
*/
|
||||||
|
@Method(requestType = RequestType.POST, value = "signup")
|
||||||
|
void signup(@Arg("email") String email, @Arg("password") String password, @Arg("displayName") String displayName, APICallback<AuthToken> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login with email/password
|
||||||
|
*/
|
||||||
|
@Method(requestType = RequestType.POST, value = "login")
|
||||||
|
void login(@Arg("email") String email, @Arg("password") String password, APICallback<AuthToken> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches sync state
|
||||||
|
* <p>
|
||||||
|
* Requires authorization
|
||||||
|
*/
|
||||||
|
@Method("sync/getState")
|
||||||
|
void syncGetState(APICallback<SyncState> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads new data to the server
|
||||||
|
* <p>
|
||||||
|
* @param data New base64 encoded data
|
||||||
|
* <p>
|
||||||
|
* Requires authorization
|
||||||
|
*/
|
||||||
|
@Method(requestType = RequestType.POST, value = "sync/upload")
|
||||||
|
void syncUpload(@Arg("") String data, @Header("Content-Type") String type, APICallback<SyncState> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads base64 data
|
||||||
|
* <p>
|
||||||
|
* Requires authorization
|
||||||
|
*/
|
||||||
|
@Method("sync/get")
|
||||||
|
void syncGet(APICallback<String> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys token
|
||||||
|
* <p>
|
||||||
|
* Requires authorization
|
||||||
|
*/
|
||||||
|
@Method("logout")
|
||||||
|
void logout(APICallback<Boolean> callback);
|
||||||
|
|
||||||
|
final class LoginData {
|
||||||
|
/**
|
||||||
|
* Url that should be clicked by the user to authorize
|
||||||
|
*/
|
||||||
|
public String url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session identifier
|
||||||
|
*/
|
||||||
|
public String sessionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time at which session should be considered expired if not logged in
|
||||||
|
*/
|
||||||
|
public long expiresAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
final class LoginState {
|
||||||
|
/**
|
||||||
|
* If user is now logged in
|
||||||
|
*/
|
||||||
|
public boolean loggedIn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bearer token if auth was successful
|
||||||
|
*/
|
||||||
|
public String bearer;
|
||||||
|
}
|
||||||
|
|
||||||
|
final class AuthToken {
|
||||||
|
/**
|
||||||
|
* Bearer token
|
||||||
|
*/
|
||||||
|
public String bearer;
|
||||||
|
}
|
||||||
|
|
||||||
|
final class UserInfo {
|
||||||
|
/**
|
||||||
|
* User's id
|
||||||
|
*/
|
||||||
|
public String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User's display name
|
||||||
|
*/
|
||||||
|
public String displayName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User's avatar. Could be null
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public String avatarUrl;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
final class SyncState {
|
||||||
|
/**
|
||||||
|
* Cloud data last updated time
|
||||||
|
*/
|
||||||
|
public long lastUpdatedDate = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used size of cloud storage
|
||||||
|
*/
|
||||||
|
public long usedSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max storage size
|
||||||
|
*/
|
||||||
|
public long maxSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,420 @@
|
|||||||
|
package com.dark98.santoku.cloud;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import ru.ytkab0bp.sapil.APICallback;
|
||||||
|
import ru.ytkab0bp.sapil.APIRequestHandle;
|
||||||
|
import com.dark98.santoku.R;
|
||||||
|
import com.dark98.santoku.Santoku;
|
||||||
|
import com.dark98.santoku.components.BeamAlertDialogBuilder;
|
||||||
|
import com.dark98.santoku.events.CloudLoginStateUpdatedEvent;
|
||||||
|
import com.dark98.santoku.events.CloudSyncFinishedEvent;
|
||||||
|
import com.dark98.santoku.events.CloudUserInfoUpdatedEvent;
|
||||||
|
import com.dark98.santoku.events.NeedDismissSnackbarEvent;
|
||||||
|
import com.dark98.santoku.events.NeedSnackbarEvent;
|
||||||
|
import com.dark98.santoku.slic3r.Slic3rConfigWrapper;
|
||||||
|
import com.dark98.santoku.utils.IOUtils;
|
||||||
|
import com.dark98.santoku.utils.Prefs;
|
||||||
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
import com.dark98.santoku.view.SnackbarsLayout;
|
||||||
|
|
||||||
|
public class CloudController {
|
||||||
|
public final static String CLOUD_SYNC_TAG = "cloud_sync";
|
||||||
|
|
||||||
|
private final static String TAG = "cloud";
|
||||||
|
private final static long MIN_SYNC_DELTA = 5 * 60 * 1000L; // Once in 5 minutes
|
||||||
|
private static boolean isSyncInProgress;
|
||||||
|
private static CloudAPI.UserInfo userInfo;
|
||||||
|
|
||||||
|
private static boolean isLoggingIn;
|
||||||
|
private static APIRequestHandle beginLoginHandle;
|
||||||
|
private static String loginSessionId;
|
||||||
|
private static Runnable loginAutoCancel = () -> {
|
||||||
|
loginSessionId = null;
|
||||||
|
isLoggingIn = false;
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
|
||||||
|
};
|
||||||
|
private static Runnable loginCheck = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
CloudAPI api = getApiSafe();
|
||||||
|
if (api == null) {
|
||||||
|
loginSessionId = null;
|
||||||
|
isLoggingIn = false;
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
api.loginCheck(loginSessionId, new APICallback<CloudAPI.LoginState>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(CloudAPI.LoginState response) {
|
||||||
|
if (response.loggedIn) {
|
||||||
|
Prefs.setCloudAPIToken(response.bearer);
|
||||||
|
loadUserInfo();
|
||||||
|
ViewUtils.removeCallbacks(loginAutoCancel);
|
||||||
|
} else if (isLoggingIn) {
|
||||||
|
ViewUtils.postOnMainThread(loginCheck, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onException(Exception e) {
|
||||||
|
Log.e(TAG, "Failed to check login state", e);
|
||||||
|
|
||||||
|
if (isLoggingIn) {
|
||||||
|
ViewUtils.postOnMainThread(loginCheck, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static Gson gson = new Gson();
|
||||||
|
|
||||||
|
public static CloudAPI getApiSafe() {
|
||||||
|
try {
|
||||||
|
return CloudAPI.INSTANCE;
|
||||||
|
} catch (Throwable t) {
|
||||||
|
Log.e(TAG, "Cloud API unavailable", t);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void initCached() {
|
||||||
|
if (Prefs.getCloudAPIToken() != null) {
|
||||||
|
if (Prefs.getCloudCachedUserInfo() != null) {
|
||||||
|
userInfo = gson.fromJson(Prefs.getCloudCachedUserInfo(), CloudAPI.UserInfo.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
if (Prefs.getCloudAPIToken() != null) {
|
||||||
|
long now = Santoku.TRUE_TIME.now().getTime();
|
||||||
|
if (userInfo == null) {
|
||||||
|
loadUserInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userInfo != null && isSyncAvailable() && Prefs.isCloudProfileSyncEnabled()) {
|
||||||
|
if (now - Prefs.getCloudLastSync() > MIN_SYNC_DELTA) {
|
||||||
|
syncData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadUserInfo() {
|
||||||
|
CloudAPI api = getApiSafe();
|
||||||
|
if (api == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
api.userGetInfo(new APICallback<CloudAPI.UserInfo>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(CloudAPI.UserInfo response) {
|
||||||
|
userInfo = response;
|
||||||
|
|
||||||
|
if (userInfo.id.equals("null")) {
|
||||||
|
userInfo = null;
|
||||||
|
Prefs.setCloudAPIToken(null);
|
||||||
|
Prefs.setCloudCachedUserInfo(null);
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent());
|
||||||
|
|
||||||
|
if (isLoggingIn) {
|
||||||
|
isLoggingIn = false;
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Prefs.setCloudCachedUserInfo(gson.toJson(userInfo));
|
||||||
|
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent());
|
||||||
|
|
||||||
|
if (isLoggingIn) {
|
||||||
|
isLoggingIn = false;
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSyncAvailable() && Prefs.isCloudProfileSyncEnabled()) {
|
||||||
|
syncData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onException(Exception e) {
|
||||||
|
Log.e(TAG, "Failed to get user info", e);
|
||||||
|
ViewUtils.postOnMainThread(CloudController::init, 15000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isLoggingIn() {
|
||||||
|
return isLoggingIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void beginLogin0() {
|
||||||
|
CloudAPI api = getApiSafe();
|
||||||
|
if (api == null) {
|
||||||
|
isLoggingIn = false;
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
beginLoginHandle = api.loginBegin(new APICallback<CloudAPI.LoginData>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(CloudAPI.LoginData response) {
|
||||||
|
loginSessionId = response.sessionId;
|
||||||
|
|
||||||
|
ViewUtils.postOnMainThread(loginAutoCancel, response.expiresAt * 1000L - Santoku.TRUE_TIME.now().getTime());
|
||||||
|
ViewUtils.postOnMainThread(loginCheck, 5000);
|
||||||
|
ViewUtils.postOnMainThread(() -> Santoku.INSTANCE.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(response.url)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onException(Exception e) {
|
||||||
|
ViewUtils.postOnMainThread(CloudController::beginLogin0, 15000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void beginLogin() {
|
||||||
|
isLoggingIn = true;
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
|
||||||
|
beginLogin0();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void cancelLogin() {
|
||||||
|
isLoggingIn = false;
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
|
||||||
|
if (loginSessionId != null) {
|
||||||
|
CloudAPI api = getApiSafe();
|
||||||
|
if (api != null) {
|
||||||
|
api.loginCancel(loginSessionId, response -> {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (beginLoginHandle != null && beginLoginHandle.isRunning()) {
|
||||||
|
beginLoginHandle.cancel();
|
||||||
|
beginLoginHandle = null;
|
||||||
|
}
|
||||||
|
ViewUtils.removeCallbacks(loginCheck);
|
||||||
|
ViewUtils.removeCallbacks(loginAutoCancel);
|
||||||
|
loginSessionId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void logout() {
|
||||||
|
CloudAPI api = getApiSafe();
|
||||||
|
if (api != null) {
|
||||||
|
api.logout(response -> {});
|
||||||
|
}
|
||||||
|
Prefs.setCloudAPIToken(null);
|
||||||
|
userInfo = null;
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CloudAPI.UserInfo getUserInfo() {
|
||||||
|
return userInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasAccountFeatures() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isSyncAvailable() {
|
||||||
|
return Prefs.getCloudAPIToken() != null && userInfo != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void downloadData(long lastModified) {
|
||||||
|
CloudAPI api = getApiSafe();
|
||||||
|
if (api == null) {
|
||||||
|
isSyncInProgress = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
api.syncGet(new APICallback<String>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(String response) {
|
||||||
|
IOUtils.IO_POOL.submit(() -> {
|
||||||
|
try {
|
||||||
|
File f = Santoku.getConfigFile();
|
||||||
|
byte[] data = Base64.decode(response, 0);
|
||||||
|
FileOutputStream fos = new FileOutputStream(f);
|
||||||
|
fos.write(data);
|
||||||
|
fos.close();
|
||||||
|
|
||||||
|
Santoku.CONFIG = new Slic3rConfigWrapper(f);
|
||||||
|
|
||||||
|
Prefs.setCloudLocalLastModified(lastModified);
|
||||||
|
Prefs.setCloudLocalLastSentModified(lastModified);
|
||||||
|
Prefs.setCloudRemoteLastModified(lastModified);
|
||||||
|
Prefs.setCloudLastSync(Santoku.TRUE_TIME.now().getTime());
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.CloudSyncSuccess));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Failed to write data", e);
|
||||||
|
isSyncInProgress = false;
|
||||||
|
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onException(Exception e) {
|
||||||
|
Log.e(TAG, "Failed to download data", e);
|
||||||
|
isSyncInProgress = false;
|
||||||
|
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void syncData() {
|
||||||
|
if (isSyncInProgress) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CloudAPI api = getApiSafe();
|
||||||
|
if (api == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
long modified = Prefs.getCloudLocalLastModified();
|
||||||
|
isSyncInProgress = true;
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.CloudSyncInProgress).tag(CLOUD_SYNC_TAG));
|
||||||
|
|
||||||
|
api.syncGetState(new APICallback<CloudAPI.SyncState>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(CloudAPI.SyncState response) {
|
||||||
|
if (Santoku.CONFIG == null && response.usedSize != 0) {
|
||||||
|
// Setup screen, no config yet
|
||||||
|
downloadData(response.lastUpdatedDate);
|
||||||
|
} else if (response.usedSize == 0) {
|
||||||
|
if (Santoku.CONFIG == null) {
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No data on server yet, send anyway
|
||||||
|
uploadData(modified);
|
||||||
|
} else if (response.lastUpdatedDate != Prefs.getCloudRemoteLastModified()) {
|
||||||
|
if (Prefs.getCloudLocalLastSentModified() == modified) {
|
||||||
|
// Modified only on server
|
||||||
|
downloadData(response.lastUpdatedDate);
|
||||||
|
} else {
|
||||||
|
// Modified on client and on server
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.WARNING, R.string.CloudSyncConflict).button(R.string.CloudSyncConflictResolve, v -> {
|
||||||
|
SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy HH:mm", Locale.getDefault());
|
||||||
|
new BeamAlertDialogBuilder(v.getContext())
|
||||||
|
.setTitle(R.string.CloudSyncConflict)
|
||||||
|
.setMessage(v.getContext().getString(R.string.CloudSyncConflictResolveMessage, format.format(new Date(response.lastUpdatedDate)), format.format(new Date(Prefs.getCloudLocalLastModified()))))
|
||||||
|
.setPositiveButton(R.string.CloudSyncConflictChooseRemote, (dialog, which) -> downloadData(response.lastUpdatedDate))
|
||||||
|
.setNegativeButton(R.string.CloudSyncConflictChooseLocal, (dialog, which) -> uploadData(modified))
|
||||||
|
.show();
|
||||||
|
}).tag(CLOUD_SYNC_TAG));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (Prefs.getCloudLocalLastSentModified() != modified) {
|
||||||
|
// Modified only on client
|
||||||
|
uploadData(modified);
|
||||||
|
} else {
|
||||||
|
// Not modified on server and on client
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onException(Exception e) {
|
||||||
|
Log.e(TAG, "Failed to get sync state", e);
|
||||||
|
isSyncInProgress = false;
|
||||||
|
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void uploadData(long modified) {
|
||||||
|
IOUtils.IO_POOL.submit(() -> {
|
||||||
|
try {
|
||||||
|
File f = Santoku.getConfigFile();
|
||||||
|
FileInputStream fis = new FileInputStream(f);
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
byte[] buffer = new byte[10240];
|
||||||
|
int c;
|
||||||
|
while ((c = fis.read(buffer)) != -1) {
|
||||||
|
bos.write(buffer, 0, c);
|
||||||
|
}
|
||||||
|
bos.close();
|
||||||
|
fis.close();
|
||||||
|
|
||||||
|
CloudAPI api = getApiSafe();
|
||||||
|
if (api == null) {
|
||||||
|
isSyncInProgress = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
api.syncUpload(Base64.encodeToString(bos.toByteArray(), Base64.NO_WRAP), "application/ini", new APICallback<CloudAPI.SyncState>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(CloudAPI.SyncState response) {
|
||||||
|
isSyncInProgress = false;
|
||||||
|
if (Prefs.getCloudLocalLastModified() != modified) { // Re-send otherwise
|
||||||
|
syncData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Prefs.setCloudRemoteLastModified(response.lastUpdatedDate);
|
||||||
|
Prefs.setCloudLocalLastSentModified(modified);
|
||||||
|
Prefs.setCloudLastSync(Santoku.TRUE_TIME.now().getTime());
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.CloudSyncSuccess));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onException(Exception e) {
|
||||||
|
Log.e(TAG, "Failed to upload sync data", e);
|
||||||
|
isSyncInProgress = false;
|
||||||
|
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Failed to read sync data", e);
|
||||||
|
isSyncInProgress = false;
|
||||||
|
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void notifyDataChanged() {
|
||||||
|
long now = Santoku.TRUE_TIME.now().getTime();
|
||||||
|
Prefs.setCloudLocalLastModified(now);
|
||||||
|
if (!isSyncAvailable() || !Prefs.isCloudProfileSyncEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (now - Prefs.getCloudLastSync() > MIN_SYNC_DELTA) {
|
||||||
|
syncData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-4
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.components;
|
package com.dark98.santoku.components;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -21,9 +21,9 @@ import androidx.appcompat.widget.AppCompatCheckedTextView;
|
|||||||
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
|
||||||
public class BeamAlertDialogBuilder extends MaterialAlertDialogBuilder {
|
public class BeamAlertDialogBuilder extends MaterialAlertDialogBuilder {
|
||||||
public BeamAlertDialogBuilder(@NonNull Context context) {
|
public BeamAlertDialogBuilder(@NonNull Context context) {
|
||||||
+4
-4
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.components;
|
package com.dark98.santoku.components;
|
||||||
|
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -8,9 +8,9 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import com.mrudultora.colorpicker.ColorPickerPopUp;
|
import com.mrudultora.colorpicker.ColorPickerPopUp;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
|
||||||
public class BeamColorPickerPopUp extends ColorPickerPopUp {
|
public class BeamColorPickerPopUp extends ColorPickerPopUp {
|
||||||
public BeamColorPickerPopUp(Context context) {
|
public BeamColorPickerPopUp(Context context) {
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
package com.dark98.santoku.components;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.drawable.GradientDrawable;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import com.dark98.santoku.R;
|
||||||
|
import com.dark98.santoku.Santoku;
|
||||||
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
import com.dark98.santoku.view.BeamButton;
|
||||||
|
|
||||||
|
public class ChangeLogBottomSheet extends BottomSheetDialog {
|
||||||
|
private ScrollView scrollView;
|
||||||
|
|
||||||
|
public ChangeLogBottomSheet(@NonNull Context context) {
|
||||||
|
super(context);
|
||||||
|
|
||||||
|
LinearLayout ll = new LinearLayout(context);
|
||||||
|
ll.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
GradientDrawable gd = new GradientDrawable();
|
||||||
|
gd.setCornerRadii(new float[] {
|
||||||
|
ViewUtils.dp(28), ViewUtils.dp(28),
|
||||||
|
ViewUtils.dp(28), ViewUtils.dp(28),
|
||||||
|
0, 0,
|
||||||
|
0, 0
|
||||||
|
});
|
||||||
|
gd.setColor(ThemesRepo.getColor(R.attr.dialogBackground));
|
||||||
|
ll.setBackground(gd);
|
||||||
|
ll.setPadding(0, ViewUtils.dp(12), 0, ViewUtils.dp(12));
|
||||||
|
|
||||||
|
FrameLayout fl = new FrameLayout(context);
|
||||||
|
TextView titleA = new TextView(context);
|
||||||
|
titleA.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20);
|
||||||
|
titleA.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||||
|
titleA.setText(R.string.Changelog);
|
||||||
|
titleA.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||||
|
titleA.setGravity(Gravity.CENTER);
|
||||||
|
titleA.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||||
|
leftMargin = rightMargin = ViewUtils.dp(21);
|
||||||
|
}});
|
||||||
|
fl.addView(titleA);
|
||||||
|
|
||||||
|
ll.addView(fl);
|
||||||
|
|
||||||
|
scrollView = new ScrollView(context);
|
||||||
|
TextView text = new TextView(context);
|
||||||
|
text.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||||
|
text.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||||
|
text.setPadding(ViewUtils.dp(16), ViewUtils.dp(12), ViewUtils.dp(16), ViewUtils.dp(12));
|
||||||
|
|
||||||
|
try {
|
||||||
|
InputStream in = getContext().getAssets().open("update.json");
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
byte[] buffer = new byte[10240]; int c;
|
||||||
|
while ((c = in.read(buffer)) != -1) {
|
||||||
|
bos.write(buffer, 0, c);
|
||||||
|
}
|
||||||
|
bos.close();
|
||||||
|
in.close();
|
||||||
|
|
||||||
|
JSONObject obj = new JSONObject(bos.toString());
|
||||||
|
String code = Locale.getDefault().getLanguage();
|
||||||
|
if (obj.has(code)) {
|
||||||
|
text.setText(obj.getString(code));
|
||||||
|
} else {
|
||||||
|
text.setText(obj.getString("en"));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e("Changelog", "Failed to open update file", e);
|
||||||
|
}
|
||||||
|
scrollView.addView(text);
|
||||||
|
|
||||||
|
DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||||
|
ll.addView(scrollView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) (dm.heightPixels * 0.45f)));
|
||||||
|
|
||||||
|
BeamButton btn = new BeamButton(context);
|
||||||
|
btn.setText(R.string.ChangelogOK);
|
||||||
|
btn.setOnClickListener(v -> dismiss());
|
||||||
|
ll.addView(btn, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(48)) {{
|
||||||
|
leftMargin = topMargin = rightMargin = bottomMargin = ViewUtils.dp(12);
|
||||||
|
}});
|
||||||
|
|
||||||
|
ll.setFitsSystemWindows(true);
|
||||||
|
setContentView(ll);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void show() {
|
||||||
|
super.show();
|
||||||
|
getBehavior().setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
package com.dark98.santoku.components;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.graphics.drawable.GradientDrawable;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.graphics.ColorUtils;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.dark98.santoku.R;
|
||||||
|
import com.dark98.santoku.Santoku;
|
||||||
|
import com.dark98.santoku.BuildConfig;
|
||||||
|
import com.dark98.santoku.cloud.CloudController;
|
||||||
|
import com.dark98.santoku.events.NeedDismissSnackbarEvent;
|
||||||
|
import com.dark98.santoku.recycler.PreferenceSwitchItem;
|
||||||
|
import com.dark98.santoku.recycler.SimpleRecyclerAdapter;
|
||||||
|
import com.dark98.santoku.recycler.SimpleRecyclerItem;
|
||||||
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
|
import com.dark98.santoku.utils.Prefs;
|
||||||
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
import com.dark98.santoku.view.TextColorImageSpan;
|
||||||
|
|
||||||
|
public class CloudManageBottomSheet extends BottomSheetDialog {
|
||||||
|
public CloudManageBottomSheet(@NonNull Context context) {
|
||||||
|
super(context);
|
||||||
|
|
||||||
|
LinearLayout ll = new LinearLayout(context);
|
||||||
|
ll.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
GradientDrawable gd = new GradientDrawable();
|
||||||
|
gd.setCornerRadii(new float[] {
|
||||||
|
ViewUtils.dp(28), ViewUtils.dp(28),
|
||||||
|
ViewUtils.dp(28), ViewUtils.dp(28),
|
||||||
|
0, 0,
|
||||||
|
0, 0
|
||||||
|
});
|
||||||
|
gd.setColor(ThemesRepo.getColor(R.attr.dialogBackground));
|
||||||
|
ll.setBackground(gd);
|
||||||
|
ll.setPadding(0, ViewUtils.dp(12), 0, ViewUtils.dp(12));
|
||||||
|
|
||||||
|
TextView title = new TextView(context);
|
||||||
|
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20);
|
||||||
|
title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||||
|
title.setText(R.string.SettingsCloudManageButtonManage);
|
||||||
|
title.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||||
|
title.setGravity(Gravity.CENTER);
|
||||||
|
title.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||||
|
leftMargin = rightMargin = ViewUtils.dp(21);
|
||||||
|
}});
|
||||||
|
ll.addView(title);
|
||||||
|
|
||||||
|
List<SimpleRecyclerItem> items = new ArrayList<>();
|
||||||
|
items.add(new PreferenceSwitchItem()
|
||||||
|
.setIcon(R.drawable.sync_outline_28)
|
||||||
|
.setTitle(context.getString(R.string.SettingsCloudManageFeatureCloudSync))
|
||||||
|
.setValueProvider(Prefs::isCloudProfileSyncEnabled)
|
||||||
|
.setChangeListener((buttonView, isChecked) -> {
|
||||||
|
Prefs.setCloudProfileSyncEnabled(isChecked);
|
||||||
|
if (isChecked) {
|
||||||
|
CloudController.notifyDataChanged();
|
||||||
|
} else {
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CloudController.CLOUD_SYNC_TAG));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
RecyclerView recyclerView = new RecyclerView(context);
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(context));
|
||||||
|
recyclerView.setBackground(ViewUtils.createRipple(0, ColorUtils.setAlphaComponent(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 0x10), 16));
|
||||||
|
SimpleRecyclerAdapter adapter = new SimpleRecyclerAdapter();
|
||||||
|
adapter.setItems(items);
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
|
ll.addView(recyclerView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||||
|
topMargin = ViewUtils.dp(16);
|
||||||
|
leftMargin = rightMargin = ViewUtils.dp(16);
|
||||||
|
}});
|
||||||
|
|
||||||
|
TextView manageButton = new TextView(context);
|
||||||
|
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(context.getString(R.string.SettingsCloudManageSubscription)).append(" ");
|
||||||
|
Drawable dr = ContextCompat.getDrawable(context, R.drawable.external_link_outline_24);
|
||||||
|
int size = ViewUtils.dp(16);
|
||||||
|
dr.setBounds(0, 0, size, size);
|
||||||
|
sb.append("d", new TextColorImageSpan(dr, ViewUtils.dp(2f)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
manageButton.setText(sb);
|
||||||
|
manageButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
|
||||||
|
manageButton.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||||
|
manageButton.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||||
|
manageButton.setGravity(Gravity.CENTER);
|
||||||
|
manageButton.setPadding(ViewUtils.dp(12), ViewUtils.dp(8), ViewUtils.dp(12), ViewUtils.dp(8));
|
||||||
|
manageButton.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
|
||||||
|
manageButton.setOnClickListener(v -> {
|
||||||
|
String url = BuildConfig.BEAM_BASE_URL_PROD + "/account";
|
||||||
|
String token = Prefs.getCloudAPIToken();
|
||||||
|
if (token != null) {
|
||||||
|
Uri uri = Uri.parse(url);
|
||||||
|
url = uri.buildUpon().appendQueryParameter("token", token).build().toString();
|
||||||
|
}
|
||||||
|
v.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
|
||||||
|
});
|
||||||
|
ll.addView(manageButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(48)) {{
|
||||||
|
leftMargin = rightMargin = ViewUtils.dp(16);
|
||||||
|
topMargin = bottomMargin = ViewUtils.dp(6);
|
||||||
|
}});
|
||||||
|
|
||||||
|
TextView buttonView = new TextView(context);
|
||||||
|
buttonView.setText(R.string.SettingsCloudManageButtonLogOut);
|
||||||
|
buttonView.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
|
||||||
|
buttonView.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||||
|
buttonView.setGravity(Gravity.CENTER);
|
||||||
|
buttonView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||||
|
buttonView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), ThemesRepo.getColor(R.attr.textColorNegative), 16));
|
||||||
|
buttonView.setOnClickListener(v-> {
|
||||||
|
CloudController.logout();
|
||||||
|
dismiss();
|
||||||
|
});
|
||||||
|
ll.addView(buttonView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{
|
||||||
|
leftMargin = rightMargin = ViewUtils.dp(16);
|
||||||
|
bottomMargin = ViewUtils.dp(4);
|
||||||
|
}});
|
||||||
|
|
||||||
|
setContentView(ll);
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
-9
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.components;
|
package com.dark98.santoku.components;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
@@ -21,12 +21,12 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ru.ytkab0bp.eventbus.EventHandler;
|
import ru.ytkab0bp.eventbus.EventHandler;
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.events.SlicingProgressEvent;
|
import com.dark98.santoku.events.SlicingProgressEvent;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
|
import com.dark98.santoku.slic3r.Slic3rLocalization;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
|
||||||
public class SliceProgressBottomSheet extends BottomSheetDialog {
|
public class SliceProgressBottomSheet extends BottomSheetDialog {
|
||||||
private RecyclerView recyclerView;
|
private RecyclerView recyclerView;
|
||||||
@@ -122,12 +122,12 @@ public class SliceProgressBottomSheet extends BottomSheetDialog {
|
|||||||
@Override
|
@Override
|
||||||
public void onAttachedToWindow() {
|
public void onAttachedToWindow() {
|
||||||
super.onAttachedToWindow();
|
super.onAttachedToWindow();
|
||||||
SliceBeam.EVENT_BUS.registerListener(this);
|
Santoku.EVENT_BUS.registerListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDetachedFromWindow() {
|
public void onDetachedFromWindow() {
|
||||||
super.onDetachedFromWindow();
|
super.onDetachedFromWindow();
|
||||||
SliceBeam.EVENT_BUS.unregisterListener(this);
|
Santoku.EVENT_BUS.unregisterListener(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+9
-6
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.components;
|
package com.dark98.santoku.components;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
@@ -18,10 +18,10 @@ import androidx.dynamicanimation.animation.FloatValueHolder;
|
|||||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||||
import androidx.dynamicanimation.animation.SpringForce;
|
import androidx.dynamicanimation.animation.SpringForce;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
|
import com.dark98.santoku.fragment.BedFragment;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
import ru.ytkab0bp.slicebeam.view.MirrorView;
|
import com.dark98.santoku.view.MirrorView;
|
||||||
|
|
||||||
public abstract class UnfoldMenu {
|
public abstract class UnfoldMenu {
|
||||||
protected BedFragment fragment;
|
protected BedFragment fragment;
|
||||||
@@ -204,10 +204,13 @@ public abstract class UnfoldMenu {
|
|||||||
}
|
}
|
||||||
fragment.getGlView().invalidate();
|
fragment.getGlView().invalidate();
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.addEndListener((animation, canceled, value, velocity) -> onShown());
|
||||||
spring.start();
|
spring.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void onShown() {}
|
||||||
|
|
||||||
public void relayout() {
|
public void relayout() {
|
||||||
FrameLayout into = containerLayout;
|
FrameLayout into = containerLayout;
|
||||||
boolean portrait = into.getWidth() < into.getHeight();
|
boolean portrait = into.getWidth() < into.getHeight();
|
||||||
+13
-13
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.components;
|
package com.dark98.santoku.components;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -23,14 +23,14 @@ import java.io.File;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.BuildConfig;
|
import com.dark98.santoku.BuildConfig;
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.events.NeedDismissCalibrationsMenu;
|
import com.dark98.santoku.events.NeedDismissCalibrationsMenu;
|
||||||
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
|
import com.dark98.santoku.fragment.BedFragment;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
import ru.ytkab0bp.slicebeam.view.DividerView;
|
import com.dark98.santoku.view.DividerView;
|
||||||
|
|
||||||
public class WebViewMenu extends UnfoldMenu {
|
public class WebViewMenu extends UnfoldMenu {
|
||||||
private final Uri uri;
|
private final Uri uri;
|
||||||
@@ -93,10 +93,10 @@ public class WebViewMenu extends UnfoldMenu {
|
|||||||
return super.dispatchTouchEvent(ev);
|
return super.dispatchTouchEvent(ev);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
webView.addJavascriptInterface(new Bridge(), "SliceBeam");
|
webView.addJavascriptInterface(new Bridge(), "Santoku");
|
||||||
|
|
||||||
WebSettings settings = webView.getSettings();
|
WebSettings settings = webView.getSettings();
|
||||||
settings.setUserAgentString(String.format(Locale.ROOT, "SliceBeam/%s-%d", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
|
settings.setUserAgentString(String.format(Locale.ROOT, "Santoku/%s-%d", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
|
||||||
settings.setJavaScriptEnabled(true);
|
settings.setJavaScriptEnabled(true);
|
||||||
settings.setDomStorageEnabled(true);
|
settings.setDomStorageEnabled(true);
|
||||||
settings.setDatabaseEnabled(true);
|
settings.setDatabaseEnabled(true);
|
||||||
@@ -131,7 +131,7 @@ public class WebViewMenu extends UnfoldMenu {
|
|||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
public void beginDownload(String filename) {
|
public void beginDownload(String filename) {
|
||||||
cacheFile = new File(SliceBeam.getModelCacheDir(), filename);
|
cacheFile = new File(Santoku.getModelCacheDir(), filename);
|
||||||
try {
|
try {
|
||||||
fileStream = new FileOutputStream(cacheFile);
|
fileStream = new FileOutputStream(cacheFile);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -155,7 +155,7 @@ public class WebViewMenu extends UnfoldMenu {
|
|||||||
|
|
||||||
ViewUtils.postOnMainThread(() -> {
|
ViewUtils.postOnMainThread(() -> {
|
||||||
dismiss(true);
|
dismiss(true);
|
||||||
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissCalibrationsMenu());
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissCalibrationsMenu());
|
||||||
ViewUtils.postOnMainThread(() -> fragment.loadGCode(cacheFile), 200);
|
ViewUtils.postOnMainThread(() -> fragment.loadGCode(cacheFile), 200);
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
+3
-3
@@ -1,12 +1,12 @@
|
|||||||
package ru.ytkab0bp.slicebeam.components.bed_menu;
|
package com.dark98.santoku.components.bed_menu;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.CallSuper;
|
import androidx.annotation.CallSuper;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
|
import com.dark98.santoku.fragment.BedFragment;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Bed3D;
|
import com.dark98.santoku.slic3r.Bed3D;
|
||||||
|
|
||||||
public abstract class BedMenu {
|
public abstract class BedMenu {
|
||||||
private View view;
|
private View view;
|
||||||
+102
-6
@@ -1,10 +1,14 @@
|
|||||||
package ru.ytkab0bp.slicebeam.components.bed_menu;
|
package com.dark98.santoku.components.bed_menu;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
|
import android.graphics.PointF;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.graphics.PorterDuffColorFilter;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
@@ -16,17 +20,24 @@ import android.widget.LinearLayout;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.core.graphics.ColorUtils;
|
import androidx.core.graphics.ColorUtils;
|
||||||
import androidx.dynamicanimation.animation.FloatValueHolder;
|
import androidx.dynamicanimation.animation.FloatValueHolder;
|
||||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||||
import androidx.dynamicanimation.animation.SpringForce;
|
import androidx.dynamicanimation.animation.SpringForce;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import java.util.ArrayList;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
|
import java.util.Iterator;
|
||||||
import ru.ytkab0bp.slicebeam.theme.IThemeView;
|
import java.util.List;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.R;
|
||||||
|
import com.dark98.santoku.Santoku;
|
||||||
|
import com.dark98.santoku.recycler.SimpleRecyclerItem;
|
||||||
|
import com.dark98.santoku.theme.IThemeView;
|
||||||
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
|
import com.dark98.santoku.utils.RandomUtils;
|
||||||
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
|
||||||
public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolderView> {
|
public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolderView> {
|
||||||
public final int titleRes;
|
public final int titleRes;
|
||||||
@@ -36,6 +47,7 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
|
|||||||
public boolean isEnabled = true;
|
public boolean isEnabled = true;
|
||||||
public boolean isChecked = false;
|
public boolean isChecked = false;
|
||||||
public boolean isCheckable = false;
|
public boolean isCheckable = false;
|
||||||
|
public boolean isShiny = false;
|
||||||
public View.OnClickListener clickListener;
|
public View.OnClickListener clickListener;
|
||||||
public CompoundButton.OnCheckedChangeListener checkedChangeListener;
|
public CompoundButton.OnCheckedChangeListener checkedChangeListener;
|
||||||
|
|
||||||
@@ -61,6 +73,11 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BedMenuItem setShiny(boolean shiny) {
|
||||||
|
isShiny = shiny;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public BedMenuItem setSingleLine(boolean singleLine) {
|
public BedMenuItem setSingleLine(boolean singleLine) {
|
||||||
isSingleLine = singleLine;
|
isSingleLine = singleLine;
|
||||||
return this;
|
return this;
|
||||||
@@ -77,6 +94,9 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final static class BedMenuItemHolderView extends LinearLayout implements IThemeView {
|
public final static class BedMenuItemHolderView extends LinearLayout implements IThemeView {
|
||||||
|
private final static float IN_BOUND = 0.05f;
|
||||||
|
private final static float OUT_BOUND = 0.1f;
|
||||||
|
|
||||||
private ImageView icon;
|
private ImageView icon;
|
||||||
private TextView title;
|
private TextView title;
|
||||||
|
|
||||||
@@ -84,8 +104,13 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
|
|||||||
private Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
private Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
|
||||||
private Path path = new Path();
|
private Path path = new Path();
|
||||||
|
private Path path2 = new Path();
|
||||||
private float checkedProgress;
|
private float checkedProgress;
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
|
private boolean shiny;
|
||||||
|
private List<Sparkle> sparkles;
|
||||||
|
private long lastDraw;
|
||||||
|
private Drawable sparkleDrawable;
|
||||||
|
|
||||||
public BedMenuItemHolderView(Context context) {
|
public BedMenuItemHolderView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
@@ -109,12 +134,17 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
|
|||||||
setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT) {{
|
setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT) {{
|
||||||
leftMargin = topMargin = bottomMargin = ViewUtils.dp(6);
|
leftMargin = topMargin = bottomMargin = ViewUtils.dp(6);
|
||||||
}});
|
}});
|
||||||
|
setClipToPadding(false);
|
||||||
|
setClipChildren(false);
|
||||||
setWillNotDraw(false);
|
setWillNotDraw(false);
|
||||||
onApplyTheme();
|
onApplyTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(@NonNull Canvas canvas) {
|
public void draw(@NonNull Canvas canvas) {
|
||||||
|
long dt = Math.min(System.currentTimeMillis() - lastDraw, 16);
|
||||||
|
lastDraw = System.currentTimeMillis();
|
||||||
|
|
||||||
int rad = ViewUtils.dp(16);
|
int rad = ViewUtils.dp(16);
|
||||||
canvas.drawRoundRect(0, 0, getWidth(), getHeight(), rad, rad, bgPaint);
|
canvas.drawRoundRect(0, 0, getWidth(), getHeight(), rad, rad, bgPaint);
|
||||||
|
|
||||||
@@ -133,6 +163,61 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
|
|||||||
}
|
}
|
||||||
|
|
||||||
super.draw(canvas);
|
super.draw(canvas);
|
||||||
|
|
||||||
|
if (shiny) {
|
||||||
|
float side = Math.min(getWidth(), getHeight());
|
||||||
|
canvas.save();
|
||||||
|
if (sparkles == null) sparkles = new ArrayList<>();
|
||||||
|
if (sparkleDrawable == null) {
|
||||||
|
sparkleDrawable = ContextCompat.getDrawable(Santoku.INSTANCE, R.drawable.sparkle_28);
|
||||||
|
sparkleDrawable.setColorFilter(new PorterDuffColorFilter(ThemesRepo.getColor(android.R.attr.colorAccent), PorterDuff.Mode.SRC_IN));
|
||||||
|
}
|
||||||
|
|
||||||
|
float p = dt / 1000f;
|
||||||
|
for (Iterator<Sparkle> iterator = sparkles.iterator(); iterator.hasNext(); ) {
|
||||||
|
Sparkle sparkle = iterator.next();
|
||||||
|
sparkle.position.x += sparkle.velocity.x * p;
|
||||||
|
sparkle.position.y += sparkle.velocity.y * p;
|
||||||
|
sparkle.velocity.x *= 0.9999f;
|
||||||
|
sparkle.velocity.y *= 0.9999f;
|
||||||
|
sparkle.living += dt;
|
||||||
|
|
||||||
|
int size = (int) (side * sparkle.size);
|
||||||
|
|
||||||
|
float fadems = 200;
|
||||||
|
if ((sparkle.position.x - sparkle.size > 0 && sparkle.position.x + sparkle.size < 1f) &&
|
||||||
|
sparkle.lifetime - sparkle.living > fadems) {
|
||||||
|
sparkle.living = (long) (sparkle.lifetime - fadems);
|
||||||
|
}
|
||||||
|
if (sparkle.living >= sparkle.lifetime) {
|
||||||
|
iterator.remove();
|
||||||
|
} else {
|
||||||
|
float alpha = sparkle.living < fadems ? sparkle.living / fadems : sparkle.living > sparkle.lifetime - fadems ? (sparkle.lifetime - sparkle.living) / fadems : 1f;
|
||||||
|
canvas.saveLayerAlpha(-OUT_BOUND * side, -OUT_BOUND * side, getWidth() + OUT_BOUND * side, getHeight() + OUT_BOUND * side, (int) (alpha * sparkle.alpha * 0xFF));
|
||||||
|
canvas.translate(sparkle.position.x * side, sparkle.position.y * side);
|
||||||
|
sparkleDrawable.setBounds(-size / 2, -size / 2, size / 2, size / 2);
|
||||||
|
sparkleDrawable.draw(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sparkles.size() < 20) {
|
||||||
|
int s = 20 - sparkles.size();
|
||||||
|
for (int i = 0; i < s; i++) {
|
||||||
|
if (RandomUtils.RANDOM.nextFloat() < 0.01f) {
|
||||||
|
Sparkle sparkle = new Sparkle();
|
||||||
|
boolean leftSide = RandomUtils.RANDOM.nextBoolean();
|
||||||
|
sparkle.position = new PointF(leftSide ? RandomUtils.randomf(-OUT_BOUND, 0) : RandomUtils.randomf(1, 1 + OUT_BOUND), RandomUtils.randomf(-OUT_BOUND, 1 + OUT_BOUND));
|
||||||
|
sparkle.velocity = new PointF(RandomUtils.randomf(-0.05f, 0.05f), RandomUtils.randomf(-0.05f, 0.05f));
|
||||||
|
sparkle.size = RandomUtils.randomf(0.1f, 0.12f);
|
||||||
|
sparkle.alpha = RandomUtils.randomf(0.5f, 1f);
|
||||||
|
sparkle.lifetime = RandomUtils.randoml(4000, 10000);
|
||||||
|
sparkles.add(sparkle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
invalidate();
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -146,10 +231,12 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
|
|||||||
|
|
||||||
public void bind(BedMenuItem item) {
|
public void bind(BedMenuItem item) {
|
||||||
enabled = item.isEnabled;
|
enabled = item.isEnabled;
|
||||||
|
shiny = item.isShiny;
|
||||||
title.setMaxLines(item.isSingleLine ? 1 : 2);
|
title.setMaxLines(item.isSingleLine ? 1 : 2);
|
||||||
title.setText(item.titleRes);
|
title.setText(item.titleRes);
|
||||||
icon.setImageResource(item.iconRes);
|
icon.setImageResource(item.iconRes);
|
||||||
checkedProgress = item.isCheckable && item.isChecked ? 1 : 0;
|
checkedProgress = item.isCheckable && item.isChecked ? 1 : 0;
|
||||||
|
onApplyTheme();
|
||||||
title.setTextColor(ColorUtils.blendARGB(ThemesRepo.getColor(android.R.attr.textColorPrimary), ThemesRepo.getColor(R.attr.textColorOnAccent), checkedProgress));
|
title.setTextColor(ColorUtils.blendARGB(ThemesRepo.getColor(android.R.attr.textColorPrimary), ThemesRepo.getColor(R.attr.textColorOnAccent), checkedProgress));
|
||||||
icon.setImageTintList(ColorStateList.valueOf(ColorUtils.blendARGB(ThemesRepo.getColor(android.R.attr.textColorSecondary), ThemesRepo.getColor(R.attr.textColorOnAccent), checkedProgress)));
|
icon.setImageTintList(ColorStateList.valueOf(ColorUtils.blendARGB(ThemesRepo.getColor(android.R.attr.textColorSecondary), ThemesRepo.getColor(R.attr.textColorOnAccent), checkedProgress)));
|
||||||
|
|
||||||
@@ -187,5 +274,14 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
|
|||||||
bgPaint.setColor(ColorUtils.setAlphaComponent(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 0x10));
|
bgPaint.setColor(ColorUtils.setAlphaComponent(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 0x10));
|
||||||
accentPaint.setColor(ThemesRepo.getColor(android.R.attr.colorAccent));
|
accentPaint.setColor(ThemesRepo.getColor(android.R.attr.colorAccent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final static class Sparkle {
|
||||||
|
private PointF position;
|
||||||
|
private PointF velocity;
|
||||||
|
private float size;
|
||||||
|
private float alpha;
|
||||||
|
private long lifetime;
|
||||||
|
private long living;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+26
-11
@@ -1,5 +1,6 @@
|
|||||||
package ru.ytkab0bp.slicebeam.components.bed_menu;
|
package com.dark98.santoku.components.bed_menu;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.dynamicanimation.animation.FloatValueHolder;
|
import androidx.dynamicanimation.animation.FloatValueHolder;
|
||||||
@@ -9,15 +10,16 @@ import androidx.dynamicanimation.animation.SpringForce;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
|
import com.dark98.santoku.components.BeamAlertDialogBuilder;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
|
import com.dark98.santoku.recycler.SimpleRecyclerItem;
|
||||||
import ru.ytkab0bp.slicebeam.render.Camera;
|
import com.dark98.santoku.recycler.SpaceItem;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Bed3D;
|
import com.dark98.santoku.render.Camera;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Prefs;
|
import com.dark98.santoku.slic3r.Bed3D;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Vec3d;
|
import com.dark98.santoku.utils.Prefs;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.Vec3d;
|
||||||
import ru.ytkab0bp.slicebeam.view.GLView;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
import com.dark98.santoku.view.GLView;
|
||||||
|
|
||||||
public class CameraMenu extends ListBedMenu {
|
public class CameraMenu extends ListBedMenu {
|
||||||
private boolean checkInvalidBed() {
|
private boolean checkInvalidBed() {
|
||||||
@@ -122,7 +124,20 @@ public class CameraMenu extends ListBedMenu {
|
|||||||
animateTo(toOrigin, toPosition);
|
animateTo(toOrigin, toPosition);
|
||||||
}),
|
}),
|
||||||
new SpaceItem(portrait ? ViewUtils.dp(8) : 0, portrait ? 0 : ViewUtils.dp(8)),
|
new SpaceItem(portrait ? ViewUtils.dp(8) : 0, portrait ? 0 : ViewUtils.dp(8)),
|
||||||
new BedMenuItem(R.string.MenuCameraEnableRotation, R.drawable.sync_outline_28).setCheckable((buttonView, isChecked) -> Prefs.setRotationEnabled(isChecked), Prefs.isRotationEnabled()),
|
new BedMenuItem(R.string.MenuCameraControlMode, R.drawable.rectangle_hand_point_up_28).onClick(v -> {
|
||||||
|
Context ctx = v.getContext();
|
||||||
|
new BeamAlertDialogBuilder(v.getContext())
|
||||||
|
.setTitle(R.string.MenuCameraControlModeFull)
|
||||||
|
.setSingleChoiceItems(new CharSequence[] {
|
||||||
|
ctx.getString(R.string.MenuCameraControlModeOne),
|
||||||
|
ctx.getString(R.string.MenuCameraControlModeTwo),
|
||||||
|
ctx.getString(R.string.MenuCameraControlModeThree)
|
||||||
|
}, Prefs.getCameraControlMode(), (dialog, which) -> {
|
||||||
|
Prefs.setCameraControlMode(which);
|
||||||
|
dialog.dismiss();
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
}),
|
||||||
new BedMenuItem(R.string.MenuCameraOrtho, R.drawable.image_format_32).setCheckable((buttonView, isChecked) -> {
|
new BedMenuItem(R.string.MenuCameraOrtho, R.drawable.image_format_32).setCheckable((buttonView, isChecked) -> {
|
||||||
Prefs.setOrthoProjectionEnabled(isChecked);
|
Prefs.setOrthoProjectionEnabled(isChecked);
|
||||||
fragment.getGlView().getRenderer().updateProjection();
|
fragment.getGlView().getRenderer().updateProjection();
|
||||||
+84
-48
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.components.bed_menu;
|
package com.dark98.santoku.components.bed_menu;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -20,6 +20,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -30,33 +31,37 @@ import java.util.regex.Matcher;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import ru.ytkab0bp.eventbus.EventHandler;
|
import ru.ytkab0bp.eventbus.EventHandler;
|
||||||
import ru.ytkab0bp.slicebeam.MainActivity;
|
import com.dark98.santoku.MainActivity;
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
|
import com.dark98.santoku.components.BeamAlertDialogBuilder;
|
||||||
import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
|
import com.dark98.santoku.components.UnfoldMenu;
|
||||||
import ru.ytkab0bp.slicebeam.components.WebViewMenu;
|
import com.dark98.santoku.components.WebViewMenu;
|
||||||
import ru.ytkab0bp.slicebeam.config.ConfigObject;
|
import com.dark98.santoku.config.ConfigObject;
|
||||||
import ru.ytkab0bp.slicebeam.events.NeedDismissCalibrationsMenu;
|
import com.dark98.santoku.events.NeedDismissCalibrationsMenu;
|
||||||
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
|
import com.dark98.santoku.events.NeedDismissSnackbarEvent;
|
||||||
import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
|
import com.dark98.santoku.events.NeedSnackbarEvent;
|
||||||
import ru.ytkab0bp.slicebeam.events.SelectedObjectChangedEvent;
|
import com.dark98.santoku.events.ObjectsListChangedEvent;
|
||||||
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
|
import com.dark98.santoku.events.SelectedObjectChangedEvent;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.PreferenceItem;
|
import com.dark98.santoku.fragment.BedFragment;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerAdapter;
|
import com.dark98.santoku.recycler.PreferenceItem;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
|
import com.dark98.santoku.recycler.SimpleRecyclerAdapter;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
|
import com.dark98.santoku.recycler.SimpleRecyclerItem;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Bed3D;
|
import com.dark98.santoku.recycler.SpaceItem;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Slic3rRuntimeError;
|
import com.dark98.santoku.slic3r.Bed3D;
|
||||||
import ru.ytkab0bp.slicebeam.theme.BeamTheme;
|
import com.dark98.santoku.slic3r.Slic3rRuntimeError;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.BeamTheme;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.view.DividerView;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
import ru.ytkab0bp.slicebeam.view.FadeRecyclerView;
|
import com.dark98.santoku.view.DividerView;
|
||||||
|
import com.dark98.santoku.view.FadeRecyclerView;
|
||||||
|
import com.dark98.santoku.view.SnackbarsLayout;
|
||||||
|
|
||||||
public class FileMenu extends ListBedMenu {
|
public class FileMenu extends ListBedMenu {
|
||||||
private final static List<String> K3D_SUPPORTED_LANGUAGES = Arrays.asList("en", "ru");
|
private final static List<String> K3D_SUPPORTED_LANGUAGES = Arrays.asList("en", "ru");
|
||||||
|
|
||||||
|
private boolean wasPortrait;
|
||||||
|
|
||||||
private String getK3DLanguage() {
|
private String getK3DLanguage() {
|
||||||
String lang = Locale.getDefault().getLanguage();
|
String lang = Locale.getDefault().getLanguage();
|
||||||
return K3D_SUPPORTED_LANGUAGES.contains(lang) ? lang : "en";
|
return K3D_SUPPORTED_LANGUAGES.contains(lang) ? lang : "en";
|
||||||
@@ -74,13 +79,18 @@ public class FileMenu extends ListBedMenu {
|
|||||||
.replace("\"", "\\\"");
|
.replace("\"", "\\\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasModel() {
|
||||||
|
return fragment.getGlView().getRenderer().getModel() != null;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean hasSelection() {
|
private boolean hasSelection() {
|
||||||
return fragment.getGlView().getRenderer().getModel() != null && fragment.getGlView().getRenderer().getSelectedObject() != -1;
|
return hasModel() && fragment.getGlView().getRenderer().getSelectedObject() != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<SimpleRecyclerItem> onCreateItems(boolean portrait) {
|
protected List<SimpleRecyclerItem> onCreateItems(boolean portrait) {
|
||||||
return Arrays.asList(
|
wasPortrait = portrait;
|
||||||
|
List<SimpleRecyclerItem> list = new ArrayList<>(Arrays.asList(
|
||||||
new BedMenuItem(R.string.MenuFileOpen, R.drawable.folder_simple_plus_outline_28).onClick(v -> {
|
new BedMenuItem(R.string.MenuFileOpen, R.drawable.folder_simple_plus_outline_28).onClick(v -> {
|
||||||
if (!fragment.getGlView().getRenderer().getBed().isValid()) {
|
if (!fragment.getGlView().getRenderer().getBed().isValid()) {
|
||||||
Toast.makeText(fragment.getContext(), R.string.BedConfigurationError, Toast.LENGTH_SHORT).show();
|
Toast.makeText(fragment.getContext(), R.string.BedConfigurationError, Toast.LENGTH_SHORT).show();
|
||||||
@@ -104,7 +114,8 @@ public class FileMenu extends ListBedMenu {
|
|||||||
fragment.updateModel();
|
fragment.updateModel();
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
new SpaceItem(portrait ? ViewUtils.dp(3) : 0, portrait ? 0 : ViewUtils.dp(3)),
|
new SpaceItem(portrait ? ViewUtils.dp(3) : 0, portrait ? 0 : ViewUtils.dp(3))));
|
||||||
|
list.addAll(Arrays.asList(
|
||||||
new BedMenuItem(R.string.MenuFileCalibrations, R.drawable.wrench_outline_28).setSingleLine(true).onClick(v -> {
|
new BedMenuItem(R.string.MenuFileCalibrations, R.drawable.wrench_outline_28).setSingleLine(true).onClick(v -> {
|
||||||
if (!fragment.getGlView().getRenderer().getBed().isValid()) {
|
if (!fragment.getGlView().getRenderer().getBed().isValid()) {
|
||||||
Toast.makeText(fragment.getContext(), R.string.BedConfigurationError, Toast.LENGTH_SHORT).show();
|
Toast.makeText(fragment.getContext(), R.string.BedConfigurationError, Toast.LENGTH_SHORT).show();
|
||||||
@@ -124,24 +135,24 @@ public class FileMenu extends ListBedMenu {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
new BedMenuItem(R.string.MenuFileExportProfiles, R.drawable.folder_simple_arrow_right_outline_28).onClick(v -> {
|
new BedMenuItem(R.string.MenuFileExportProfiles, R.drawable.folder_simple_arrow_right_outline_28).onClick(v -> {
|
||||||
CharSequence[] prints = new CharSequence[SliceBeam.CONFIG.printConfigs.size()];
|
CharSequence[] prints = new CharSequence[Santoku.CONFIG.printConfigs.size()];
|
||||||
boolean[] enabledPrints = new boolean[prints.length];
|
boolean[] enabledPrints = new boolean[prints.length];
|
||||||
for (int i = 0; i < prints.length; i++) {
|
for (int i = 0; i < prints.length; i++) {
|
||||||
prints[i] = SliceBeam.CONFIG.printConfigs.get(i).getTitle();
|
prints[i] = Santoku.CONFIG.printConfigs.get(i).getTitle();
|
||||||
enabledPrints[i] = true;
|
enabledPrints[i] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CharSequence[] filaments = new CharSequence[SliceBeam.CONFIG.filamentConfigs.size()];
|
CharSequence[] filaments = new CharSequence[Santoku.CONFIG.filamentConfigs.size()];
|
||||||
boolean[] enabledFilaments = new boolean[filaments.length];
|
boolean[] enabledFilaments = new boolean[filaments.length];
|
||||||
for (int i = 0; i < filaments.length; i++) {
|
for (int i = 0; i < filaments.length; i++) {
|
||||||
filaments[i] = SliceBeam.CONFIG.filamentConfigs.get(i).getTitle();
|
filaments[i] = Santoku.CONFIG.filamentConfigs.get(i).getTitle();
|
||||||
enabledFilaments[i] = true;
|
enabledFilaments[i] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CharSequence[] printers = new CharSequence[SliceBeam.CONFIG.printerConfigs.size()];
|
CharSequence[] printers = new CharSequence[Santoku.CONFIG.printerConfigs.size()];
|
||||||
boolean[] enabledPrinters = new boolean[printers.length];
|
boolean[] enabledPrinters = new boolean[printers.length];
|
||||||
for (int i = 0; i < printers.length; i++) {
|
for (int i = 0; i < printers.length; i++) {
|
||||||
printers[i] = SliceBeam.CONFIG.printerConfigs.get(i).getTitle();
|
printers[i] = Santoku.CONFIG.printerConfigs.get(i).getTitle();
|
||||||
enabledPrinters[i] = true;
|
enabledPrinters[i] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,21 +171,21 @@ public class FileMenu extends ListBedMenu {
|
|||||||
for (int i = 0; i < enabledPrints.length; i++) {
|
for (int i = 0; i < enabledPrints.length; i++) {
|
||||||
if (enabledPrints[i]) {
|
if (enabledPrints[i]) {
|
||||||
hasEnabled = true;
|
hasEnabled = true;
|
||||||
MainActivity.EXPORTING_PRINTS.add(SliceBeam.CONFIG.printConfigs.get(i));
|
MainActivity.EXPORTING_PRINTS.add(Santoku.CONFIG.printConfigs.get(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MainActivity.EXPORTING_FILAMENTS = new ArrayList<>();
|
MainActivity.EXPORTING_FILAMENTS = new ArrayList<>();
|
||||||
for (int i = 0; i < enabledFilaments.length; i++) {
|
for (int i = 0; i < enabledFilaments.length; i++) {
|
||||||
if (enabledFilaments[i]) {
|
if (enabledFilaments[i]) {
|
||||||
hasEnabled = true;
|
hasEnabled = true;
|
||||||
MainActivity.EXPORTING_FILAMENTS.add(SliceBeam.CONFIG.filamentConfigs.get(i));
|
MainActivity.EXPORTING_FILAMENTS.add(Santoku.CONFIG.filamentConfigs.get(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MainActivity.EXPORTING_PRINTERS = new ArrayList<>();
|
MainActivity.EXPORTING_PRINTERS = new ArrayList<>();
|
||||||
for (int i = 0; i < enabledPrinters.length; i++) {
|
for (int i = 0; i < enabledPrinters.length; i++) {
|
||||||
if (enabledPrinters[i]) {
|
if (enabledPrinters[i]) {
|
||||||
hasEnabled = true;
|
hasEnabled = true;
|
||||||
MainActivity.EXPORTING_PRINTERS.add(SliceBeam.CONFIG.printerConfigs.get(i));
|
MainActivity.EXPORTING_PRINTERS.add(Santoku.CONFIG.printerConfigs.get(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasEnabled) {
|
if (!hasEnabled) {
|
||||||
@@ -190,7 +201,7 @@ public class FileMenu extends ListBedMenu {
|
|||||||
Activity act = (Activity) fragment.getContext();
|
Activity act = (Activity) fragment.getContext();
|
||||||
Intent i = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
Intent i = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
||||||
i.setType("application/ini");
|
i.setType("application/ini");
|
||||||
i.putExtra(Intent.EXTRA_TITLE, "SliceBeam_config_bundle.ini");
|
i.putExtra(Intent.EXTRA_TITLE, "Santoku_config_bundle.ini");
|
||||||
act.startActivityForResult(i, MainActivity.REQUEST_CODE_EXPORT_PROFILES);
|
act.startActivityForResult(i, MainActivity.REQUEST_CODE_EXPORT_PROFILES);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -200,14 +211,25 @@ public class FileMenu extends ListBedMenu {
|
|||||||
.show())
|
.show())
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
.show();
|
.show();
|
||||||
|
}),
|
||||||
|
new BedMenuItem(R.string.MenuFileExport3mf, R.drawable.arrow_down_to_square_outline_28).setEnabled(hasModel()).onClick(v -> {
|
||||||
|
if (fragment.getContext() instanceof Activity) {
|
||||||
|
Activity act = (Activity) fragment.getContext();
|
||||||
|
Intent i = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
||||||
|
i.setType("application/3mf");
|
||||||
|
i.putExtra(Intent.EXTRA_TITLE, "Santoku_project.3mf");
|
||||||
|
act.startActivityForResult(i, MainActivity.REQUEST_CODE_EXPORT_3MF);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
);
|
));
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(runOnMainThread = true)
|
@EventHandler(runOnMainThread = true)
|
||||||
public void onObjectsChanged(ObjectsListChangedEvent e) {
|
public void onObjectsChanged(ObjectsListChangedEvent e) {
|
||||||
((BedMenuItem) adapter.getItems().get(1)).setEnabled(hasSelection());
|
((BedMenuItem) adapter.getItems().get(1)).setEnabled(hasSelection());
|
||||||
adapter.notifyItemChanged(1);
|
adapter.notifyItemChanged(1);
|
||||||
|
updateModelItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(runOnMainThread = true)
|
@EventHandler(runOnMainThread = true)
|
||||||
@@ -216,15 +238,29 @@ public class FileMenu extends ListBedMenu {
|
|||||||
adapter.notifyItemChanged(1);
|
adapter.notifyItemChanged(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class CalibrationsMenu extends UnfoldMenu {
|
private void updateModelItems() {
|
||||||
|
int idx = -1;
|
||||||
|
for (int j = adapter.getItems().size() - 1; j >= 0; j--) {
|
||||||
|
if (adapter.getItems().get(j) instanceof BedMenuItem) {
|
||||||
|
idx = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (idx != -1) {
|
||||||
|
((BedMenuItem) adapter.getItems().get(idx)).setEnabled(hasModel());
|
||||||
|
adapter.notifyItemChanged(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class CalibrationsMenu extends UnfoldMenu {
|
||||||
|
@Override
|
||||||
public int getRequestedSize(FrameLayout into, boolean portrait) {
|
public int getRequestedSize(FrameLayout into, boolean portrait) {
|
||||||
return (int) (portrait ? into.getHeight() * 0.35f : into.getWidth() * 0.6f);
|
return (int) (portrait ? into.getHeight() * 0.35f : into.getWidth() * 0.6f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String loadJSLoader(String key) {
|
private String loadJSLoader(String key) {
|
||||||
try {
|
try {
|
||||||
InputStream in = SliceBeam.INSTANCE.getAssets().open("js_loader/" + key + ".js");
|
InputStream in = Santoku.INSTANCE.getAssets().open("js_loader/" + key + ".js");
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
byte[] buffer = new byte[10240]; int c;
|
byte[] buffer = new byte[10240]; int c;
|
||||||
while ((c = in.read(buffer)) != -1) {
|
while ((c = in.read(buffer)) != -1) {
|
||||||
@@ -233,7 +269,7 @@ public class FileMenu extends ListBedMenu {
|
|||||||
bos.close();
|
bos.close();
|
||||||
in.close();
|
in.close();
|
||||||
|
|
||||||
ConfigObject cfg = SliceBeam.buildCurrentConfigObject();
|
ConfigObject cfg = Santoku.buildCurrentConfigObject();
|
||||||
Bed3D bed = FileMenu.this.fragment.getGlView().getRenderer().getBed();
|
Bed3D bed = FileMenu.this.fragment.getGlView().getRenderer().getBed();
|
||||||
double bedX = bed.getVolumeMax().x - bed.getVolumeMin().x;
|
double bedX = bed.getVolumeMax().x - bed.getVolumeMin().x;
|
||||||
double bedY = bed.getVolumeMax().y - bed.getVolumeMin().y;
|
double bedY = bed.getVolumeMax().y - bed.getVolumeMin().y;
|
||||||
@@ -359,14 +395,14 @@ public class FileMenu extends ListBedMenu {
|
|||||||
protected void onCreate() {
|
protected void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
SliceBeam.EVENT_BUS.registerListener(this);
|
Santoku.EVENT_BUS.registerListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|
||||||
SliceBeam.EVENT_BUS.unregisterListener(this);
|
Santoku.EVENT_BUS.unregisterListener(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,10 +410,10 @@ public class FileMenu extends ListBedMenu {
|
|||||||
private void loadModel(String key) {
|
private void loadModel(String key) {
|
||||||
BedFragment fragment = this.fragment;
|
BedFragment fragment = this.fragment;
|
||||||
ViewUtils.postOnMainThread(() -> {
|
ViewUtils.postOnMainThread(() -> {
|
||||||
File f = new File(SliceBeam.getModelCacheDir(), "calibration_" + key + ".stl");
|
File f = new File(Santoku.getModelCacheDir(), "calibration_" + key + ".stl");
|
||||||
new Thread(()->{
|
new Thread(()->{
|
||||||
try {
|
try {
|
||||||
InputStream in = SliceBeam.INSTANCE.getAssets().open("models/" + key + ".stl");
|
InputStream in = Santoku.INSTANCE.getAssets().open("models/" + key + ".stl");
|
||||||
FileOutputStream fos = new FileOutputStream(f);
|
FileOutputStream fos = new FileOutputStream(f);
|
||||||
byte[] buffer = new byte[10240]; int c;
|
byte[] buffer = new byte[10240]; int c;
|
||||||
while ((c = in.read(buffer)) != -1) {
|
while ((c = in.read(buffer)) != -1) {
|
||||||
@@ -392,9 +428,9 @@ public class FileMenu extends ListBedMenu {
|
|||||||
fragment.loadGCode(f);
|
fragment.loadGCode(f);
|
||||||
} else {
|
} else {
|
||||||
fragment.loadModel(f);
|
fragment.loadModel(f);
|
||||||
SliceBeam.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
|
Santoku.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
|
||||||
}
|
}
|
||||||
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileOpenFileLoaded));
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileOpenFileLoaded));
|
||||||
} catch (Slic3rRuntimeError e) {
|
} catch (Slic3rRuntimeError e) {
|
||||||
f.delete();
|
f.delete();
|
||||||
|
|
||||||
@@ -415,7 +451,7 @@ public class FileMenu extends ListBedMenu {
|
|||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
}, 200);
|
}, 200);
|
||||||
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissCalibrationsMenu());
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissCalibrationsMenu());
|
||||||
dismiss(true);
|
dismiss(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
+10
-8
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.components.bed_menu;
|
package com.dark98.santoku.components.bed_menu;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
@@ -11,11 +11,11 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
|
import com.dark98.santoku.fragment.BedFragment;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerAdapter;
|
import com.dark98.santoku.recycler.SimpleRecyclerAdapter;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
|
import com.dark98.santoku.recycler.SimpleRecyclerItem;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
|
||||||
public abstract class ListBedMenu extends BedMenu {
|
public abstract class ListBedMenu extends BedMenu {
|
||||||
protected BedFragment fragment;
|
protected BedFragment fragment;
|
||||||
@@ -32,6 +32,8 @@ public abstract class ListBedMenu extends BedMenu {
|
|||||||
recyclerView = new RecyclerView(ctx);
|
recyclerView = new RecyclerView(ctx);
|
||||||
recyclerView.setLayoutManager(new LinearLayoutManager(ctx, portrait ? RecyclerView.HORIZONTAL : RecyclerView.VERTICAL, false));
|
recyclerView.setLayoutManager(new LinearLayoutManager(ctx, portrait ? RecyclerView.HORIZONTAL : RecyclerView.VERTICAL, false));
|
||||||
recyclerView.setItemAnimator(null);
|
recyclerView.setItemAnimator(null);
|
||||||
|
recyclerView.setClipToPadding(false);
|
||||||
|
recyclerView.setClipChildren(false);
|
||||||
adapter = new SimpleRecyclerAdapter() {
|
adapter = new SimpleRecyclerAdapter() {
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
@@ -67,13 +69,13 @@ public abstract class ListBedMenu extends BedMenu {
|
|||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View v) {
|
public void onViewCreated(View v) {
|
||||||
super.onViewCreated(v);
|
super.onViewCreated(v);
|
||||||
SliceBeam.EVENT_BUS.registerListener(ListBedMenu.this);
|
Santoku.EVENT_BUS.registerListener(ListBedMenu.this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewDestroyed() {
|
public void onViewDestroyed() {
|
||||||
super.onViewDestroyed();
|
super.onViewDestroyed();
|
||||||
SliceBeam.EVENT_BUS.unregisterListener(ListBedMenu.this);
|
Santoku.EVENT_BUS.unregisterListener(ListBedMenu.this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract List<SimpleRecyclerItem> onCreateItems(boolean portrait);
|
protected abstract List<SimpleRecyclerItem> onCreateItems(boolean portrait);
|
||||||
+49
-30
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.components.bed_menu;
|
package com.dark98.santoku.components.bed_menu;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
@@ -26,24 +26,25 @@ import java.util.List;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import ru.ytkab0bp.eventbus.EventHandler;
|
import ru.ytkab0bp.eventbus.EventHandler;
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
|
import com.dark98.santoku.components.BeamAlertDialogBuilder;
|
||||||
import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
|
import com.dark98.santoku.components.UnfoldMenu;
|
||||||
import ru.ytkab0bp.slicebeam.events.FlattenModeResetEvent;
|
import com.dark98.santoku.events.FlattenModeResetEvent;
|
||||||
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
|
import com.dark98.santoku.events.LongClickTranslationEvent;
|
||||||
import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
|
import com.dark98.santoku.events.NeedSnackbarEvent;
|
||||||
import ru.ytkab0bp.slicebeam.events.SelectedObjectChangedEvent;
|
import com.dark98.santoku.events.ObjectsListChangedEvent;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
|
import com.dark98.santoku.events.SelectedObjectChangedEvent;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
|
import com.dark98.santoku.recycler.SimpleRecyclerItem;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Model;
|
import com.dark98.santoku.recycler.SpaceItem;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.slic3r.Model;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Vec3d;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.Vec3d;
|
||||||
import ru.ytkab0bp.slicebeam.view.BeamButton;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
import ru.ytkab0bp.slicebeam.view.DividerView;
|
import com.dark98.santoku.view.BeamButton;
|
||||||
import ru.ytkab0bp.slicebeam.view.PositionScrollView;
|
import com.dark98.santoku.view.DividerView;
|
||||||
import ru.ytkab0bp.slicebeam.view.TextColorImageSpan;
|
import com.dark98.santoku.view.PositionScrollView;
|
||||||
|
import com.dark98.santoku.view.TextColorImageSpan;
|
||||||
|
|
||||||
public class OrientationMenu extends ListBedMenu {
|
public class OrientationMenu extends ListBedMenu {
|
||||||
private boolean hasSelection() {
|
private boolean hasSelection() {
|
||||||
@@ -60,7 +61,7 @@ public class OrientationMenu extends ListBedMenu {
|
|||||||
fragment.getGlView().requestRender();
|
fragment.getGlView().requestRender();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuOrientationArrangeFinished));
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuOrientationArrangeFinished));
|
||||||
}).setEnabled(fragment.getGlView().getRenderer().getModel() != null),
|
}).setEnabled(fragment.getGlView().getRenderer().getModel() != null),
|
||||||
new SpaceItem(portrait ? ViewUtils.dp(8) : 0, portrait ? 0 : ViewUtils.dp(8)),
|
new SpaceItem(portrait ? ViewUtils.dp(8) : 0, portrait ? 0 : ViewUtils.dp(8)),
|
||||||
new BedMenuItem(R.string.MenuOrientationAutoOrient, R.drawable.menu_orientation_auto_28).setEnabled(hasSelection()).onClick(view -> {
|
new BedMenuItem(R.string.MenuOrientationAutoOrient, R.drawable.menu_orientation_auto_28).setEnabled(hasSelection()).onClick(view -> {
|
||||||
@@ -75,7 +76,7 @@ public class OrientationMenu extends ListBedMenu {
|
|||||||
fragment.getGlView().getRenderer().invalidateGlModel(i);
|
fragment.getGlView().getRenderer().invalidateGlModel(i);
|
||||||
fragment.getGlView().requestRender();
|
fragment.getGlView().requestRender();
|
||||||
|
|
||||||
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuOrientationAutoOrientDone, Snackbar.LENGTH_SHORT));
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuOrientationAutoOrientDone, Snackbar.LENGTH_SHORT));
|
||||||
}),
|
}),
|
||||||
new BedMenuItem(R.string.MenuOrientationFlatten, R.drawable.menu_orientation_flatten_28).setEnabled(hasSelection()).setCheckable((buttonView, isChecked) -> {
|
new BedMenuItem(R.string.MenuOrientationFlatten, R.drawable.menu_orientation_flatten_28).setEnabled(hasSelection()).setCheckable((buttonView, isChecked) -> {
|
||||||
fragment.getGlView().getRenderer().setInFlattenMode(isChecked);
|
fragment.getGlView().getRenderer().setInFlattenMode(isChecked);
|
||||||
@@ -139,7 +140,7 @@ public class OrientationMenu extends ListBedMenu {
|
|||||||
private Vec3d tempVec = new Vec3d();
|
private Vec3d tempVec = new Vec3d();
|
||||||
private int startedScrollObject;
|
private int startedScrollObject;
|
||||||
|
|
||||||
private void translateVisual(Double x, Double y, Double z) {
|
public void translateVisual(Double x, Double y, Double z) {
|
||||||
int j = fragment.getGlView().getRenderer().getSelectedObject();
|
int j = fragment.getGlView().getRenderer().getSelectedObject();
|
||||||
if (j == -1) return;
|
if (j == -1) return;
|
||||||
startedScrollObject = j;
|
startedScrollObject = j;
|
||||||
@@ -199,10 +200,10 @@ public class OrientationMenu extends ListBedMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private CharSequence formatTrackTitle(int res, double value) {
|
private CharSequence formatTrackTitle(int res, double value) {
|
||||||
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(SliceBeam.INSTANCE.getString(res, value));
|
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(Santoku.INSTANCE.getString(res, value));
|
||||||
sb.append(" d");
|
sb.append(" d");
|
||||||
int size = ViewUtils.dp(14);
|
int size = ViewUtils.dp(14);
|
||||||
Drawable dr = ContextCompat.getDrawable(SliceBeam.INSTANCE, R.drawable.edit_outline_28);
|
Drawable dr = ContextCompat.getDrawable(Santoku.INSTANCE, R.drawable.edit_outline_28);
|
||||||
dr.setTint(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
dr.setTint(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||||
dr.setBounds(0, 0, size, size);
|
dr.setBounds(0, 0, size, size);
|
||||||
sb.setSpan(new TextColorImageSpan(dr, 0), sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
sb.setSpan(new TextColorImageSpan(dr, 0), sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
@@ -354,7 +355,7 @@ public class OrientationMenu extends ListBedMenu {
|
|||||||
protected void onCreate() {
|
protected void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
SliceBeam.EVENT_BUS.registerListener(this);
|
Santoku.EVENT_BUS.registerListener(this);
|
||||||
setSelectionValues();
|
setSelectionValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +363,7 @@ public class OrientationMenu extends ListBedMenu {
|
|||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|
||||||
SliceBeam.EVENT_BUS.unregisterListener(this);
|
Santoku.EVENT_BUS.unregisterListener(this);
|
||||||
stopScroll();
|
stopScroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,6 +396,24 @@ public class OrientationMenu extends ListBedMenu {
|
|||||||
startedScrollObject = -1;
|
startedScrollObject = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventHandler(runOnMainThread = true)
|
||||||
|
public void onLongClickTranslation(LongClickTranslationEvent e) {
|
||||||
|
if (e.visual) {
|
||||||
|
int j = fragment.getGlView().getRenderer().getSelectedObject();
|
||||||
|
if (j == -1) return;
|
||||||
|
|
||||||
|
Model model = fragment.getGlView().getRenderer().getModel();
|
||||||
|
model.getTranslation(j, tempVec);
|
||||||
|
|
||||||
|
xTitle.setText(formatTrackTitle(R.string.MenuOrientationPositionXValue, tempVec.x + e.x));
|
||||||
|
yTitle.setText(formatTrackTitle(R.string.MenuOrientationPositionYValue, tempVec.y + e.y));
|
||||||
|
xTrack.setCurrentPosition((int) (tempVec.x + e.x));
|
||||||
|
yTrack.setCurrentPosition((int) (tempVec.y + e.y));
|
||||||
|
} else {
|
||||||
|
setSelectionValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@EventHandler(runOnMainThread = true)
|
@EventHandler(runOnMainThread = true)
|
||||||
public void onSelectedObjectChanged(SelectedObjectChangedEvent e) {
|
public void onSelectedObjectChanged(SelectedObjectChangedEvent e) {
|
||||||
stopScroll();
|
stopScroll();
|
||||||
@@ -482,10 +501,10 @@ public class OrientationMenu extends ListBedMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private CharSequence formatTrackTitle(int res, double value) {
|
private CharSequence formatTrackTitle(int res, double value) {
|
||||||
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(SliceBeam.INSTANCE.getString(res, value));
|
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(Santoku.INSTANCE.getString(res, value));
|
||||||
sb.append(" d");
|
sb.append(" d");
|
||||||
int size = ViewUtils.dp(14);
|
int size = ViewUtils.dp(14);
|
||||||
Drawable dr = ContextCompat.getDrawable(SliceBeam.INSTANCE, R.drawable.edit_outline_28);
|
Drawable dr = ContextCompat.getDrawable(Santoku.INSTANCE, R.drawable.edit_outline_28);
|
||||||
dr.setTint(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
dr.setTint(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||||
dr.setBounds(0, 0, size, size);
|
dr.setBounds(0, 0, size, size);
|
||||||
sb.setSpan(new TextColorImageSpan(dr, 0), sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
sb.setSpan(new TextColorImageSpan(dr, 0), sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
@@ -647,7 +666,7 @@ public class OrientationMenu extends ListBedMenu {
|
|||||||
protected void onCreate() {
|
protected void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
SliceBeam.EVENT_BUS.registerListener(this);
|
Santoku.EVENT_BUS.registerListener(this);
|
||||||
setSelectionValues();
|
setSelectionValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -655,7 +674,7 @@ public class OrientationMenu extends ListBedMenu {
|
|||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|
||||||
SliceBeam.EVENT_BUS.unregisterListener(this);
|
Santoku.EVENT_BUS.unregisterListener(this);
|
||||||
stopScroll();
|
stopScroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
+141
-42
@@ -1,6 +1,6 @@
|
|||||||
package ru.ytkab0bp.slicebeam.components.bed_menu;
|
package com.dark98.santoku.components.bed_menu;
|
||||||
|
|
||||||
import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
|
import static com.dark98.santoku.utils.DebugUtils.assertTrue;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
@@ -41,44 +41,48 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import cz.msebera.android.httpclient.Header;
|
import cz.msebera.android.httpclient.Header;
|
||||||
import cz.msebera.android.httpclient.entity.ContentType;
|
import cz.msebera.android.httpclient.entity.ContentType;
|
||||||
import cz.msebera.android.httpclient.message.BasicHeader;
|
import cz.msebera.android.httpclient.message.BasicHeader;
|
||||||
import ru.ytkab0bp.slicebeam.BuildConfig;
|
import com.dark98.santoku.BuildConfig;
|
||||||
import ru.ytkab0bp.slicebeam.MainActivity;
|
import com.dark98.santoku.MainActivity;
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
|
import com.dark98.santoku.components.BeamAlertDialogBuilder;
|
||||||
import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
|
import com.dark98.santoku.components.UnfoldMenu;
|
||||||
import ru.ytkab0bp.slicebeam.config.ConfigObject;
|
import com.dark98.santoku.config.ConfigObject;
|
||||||
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
|
import com.dark98.santoku.events.NeedDismissSnackbarEvent;
|
||||||
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
|
import com.dark98.santoku.events.NeedSnackbarEvent;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
|
import com.dark98.santoku.fragment.BedFragment;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.GCodeProcessorResult;
|
import com.dark98.santoku.print_host.ElegooLinkClient;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.GCodeViewer;
|
import com.dark98.santoku.recycler.SimpleRecyclerItem;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
|
import com.dark98.santoku.slic3r.GCodeProcessorResult;
|
||||||
import ru.ytkab0bp.slicebeam.theme.IThemeView;
|
import com.dark98.santoku.slic3r.GCodeViewer;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.slic3r.Slic3rLocalization;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.theme.IThemeView;
|
||||||
import ru.ytkab0bp.slicebeam.view.DividerView;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.view.PositionScrollView;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
import ru.ytkab0bp.slicebeam.view.SegmentsView;
|
import com.dark98.santoku.view.DividerView;
|
||||||
|
import com.dark98.santoku.view.PositionScrollView;
|
||||||
|
import com.dark98.santoku.view.SegmentsView;
|
||||||
|
import com.dark98.santoku.view.SnackbarsLayout;
|
||||||
|
|
||||||
public class SliceMenu extends ListBedMenu {
|
public class SliceMenu extends ListBedMenu {
|
||||||
private AsyncHttpClient client = new AsyncHttpClient();
|
private AsyncHttpClient client = new AsyncHttpClient();
|
||||||
|
|
||||||
{
|
{
|
||||||
client.setLoggingEnabled(true);
|
client.setLoggingEnabled(true);
|
||||||
client.setMaxRetriesAndTimeout(0, 5000);
|
client.setMaxRetriesAndTimeout(0, 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final static List<String> SUPPORTED_SEND = Collections.singletonList("octoprint");
|
private final static List<String> SUPPORTED_SEND = Arrays.asList("octoprint", "klipper", "moonraker", "elegoolink");
|
||||||
private int lastUid;
|
private int lastUid;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<SimpleRecyclerItem> onCreateItems(boolean portrait) {
|
protected List<SimpleRecyclerItem> onCreateItems(boolean portrait) {
|
||||||
lastUid = SliceBeam.CONFIG_UID;
|
lastUid = Santoku.CONFIG_UID;
|
||||||
List<SimpleRecyclerItem> items = new ArrayList<>(Arrays.asList(
|
List<SimpleRecyclerItem> items = new ArrayList<>(Arrays.asList(
|
||||||
new BedMenuItem(R.string.MenuSliceInfo, R.drawable.clock_circle_dashed_outline_24).onClick(v -> fragment.showUnfoldMenu(new InfoMenu(), v)),
|
new BedMenuItem(R.string.MenuSliceInfo, R.drawable.clock_circle_dashed_outline_24).onClick(v -> fragment.showUnfoldMenu(new InfoMenu(), v)),
|
||||||
new BedMenuItem(R.string.MenuSliceLayers, R.drawable.square_stack_up_outline_28).onClick(v -> fragment.showUnfoldMenu(new LayersMenu(), v)),
|
new BedMenuItem(R.string.MenuSliceLayers, R.drawable.square_stack_up_outline_28).onClick(v -> fragment.showUnfoldMenu(new LayersMenu(), v)),
|
||||||
@@ -110,7 +114,7 @@ public class SliceMenu extends ListBedMenu {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
));
|
));
|
||||||
ConfigObject obj = SliceBeam.CONFIG.findPrinter(SliceBeam.CONFIG.presets.get("printer"));
|
ConfigObject obj = Santoku.CONFIG.findPrinter(Santoku.CONFIG.presets.get("printer"));
|
||||||
assertTrue(obj != null);
|
assertTrue(obj != null);
|
||||||
String type = obj.get("host_type");
|
String type = obj.get("host_type");
|
||||||
if (type == null) type = "octoprint";
|
if (type == null) type = "octoprint";
|
||||||
@@ -118,21 +122,23 @@ public class SliceMenu extends ListBedMenu {
|
|||||||
String apiKey = obj.get("printhost_apikey");
|
String apiKey = obj.get("printhost_apikey");
|
||||||
if (SUPPORTED_SEND.contains(type) && !TextUtils.isEmpty(host)) {
|
if (SUPPORTED_SEND.contains(type) && !TextUtils.isEmpty(host)) {
|
||||||
String finalType = type;
|
String finalType = type;
|
||||||
items.add(new BedMenuItem(R.string.MenuSliceSendToPrinter, R.drawable.send_outline_28).onClick(v -> upload(finalType, host, apiKey, false)));
|
ConfigObject finalObj = obj;
|
||||||
items.add(new BedMenuItem(R.string.MenuSliceSendToPrinterAndPrint, R.drawable.send_28).onClick(v -> upload(finalType, host, apiKey, true)));
|
items.add(new BedMenuItem(R.string.MenuSliceSendToPrinter, R.drawable.send_outline_28).onClick(v -> upload(finalType, host, apiKey, false, finalObj)));
|
||||||
|
items.add(new BedMenuItem(R.string.MenuSliceSendToPrinterAndPrint, R.drawable.send_28).onClick(v -> upload(finalType, host, apiKey, true, finalObj)));
|
||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void upload(String type, String host, String apiKey, boolean print) {
|
private void upload(String type, String host, String apiKey, boolean print, ConfigObject config) {
|
||||||
String name = fragment.getGlView().getRenderer().getGcodeResult().getRecommendedName();
|
String name = fragment.getGlView().getRenderer().getGcodeResult().getRecommendedName();
|
||||||
switch (type) {
|
switch (type) {
|
||||||
default:
|
case "klipper":
|
||||||
case "octoprint":
|
case "moonraker": {
|
||||||
if (!host.startsWith("http://")) {
|
if (!host.startsWith("http://") && !host.startsWith("https://")) {
|
||||||
host = "http://" + host;
|
host = "http://" + host;
|
||||||
}
|
}
|
||||||
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuSliceSendToPrinterStarted));
|
String tag = UUID.randomUUID().toString();
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuSliceSendToPrinterLoading).tag(tag));
|
||||||
Header[] headers = TextUtils.isEmpty(apiKey) ? new Header[0] : new Header[] {new BasicHeader("X-Api-Key", apiKey)};
|
Header[] headers = TextUtils.isEmpty(apiKey) ? new Header[0] : new Header[] {new BasicHeader("X-Api-Key", apiKey)};
|
||||||
RequestParams params = new RequestParams();
|
RequestParams params = new RequestParams();
|
||||||
try {
|
try {
|
||||||
@@ -143,15 +149,27 @@ public class SliceMenu extends ListBedMenu {
|
|||||||
params.put("select", String.valueOf(print));
|
params.put("select", String.valueOf(print));
|
||||||
params.put("print", String.valueOf(print));
|
params.put("print", String.valueOf(print));
|
||||||
|
|
||||||
client.post(SliceBeam.INSTANCE, host + "/api/files/local", headers, params, null, new AsyncHttpResponseHandler() {
|
String url = host + "/server/files/upload";
|
||||||
|
client.post(Santoku.INSTANCE, url, headers, params, null, new AsyncHttpResponseHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
|
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
|
||||||
try {
|
try {
|
||||||
JSONObject obj = new JSONObject(new String(responseBody));
|
JSONObject obj = new JSONObject(new String(responseBody));
|
||||||
if (!obj.has("action") && !obj.has("files")) {
|
// Moonraker responses may wrap data in "result"
|
||||||
throw new JSONException(obj.toString());
|
JSONObject payload = obj;
|
||||||
|
if (obj.has("result") && obj.opt("result") instanceof JSONObject) {
|
||||||
|
payload = obj.optJSONObject("result");
|
||||||
}
|
}
|
||||||
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(print ? R.string.MenuSliceSendToPrinterPrintStarted : R.string.MenuSliceSendToPrinterOK));
|
// Moonraker upload response includes "item" + "action" + "print_started"
|
||||||
|
boolean success = payload != null && (payload.has("item")
|
||||||
|
|| payload.has("action")
|
||||||
|
|| payload.has("print_started")
|
||||||
|
|| payload.has("print_queued"));
|
||||||
|
if (!success) {
|
||||||
|
throw new JSONException("Unexpected response: " + obj.toString());
|
||||||
|
}
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(print ? SnackbarsLayout.Type.INFO : SnackbarsLayout.Type.DONE, print ? R.string.MenuSliceSendToPrinterPrintStarted : R.string.MenuSliceSendToPrinterOK));
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
onFailure(statusCode, headers, responseBody, e);
|
onFailure(statusCode, headers, responseBody, e);
|
||||||
}
|
}
|
||||||
@@ -159,6 +177,7 @@ public class SliceMenu extends ListBedMenu {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
|
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
|
||||||
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(fragment.getContext())
|
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(fragment.getContext())
|
||||||
.setTitle(R.string.MenuSliceSendToPrinterFailed)
|
.setTitle(R.string.MenuSliceSendToPrinterFailed)
|
||||||
.setMessage(error.toString())
|
.setMessage(error.toString())
|
||||||
@@ -167,6 +186,86 @@ public class SliceMenu extends ListBedMenu {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
case "octoprint":
|
||||||
|
if (!host.startsWith("http://") && !host.startsWith("https://")) {
|
||||||
|
host = "http://" + host;
|
||||||
|
}
|
||||||
|
String tag = UUID.randomUUID().toString();
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuSliceSendToPrinterLoading).tag(tag));
|
||||||
|
Header[] headers = TextUtils.isEmpty(apiKey) ? new Header[0] : new Header[] {new BasicHeader("X-Api-Key", apiKey)};
|
||||||
|
RequestParams params = new RequestParams();
|
||||||
|
try {
|
||||||
|
params.put("file", new FileInputStream(BedFragment.getTempGCodePath()), name, ContentType.TEXT_PLAIN.getMimeType());
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
params.put("select", String.valueOf(print));
|
||||||
|
params.put("print", String.valueOf(print));
|
||||||
|
|
||||||
|
String url = host + "/api/files/local";
|
||||||
|
client.post(Santoku.INSTANCE, url, headers, params, null, new AsyncHttpResponseHandler() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
|
||||||
|
try {
|
||||||
|
JSONObject obj = new JSONObject(new String(responseBody));
|
||||||
|
// OctoPrint: check for "action" or "files"
|
||||||
|
if (!obj.has("action") && !obj.has("files")) {
|
||||||
|
throw new JSONException("Unexpected response: " + obj.toString());
|
||||||
|
}
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(print ? SnackbarsLayout.Type.INFO : SnackbarsLayout.Type.DONE, print ? R.string.MenuSliceSendToPrinterPrintStarted : R.string.MenuSliceSendToPrinterOK));
|
||||||
|
} catch (JSONException e) {
|
||||||
|
onFailure(statusCode, headers, responseBody, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
|
||||||
|
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(fragment.getContext())
|
||||||
|
.setTitle(R.string.MenuSliceSendToPrinterFailed)
|
||||||
|
.setMessage(error.toString())
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "elegoolink": {
|
||||||
|
if (!host.startsWith("http://") && !host.startsWith("https://")) {
|
||||||
|
host = "http://" + host;
|
||||||
|
}
|
||||||
|
String elegooTag = UUID.randomUUID().toString();
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuSliceSendToPrinterLoading).tag(elegooTag));
|
||||||
|
String finalHost = host;
|
||||||
|
final boolean timelapse = config != null && "1".equals(config.get("elegoolink_timelapse"));
|
||||||
|
final boolean bedLeveling = config != null && "1".equals(config.get("elegoolink_bed_leveling"));
|
||||||
|
int bedTypeVal = 0;
|
||||||
|
if (config != null) {
|
||||||
|
String bedTypeValue = config.get("elegoolink_bed_type");
|
||||||
|
if ("1".equals(bedTypeValue) || "pc".equalsIgnoreCase(bedTypeValue)) {
|
||||||
|
bedTypeVal = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final int bedType = bedTypeVal;
|
||||||
|
new Thread(() -> {
|
||||||
|
ElegooLinkClient.Result result = ElegooLinkClient.upload(BedFragment.getTempGCodePath(), finalHost, name, print, timelapse, bedLeveling, bedType);
|
||||||
|
ViewUtils.postOnMainThread(() -> {
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(elegooTag));
|
||||||
|
if (result.ok) {
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(print ? SnackbarsLayout.Type.INFO : SnackbarsLayout.Type.DONE, print ? R.string.MenuSliceSendToPrinterPrintStarted : R.string.MenuSliceSendToPrinterOK));
|
||||||
|
} else {
|
||||||
|
new BeamAlertDialogBuilder(fragment.getContext())
|
||||||
|
.setTitle(R.string.MenuSliceSendToPrinterFailed)
|
||||||
|
.setMessage(result.error)
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).start();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +275,7 @@ public class SliceMenu extends ListBedMenu {
|
|||||||
v.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
|
v.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onViewAttachedToWindow(@NonNull View v) {
|
public void onViewAttachedToWindow(@NonNull View v) {
|
||||||
if (lastUid != SliceBeam.CONFIG_UID) {
|
if (lastUid != Santoku.CONFIG_UID) {
|
||||||
adapter.setItems(onCreateItems(v.getWidth() < v.getHeight()));
|
adapter.setItems(onCreateItems(v.getWidth() < v.getHeight()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,9 +443,9 @@ public class SliceMenu extends ListBedMenu {
|
|||||||
private static String formatComplex(double weight, double length, float time) {
|
private static String formatComplex(double weight, double length, float time) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
if (weight > 0) {
|
if (weight > 0) {
|
||||||
sb.append(format.format(weight)).append(" ").append(SliceBeam.INSTANCE.getString(R.string.MenuSliceInfoWeight)).append(" | ");
|
sb.append(format.format(weight)).append(" ").append(Santoku.INSTANCE.getString(R.string.MenuSliceInfoWeight)).append(" | ");
|
||||||
}
|
}
|
||||||
sb.append(format.format(length)).append(" ").append(SliceBeam.INSTANCE.getString(R.string.MenuSliceInfoLength)).append(" | ");
|
sb.append(format.format(length)).append(" ").append(Santoku.INSTANCE.getString(R.string.MenuSliceInfoLength)).append(" | ");
|
||||||
sb.append(formatTime(time));
|
sb.append(formatTime(time));
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
@@ -359,17 +458,17 @@ public class SliceMenu extends ListBedMenu {
|
|||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
if (hours > 0) {
|
if (hours > 0) {
|
||||||
sb.append(hours).append(" ").append(SliceBeam.INSTANCE.getString(R.string.MenuSliceInfoHour));
|
sb.append(hours).append(" ").append(Santoku.INSTANCE.getString(R.string.MenuSliceInfoHour));
|
||||||
}
|
}
|
||||||
if (minutes > 0) {
|
if (minutes > 0) {
|
||||||
if (sb.length() > 0) sb.append(" ");
|
if (sb.length() > 0) sb.append(" ");
|
||||||
|
|
||||||
sb.append(minutes).append(" ").append(SliceBeam.INSTANCE.getString(R.string.MenuSliceInfoMinute));
|
sb.append(minutes).append(" ").append(Santoku.INSTANCE.getString(R.string.MenuSliceInfoMinute));
|
||||||
}
|
}
|
||||||
if (seconds > 0 || sb.length() == 0) {
|
if (seconds > 0 || sb.length() == 0) {
|
||||||
if (sb.length() > 0) sb.append(" ");
|
if (sb.length() > 0) sb.append(" ");
|
||||||
|
|
||||||
sb.append(seconds).append(" ").append(SliceBeam.INSTANCE.getString(R.string.MenuSliceInfoSecond));
|
sb.append(seconds).append(" ").append(Santoku.INSTANCE.getString(R.string.MenuSliceInfoSecond));
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
+210
-49
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.components.bed_menu;
|
package com.dark98.santoku.components.bed_menu;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
@@ -16,31 +16,34 @@ import android.widget.ImageView;
|
|||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.Space;
|
import android.widget.Space;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import ru.ytkab0bp.eventbus.EventHandler;
|
import ru.ytkab0bp.eventbus.EventHandler;
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
|
import com.dark98.santoku.components.BeamAlertDialogBuilder;
|
||||||
import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
|
import com.dark98.santoku.components.UnfoldMenu;
|
||||||
import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
|
import com.dark98.santoku.events.ObjectsListChangedEvent;
|
||||||
import ru.ytkab0bp.slicebeam.events.SelectedObjectChangedEvent;
|
import com.dark98.santoku.events.SelectedObjectChangedEvent;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.PreferenceSwitchItem;
|
import com.dark98.santoku.recycler.PreferenceSwitchItem;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
|
import com.dark98.santoku.recycler.SimpleRecyclerItem;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Model;
|
import com.dark98.santoku.render.GLRenderer;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.slic3r.Model;
|
||||||
import ru.ytkab0bp.slicebeam.utils.DoubleMatrix;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Prefs;
|
import com.dark98.santoku.utils.DoubleMatrix;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Vec3d;
|
import com.dark98.santoku.utils.Prefs;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.Vec3d;
|
||||||
import ru.ytkab0bp.slicebeam.view.DividerView;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
import ru.ytkab0bp.slicebeam.view.PositionScrollView;
|
import com.dark98.santoku.view.DividerView;
|
||||||
import ru.ytkab0bp.slicebeam.view.TextColorImageSpan;
|
import com.dark98.santoku.view.PositionScrollView;
|
||||||
|
import com.dark98.santoku.view.TextColorImageSpan;
|
||||||
|
|
||||||
public class TransformMenu extends ListBedMenu {
|
public class TransformMenu extends ListBedMenu {
|
||||||
private double[] tempMatrix = new double[16];
|
private double[] tempMatrix = new double[16];
|
||||||
@@ -54,6 +57,77 @@ public class TransformMenu extends ListBedMenu {
|
|||||||
protected List<SimpleRecyclerItem> onCreateItems(boolean portrait) {
|
protected List<SimpleRecyclerItem> onCreateItems(boolean portrait) {
|
||||||
return Arrays.asList(
|
return Arrays.asList(
|
||||||
new BedMenuItem(R.string.MenuTransformScale, R.drawable.arrow_up_right_corner_outline_24).setEnabled(hasSelection()).onClick(v -> fragment.showUnfoldMenu(new ScaleMenu(), v)),
|
new BedMenuItem(R.string.MenuTransformScale, R.drawable.arrow_up_right_corner_outline_24).setEnabled(hasSelection()).onClick(v -> fragment.showUnfoldMenu(new ScaleMenu(), v)),
|
||||||
|
new BedMenuItem(R.string.MenuTransformClone, R.drawable.square_stack_up_outline_28).setEnabled(hasSelection()).onClick(v -> {
|
||||||
|
Context ctx = fragment.getContext();
|
||||||
|
LinearLayout ll = new LinearLayout(ctx);
|
||||||
|
ll.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
|
||||||
|
AtomicBoolean autoArrange = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
EditText countInput = new EditText(ctx);
|
||||||
|
countInput.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||||
|
countInput.setHint(R.string.MenuTransformCloneCountHint);
|
||||||
|
countInput.setText("1");
|
||||||
|
countInput.setSelection(countInput.getText().length());
|
||||||
|
countInput.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||||
|
ll.addView(countInput, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||||
|
leftMargin = rightMargin = ViewUtils.dp(21);
|
||||||
|
}});
|
||||||
|
|
||||||
|
PreferenceSwitchItem.SwitchPreferenceHolderView holderView = new PreferenceSwitchItem.SwitchPreferenceHolderView(ctx);
|
||||||
|
holderView.title.setText(R.string.MenuTransformCloneAutoArrange);
|
||||||
|
holderView.title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||||
|
holderView.subtitle.setVisibility(View.GONE);
|
||||||
|
holderView.icon.setVisibility(View.GONE);
|
||||||
|
holderView.matSwitch.setChecked(autoArrange.get());
|
||||||
|
holderView.setOnClickListener(v1 -> {
|
||||||
|
autoArrange.set(!autoArrange.get());
|
||||||
|
holderView.matSwitch.setChecked(autoArrange.get());
|
||||||
|
});
|
||||||
|
ll.addView(holderView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||||
|
leftMargin = rightMargin = ViewUtils.dp(8);
|
||||||
|
}});
|
||||||
|
|
||||||
|
new BeamAlertDialogBuilder(ctx)
|
||||||
|
.setTitle(R.string.MenuTransformCloneCountTitle)
|
||||||
|
.setView(ll)
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||||
|
int count = 1;
|
||||||
|
try {
|
||||||
|
count = Integer.parseInt(countInput.getText().toString());
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
if (count <= 0) return;
|
||||||
|
int clones = count;
|
||||||
|
boolean arrange = autoArrange.get();
|
||||||
|
fragment.getGlView().queueEvent(() -> {
|
||||||
|
GLRenderer renderer = fragment.getGlView().getRenderer();
|
||||||
|
Model model = renderer.getModel();
|
||||||
|
int selected = renderer.getSelectedObject();
|
||||||
|
if (model == null || selected == -1) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < clones; i++) {
|
||||||
|
model.addObject(model, selected);
|
||||||
|
}
|
||||||
|
if (arrange) {
|
||||||
|
renderer.getBed().arrange(model);
|
||||||
|
renderer.resetGlModels();
|
||||||
|
}
|
||||||
|
fragment.getGlView().requestRender();
|
||||||
|
Santoku.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
|
||||||
|
ViewUtils.postOnMainThread(() -> Toast.makeText(fragment.getContext(), fragment.getContext().getResources().getQuantityString(R.plurals.MenuTransformCloneSuccessCount, clones, clones), Toast.LENGTH_SHORT).show());
|
||||||
|
});
|
||||||
|
fragment.getGlView().requestRender();
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
ViewUtils.postOnMainThread(() -> {
|
||||||
|
countInput.requestFocus();
|
||||||
|
InputMethodManager imm = (InputMethodManager) ctx.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
imm.showSoftInput(countInput, 0);
|
||||||
|
}, 200);
|
||||||
|
}),
|
||||||
new BedMenuItem(R.string.MenuTransformMirror, R.drawable.menu_transform_cut_or_mirror_28).setEnabled(hasSelection()).onClick(v -> {
|
new BedMenuItem(R.string.MenuTransformMirror, R.drawable.menu_transform_cut_or_mirror_28).setEnabled(hasSelection()).onClick(v -> {
|
||||||
Context ctx = fragment.getContext();
|
Context ctx = fragment.getContext();
|
||||||
new BeamAlertDialogBuilder(ctx)
|
new BeamAlertDialogBuilder(ctx)
|
||||||
@@ -104,6 +178,9 @@ public class TransformMenu extends ListBedMenu {
|
|||||||
|
|
||||||
((BedMenuItem) adapter.getItems().get(1)).setEnabled(hasSelection());
|
((BedMenuItem) adapter.getItems().get(1)).setEnabled(hasSelection());
|
||||||
adapter.notifyItemChanged(1);
|
adapter.notifyItemChanged(1);
|
||||||
|
|
||||||
|
((BedMenuItem) adapter.getItems().get(2)).setEnabled(hasSelection());
|
||||||
|
adapter.notifyItemChanged(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(runOnMainThread = true)
|
@EventHandler(runOnMainThread = true)
|
||||||
@@ -113,6 +190,9 @@ public class TransformMenu extends ListBedMenu {
|
|||||||
|
|
||||||
((BedMenuItem) adapter.getItems().get(1)).setEnabled(hasSelection());
|
((BedMenuItem) adapter.getItems().get(1)).setEnabled(hasSelection());
|
||||||
adapter.notifyItemChanged(1);
|
adapter.notifyItemChanged(1);
|
||||||
|
|
||||||
|
((BedMenuItem) adapter.getItems().get(2)).setEnabled(hasSelection());
|
||||||
|
adapter.notifyItemChanged(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class ScaleMenu extends UnfoldMenu {
|
public final class ScaleMenu extends UnfoldMenu {
|
||||||
@@ -152,18 +232,21 @@ public class TransformMenu extends ListBedMenu {
|
|||||||
if (j == -1) return;
|
if (j == -1) return;
|
||||||
startedScrollObject = j;
|
startedScrollObject = j;
|
||||||
|
|
||||||
|
Model model = fragment.getGlView().getRenderer().getModel();
|
||||||
|
model.getScale(j, tempVec);
|
||||||
|
double scaleX = tempVec.x, scaleY = tempVec.y, scaleZ = tempVec.z;
|
||||||
|
model.getBoundingBoxExact(j, tempVec, tempVec2);
|
||||||
|
|
||||||
if (x != null) {
|
if (x != null) {
|
||||||
xTitle.setText(formatTrackTitle(R.string.MenuTransformScaleXValue, x * 100));
|
xTitle.setText(formatTrackTitle(R.string.MenuTransformScaleXValue, x * 100, (tempVec2.x - tempVec.x) / scaleX * x));
|
||||||
}
|
}
|
||||||
if (y != null) {
|
if (y != null) {
|
||||||
yTitle.setText(formatTrackTitle(R.string.MenuTransformScaleYValue, y * 100));
|
yTitle.setText(formatTrackTitle(R.string.MenuTransformScaleYValue, y * 100, (tempVec2.y - tempVec.y) / scaleY * y));
|
||||||
}
|
}
|
||||||
if (z != null) {
|
if (z != null) {
|
||||||
zTitle.setText(formatTrackTitle(R.string.MenuTransformScaleZValue, z * 100));
|
zTitle.setText(formatTrackTitle(R.string.MenuTransformScaleZValue, z * 100, (tempVec2.z - tempVec.z) / scaleZ * z));
|
||||||
}
|
}
|
||||||
|
|
||||||
Model model = fragment.getGlView().getRenderer().getModel();
|
|
||||||
|
|
||||||
model.getRotation(j, tempVec);
|
model.getRotation(j, tempVec);
|
||||||
DoubleMatrix.setIdentityM(tempMatrix, 0);
|
DoubleMatrix.setIdentityM(tempMatrix, 0);
|
||||||
DoubleMatrix.rotateM(tempMatrix, 0, Math.toDegrees(tempVec.x), 1, 0, 0);
|
DoubleMatrix.rotateM(tempMatrix, 0, Math.toDegrees(tempVec.x), 1, 0, 0);
|
||||||
@@ -197,19 +280,22 @@ public class TransformMenu extends ListBedMenu {
|
|||||||
|
|
||||||
fragment.getGlView().queueEvent(() -> {
|
fragment.getGlView().queueEvent(() -> {
|
||||||
Model model = fragment.getGlView().getRenderer().getModel();
|
Model model = fragment.getGlView().getRenderer().getModel();
|
||||||
|
model.getScale(j, tempVec);
|
||||||
|
double scaleX = tempVec.x, scaleY = tempVec.y, scaleZ = tempVec.z;
|
||||||
|
model.getBoundingBoxExact(j, tempVec, tempVec2);
|
||||||
|
|
||||||
double dx = 1f, dy = 1f, dz = 1f;
|
double dx = 1f, dy = 1f, dz = 1f;
|
||||||
if (x != null) {
|
if (x != null) {
|
||||||
dx = x;
|
dx = x;
|
||||||
xTitle.setText(formatTrackTitle(R.string.MenuTransformScaleXValue, x * 100));
|
xTitle.setText(formatTrackTitle(R.string.MenuTransformScaleXValue, x * 100, (tempVec2.x - tempVec.x) / scaleX * x));
|
||||||
}
|
}
|
||||||
if (y != null) {
|
if (y != null) {
|
||||||
dy = y;
|
dy = y;
|
||||||
yTitle.setText(formatTrackTitle(R.string.MenuTransformScaleYValue, y * 100));
|
yTitle.setText(formatTrackTitle(R.string.MenuTransformScaleYValue, y * 100, (tempVec2.y - tempVec.y) / scaleY * y));
|
||||||
}
|
}
|
||||||
if (z != null) {
|
if (z != null) {
|
||||||
dz = z;
|
dz = z;
|
||||||
zTitle.setText(formatTrackTitle(R.string.MenuTransformScaleZValue, z * 100));
|
zTitle.setText(formatTrackTitle(R.string.MenuTransformScaleZValue, z * 100, (tempVec2.z - tempVec.z) / scaleZ * z));
|
||||||
}
|
}
|
||||||
|
|
||||||
model.getRotation(j, tempVec);
|
model.getRotation(j, tempVec);
|
||||||
@@ -233,9 +319,7 @@ public class TransformMenu extends ListBedMenu {
|
|||||||
|
|
||||||
model.getScale(j, tempVec);
|
model.getScale(j, tempVec);
|
||||||
model.scale(j, dx, dy, dz);
|
model.scale(j, dx, dy, dz);
|
||||||
|
model.ensureOnBed(j);
|
||||||
model.getBoundingBoxExact(j, tempVec, tempVec2);
|
|
||||||
model.translate(j, 0, 0, -tempVec.z);
|
|
||||||
|
|
||||||
fragment.getGlView().getRenderer().invalidateSelectionObject();
|
fragment.getGlView().getRenderer().invalidateSelectionObject();
|
||||||
fragment.getGlView().getRenderer().setSelectionScale(1, 1, 1);
|
fragment.getGlView().getRenderer().setSelectionScale(1, 1, 1);
|
||||||
@@ -245,11 +329,11 @@ public class TransformMenu extends ListBedMenu {
|
|||||||
fragment.getGlView().requestRender();
|
fragment.getGlView().requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
private CharSequence formatTrackTitle(int res, double value) {
|
private CharSequence formatTrackTitle(int res, double value, double mm) {
|
||||||
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(SliceBeam.INSTANCE.getString(res, value));
|
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(Santoku.INSTANCE.getString(res, value, mm));
|
||||||
sb.append(" d");
|
sb.append(" d");
|
||||||
int size = ViewUtils.dp(14);
|
int size = ViewUtils.dp(14);
|
||||||
Drawable dr = ContextCompat.getDrawable(SliceBeam.INSTANCE, R.drawable.edit_outline_28);
|
Drawable dr = ContextCompat.getDrawable(Santoku.INSTANCE, R.drawable.edit_outline_28);
|
||||||
dr.setTint(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
dr.setTint(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||||
dr.setBounds(0, 0, size, size);
|
dr.setBounds(0, 0, size, size);
|
||||||
sb.setSpan(new TextColorImageSpan(dr, 0), sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
sb.setSpan(new TextColorImageSpan(dr, 0), sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
@@ -263,33 +347,108 @@ public class TransformMenu extends ListBedMenu {
|
|||||||
Model model = fragment.getGlView().getRenderer().getModel();
|
Model model = fragment.getGlView().getRenderer().getModel();
|
||||||
model.getScale(j, tempVec);
|
model.getScale(j, tempVec);
|
||||||
|
|
||||||
double current;
|
Context ctx = getView().getContext();
|
||||||
|
LinearLayout ll = new LinearLayout(ctx);
|
||||||
|
ll.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
|
||||||
|
AtomicBoolean inputInMM = new AtomicBoolean(Prefs.isScaleInputInMM());
|
||||||
|
|
||||||
|
double cur;
|
||||||
if (x) {
|
if (x) {
|
||||||
current = tempVec.x * 100;
|
cur = tempVec.x * 100;
|
||||||
} else if (y) {
|
} else if (y) {
|
||||||
current = tempVec.y * 100;
|
cur = tempVec.y * 100;
|
||||||
} else {
|
} else {
|
||||||
current = tempVec.z * 100;
|
cur = tempVec.z * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
Context ctx = getView().getContext();
|
model.getScale(j, tempVec);
|
||||||
FrameLayout fl = new FrameLayout(ctx);
|
double scaleX = tempVec.x, scaleY = tempVec.y, scaleZ = tempVec.z;
|
||||||
|
model.getBoundingBoxExact(j, tempVec, tempVec2);
|
||||||
|
if (inputInMM.get()) {
|
||||||
|
cur /= 100.0;
|
||||||
|
|
||||||
|
if (x) {
|
||||||
|
cur *= (tempVec2.x - tempVec.x) / scaleX;
|
||||||
|
} else if (y) {
|
||||||
|
cur *= (tempVec2.y - tempVec.y) / scaleY;
|
||||||
|
} else {
|
||||||
|
cur *= (tempVec2.z - tempVec.z) / scaleZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double current = cur;
|
||||||
|
|
||||||
EditText text = new EditText(ctx);
|
EditText text = new EditText(ctx);
|
||||||
text.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
|
text.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
|
||||||
text.setText(String.format(Locale.ROOT, "%.2f", current));
|
text.setText(String.format(Locale.ROOT, "%.2f", current));
|
||||||
text.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
text.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||||
fl.addView(text, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
ll.addView(text, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||||
leftMargin = rightMargin = ViewUtils.dp(21);
|
leftMargin = rightMargin = ViewUtils.dp(21);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
|
PreferenceSwitchItem.SwitchPreferenceHolderView holderView = new PreferenceSwitchItem.SwitchPreferenceHolderView(ctx);
|
||||||
|
holderView.title.setText(R.string.MenuTransformScaleMM);
|
||||||
|
holderView.title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||||
|
holderView.matSwitch.setChecked(inputInMM.get());
|
||||||
|
holderView.subtitle.setVisibility(View.GONE);
|
||||||
|
holderView.setOnClickListener(v -> {
|
||||||
|
inputInMM.set(!inputInMM.get());
|
||||||
|
|
||||||
|
double value;
|
||||||
|
try {
|
||||||
|
value = Double.parseDouble(text.getText().toString());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
value = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputInMM.get()) {
|
||||||
|
value /= 100.0;
|
||||||
|
|
||||||
|
if (x) {
|
||||||
|
value *= (tempVec2.x - tempVec.x) / scaleX;
|
||||||
|
} else if (y) {
|
||||||
|
value *= (tempVec2.y - tempVec.y) / scaleY;
|
||||||
|
} else {
|
||||||
|
value *= (tempVec2.z - tempVec.z) / scaleZ;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (x) {
|
||||||
|
value /= (tempVec2.x - tempVec.x) / scaleX;
|
||||||
|
} else if (y) {
|
||||||
|
value /= (tempVec2.y - tempVec.y) / scaleY;
|
||||||
|
} else {
|
||||||
|
value /= (tempVec2.z - tempVec.z) / scaleZ;
|
||||||
|
}
|
||||||
|
value *= 100.0;
|
||||||
|
}
|
||||||
|
text.setText(String.format(Locale.ROOT, "%.2f", value));
|
||||||
|
holderView.matSwitch.setChecked(inputInMM.get());
|
||||||
|
Prefs.setScaleInputInMM(inputInMM.get());
|
||||||
|
});
|
||||||
|
ll.addView(holderView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||||
|
leftMargin = rightMargin = ViewUtils.dp(8);
|
||||||
|
}});
|
||||||
|
|
||||||
new BeamAlertDialogBuilder(ctx)
|
new BeamAlertDialogBuilder(ctx)
|
||||||
.setTitle(title)
|
.setTitle(title)
|
||||||
.setView(fl)
|
.setView(ll)
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||||
double value;
|
double value;
|
||||||
try {
|
try {
|
||||||
value = Double.parseDouble(text.getText().toString()) / 100.0;
|
value = Double.parseDouble(text.getText().toString());
|
||||||
|
|
||||||
|
if (inputInMM.get()) {
|
||||||
|
if (x) {
|
||||||
|
value /= (tempVec2.x - tempVec.x) / scaleX;
|
||||||
|
} else if (y) {
|
||||||
|
value /= (tempVec2.y - tempVec.y) / scaleY;
|
||||||
|
} else {
|
||||||
|
value /= (tempVec2.z - tempVec.z) / scaleZ;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value /= 100.0;
|
||||||
|
}
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
value = current;
|
value = current;
|
||||||
}
|
}
|
||||||
@@ -429,7 +588,7 @@ public class TransformMenu extends ListBedMenu {
|
|||||||
protected void onCreate() {
|
protected void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
SliceBeam.EVENT_BUS.registerListener(this);
|
Santoku.EVENT_BUS.registerListener(this);
|
||||||
setSelectionValues();
|
setSelectionValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,7 +596,7 @@ public class TransformMenu extends ListBedMenu {
|
|||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|
||||||
SliceBeam.EVENT_BUS.unregisterListener(this);
|
Santoku.EVENT_BUS.unregisterListener(this);
|
||||||
stopScroll();
|
stopScroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,14 +608,16 @@ public class TransformMenu extends ListBedMenu {
|
|||||||
|
|
||||||
Model model = fragment.getGlView().getRenderer().getModel();
|
Model model = fragment.getGlView().getRenderer().getModel();
|
||||||
model.getScale(j, tempVec);
|
model.getScale(j, tempVec);
|
||||||
|
double scaleX = tempVec.x, scaleY = tempVec.y, scaleZ = tempVec.z;
|
||||||
|
|
||||||
xTrack.setCurrentPosition((int) Math.round(tempVec.x * 100));
|
xTrack.setCurrentPosition((int) Math.round(scaleX * 100));
|
||||||
yTrack.setCurrentPosition((int) Math.round(tempVec.y * 100));
|
yTrack.setCurrentPosition((int) Math.round(scaleY * 100));
|
||||||
zTrack.setCurrentPosition((int) Math.round(tempVec.z * 100));
|
zTrack.setCurrentPosition((int) Math.round(scaleZ * 100));
|
||||||
|
|
||||||
xTitle.setText(formatTrackTitle(R.string.MenuTransformScaleXValue, tempVec.x * 100));
|
model.getBoundingBoxExact(j, tempVec, tempVec2);
|
||||||
yTitle.setText(formatTrackTitle(R.string.MenuTransformScaleYValue, tempVec.y * 100));
|
xTitle.setText(formatTrackTitle(R.string.MenuTransformScaleXValue, scaleX * 100, (tempVec2.x - tempVec.x)));
|
||||||
zTitle.setText(formatTrackTitle(R.string.MenuTransformScaleZValue, tempVec.z * 100));
|
yTitle.setText(formatTrackTitle(R.string.MenuTransformScaleYValue, scaleY * 100, (tempVec2.y - tempVec.y)));
|
||||||
|
zTitle.setText(formatTrackTitle(R.string.MenuTransformScaleZValue, scaleZ * 100, (tempVec2.z - tempVec.z)));
|
||||||
|
|
||||||
xTrack.updateSyncDeltas();
|
xTrack.updateSyncDeltas();
|
||||||
yTrack.updateSyncDeltas();
|
yTrack.updateSyncDeltas();
|
||||||
+38
-12
@@ -1,12 +1,12 @@
|
|||||||
package ru.ytkab0bp.slicebeam.config;
|
package com.dark98.santoku.config;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.BuildConfig;
|
import com.dark98.santoku.BuildConfig;
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.fragment.ProfileListFragment;
|
import com.dark98.santoku.fragment.ProfileListFragment;
|
||||||
|
|
||||||
/** @noinspection CopyConstructorMissesField*/
|
/** @noinspection CopyConstructorMissesField*/
|
||||||
public class ConfigObject implements ProfileListFragment.ProfileListItem {
|
public class ConfigObject implements ProfileListFragment.ProfileListItem {
|
||||||
@@ -34,10 +34,25 @@ public class ConfigObject implements ProfileListFragment.ProfileListItem {
|
|||||||
this.values.putAll(from.values);
|
this.values.putAll(from.values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note: suitable only from "printer" config
|
||||||
|
*/
|
||||||
|
public int getExtruderCount() {
|
||||||
|
return get("nozzle_diameter") != null ? get("nozzle_diameter").replaceAll("[^.]+", "").length() : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean has(String key) {
|
||||||
|
return values.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
public String get(String key) {
|
public String get(String key) {
|
||||||
return values.get(key);
|
return values.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void remove(String key) {
|
||||||
|
values.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
public void put(String key, String value) {
|
public void put(String key, String value) {
|
||||||
values.put(key, value);
|
values.put(key, value);
|
||||||
}
|
}
|
||||||
@@ -55,28 +70,35 @@ public class ConfigObject implements ProfileListFragment.ProfileListItem {
|
|||||||
public boolean isSelected() {
|
public boolean isSelected() {
|
||||||
switch (profileListType) {
|
switch (profileListType) {
|
||||||
case PROFILE_LIST_PRINT:
|
case PROFILE_LIST_PRINT:
|
||||||
return getTitle().equals(SliceBeam.CONFIG.presets.get("print"));
|
return getTitle().equals(Santoku.CONFIG.presets.get("print"));
|
||||||
case PROFILE_LIST_FILAMENT:
|
case PROFILE_LIST_FILAMENT:
|
||||||
return getTitle().equals(SliceBeam.CONFIG.presets.get("filament"));
|
return getTitle().equals(Santoku.CONFIG.presets.get("filament"));
|
||||||
case PROFILE_LIST_PRINTER:
|
case PROFILE_LIST_PRINTER:
|
||||||
return getTitle().equals(SliceBeam.CONFIG.presets.get("printer"));
|
return getTitle().equals(Santoku.CONFIG.presets.get("printer"));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String serialize() {
|
public String serialize() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("# generated by Slice Beam ").append(BuildConfig.VERSION_NAME).append("\n\n");
|
sb.append("# generated by Santoku ").append(BuildConfig.VERSION_NAME).append("\n\n");
|
||||||
for (Map.Entry<String, String> en : values.entrySet()) {
|
for (Map.Entry<String, String> en : values.entrySet()) {
|
||||||
sb.append(en.getKey()).append(" = ").append(en.getValue().replace("\n", "\\n")).append("\n");
|
String value = en.getValue();
|
||||||
|
if (value != null) {
|
||||||
|
value = value.replace("\r\n", "\n").replace("\r", "\n");
|
||||||
|
value = value.replace("\n", "\\n");
|
||||||
|
}
|
||||||
|
sb.append(en.getKey()).append(" = ").append(value).append("\n");
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ConfigObject createCustomPrinterProfile() {
|
public static ConfigObject createCustomPrinterProfile() {
|
||||||
ConfigObject custom = new ConfigObject(SliceBeam.INSTANCE.getString(R.string.IntroCustomProfileName));
|
ConfigObject custom = new ConfigObject(Santoku.INSTANCE.getString(R.string.IntroCustomProfileName));
|
||||||
custom.put("printer_technology", "FFF");
|
custom.put("printer_technology", "FFF");
|
||||||
custom.put("bed_shape", "0x0,200x0,200x200,0x200");
|
custom.put("bed_shape", "0x0,200x0,200x200,0x200");
|
||||||
|
custom.put("auto_arrange_bed_clearance", "5");
|
||||||
|
custom.put("auto_arrange_rotate", "1");
|
||||||
custom.put("binary_gcode", "0");
|
custom.put("binary_gcode", "0");
|
||||||
custom.put("gcode_flavor", "marlin");
|
custom.put("gcode_flavor", "marlin");
|
||||||
custom.put("max_print_height", "200");
|
custom.put("max_print_height", "200");
|
||||||
@@ -109,6 +131,10 @@ public class ConfigObject implements ProfileListFragment.ProfileListItem {
|
|||||||
custom.put("machine_min_extruding_rate", "0");
|
custom.put("machine_min_extruding_rate", "0");
|
||||||
custom.put("machine_min_travel_rate", "0");
|
custom.put("machine_min_travel_rate", "0");
|
||||||
|
|
||||||
|
custom.put("elegoolink_timelapse", "0");
|
||||||
|
custom.put("elegoolink_bed_leveling", "0");
|
||||||
|
custom.put("elegoolink_bed_type", "pte");
|
||||||
|
|
||||||
custom.put("start_gcode", "G90 ; use absolute coordinates\\nM83 ; extruder relative mode\\nM104 S{is_nil(idle_temperature[0]) ? 150 : idle_temperature[0]} ; set temporary nozzle temp to prevent oozing during homing\\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\\nG4 S30 ; allow partial nozzle warmup\\nG28 ; home all axis\\nG1 Z50 F240\\nG1 X2.0 Y10 F3000\\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\\nG1 Z0.28 F240\\nG92 E0\\nG1 X2.0 Y140 E10 F1500 ; prime the nozzle\\nG1 X2.3 Y140 F5000\\nG92 E0\\nG1 X2.3 Y10 E10 F1200 ; prime the nozzle\\nG92 E0");
|
custom.put("start_gcode", "G90 ; use absolute coordinates\\nM83 ; extruder relative mode\\nM104 S{is_nil(idle_temperature[0]) ? 150 : idle_temperature[0]} ; set temporary nozzle temp to prevent oozing during homing\\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\\nG4 S30 ; allow partial nozzle warmup\\nG28 ; home all axis\\nG1 Z50 F240\\nG1 X2.0 Y10 F3000\\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\\nG1 Z0.28 F240\\nG92 E0\\nG1 X2.0 Y140 E10 F1500 ; prime the nozzle\\nG1 X2.3 Y140 F5000\\nG92 E0\\nG1 X2.3 Y10 E10 F1200 ; prime the nozzle\\nG92 E0");
|
||||||
custom.put("end_gcode", "{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F600 ; Move print head up{endif}\\nG1 X5 Y{print_bed_max[1]*0.85} F{travel_speed*60} ; present print\\n{if max_layer_z < max_print_height-10}G1 Z{z_offset+min(max_layer_z+70, max_print_height-10)} F600 ; Move print head further up{endif}\\n{if max_layer_z < max_print_height*0.6}G1 Z{max_print_height*0.6} F600 ; Move print head further up{endif}\\nM140 S0 ; turn off heatbed\\nM104 S0 ; turn off temperature\\nM107 ; turn off fan\\nM84 X Y E ; disable motors");
|
custom.put("end_gcode", "{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F600 ; Move print head up{endif}\\nG1 X5 Y{print_bed_max[1]*0.85} F{travel_speed*60} ; present print\\n{if max_layer_z < max_print_height-10}G1 Z{z_offset+min(max_layer_z+70, max_print_height-10)} F600 ; Move print head further up{endif}\\n{if max_layer_z < max_print_height*0.6}G1 Z{max_print_height*0.6} F600 ; Move print head further up{endif}\\nM140 S0 ; turn off heatbed\\nM104 S0 ; turn off temperature\\nM107 ; turn off fan\\nM84 X Y E ; disable motors");
|
||||||
|
|
||||||
@@ -116,7 +142,7 @@ public class ConfigObject implements ProfileListFragment.ProfileListItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static ConfigObject createCustomFilamentProfile() {
|
public static ConfigObject createCustomFilamentProfile() {
|
||||||
ConfigObject genericFilament = new ConfigObject(SliceBeam.INSTANCE.getString(R.string.IntroCustomProfileFilamentName));
|
ConfigObject genericFilament = new ConfigObject(Santoku.INSTANCE.getString(R.string.IntroCustomProfileFilamentName));
|
||||||
genericFilament.profileListType = ConfigObject.PROFILE_LIST_FILAMENT;
|
genericFilament.profileListType = ConfigObject.PROFILE_LIST_FILAMENT;
|
||||||
genericFilament.put("first_layer_bed_temperature", "60");
|
genericFilament.put("first_layer_bed_temperature", "60");
|
||||||
genericFilament.put("bed_temperature", "60");
|
genericFilament.put("bed_temperature", "60");
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.dark98.santoku.events;
|
||||||
|
|
||||||
|
import ru.ytkab0bp.eventbus.Event;
|
||||||
|
|
||||||
|
@Event
|
||||||
|
public class CloudLoginStateUpdatedEvent {}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.dark98.santoku.events;
|
||||||
|
|
||||||
|
import ru.ytkab0bp.eventbus.Event;
|
||||||
|
|
||||||
|
@Event
|
||||||
|
public class CloudSyncFinishedEvent {}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.dark98.santoku.events;
|
||||||
|
|
||||||
|
import ru.ytkab0bp.eventbus.Event;
|
||||||
|
|
||||||
|
@Event
|
||||||
|
public class CloudUserInfoUpdatedEvent {
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.events;
|
package com.dark98.santoku.events;
|
||||||
|
|
||||||
import ru.ytkab0bp.eventbus.Event;
|
import ru.ytkab0bp.eventbus.Event;
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.dark98.santoku.events;
|
||||||
|
|
||||||
|
import ru.ytkab0bp.eventbus.Event;
|
||||||
|
|
||||||
|
@Event
|
||||||
|
public class LongClickTranslationEvent {
|
||||||
|
public final double x;
|
||||||
|
public final double y;
|
||||||
|
public final boolean visual;
|
||||||
|
|
||||||
|
public LongClickTranslationEvent(double x, double y, boolean visual) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.visual = visual;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.events;
|
package com.dark98.santoku.events;
|
||||||
|
|
||||||
import ru.ytkab0bp.eventbus.Event;
|
import ru.ytkab0bp.eventbus.Event;
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.dark98.santoku.events;
|
||||||
|
|
||||||
|
import ru.ytkab0bp.eventbus.Event;
|
||||||
|
|
||||||
|
@Event
|
||||||
|
public class NeedDismissSnackbarEvent {
|
||||||
|
public final String tag;
|
||||||
|
|
||||||
|
public NeedDismissSnackbarEvent(String tag) {
|
||||||
|
this.tag = tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.dark98.santoku.events;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import ru.ytkab0bp.eventbus.Event;
|
||||||
|
import com.dark98.santoku.Santoku;
|
||||||
|
import com.dark98.santoku.view.SnackbarsLayout;
|
||||||
|
|
||||||
|
@Event
|
||||||
|
public class NeedSnackbarEvent {
|
||||||
|
public final CharSequence title;
|
||||||
|
public SnackbarsLayout.Type type = SnackbarsLayout.Type.DONE;
|
||||||
|
public String tag;
|
||||||
|
|
||||||
|
public CharSequence buttonTitle;
|
||||||
|
public View.OnClickListener buttonClick;
|
||||||
|
|
||||||
|
public NeedSnackbarEvent(SnackbarsLayout.Type type, CharSequence title) {
|
||||||
|
this.type = type;
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NeedSnackbarEvent(CharSequence title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NeedSnackbarEvent(int title, Object... args) {
|
||||||
|
this.title = Santoku.INSTANCE.getString(title, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NeedSnackbarEvent(SnackbarsLayout.Type type, int title, Object... args) {
|
||||||
|
this.type = type;
|
||||||
|
this.title = Santoku.INSTANCE.getString(title, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NeedSnackbarEvent tag(String tag) {
|
||||||
|
this.tag = tag;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NeedSnackbarEvent button(int title, View.OnClickListener click) {
|
||||||
|
this.buttonTitle = Santoku.INSTANCE.getString(title);
|
||||||
|
this.buttonClick = click;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.events;
|
package com.dark98.santoku.events;
|
||||||
|
|
||||||
import ru.ytkab0bp.eventbus.Event;
|
import ru.ytkab0bp.eventbus.Event;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.events;
|
package com.dark98.santoku.events;
|
||||||
|
|
||||||
import ru.ytkab0bp.eventbus.Event;
|
import ru.ytkab0bp.eventbus.Event;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.events;
|
package com.dark98.santoku.events;
|
||||||
|
|
||||||
public class SlicingProgressEvent {
|
public class SlicingProgressEvent {
|
||||||
public final int progress;
|
public final int progress;
|
||||||
+152
-56
@@ -1,65 +1,75 @@
|
|||||||
package ru.ytkab0bp.slicebeam.fragment;
|
package com.dark98.santoku.fragment;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.res.ColorStateList;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.Gravity;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
|
import android.webkit.WebViewClient;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
|
||||||
import androidx.dynamicanimation.animation.FloatValueHolder;
|
import androidx.dynamicanimation.animation.FloatValueHolder;
|
||||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||||
import androidx.dynamicanimation.animation.SpringForce;
|
import androidx.dynamicanimation.animation.SpringForce;
|
||||||
|
|
||||||
import com.google.android.material.navigation.NavigationBarView;
|
import com.google.android.material.navigation.NavigationBarView;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import ru.ytkab0bp.eventbus.EventHandler;
|
import ru.ytkab0bp.eventbus.EventHandler;
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
|
import com.dark98.santoku.components.BeamAlertDialogBuilder;
|
||||||
import ru.ytkab0bp.slicebeam.components.SliceProgressBottomSheet;
|
import com.dark98.santoku.components.SliceProgressBottomSheet;
|
||||||
import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
|
import com.dark98.santoku.components.UnfoldMenu;
|
||||||
import ru.ytkab0bp.slicebeam.components.bed_menu.BedMenu;
|
import com.dark98.santoku.components.bed_menu.BedMenu;
|
||||||
import ru.ytkab0bp.slicebeam.components.bed_menu.CameraMenu;
|
import com.dark98.santoku.components.bed_menu.CameraMenu;
|
||||||
import ru.ytkab0bp.slicebeam.components.bed_menu.FileMenu;
|
import com.dark98.santoku.components.bed_menu.FileMenu;
|
||||||
import ru.ytkab0bp.slicebeam.components.bed_menu.OrientationMenu;
|
import com.dark98.santoku.components.bed_menu.OrientationMenu;
|
||||||
import ru.ytkab0bp.slicebeam.components.bed_menu.SliceMenu;
|
import com.dark98.santoku.components.bed_menu.SliceMenu;
|
||||||
import ru.ytkab0bp.slicebeam.components.bed_menu.TransformMenu;
|
import com.dark98.santoku.components.bed_menu.TransformMenu;
|
||||||
import ru.ytkab0bp.slicebeam.config.ConfigObject;
|
import com.dark98.santoku.config.ConfigObject;
|
||||||
import ru.ytkab0bp.slicebeam.events.FlattenModeResetEvent;
|
import com.dark98.santoku.events.FlattenModeResetEvent;
|
||||||
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
|
import com.dark98.santoku.events.NeedDismissSnackbarEvent;
|
||||||
import ru.ytkab0bp.slicebeam.events.SlicingProgressEvent;
|
import com.dark98.santoku.events.NeedSnackbarEvent;
|
||||||
import ru.ytkab0bp.slicebeam.navigation.Fragment;
|
import com.dark98.santoku.events.SlicingProgressEvent;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Bed3D;
|
import com.dark98.santoku.navigation.Fragment;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.GCodeProcessorResult;
|
import com.dark98.santoku.slic3r.Bed3D;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Model;
|
import com.dark98.santoku.slic3r.GCodeProcessorResult;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Slic3rRuntimeError;
|
import com.dark98.santoku.slic3r.GCodeThumbnailer;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.slic3r.Model;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Vec3d;
|
import com.dark98.santoku.slic3r.Slic3rRuntimeError;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.view.BedSwipeDownLayout;
|
import com.dark98.santoku.utils.Vec3d;
|
||||||
import ru.ytkab0bp.slicebeam.view.DividerView;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
import ru.ytkab0bp.slicebeam.view.GLView;
|
import com.dark98.santoku.view.BedSwipeDownLayout;
|
||||||
import ru.ytkab0bp.slicebeam.view.ThemeBottomNavigationView;
|
import com.dark98.santoku.view.DividerView;
|
||||||
import ru.ytkab0bp.slicebeam.view.ThemeRailNavigationView;
|
import com.dark98.santoku.view.GLView;
|
||||||
|
import com.dark98.santoku.view.SnackbarsLayout;
|
||||||
|
import com.dark98.santoku.view.ThemeBottomNavigationView;
|
||||||
|
import com.dark98.santoku.view.ThemeRailNavigationView;
|
||||||
|
|
||||||
public class BedFragment extends Fragment {
|
public class BedFragment extends Fragment {
|
||||||
private final static boolean DEBUG_VIEWER = false;
|
private final static boolean DEBUG_VIEWER = false;
|
||||||
private final static int MENU_SIZE_DP = 80;
|
private final static int MENU_SIZE_DP = 80;
|
||||||
|
|
||||||
private FrameLayout overlayLayout;
|
private FrameLayout overlayLayout;
|
||||||
private CoordinatorLayout snackbarsLayout;
|
private SnackbarsLayout snackbarsLayout;
|
||||||
private GLView glView;
|
private GLView glView;
|
||||||
private NavigationBarView navigationView;
|
private NavigationBarView navigationView;
|
||||||
|
|
||||||
@@ -104,7 +114,12 @@ public class BedFragment extends Fragment {
|
|||||||
private UnfoldMenu currentUnfoldMenu;
|
private UnfoldMenu currentUnfoldMenu;
|
||||||
|
|
||||||
private BedSwipeDownLayout swipeDownLayout;
|
private BedSwipeDownLayout swipeDownLayout;
|
||||||
|
private boolean hasWebError;
|
||||||
private WebView panelWebView;
|
private WebView panelWebView;
|
||||||
|
private LinearLayout panelWebViewError;
|
||||||
|
private ImageView webViewErrIcon;
|
||||||
|
private TextView webViewErrDescription;
|
||||||
|
private ProgressBar webViewProgressBar;
|
||||||
|
|
||||||
private static String tempFileName;
|
private static String tempFileName;
|
||||||
private static File tempExportingFile;
|
private static File tempExportingFile;
|
||||||
@@ -114,19 +129,33 @@ public class BedFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static File getTempGCodePath() {
|
public static File getTempGCodePath() {
|
||||||
return tempExportingFile != null ? tempExportingFile : new File(SliceBeam.INSTANCE.getCacheDir(), "temp.gcode");
|
return tempExportingFile != null ? tempExportingFile : new File(Santoku.INSTANCE.getCacheDir(), "temp.gcode");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
SliceBeam.EVENT_BUS.registerListener(this);
|
Santoku.EVENT_BUS.registerListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(runOnMainThread = true)
|
@EventHandler(runOnMainThread = true)
|
||||||
public void onNeedSnackbar(NeedSnackbarEvent e) {
|
public void onNeedSnackbar(NeedSnackbarEvent e) {
|
||||||
Snackbar.make(snackbarsLayout, e.title, Snackbar.LENGTH_SHORT).show();
|
SnackbarsLayout.Snackbar s = new SnackbarsLayout.Snackbar(e.type, e.title);
|
||||||
|
if (e.tag != null) {
|
||||||
|
s.tag(e.tag);
|
||||||
|
}
|
||||||
|
if (e.buttonTitle != null) {
|
||||||
|
s.lifetime = 0;
|
||||||
|
s.buttonTitle = e.buttonTitle;
|
||||||
|
s.buttonClick = e.buttonClick;
|
||||||
|
}
|
||||||
|
snackbarsLayout.show(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(runOnMainThread = true)
|
||||||
|
public void onDismissSnackbar(NeedDismissSnackbarEvent e) {
|
||||||
|
snackbarsLayout.dismiss(e.tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showUnfoldMenu(UnfoldMenu menu, View from) {
|
public void showUnfoldMenu(UnfoldMenu menu, View from) {
|
||||||
@@ -181,7 +210,7 @@ public class BedFragment extends Fragment {
|
|||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|
||||||
SliceBeam.EVENT_BUS.unregisterListener(this);
|
Santoku.EVENT_BUS.unregisterListener(this);
|
||||||
|
|
||||||
for (int i = 0; i < menuMap.size(); i++) {
|
for (int i = 0; i < menuMap.size(); i++) {
|
||||||
menuMap.valueAt(i).onViewDestroyed();
|
menuMap.valueAt(i).onViewDestroyed();
|
||||||
@@ -203,8 +232,8 @@ public class BedFragment extends Fragment {
|
|||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
glView.onResume();
|
glView.onResume();
|
||||||
ConfigObject cfg = SliceBeam.CONFIG.findPrinter(SliceBeam.CONFIG.presets.get("printer"));
|
ConfigObject cfg = Santoku.CONFIG.findPrinter(Santoku.CONFIG.presets.get("printer"));
|
||||||
boolean enable = cfg != null && cfg.get("host_type") != null && !TextUtils.isEmpty(cfg.get("print_host"));
|
boolean enable = cfg != null && cfg.get("host_type") != null && !TextUtils.isEmpty(cfg.get("print_host")) && panelWebView != null;
|
||||||
swipeDownLayout.setEnableTop(enable);
|
swipeDownLayout.setEnableTop(enable);
|
||||||
if (enable) {
|
if (enable) {
|
||||||
String host = cfg.get("print_host");
|
String host = cfg.get("print_host");
|
||||||
@@ -219,7 +248,16 @@ public class BedFragment extends Fragment {
|
|||||||
if (!host.startsWith("http://")) {
|
if (!host.startsWith("http://")) {
|
||||||
host = "http://" + host;
|
host = "http://" + host;
|
||||||
}
|
}
|
||||||
|
webViewProgressBar.animate().alpha(1).setDuration(150).start();
|
||||||
|
panelWebView.setAlpha(0f);
|
||||||
|
hasWebError = false;
|
||||||
panelWebView.loadUrl(host);
|
panelWebView.loadUrl(host);
|
||||||
|
panelWebViewError.animate().alpha(0).setDuration(150).setListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
panelWebViewError.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,8 +299,58 @@ public class BedFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
swipeDownLayout = new BedSwipeDownLayout(ctx);
|
swipeDownLayout = new BedSwipeDownLayout(ctx);
|
||||||
panelWebView = new WebView(ctx);
|
FrameLayout wfl = new FrameLayout(ctx);
|
||||||
panelWebView.getSettings().setJavaScriptEnabled(true);
|
try {
|
||||||
|
panelWebView = new WebView(ctx);
|
||||||
|
panelWebView.getSettings().setJavaScriptEnabled(true);
|
||||||
|
panelWebView.setWebViewClient(new WebViewClient() {
|
||||||
|
@Override
|
||||||
|
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
|
||||||
|
hasWebError = true;
|
||||||
|
webViewErrDescription.setText(description);
|
||||||
|
panelWebViewError.setVisibility(View.VISIBLE);
|
||||||
|
panelWebViewError.setAlpha(0f);
|
||||||
|
panelWebViewError.animate().alpha(1).setDuration(150).setListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
panelWebView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
webViewProgressBar.animate().alpha(0).setDuration(150).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageFinished(WebView view, String url) {
|
||||||
|
if (!hasWebError) {
|
||||||
|
panelWebView.animate().alpha(1).setDuration(150).start();
|
||||||
|
webViewProgressBar.animate().alpha(0).setDuration(150).start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
wfl.addView(panelWebView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
panelWebViewError = new LinearLayout(ctx);
|
||||||
|
panelWebViewError.setVisibility(View.GONE);
|
||||||
|
panelWebViewError.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
panelWebViewError.setGravity(Gravity.CENTER);
|
||||||
|
panelWebViewError.setPadding(ViewUtils.dp(12), ViewUtils.dp(12), ViewUtils.dp(12), ViewUtils.dp(12));
|
||||||
|
webViewErrIcon = new ImageView(ctx);
|
||||||
|
webViewErrIcon.setImageResource(R.drawable.globe_cross_outline_28);
|
||||||
|
panelWebViewError.addView(webViewErrIcon, new LinearLayout.LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28)) {{
|
||||||
|
bottomMargin = ViewUtils.dp(8);
|
||||||
|
}});
|
||||||
|
webViewErrDescription = new TextView(ctx);
|
||||||
|
webViewErrDescription.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||||
|
webViewErrDescription.setGravity(Gravity.CENTER);
|
||||||
|
panelWebViewError.addView(webViewErrDescription);
|
||||||
|
wfl.addView(panelWebViewError, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
|
||||||
|
webViewProgressBar = new ProgressBar(ctx);
|
||||||
|
webViewProgressBar.setAlpha(0f);
|
||||||
|
wfl.addView(webViewProgressBar, new FrameLayout.LayoutParams(ViewUtils.dp(36), ViewUtils.dp(36), Gravity.CENTER));
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.wtf("BedFragment", "Failed to initialize webview", e);
|
||||||
|
}
|
||||||
|
|
||||||
if (portrait) {
|
if (portrait) {
|
||||||
LinearLayout inner = new LinearLayout(ctx);
|
LinearLayout inner = new LinearLayout(ctx);
|
||||||
@@ -271,10 +359,10 @@ public class BedFragment extends Fragment {
|
|||||||
|
|
||||||
inner.addView(glView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
|
inner.addView(glView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
|
||||||
swipeDownLayout.addView(inner);
|
swipeDownLayout.addView(inner);
|
||||||
swipeDownLayout.addView(panelWebView);
|
swipeDownLayout.addView(wfl);
|
||||||
} else {
|
} else {
|
||||||
swipeDownLayout.addView(glView);
|
swipeDownLayout.addView(glView);
|
||||||
swipeDownLayout.addView(panelWebView);
|
swipeDownLayout.addView(wfl);
|
||||||
ll.addView(swipeDownLayout, new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f));
|
ll.addView(swipeDownLayout, new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +388,7 @@ public class BedFragment extends Fragment {
|
|||||||
.show();
|
.show();
|
||||||
} else {
|
} else {
|
||||||
tempExportingFile = null;
|
tempExportingFile = null;
|
||||||
File cfg = SliceBeam.getCurrentConfigFile();
|
File cfg = Santoku.getCurrentConfigFile();
|
||||||
File gcode = getTempGCodePath();
|
File gcode = getTempGCodePath();
|
||||||
|
|
||||||
if (!DEBUG_VIEWER) {
|
if (!DEBUG_VIEWER) {
|
||||||
@@ -311,12 +399,12 @@ public class BedFragment extends Fragment {
|
|||||||
Process.setThreadPriority(-20);
|
Process.setThreadPriority(-20);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SliceBeam.genCurrentConfig();
|
Santoku.genCurrentConfig();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e("BedFragment", "Failed to write config", e);
|
Log.e("BedFragment", "Failed to write config", e);
|
||||||
|
|
||||||
ViewUtils.postOnMainThread(()->{
|
ViewUtils.postOnMainThread(()->{
|
||||||
SliceBeam.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, ""));
|
Santoku.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, ""));
|
||||||
new BeamAlertDialogBuilder(ctx)
|
new BeamAlertDialogBuilder(ctx)
|
||||||
.setTitle(R.string.SliceFailed)
|
.setTitle(R.string.SliceFailed)
|
||||||
.setMessage(e.getMessage())
|
.setMessage(e.getMessage())
|
||||||
@@ -325,12 +413,14 @@ public class BedFragment extends Fragment {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DEBUG_VIEWER) {
|
if (!DEBUG_VIEWER) {
|
||||||
gCodeResult = glView.getRenderer().getModel().slice(cfg.getAbsolutePath(), gcode.getAbsolutePath(), (progress, text) -> SliceBeam.EVENT_BUS.fireEvent(new SlicingProgressEvent(progress, text)));
|
gCodeResult = glView.getRenderer().getModel().slice(cfg.getAbsolutePath(), gcode.getAbsolutePath(), (progress, text) -> Santoku.EVENT_BUS.fireEvent(new SlicingProgressEvent(progress, text)));
|
||||||
SliceBeam.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, ""));
|
GCodeThumbnailer.addThumbnailsToGcode(gcode, Santoku.buildCurrentConfigObject(), glView);
|
||||||
} else {
|
Santoku.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, ""));
|
||||||
gCodeResult = new GCodeProcessorResult(gcode);
|
} else {
|
||||||
}
|
gCodeResult = new GCodeProcessorResult(gcode);
|
||||||
|
GCodeThumbnailer.addThumbnailsToGcode(gcode, Santoku.buildCurrentConfigObject(), glView);
|
||||||
|
}
|
||||||
ViewUtils.postOnMainThread(()-> {
|
ViewUtils.postOnMainThread(()-> {
|
||||||
glView.queueEvent(()->{
|
glView.queueEvent(()->{
|
||||||
glView.getRenderer().setGCodeViewer(gCodeResult);
|
glView.getRenderer().setGCodeViewer(gCodeResult);
|
||||||
@@ -348,7 +438,7 @@ public class BedFragment extends Fragment {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e("BedFragment", "Slice failed", e);
|
Log.e("BedFragment", "Slice failed", e);
|
||||||
ViewUtils.postOnMainThread(()->{
|
ViewUtils.postOnMainThread(()->{
|
||||||
SliceBeam.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, ""));
|
Santoku.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, ""));
|
||||||
new BeamAlertDialogBuilder(ctx)
|
new BeamAlertDialogBuilder(ctx)
|
||||||
.setTitle(R.string.SliceFailed)
|
.setTitle(R.string.SliceFailed)
|
||||||
.setMessage(e.getMessage())
|
.setMessage(e.getMessage())
|
||||||
@@ -380,7 +470,7 @@ public class BedFragment extends Fragment {
|
|||||||
} else {
|
} else {
|
||||||
overlayLayout.addView(contentView = ll);
|
overlayLayout.addView(contentView = ll);
|
||||||
}
|
}
|
||||||
overlayLayout.addView(snackbarsLayout = new CoordinatorLayout(ctx), new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) {{
|
overlayLayout.addView(snackbarsLayout = new SnackbarsLayout(ctx), new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) {{
|
||||||
if (portrait) {
|
if (portrait) {
|
||||||
bottomMargin = ViewUtils.dp(80 * 2);
|
bottomMargin = ViewUtils.dp(80 * 2);
|
||||||
} else {
|
} else {
|
||||||
@@ -390,7 +480,7 @@ public class BedFragment extends Fragment {
|
|||||||
return overlayLayout;
|
return overlayLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CoordinatorLayout getSnackbarsLayout() {
|
public SnackbarsLayout getSnackbarsLayout() {
|
||||||
return snackbarsLayout;
|
return snackbarsLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,7 +491,7 @@ public class BedFragment extends Fragment {
|
|||||||
private void selectMenu(Context ctx, boolean portrait, int slot) {
|
private void selectMenu(Context ctx, boolean portrait, int slot) {
|
||||||
if (glView.getRenderer().resetFlattenMode()) {
|
if (glView.getRenderer().resetFlattenMode()) {
|
||||||
glView.requestRender();
|
glView.requestRender();
|
||||||
SliceBeam.EVENT_BUS.fireEvent(new FlattenModeResetEvent());
|
Santoku.EVENT_BUS.fireEvent(new FlattenModeResetEvent());
|
||||||
}
|
}
|
||||||
isAnimatingMenu = true;
|
isAnimatingMenu = true;
|
||||||
|
|
||||||
@@ -498,7 +588,6 @@ public class BedFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
glView.getRenderer().setModel(model = m);
|
|
||||||
glView.queueEvent(new Runnable() {
|
glView.queueEvent(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -507,6 +596,8 @@ public class BedFragment extends Fragment {
|
|||||||
ViewUtils.postOnMainThread(()-> glView.queueEvent(this));
|
ViewUtils.postOnMainThread(()-> glView.queueEvent(this));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
glView.getRenderer().setModel(model = m);
|
||||||
|
|
||||||
Vec3d center = bed.getVolumeMin().center(bed.getVolumeMax());
|
Vec3d center = bed.getVolumeMin().center(bed.getVolumeMax());
|
||||||
Vec3d objMin = new Vec3d(), objMax = new Vec3d();
|
Vec3d objMin = new Vec3d(), objMax = new Vec3d();
|
||||||
Vec3d objTranslate = new Vec3d();
|
Vec3d objTranslate = new Vec3d();
|
||||||
@@ -526,6 +617,11 @@ public class BedFragment extends Fragment {
|
|||||||
public void onApplyTheme() {
|
public void onApplyTheme() {
|
||||||
super.onApplyTheme();
|
super.onApplyTheme();
|
||||||
|
|
||||||
|
if (panelWebView != null) {
|
||||||
|
webViewErrIcon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.textColorSecondary)));
|
||||||
|
webViewErrDescription.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||||
|
webViewProgressBar.setIndeterminateTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.textColorSecondary)));
|
||||||
|
}
|
||||||
menuView.setBackgroundColor(ThemesRepo.getColor(android.R.attr.windowBackground));
|
menuView.setBackgroundColor(ThemesRepo.getColor(android.R.attr.windowBackground));
|
||||||
for (int i = 0; i < MenuCategory.values().length; i++) {
|
for (int i = 0; i < MenuCategory.values().length; i++) {
|
||||||
if (i != currentMenuSlot) {
|
if (i != currentMenuSlot) {
|
||||||
+34
-30
@@ -1,18 +1,18 @@
|
|||||||
package ru.ytkab0bp.slicebeam.fragment;
|
package com.dark98.santoku.fragment;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.config.ConfigObject;
|
import com.dark98.santoku.config.ConfigObject;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
|
import com.dark98.santoku.recycler.SpaceItem;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
|
import com.dark98.santoku.slic3r.PrintConfigDef;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
|
import com.dark98.santoku.slic3r.Slic3rLocalization;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Slic3rUtils;
|
import com.dark98.santoku.slic3r.Slic3rUtils;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
|
||||||
public class FilamentConfigFragment extends ProfileListFragment {
|
public class FilamentConfigFragment extends ProfileListFragment {
|
||||||
private List<ProfileListItem> compatItems;
|
private List<ProfileListItem> compatItems;
|
||||||
@@ -29,18 +29,18 @@ public class FilamentConfigFragment extends ProfileListFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<ProfileListItem> getItems(boolean filter) {
|
protected List<ProfileListItem> getItems(boolean filter) {
|
||||||
List<ConfigObject> list = SliceBeam.CONFIG.filamentConfigs;
|
List<ConfigObject> list = Santoku.CONFIG.filamentConfigs;
|
||||||
if (filter) {
|
if (filter) {
|
||||||
String printer = SliceBeam.CONFIG.presets.get("printer");
|
String printer = Santoku.CONFIG.presets.get("printer");
|
||||||
String print = SliceBeam.CONFIG.presets.get("print");
|
String print = Santoku.CONFIG.presets.get("print");
|
||||||
if (Objects.equals(lastPrinter, printer) && Objects.equals(lastPrint, print) && compatItems != null && lastUid == SliceBeam.CONFIG_UID) {
|
if (Objects.equals(lastPrinter, printer) && Objects.equals(lastPrint, print) && compatItems != null && lastUid == Santoku.CONFIG_UID) {
|
||||||
return compatItems;
|
return compatItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ConfigObject> nList = new ArrayList<>(list.size());
|
List<ConfigObject> nList = new ArrayList<>(list.size());
|
||||||
Slic3rUtils.ConfigChecker checker = new Slic3rUtils.ConfigChecker(SliceBeam.CONFIG.findPrinter(printer).serialize());
|
Slic3rUtils.ConfigChecker checker = new Slic3rUtils.ConfigChecker(Santoku.CONFIG.findPrinter(printer).serialize());
|
||||||
if (SliceBeam.CONFIG.findPrint(print) != null) {
|
if (Santoku.CONFIG.findPrint(print) != null) {
|
||||||
Slic3rUtils.ConfigChecker printChecker = new Slic3rUtils.ConfigChecker(SliceBeam.CONFIG.findPrint(print).serialize());
|
Slic3rUtils.ConfigChecker printChecker = new Slic3rUtils.ConfigChecker(Santoku.CONFIG.findPrint(print).serialize());
|
||||||
for (ConfigObject obj : list) {
|
for (ConfigObject obj : list) {
|
||||||
if (checker.checkCompatibility(obj.get("compatible_printers_condition")) && printChecker.checkCompatibility(obj.get("compatible_prints_condition"))) {
|
if (checker.checkCompatibility(obj.get("compatible_printers_condition")) && printChecker.checkCompatibility(obj.get("compatible_prints_condition"))) {
|
||||||
nList.add(obj);
|
nList.add(obj);
|
||||||
@@ -51,7 +51,7 @@ public class FilamentConfigFragment extends ProfileListFragment {
|
|||||||
checker.release();
|
checker.release();
|
||||||
lastPrinter = printer;
|
lastPrinter = printer;
|
||||||
lastPrint = print;
|
lastPrint = print;
|
||||||
lastUid = SliceBeam.CONFIG_UID;
|
lastUid = Santoku.CONFIG_UID;
|
||||||
return compatItems = (List) nList;
|
return compatItems = (List) nList;
|
||||||
}
|
}
|
||||||
return (List) list;
|
return (List) list;
|
||||||
@@ -218,14 +218,14 @@ public class FilamentConfigFragment extends ProfileListFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void cloneCurrentProfile() {
|
protected void cloneCurrentProfile() {
|
||||||
ConfigObject obj = new ConfigObject(SliceBeam.INSTANCE.getString(R.string.SettingsProfileCopy, currentConfig.getTitle()));
|
ConfigObject obj = new ConfigObject(Santoku.INSTANCE.getString(R.string.SettingsProfileCopy, currentConfig.getTitle()));
|
||||||
obj.values.putAll(currentConfig.values);
|
obj.values.putAll(currentConfig.values);
|
||||||
currentConfig = new ConfigObject(obj);
|
currentConfig = new ConfigObject(obj);
|
||||||
|
|
||||||
SliceBeam.CONFIG.filamentConfigs.add(obj);
|
Santoku.CONFIG.filamentConfigs.add(obj);
|
||||||
SliceBeam.CONFIG.presets.put("filament", obj.getTitle());
|
Santoku.CONFIG.presets.put("filament", obj.getTitle());
|
||||||
SliceBeam.saveConfig();
|
Santoku.saveConfig();
|
||||||
SliceBeam.getCurrentConfigFile().delete();
|
Santoku.getCurrentConfigFile().delete();
|
||||||
|
|
||||||
currentConfig = new ConfigObject(obj);
|
currentConfig = new ConfigObject(obj);
|
||||||
dropdownView.setTitle(getCurrentConfig().getTitle());
|
dropdownView.setTitle(getCurrentConfig().getTitle());
|
||||||
@@ -235,7 +235,7 @@ public class FilamentConfigFragment extends ProfileListFragment {
|
|||||||
@Override
|
@Override
|
||||||
protected void deleteCurrentProfile() {
|
protected void deleteCurrentProfile() {
|
||||||
compatItems = null;
|
compatItems = null;
|
||||||
SliceBeam.CONFIG.filamentConfigs.remove(SliceBeam.CONFIG.findFilament(currentConfig.getTitle()));
|
Santoku.CONFIG.filamentConfigs.remove(Santoku.CONFIG.findFilament(currentConfig.getTitle()));
|
||||||
selectItem(getItems(true).get(0));
|
selectItem(getItems(true).get(0));
|
||||||
|
|
||||||
dropdownView.setTitle(getCurrentConfig().getTitle());
|
dropdownView.setTitle(getCurrentConfig().getTitle());
|
||||||
@@ -244,21 +244,25 @@ public class FilamentConfigFragment extends ProfileListFragment {
|
|||||||
@Override
|
@Override
|
||||||
protected void onApplyConfig(String title) {
|
protected void onApplyConfig(String title) {
|
||||||
compatItems = null;
|
compatItems = null;
|
||||||
ConfigObject obj = SliceBeam.CONFIG.findFilament(currentConfig.getTitle());
|
ConfigObject obj = Santoku.CONFIG.findFilament(currentConfig.getTitle());
|
||||||
obj.setTitle(title);
|
obj.setTitle(title);
|
||||||
obj.values.putAll(currentConfig.values);
|
obj.values.putAll(currentConfig.values);
|
||||||
currentConfig.setTitle(title);
|
currentConfig.setTitle(title);
|
||||||
|
|
||||||
SliceBeam.CONFIG.presets.put("filament", title);
|
Santoku.CONFIG.presets.put("filament", title);
|
||||||
SliceBeam.saveConfig();
|
Santoku.saveConfig();
|
||||||
SliceBeam.getCurrentConfigFile().delete();
|
Santoku.getCurrentConfigFile().delete();
|
||||||
|
|
||||||
dropdownView.setTitle(title);
|
dropdownView.setTitle(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResetConfig() {
|
protected void onResetConfig() {
|
||||||
currentConfig = new ConfigObject(SliceBeam.CONFIG.findFilament(SliceBeam.CONFIG.presets.get("filament")));
|
ConfigObject base = Santoku.CONFIG.findFilament(Santoku.CONFIG.presets.get("filament"));
|
||||||
|
if (base == null && Santoku.CONFIG.filamentConfigs != null && !Santoku.CONFIG.filamentConfigs.isEmpty()) {
|
||||||
|
base = Santoku.CONFIG.filamentConfigs.get(0);
|
||||||
|
}
|
||||||
|
currentConfig = base != null ? new ConfigObject(base) : ConfigObject.createCustomFilamentProfile();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -274,7 +278,7 @@ public class FilamentConfigFragment extends ProfileListFragment {
|
|||||||
@Override
|
@Override
|
||||||
protected void selectItem(ProfileListItem item) {
|
protected void selectItem(ProfileListItem item) {
|
||||||
currentConfig = new ConfigObject((ConfigObject) item);
|
currentConfig = new ConfigObject((ConfigObject) item);
|
||||||
SliceBeam.CONFIG.presets.put("filament", item.getTitle());
|
Santoku.CONFIG.presets.put("filament", item.getTitle());
|
||||||
SliceBeam.saveConfig();
|
Santoku.saveConfig();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+30
-30
@@ -1,17 +1,17 @@
|
|||||||
package ru.ytkab0bp.slicebeam.fragment;
|
package com.dark98.santoku.fragment;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.config.ConfigObject;
|
import com.dark98.santoku.config.ConfigObject;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
|
import com.dark98.santoku.recycler.SpaceItem;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
|
import com.dark98.santoku.slic3r.PrintConfigDef;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Slic3rUtils;
|
import com.dark98.santoku.slic3r.Slic3rUtils;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
|
||||||
public class PrintConfigFragment extends ProfileListFragment {
|
public class PrintConfigFragment extends ProfileListFragment {
|
||||||
private List<ProfileListItem> compatItems;
|
private List<ProfileListItem> compatItems;
|
||||||
@@ -28,15 +28,15 @@ public class PrintConfigFragment extends ProfileListFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<ProfileListItem> getItems(boolean filter) {
|
protected List<ProfileListItem> getItems(boolean filter) {
|
||||||
List<ConfigObject> list = SliceBeam.CONFIG.printConfigs;
|
List<ConfigObject> list = Santoku.CONFIG.printConfigs;
|
||||||
if (filter) {
|
if (filter) {
|
||||||
String printer = SliceBeam.CONFIG.presets.get("printer");
|
String printer = Santoku.CONFIG.presets.get("printer");
|
||||||
if (Objects.equals(lastPrinter, printer) && compatItems != null && lastUid == SliceBeam.CONFIG_UID) {
|
if (Objects.equals(lastPrinter, printer) && compatItems != null && lastUid == Santoku.CONFIG_UID) {
|
||||||
return compatItems;
|
return compatItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ConfigObject> nList = new ArrayList<>(list.size());
|
List<ConfigObject> nList = new ArrayList<>(list.size());
|
||||||
Slic3rUtils.ConfigChecker checker = new Slic3rUtils.ConfigChecker(SliceBeam.CONFIG.findPrinter(printer).serialize());
|
Slic3rUtils.ConfigChecker checker = new Slic3rUtils.ConfigChecker(Santoku.CONFIG.findPrinter(printer).serialize());
|
||||||
for (ConfigObject obj : list) {
|
for (ConfigObject obj : list) {
|
||||||
if (checker.checkCompatibility(obj.get("compatible_printers_condition"))) {
|
if (checker.checkCompatibility(obj.get("compatible_printers_condition"))) {
|
||||||
nList.add(obj);
|
nList.add(obj);
|
||||||
@@ -44,7 +44,7 @@ public class PrintConfigFragment extends ProfileListFragment {
|
|||||||
}
|
}
|
||||||
checker.release();
|
checker.release();
|
||||||
lastPrinter = printer;
|
lastPrinter = printer;
|
||||||
lastUid = SliceBeam.CONFIG_UID;
|
lastUid = Santoku.CONFIG_UID;
|
||||||
return compatItems = (List) nList;
|
return compatItems = (List) nList;
|
||||||
}
|
}
|
||||||
return (List) list;
|
return (List) list;
|
||||||
@@ -354,14 +354,14 @@ public class PrintConfigFragment extends ProfileListFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void cloneCurrentProfile() {
|
protected void cloneCurrentProfile() {
|
||||||
ConfigObject obj = new ConfigObject(SliceBeam.INSTANCE.getString(R.string.SettingsProfileCopy, currentConfig.getTitle()));
|
ConfigObject obj = new ConfigObject(Santoku.INSTANCE.getString(R.string.SettingsProfileCopy, currentConfig.getTitle()));
|
||||||
obj.values.putAll(currentConfig.values);
|
obj.values.putAll(currentConfig.values);
|
||||||
currentConfig = new ConfigObject(obj);
|
currentConfig = new ConfigObject(obj);
|
||||||
|
|
||||||
SliceBeam.CONFIG.printConfigs.add(obj);
|
Santoku.CONFIG.printConfigs.add(obj);
|
||||||
SliceBeam.CONFIG.presets.put("print", obj.getTitle());
|
Santoku.CONFIG.presets.put("print", obj.getTitle());
|
||||||
SliceBeam.saveConfig();
|
Santoku.saveConfig();
|
||||||
SliceBeam.getCurrentConfigFile().delete();
|
Santoku.getCurrentConfigFile().delete();
|
||||||
|
|
||||||
currentConfig = new ConfigObject(obj);
|
currentConfig = new ConfigObject(obj);
|
||||||
dropdownView.setTitle(getCurrentConfig().getTitle());
|
dropdownView.setTitle(getCurrentConfig().getTitle());
|
||||||
@@ -371,7 +371,7 @@ public class PrintConfigFragment extends ProfileListFragment {
|
|||||||
@Override
|
@Override
|
||||||
protected void deleteCurrentProfile() {
|
protected void deleteCurrentProfile() {
|
||||||
compatItems = null;
|
compatItems = null;
|
||||||
SliceBeam.CONFIG.printConfigs.remove(SliceBeam.CONFIG.findPrint(currentConfig.getTitle()));
|
Santoku.CONFIG.printConfigs.remove(Santoku.CONFIG.findPrint(currentConfig.getTitle()));
|
||||||
selectItem(getItems(true).get(0));
|
selectItem(getItems(true).get(0));
|
||||||
|
|
||||||
dropdownView.setTitle(getCurrentConfig().getTitle());
|
dropdownView.setTitle(getCurrentConfig().getTitle());
|
||||||
@@ -380,28 +380,28 @@ public class PrintConfigFragment extends ProfileListFragment {
|
|||||||
@Override
|
@Override
|
||||||
protected void onApplyConfig(String title) {
|
protected void onApplyConfig(String title) {
|
||||||
compatItems = null;
|
compatItems = null;
|
||||||
ConfigObject obj = SliceBeam.CONFIG.findPrint(currentConfig.getTitle());
|
ConfigObject obj = Santoku.CONFIG.findPrint(currentConfig.getTitle());
|
||||||
obj.setTitle(title);
|
obj.setTitle(title);
|
||||||
obj.values.putAll(currentConfig.values);
|
obj.values.putAll(currentConfig.values);
|
||||||
currentConfig.setTitle(title);
|
currentConfig.setTitle(title);
|
||||||
|
|
||||||
SliceBeam.CONFIG.presets.put("print", title);
|
Santoku.CONFIG.presets.put("print", title);
|
||||||
SliceBeam.saveConfig();
|
Santoku.saveConfig();
|
||||||
SliceBeam.getCurrentConfigFile().delete();
|
Santoku.getCurrentConfigFile().delete();
|
||||||
|
|
||||||
dropdownView.setTitle(title);
|
dropdownView.setTitle(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResetConfig() {
|
protected void onResetConfig() {
|
||||||
ConfigObject print = SliceBeam.CONFIG.findPrint(SliceBeam.CONFIG.presets.get("print"));
|
ConfigObject print = Santoku.CONFIG.findPrint(Santoku.CONFIG.presets.get("print"));
|
||||||
if (print != null) {
|
if (print != null) {
|
||||||
currentConfig = new ConfigObject(print);
|
currentConfig = new ConfigObject(print);
|
||||||
} else {
|
} else {
|
||||||
currentConfig = new ConfigObject(SliceBeam.INSTANCE.getString(R.string.IntroCustomProfileName));
|
currentConfig = new ConfigObject(Santoku.INSTANCE.getString(R.string.IntroCustomProfileName));
|
||||||
SliceBeam.CONFIG.printConfigs.add(new ConfigObject(currentConfig));
|
Santoku.CONFIG.printConfigs.add(new ConfigObject(currentConfig));
|
||||||
SliceBeam.saveConfig();
|
Santoku.saveConfig();
|
||||||
SliceBeam.getCurrentConfigFile().delete();
|
Santoku.getCurrentConfigFile().delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,7 +418,7 @@ public class PrintConfigFragment extends ProfileListFragment {
|
|||||||
@Override
|
@Override
|
||||||
protected void selectItem(ProfileListItem item) {
|
protected void selectItem(ProfileListItem item) {
|
||||||
currentConfig = new ConfigObject((ConfigObject) item);
|
currentConfig = new ConfigObject((ConfigObject) item);
|
||||||
SliceBeam.CONFIG.presets.put("print", item.getTitle());
|
Santoku.CONFIG.presets.put("print", item.getTitle());
|
||||||
SliceBeam.saveConfig();
|
Santoku.saveConfig();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+81
-25
@@ -1,16 +1,17 @@
|
|||||||
package ru.ytkab0bp.slicebeam.fragment;
|
package com.dark98.santoku.fragment;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.config.ConfigObject;
|
import com.dark98.santoku.config.ConfigObject;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
|
import com.dark98.santoku.recycler.SpaceItem;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
|
import com.dark98.santoku.slic3r.PrintConfigDef;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
|
import com.dark98.santoku.slic3r.ConfigOptionDef;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.slic3r.Slic3rLocalization;
|
||||||
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
|
||||||
public class PrinterConfigFragment extends ProfileListFragment {
|
public class PrinterConfigFragment extends ProfileListFragment {
|
||||||
private ConfigObject currentConfig;
|
private ConfigObject currentConfig;
|
||||||
@@ -23,7 +24,7 @@ public class PrinterConfigFragment extends ProfileListFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<ProfileListItem> getItems(boolean filter) {
|
protected List<ProfileListItem> getItems(boolean filter) {
|
||||||
return (List) SliceBeam.CONFIG.printerConfigs;
|
return (List) Santoku.CONFIG.printerConfigs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -45,8 +46,7 @@ public class PrinterConfigFragment extends ProfileListFragment {
|
|||||||
|
|
||||||
new OptionElement(new SubHeader("Firmware")),
|
new OptionElement(new SubHeader("Firmware")),
|
||||||
new OptionElement(def.options.get("gcode_flavor")),
|
new OptionElement(def.options.get("gcode_flavor")),
|
||||||
// TODO: Thumbnails are not working *yet*
|
new OptionElement(def.options.get("thumbnails")),
|
||||||
// new OptionElement(def.options.get("thumbnails")),
|
|
||||||
new OptionElement(def.options.get("silent_mode")),
|
new OptionElement(def.options.get("silent_mode")),
|
||||||
new OptionElement(def.options.get("remaining_times")),
|
new OptionElement(def.options.get("remaining_times")),
|
||||||
new OptionElement(def.options.get("binary_gcode")),
|
new OptionElement(def.options.get("binary_gcode")),
|
||||||
@@ -113,7 +113,7 @@ public class PrinterConfigFragment extends ProfileListFragment {
|
|||||||
// TODO: m_supports_min_feedrates? <= marlin/marlin legacy
|
// TODO: m_supports_min_feedrates? <= marlin/marlin legacy
|
||||||
));
|
));
|
||||||
|
|
||||||
int count = currentConfig.get("nozzle_diameter") != null ? currentConfig.get("nozzle_diameter").replaceAll("[^.]+", "").length() : 1;
|
int count = currentConfig.getExtruderCount();
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
int j = count == 1 ? -1 : i;
|
int j = count == 1 ? -1 : i;
|
||||||
list.addAll(Arrays.asList(
|
list.addAll(Arrays.asList(
|
||||||
@@ -165,6 +165,11 @@ public class PrinterConfigFragment extends ProfileListFragment {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
list.addAll(Arrays.asList(
|
list.addAll(Arrays.asList(
|
||||||
|
new OptionElement(R.drawable.settings_outline_28, "Advanced"),
|
||||||
|
new OptionElement(new SubHeader("Auto arrange")),
|
||||||
|
new OptionElement(def.options.get("auto_arrange_bed_clearance")),
|
||||||
|
new OptionElement(def.options.get("auto_arrange_rotate")),
|
||||||
|
|
||||||
new OptionElement(R.drawable.note_pen_outline_96, "Notes"),
|
new OptionElement(R.drawable.note_pen_outline_96, "Notes"),
|
||||||
new OptionElement(new SubHeader("Notes")),
|
new OptionElement(new SubHeader("Notes")),
|
||||||
new OptionElement(def.options.get("printer_notes")),
|
new OptionElement(def.options.get("printer_notes")),
|
||||||
@@ -176,19 +181,56 @@ public class PrinterConfigFragment extends ProfileListFragment {
|
|||||||
new OptionElement(def.options.get("printhost_apikey"))
|
new OptionElement(def.options.get("printhost_apikey"))
|
||||||
));
|
));
|
||||||
|
|
||||||
|
String hostType = null;
|
||||||
|
if (diffObject != null && diffObject.has("host_type")) {
|
||||||
|
hostType = diffObject.get("host_type");
|
||||||
|
}
|
||||||
|
if (hostType == null) {
|
||||||
|
hostType = currentConfig.get("host_type");
|
||||||
|
}
|
||||||
|
if ("elegoolink".equalsIgnoreCase(hostType)) {
|
||||||
|
list.addAll(Arrays.asList(
|
||||||
|
new OptionElement(new SubHeader("ElegooLink")),
|
||||||
|
new OptionElement(def.options.get("elegoolink_timelapse")),
|
||||||
|
new OptionElement(def.options.get("elegoolink_bed_leveling")),
|
||||||
|
new OptionElement(def.options.get("elegoolink_bed_type"))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if ("moonraker".equalsIgnoreCase(hostType) || "klipper".equalsIgnoreCase(hostType)) {
|
||||||
|
int insertIndex = list.size();
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
OptionElement el = list.get(i);
|
||||||
|
if (el != null && el.simpleItem instanceof SubHeader) {
|
||||||
|
String title = ((SubHeader) el.simpleItem).title;
|
||||||
|
if ("Advanced".equals(title)) {
|
||||||
|
insertIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list.addAll(insertIndex, Arrays.asList(
|
||||||
|
new OptionElement(new SubHeader("Adaptive bed mesh")),
|
||||||
|
new OptionElement(def.options.get("bed_mesh_probe_distance")),
|
||||||
|
new OptionElement(def.options.get("bed_mesh_limit_min")),
|
||||||
|
new OptionElement(def.options.get("bed_mesh_limit_max")),
|
||||||
|
new OptionElement(def.options.get("adaptive_bed_mesh_margin")),
|
||||||
|
new OptionElement(new SpaceItem(0, ViewUtils.dp(4)))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void cloneCurrentProfile() {
|
protected void cloneCurrentProfile() {
|
||||||
ConfigObject obj = new ConfigObject(SliceBeam.INSTANCE.getString(R.string.SettingsProfileCopy, currentConfig.getTitle()));
|
ConfigObject obj = new ConfigObject(Santoku.INSTANCE.getString(R.string.SettingsProfileCopy, currentConfig.getTitle()));
|
||||||
obj.values.putAll(currentConfig.values);
|
obj.values.putAll(currentConfig.values);
|
||||||
currentConfig = new ConfigObject(obj);
|
currentConfig = new ConfigObject(obj);
|
||||||
|
|
||||||
SliceBeam.CONFIG.printerConfigs.add(obj);
|
Santoku.CONFIG.printerConfigs.add(obj);
|
||||||
SliceBeam.CONFIG.presets.put("printer", obj.getTitle());
|
Santoku.CONFIG.presets.put("printer", obj.getTitle());
|
||||||
SliceBeam.saveConfig();
|
Santoku.saveConfig();
|
||||||
SliceBeam.getCurrentConfigFile().delete();
|
Santoku.getCurrentConfigFile().delete();
|
||||||
|
|
||||||
currentConfig = new ConfigObject(obj);
|
currentConfig = new ConfigObject(obj);
|
||||||
dropdownView.setTitle(getCurrentConfig().getTitle());
|
dropdownView.setTitle(getCurrentConfig().getTitle());
|
||||||
@@ -196,28 +238,28 @@ public class PrinterConfigFragment extends ProfileListFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void deleteCurrentProfile() {
|
protected void deleteCurrentProfile() {
|
||||||
SliceBeam.CONFIG.printerConfigs.remove(SliceBeam.CONFIG.findPrinter(currentConfig.getTitle()));
|
Santoku.CONFIG.printerConfigs.remove(Santoku.CONFIG.findPrinter(currentConfig.getTitle()));
|
||||||
selectItem(getItems(true).get(0));
|
selectItem(getItems(true).get(0));
|
||||||
dropdownView.setTitle(getCurrentConfig().getTitle());
|
dropdownView.setTitle(getCurrentConfig().getTitle());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onApplyConfig(String title) {
|
protected void onApplyConfig(String title) {
|
||||||
ConfigObject obj = SliceBeam.CONFIG.findPrinter(currentConfig.getTitle());
|
ConfigObject obj = Santoku.CONFIG.findPrinter(currentConfig.getTitle());
|
||||||
obj.setTitle(title);
|
obj.setTitle(title);
|
||||||
obj.values.putAll(currentConfig.values);
|
obj.values.putAll(currentConfig.values);
|
||||||
currentConfig.setTitle(title);
|
currentConfig.setTitle(title);
|
||||||
|
|
||||||
SliceBeam.CONFIG.presets.put("printer", title);
|
Santoku.CONFIG.presets.put("printer", title);
|
||||||
SliceBeam.saveConfig();
|
Santoku.saveConfig();
|
||||||
SliceBeam.getCurrentConfigFile().delete();
|
Santoku.getCurrentConfigFile().delete();
|
||||||
|
|
||||||
dropdownView.setTitle(title);
|
dropdownView.setTitle(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResetConfig() {
|
protected void onResetConfig() {
|
||||||
currentConfig = new ConfigObject(SliceBeam.CONFIG.findPrinter(SliceBeam.CONFIG.presets.get("printer")));
|
currentConfig = new ConfigObject(Santoku.CONFIG.findPrinter(Santoku.CONFIG.presets.get("printer")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -233,9 +275,23 @@ public class PrinterConfigFragment extends ProfileListFragment {
|
|||||||
@Override
|
@Override
|
||||||
protected void selectItem(ProfileListItem item) {
|
protected void selectItem(ProfileListItem item) {
|
||||||
currentConfig = new ConfigObject((ConfigObject) item);
|
currentConfig = new ConfigObject((ConfigObject) item);
|
||||||
SliceBeam.CONFIG.presets.put("printer", item.getTitle());
|
Santoku.CONFIG.presets.put("printer", item.getTitle());
|
||||||
|
|
||||||
// TODO: Reset print/filament profiles, maybe physical profiles?
|
// TODO: Reset print/filament profiles, maybe physical profiles?
|
||||||
SliceBeam.saveConfig();
|
Santoku.saveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateConfigField(ConfigOptionDef def, int i, String value) {
|
||||||
|
super.updateConfigField(def, i, value);
|
||||||
|
if ("host_type".equals(def.key)) {
|
||||||
|
onUpdateConfigItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onUpdateConfigItems() {
|
||||||
|
setConfigItems(getConfigItems());
|
||||||
|
super.onUpdateConfigItems();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+235
-50
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.fragment;
|
package com.dark98.santoku.fragment;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -6,6 +6,8 @@ import android.content.res.ColorStateList;
|
|||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Path;
|
||||||
|
import android.graphics.RectF;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -34,6 +36,8 @@ import androidx.dynamicanimation.animation.SpringAnimation;
|
|||||||
import androidx.dynamicanimation.animation.SpringForce;
|
import androidx.dynamicanimation.animation.SpringForce;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
|
||||||
import com.mrudultora.colorpicker.ColorPickerPopUp;
|
import com.mrudultora.colorpicker.ColorPickerPopUp;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -45,29 +49,34 @@ import java.util.Map;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
|
import com.dark98.santoku.cloud.CloudAPI;
|
||||||
import ru.ytkab0bp.slicebeam.components.BeamColorPickerPopUp;
|
import com.dark98.santoku.cloud.CloudController;
|
||||||
import ru.ytkab0bp.slicebeam.config.ConfigObject;
|
import com.dark98.santoku.components.BeamAlertDialogBuilder;
|
||||||
import ru.ytkab0bp.slicebeam.navigation.Fragment;
|
import com.dark98.santoku.components.BeamColorPickerPopUp;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.CubicBezierItemAnimator;
|
import com.dark98.santoku.config.ConfigObject;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.PreferenceItem;
|
import com.dark98.santoku.navigation.Fragment;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.PreferenceSwitchItem;
|
import com.dark98.santoku.recycler.CubicBezierItemAnimator;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
|
import com.dark98.santoku.recycler.PreferenceItem;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.ConfigOptionDef;
|
import com.dark98.santoku.recycler.PreferenceSwitchItem;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
|
import com.dark98.santoku.recycler.SimpleRecyclerItem;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
|
import com.dark98.santoku.slic3r.ConfigOptionDef;
|
||||||
import ru.ytkab0bp.slicebeam.theme.IThemeView;
|
import com.dark98.santoku.slic3r.PrintConfigDef;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.slic3r.Slic3rConfigWrapper;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Prefs;
|
import com.dark98.santoku.slic3r.Slic3rLocalization;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.theme.IThemeView;
|
||||||
import ru.ytkab0bp.slicebeam.view.BeamButton;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.view.DividerView;
|
import com.dark98.santoku.utils.Prefs;
|
||||||
import ru.ytkab0bp.slicebeam.view.FadeRecyclerView;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
import ru.ytkab0bp.slicebeam.view.ProfileDropdownView;
|
import com.dark98.santoku.view.BeamButton;
|
||||||
|
import com.dark98.santoku.view.DividerView;
|
||||||
|
import com.dark98.santoku.view.FadeRecyclerView;
|
||||||
|
import com.dark98.santoku.view.ProfileDropdownView;
|
||||||
|
|
||||||
public abstract class ProfileListFragment extends Fragment {
|
public abstract class ProfileListFragment extends Fragment {
|
||||||
|
public final static int SPECIAL_TYPE_CLOUD_HEADER = 0;
|
||||||
|
|
||||||
private final static Object ROTATION_PAYLOAD = new Object();
|
private final static Object ROTATION_PAYLOAD = new Object();
|
||||||
|
|
||||||
protected ProfileDropdownView dropdownView;
|
protected ProfileDropdownView dropdownView;
|
||||||
@@ -75,7 +84,7 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
protected ImageView resetButton;
|
protected ImageView resetButton;
|
||||||
protected BeamButton saveButton;
|
protected BeamButton saveButton;
|
||||||
|
|
||||||
protected boolean changedConfig;
|
protected ConfigObject diffObject = new ConfigObject();
|
||||||
|
|
||||||
private List<OptionWrapper> currentList = Collections.emptyList();
|
private List<OptionWrapper> currentList = Collections.emptyList();
|
||||||
private SparseArray<List<OptionWrapper>> categoryElements = new SparseArray<>();
|
private SparseArray<List<OptionWrapper>> categoryElements = new SparseArray<>();
|
||||||
@@ -146,8 +155,8 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
int pos = getChildViewHolder(ch).getAdapterPosition();
|
int pos = getChildViewHolder(ch).getAdapterPosition();
|
||||||
if (pos == -1 || ch.getAlpha() < 1) continue;
|
if (pos == -1 || ch.getAlpha() < 1) continue;
|
||||||
|
|
||||||
boolean top = currentList.get(pos).title != null;
|
boolean top = currentList.get(pos).title != null || currentList.get(pos).hasSpecialType();
|
||||||
boolean bottom = pos == getAdapter().getItemCount() - 1 || currentList.get(pos + 1).title != null;
|
boolean bottom = pos == getAdapter().getItemCount() - 1 || currentList.get(pos + 1).title != null || currentList.get(pos + 1).hasSpecialType();
|
||||||
|
|
||||||
if (top && startI != -1) {
|
if (top && startI != -1) {
|
||||||
c.drawRoundRect(0, getChildAt(startI).getTop() + getChildAt(startI).getTranslationY(), getWidth(), ch.getTop() + ch.getTranslationY() - ViewUtils.dp(8), ViewUtils.dp(32), ViewUtils.dp(32), bgPaint);
|
c.drawRoundRect(0, getChildAt(startI).getTop() + getChildAt(startI).getTranslationY(), getWidth(), ch.getTop() + ch.getTranslationY() - ViewUtils.dp(8), ViewUtils.dp(32), ViewUtils.dp(32), bgPaint);
|
||||||
@@ -204,7 +213,7 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
};
|
};
|
||||||
recyclerView.setItemAnimator(new CubicBezierItemAnimator());
|
recyclerView.setItemAnimator(new CubicBezierItemAnimator());
|
||||||
recyclerView.setAdapter(new RecyclerView.Adapter() {
|
recyclerView.setAdapter(new RecyclerView.Adapter() {
|
||||||
private final static int TYPE_TITLE = 0, TYPE_SIMPLE = 1;
|
private final static int TYPE_TITLE = 0, TYPE_CLOUD_PROFILE = 1, TYPE_SIMPLE = 2;
|
||||||
|
|
||||||
private Map<Class<?>, Integer> viewType = new HashMap<>();
|
private Map<Class<?>, Integer> viewType = new HashMap<>();
|
||||||
private Map<Integer, SimpleRecyclerItem> viewCreator = new HashMap<>();
|
private Map<Integer, SimpleRecyclerItem> viewCreator = new HashMap<>();
|
||||||
@@ -219,6 +228,9 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
v = viewCreator.get(viewType).onCreateView(ctx);
|
v = viewCreator.get(viewType).onCreateView(ctx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case TYPE_CLOUD_PROFILE:
|
||||||
|
v = new CloudProfileHeaderView(ctx);
|
||||||
|
break;
|
||||||
case TYPE_TITLE:
|
case TYPE_TITLE:
|
||||||
v = new CategoryHolderView(ctx);
|
v = new CategoryHolderView(ctx);
|
||||||
break;
|
break;
|
||||||
@@ -258,6 +270,43 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
el.simpleItem.onBindView(holder.itemView);
|
el.simpleItem.onBindView(holder.itemView);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case TYPE_CLOUD_PROFILE: {
|
||||||
|
OptionWrapper w = currentList.get(position);
|
||||||
|
CloudProfileHeaderView holderView = (CloudProfileHeaderView) holder.itemView;
|
||||||
|
holderView.setTag(w.color);
|
||||||
|
if (Prefs.getCloudAPIToken() != null) {
|
||||||
|
CloudAPI.UserInfo info = CloudController.getUserInfo();
|
||||||
|
if (info != null) {
|
||||||
|
if (!TextUtils.isEmpty(info.avatarUrl)) {
|
||||||
|
holderView.hasAvatar = true;
|
||||||
|
Glide.with(holderView.avatar)
|
||||||
|
.load(info.avatarUrl)
|
||||||
|
.circleCrop()
|
||||||
|
.transition(DrawableTransitionOptions.withCrossFade())
|
||||||
|
.into(holderView.avatar);
|
||||||
|
} else {
|
||||||
|
holderView.hasAvatar = false;
|
||||||
|
holderView.avatar.setImageResource(R.drawable.user_circle_outline_28);
|
||||||
|
}
|
||||||
|
|
||||||
|
holderView.title.setText(info.displayName);
|
||||||
|
} else {
|
||||||
|
holderView.hasAvatar = false;
|
||||||
|
holderView.avatar.setImageResource(R.drawable.user_circle_outline_28);
|
||||||
|
|
||||||
|
holderView.title.setText(R.string.SettingsCloudLoading);
|
||||||
|
}
|
||||||
|
holderView.subtitle.setText(R.string.SettingsCloudTapToManage);
|
||||||
|
} else {
|
||||||
|
holderView.hasAvatar = false;
|
||||||
|
holderView.avatar.setImageResource(R.drawable.user_circle_outline_28);
|
||||||
|
holderView.title.setText(R.string.SettingsCloudNotLoggedIn);
|
||||||
|
holderView.subtitle.setText(R.string.SettingsCloudTapToShowMore);
|
||||||
|
}
|
||||||
|
holderView.onApplyTheme();
|
||||||
|
holderView.setOnClickListener(view -> w.onClick.run());
|
||||||
|
break;
|
||||||
|
}
|
||||||
case TYPE_TITLE: {
|
case TYPE_TITLE: {
|
||||||
OptionWrapper w = currentList.get(position);
|
OptionWrapper w = currentList.get(position);
|
||||||
CategoryHolderView holderView = (CategoryHolderView) holder.itemView;
|
CategoryHolderView holderView = (CategoryHolderView) holder.itemView;
|
||||||
@@ -301,6 +350,13 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
@Override
|
@Override
|
||||||
public int getItemViewType(int position) {
|
public int getItemViewType(int position) {
|
||||||
OptionWrapper w = currentList.get(position);
|
OptionWrapper w = currentList.get(position);
|
||||||
|
if (w.optionEl != null && w.optionEl.specialType != -1) {
|
||||||
|
switch (w.optionEl.specialType) {
|
||||||
|
default:
|
||||||
|
case SPECIAL_TYPE_CLOUD_HEADER:
|
||||||
|
return TYPE_CLOUD_PROFILE;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (w.title != null) return TYPE_TITLE;
|
if (w.title != null) return TYPE_TITLE;
|
||||||
|
|
||||||
if (w.optionEl.simpleItem != null) {
|
if (w.optionEl.simpleItem != null) {
|
||||||
@@ -332,21 +388,45 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
saveButton.setPadding(ViewUtils.dp(21), ViewUtils.dp(12), ViewUtils.dp(21), ViewUtils.dp(12));
|
saveButton.setPadding(ViewUtils.dp(21), ViewUtils.dp(12), ViewUtils.dp(21), ViewUtils.dp(12));
|
||||||
|
|
||||||
saveButton.setOnClickListener(v -> {
|
saveButton.setOnClickListener(v -> {
|
||||||
FrameLayout fl = new FrameLayout(ctx);
|
LinearLayout linear = new LinearLayout(ctx);
|
||||||
|
linear.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (Map.Entry<String, String> en : diffObject.values.entrySet()) {
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
ConfigOptionDef def = PrintConfigDef.getInstance().options.get(en.getKey());
|
||||||
|
sb.append(Slic3rLocalization.getString(def.getFullLabel())).append(" - ").append(opt(def, -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
ScrollView scrollView = new ScrollView(ctx);
|
||||||
|
TextView subtitle = new TextView(ctx);
|
||||||
|
subtitle.setTextAppearance(ctx, com.google.android.material.R.style.MaterialAlertDialog_Material3_Body_Text);
|
||||||
|
subtitle.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||||
|
subtitle.setText(sb.toString());
|
||||||
|
subtitle.setPadding(ViewUtils.dp(24), ViewUtils.dp(12), ViewUtils.dp(24), ViewUtils.dp(12));
|
||||||
|
scrollView.addView(subtitle, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
linear.addView(scrollView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
|
||||||
|
}
|
||||||
|
|
||||||
EditText text = new EditText(ctx);
|
EditText text = new EditText(ctx);
|
||||||
text.setText(getCurrentConfig().getTitle());
|
text.setText(getCurrentConfig().getTitle());
|
||||||
text.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
text.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||||
fl.addView(text, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
linear.addView(text, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||||
leftMargin = rightMargin = ViewUtils.dp(21);
|
leftMargin = rightMargin = ViewUtils.dp(21);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
AlertDialog dialog = new BeamAlertDialogBuilder(ctx)
|
AlertDialog dialog = new BeamAlertDialogBuilder(ctx)
|
||||||
.setTitle(R.string.SettingsSaveTitle)
|
.setTitle(R.string.SettingsSaveTitle)
|
||||||
// TODO: Draw settings delta
|
.setView(linear)
|
||||||
.setView(fl)
|
|
||||||
.setPositiveButton(android.R.string.ok, (d, which) -> {
|
.setPositiveButton(android.R.string.ok, (d, which) -> {
|
||||||
|
getCurrentConfig().values.putAll(diffObject.values);
|
||||||
|
diffObject.values.clear();
|
||||||
|
|
||||||
onApplyConfig(text.getText().toString());
|
onApplyConfig(text.getText().toString());
|
||||||
resetButton.animate().alpha(0.6f).start();
|
resetButton.animate().alpha(0.4f).setDuration(150).start();
|
||||||
resetButton.setClickable(false);
|
resetButton.setClickable(false);
|
||||||
onUpdateConfigItems();
|
onUpdateConfigItems();
|
||||||
})
|
})
|
||||||
@@ -395,13 +475,14 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
resetButton.setImageResource(R.drawable.refresh_outline_28);
|
resetButton.setImageResource(R.drawable.refresh_outline_28);
|
||||||
resetButton.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.textColorPrimary)));
|
resetButton.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.textColorPrimary)));
|
||||||
resetButton.setScaleX(-1f);
|
resetButton.setScaleX(-1f);
|
||||||
resetButton.setAlpha(0.6f);
|
resetButton.setAlpha(0.4f);
|
||||||
resetButton.setOnClickListener(v -> new BeamAlertDialogBuilder(ctx)
|
resetButton.setOnClickListener(v -> new BeamAlertDialogBuilder(ctx)
|
||||||
.setTitle(R.string.SettingsResetProfileTitle)
|
.setTitle(R.string.SettingsResetProfileTitle)
|
||||||
.setMessage(R.string.SettingsResetProfileDescription)
|
.setMessage(R.string.SettingsResetProfileDescription)
|
||||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||||
onResetConfig();
|
onResetConfig();
|
||||||
resetButton.animate().alpha(0.6f).start();
|
diffObject.values.clear();
|
||||||
|
resetButton.animate().alpha(0.4f).setDuration(150).start();
|
||||||
resetButton.setClickable(false);
|
resetButton.setClickable(false);
|
||||||
onUpdateConfigItems();
|
onUpdateConfigItems();
|
||||||
})
|
})
|
||||||
@@ -426,24 +507,31 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
SliceBeam.EVENT_BUS.registerListener(this);
|
Santoku.EVENT_BUS.registerListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
SliceBeam.EVENT_BUS.unregisterListener(this);
|
Santoku.EVENT_BUS.unregisterListener(this);
|
||||||
|
unfolded.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
protected void setConfigItems(List<OptionElement> items) {
|
protected void setConfigItems(List<OptionElement> items) {
|
||||||
|
categoryElements.clear();
|
||||||
List<OptionWrapper> list = new ArrayList<>();
|
List<OptionWrapper> list = new ArrayList<>();
|
||||||
int j = 0;
|
int j = 0;
|
||||||
for (int i = 0; i < items.size(); i++) {
|
for (int i = 0; i < items.size(); i++) {
|
||||||
OptionElement el = items.get(i);
|
OptionElement el = items.get(i);
|
||||||
if (el == null) continue;
|
if (el == null) continue;
|
||||||
OptionWrapper w = el.title != null ? new OptionWrapper(el.icon, el.title, el.onClick, el.color, el.noTint) : new OptionWrapper(el);
|
OptionWrapper w = el.title != null ? new OptionWrapper(el.icon, el.title, el.onClick, el.color, el.noTint) : new OptionWrapper(el);
|
||||||
if (el.title != null) {
|
if (el.specialType != -1) {
|
||||||
|
w.color = el.color;
|
||||||
|
w.noTint = el.noTint;
|
||||||
|
w.onClick = el.onClick;
|
||||||
|
}
|
||||||
|
if (el.title != null || el.specialType != -1) {
|
||||||
w.categoryIndex = j;
|
w.categoryIndex = j;
|
||||||
categoryElements.put(j, new ArrayList<>());
|
categoryElements.put(j, new ArrayList<>());
|
||||||
j++;
|
j++;
|
||||||
@@ -452,7 +540,21 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
categoryElements.get(j - 1).add(w);
|
categoryElements.get(j - 1).add(w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentList = list;
|
List<OptionWrapper> expanded = new ArrayList<>();
|
||||||
|
int categoryIndex = 0;
|
||||||
|
for (OptionWrapper w : list) {
|
||||||
|
expanded.add(w);
|
||||||
|
if (w.categoryIndex == categoryIndex && unfolded.get(categoryIndex)) {
|
||||||
|
List<OptionWrapper> extra = categoryElements.get(categoryIndex);
|
||||||
|
if (extra != null) {
|
||||||
|
expanded.addAll(extra);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (w.categoryIndex == categoryIndex) {
|
||||||
|
categoryIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentList = expanded;
|
||||||
recyclerView.getAdapter().notifyDataSetChanged();
|
recyclerView.getAdapter().notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -474,10 +576,16 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String opt(ConfigOptionDef def, int i) {
|
private String opt(ConfigOptionDef def, int i) {
|
||||||
String v = getCurrentConfig().get(def.key);
|
return opt(def, i, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String opt(ConfigOptionDef def, int i, boolean ignoreDiff) {
|
||||||
|
String v = ignoreDiff || !diffObject.has(def.key) ? getCurrentConfig().get(def.key) : diffObject.get(def.key);
|
||||||
if (i != -1) {
|
if (i != -1) {
|
||||||
try {
|
try {
|
||||||
v = v.split(",")[i];
|
String ch = ",";
|
||||||
|
if (def.guiType == ConfigOptionDef.GUIType.COLOR) ch = ";";
|
||||||
|
v = v.split(ch)[i];
|
||||||
} catch (ArrayIndexOutOfBoundsException e) {
|
} catch (ArrayIndexOutOfBoundsException e) {
|
||||||
Log.w("ProfileListFragment", "Failed to parse mm option", e);
|
Log.w("ProfileListFragment", "Failed to parse mm option", e);
|
||||||
}
|
}
|
||||||
@@ -487,17 +595,27 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
|
|
||||||
protected void updateConfigField(ConfigOptionDef def, int i, String value) {
|
protected void updateConfigField(ConfigOptionDef def, int i, String value) {
|
||||||
if (i != -1) {
|
if (i != -1) {
|
||||||
String[] vals = opt(def, -1).split(",");
|
String ch = ",";
|
||||||
|
if (def.guiType == ConfigOptionDef.GUIType.COLOR) ch = ";";
|
||||||
|
String[] vals = opt(def, -1, true).split(ch);
|
||||||
vals[i] = value;
|
vals[i] = value;
|
||||||
value = TextUtils.join(",", vals);
|
value = TextUtils.join(ch, vals);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Objects.equals(opt(def, i), value)) {
|
boolean wasEmpty = diffObject.values.isEmpty();
|
||||||
changedConfig = true;
|
if (Objects.equals(opt(def, i, true), value)) {
|
||||||
resetButton.animate().alpha(1).start();
|
diffObject.remove(def.key);
|
||||||
resetButton.setClickable(true);
|
} else {
|
||||||
|
diffObject.put(def.key, value);
|
||||||
|
}
|
||||||
|
boolean empty = diffObject.values.isEmpty();
|
||||||
|
if (wasEmpty && !empty) {
|
||||||
|
resetButton.animate().alpha(1.0f).setDuration(150).start();
|
||||||
|
resetButton.setClickable(true);
|
||||||
|
} else if (!wasEmpty && empty) {
|
||||||
|
resetButton.animate().alpha(0.4f).setDuration(150).start();
|
||||||
|
resetButton.setClickable(false);
|
||||||
}
|
}
|
||||||
getCurrentConfig().put(def.key, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
@@ -521,6 +639,8 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class OptionElement {
|
public final class OptionElement {
|
||||||
|
public int specialType = -1;
|
||||||
|
|
||||||
public int icon;
|
public int icon;
|
||||||
public String title;
|
public String title;
|
||||||
public int color;
|
public int color;
|
||||||
@@ -571,8 +691,8 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
String[] labels;
|
String[] labels;
|
||||||
String[] values;
|
String[] values;
|
||||||
if (Objects.equals("host_type", def.key)) {
|
if (Objects.equals("host_type", def.key)) {
|
||||||
labels = new String[]{"OctoPrint"};
|
labels = new String[]{"OctoPrint", "Klipper (Moonraker)", "ElegooLink"};
|
||||||
values = new String[]{"octoprint"};
|
values = new String[]{"octoprint", "moonraker", "elegoolink"};
|
||||||
} else {
|
} else {
|
||||||
labels = new String[def.enumLabels.length];
|
labels = new String[def.enumLabels.length];
|
||||||
values = def.enumValues;
|
values = def.enumValues;
|
||||||
@@ -697,6 +817,13 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ref.set(builder.show());
|
ref.set(builder.show());
|
||||||
|
}).setOnLongClickListener(v -> {
|
||||||
|
new BeamAlertDialogBuilder(getContext())
|
||||||
|
.setTitle(Slic3rLocalization.getString(def.getFullLabel()))
|
||||||
|
.setMessage(Slic3rLocalization.getString(def.tooltip))
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show();
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (def.type == ConfigOptionDef.ConfigOptionType.STRING || def.type == ConfigOptionDef.ConfigOptionType.STRINGS) {
|
if (def.type == ConfigOptionDef.ConfigOptionType.STRING || def.type == ConfigOptionDef.ConfigOptionType.STRINGS) {
|
||||||
@@ -727,11 +854,23 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
case BOOLS:
|
case BOOLS:
|
||||||
simpleItem = new PreferenceSwitchItem().setTitle(Slic3rLocalization.getString(def.label))
|
simpleItem = new PreferenceSwitchItem().setTitle(Slic3rLocalization.getString(def.label))
|
||||||
.setValueProvider(() -> "1".equals(opt(def, eIndex)))
|
.setValueProvider(() -> "1".equals(opt(def, eIndex)))
|
||||||
.setChangeListener((buttonView, isChecked) -> updateConfigField(def, eIndex, String.valueOf(isChecked ? 1 : 0)));
|
.setChangeListener((buttonView, isChecked) -> updateConfigField(def, eIndex, String.valueOf(isChecked ? 1 : 0)))
|
||||||
|
.setLongClickListener(v -> {
|
||||||
|
new BeamAlertDialogBuilder(getContext())
|
||||||
|
.setTitle(Slic3rLocalization.getString(def.getFullLabel()))
|
||||||
|
.setMessage(Slic3rLocalization.getString(def.tooltip))
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OptionElement(int specialType) {
|
||||||
|
this.specialType = specialType;
|
||||||
|
}
|
||||||
|
|
||||||
public OptionElement(int icon, String title) {
|
public OptionElement(int icon, String title) {
|
||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
@@ -855,10 +994,13 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
optionEl = el;
|
optionEl = el;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean hasSpecialType() {
|
||||||
|
return optionEl != null && optionEl.specialType != -1;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(Context ctx) {
|
public View onCreateView(Context ctx) {
|
||||||
FrameLayout v = new FrameLayout(ctx);
|
FrameLayout v = new FrameLayout(ctx);
|
||||||
v.setBackgroundColor(Color.GREEN);
|
|
||||||
v.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(32)) {{
|
v.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(32)) {{
|
||||||
bottomMargin = ViewUtils.dp(16);
|
bottomMargin = ViewUtils.dp(16);
|
||||||
}});
|
}});
|
||||||
@@ -866,6 +1008,49 @@ public abstract class ProfileListFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final static class CloudProfileHeaderView extends LinearLayout implements IThemeView {
|
||||||
|
private ImageView avatar;
|
||||||
|
private TextView title;
|
||||||
|
private TextView subtitle;
|
||||||
|
private boolean hasAvatar;
|
||||||
|
|
||||||
|
public CloudProfileHeaderView(Context context) {
|
||||||
|
super(context);
|
||||||
|
|
||||||
|
setOrientation(HORIZONTAL);
|
||||||
|
setGravity(Gravity.CENTER_VERTICAL);
|
||||||
|
setPadding(ViewUtils.dp(21), ViewUtils.dp(16), ViewUtils.dp(21), ViewUtils.dp(16));
|
||||||
|
|
||||||
|
avatar = new ImageView(context);
|
||||||
|
addView(avatar, new LayoutParams(ViewUtils.dp(26), ViewUtils.dp(26)) {{
|
||||||
|
setMarginEnd(ViewUtils.dp(12));
|
||||||
|
}});
|
||||||
|
|
||||||
|
LinearLayout ll = new LinearLayout(context);
|
||||||
|
ll.setOrientation(VERTICAL);
|
||||||
|
ll.setGravity(Gravity.CENTER);
|
||||||
|
title = new TextView(context);
|
||||||
|
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||||
|
title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||||
|
ll.addView(title);
|
||||||
|
subtitle = new TextView(context);
|
||||||
|
subtitle.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||||
|
ll.addView(subtitle);
|
||||||
|
addView(ll, new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
|
||||||
|
|
||||||
|
onApplyTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplyTheme() {
|
||||||
|
setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 32));
|
||||||
|
title.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||||
|
subtitle.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||||
|
if (!hasAvatar) {
|
||||||
|
avatar.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.textColorSecondary)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
private final static class CategoryHolderView extends LinearLayout implements IThemeView {
|
private final static class CategoryHolderView extends LinearLayout implements IThemeView {
|
||||||
private ImageView icon;
|
private ImageView icon;
|
||||||
private TextView title;
|
private TextView title;
|
||||||
+25
-31
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.fragment;
|
package com.dark98.santoku.fragment;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
@@ -20,18 +20,18 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ru.ytkab0bp.eventbus.EventHandler;
|
import ru.ytkab0bp.eventbus.EventHandler;
|
||||||
import ru.ytkab0bp.slicebeam.BeamServerData;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.SetupActivity;
|
||||||
import ru.ytkab0bp.slicebeam.SetupActivity;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.components.BeamAlertDialogBuilder;
|
||||||
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
|
import com.dark98.santoku.components.BeamColorPickerPopUp;
|
||||||
import ru.ytkab0bp.slicebeam.components.BeamColorPickerPopUp;
|
import com.dark98.santoku.config.ConfigObject;
|
||||||
import ru.ytkab0bp.slicebeam.config.ConfigObject;
|
import com.dark98.santoku.events.CloudUserInfoUpdatedEvent;
|
||||||
import ru.ytkab0bp.slicebeam.events.BeamServerDataUpdatedEvent;
|
import com.dark98.santoku.cloud.CloudController;
|
||||||
import ru.ytkab0bp.slicebeam.recycler.PreferenceItem;
|
import com.dark98.santoku.recycler.PreferenceItem;
|
||||||
import ru.ytkab0bp.slicebeam.theme.BeamTheme;
|
import com.dark98.santoku.theme.BeamTheme;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Prefs;
|
import com.dark98.santoku.utils.Prefs;
|
||||||
|
|
||||||
public class SettingsFragment extends ProfileListFragment {
|
public class SettingsFragment extends ProfileListFragment {
|
||||||
|
|
||||||
@@ -45,6 +45,10 @@ public class SettingsFragment extends ProfileListFragment {
|
|||||||
@Override
|
@Override
|
||||||
protected List<OptionElement> getConfigItems() {
|
protected List<OptionElement> getConfigItems() {
|
||||||
return Arrays.asList(
|
return Arrays.asList(
|
||||||
|
CloudController.hasAccountFeatures() ? new OptionElement(SPECIAL_TYPE_CLOUD_HEADER).setOnClick(() -> {
|
||||||
|
Activity act = (Activity) getContext();
|
||||||
|
act.startActivity(new Intent(act, SetupActivity.class).putExtra(SetupActivity.EXTRA_CLOUD_PROFILE, true));
|
||||||
|
}) : null,
|
||||||
new OptionElement(R.drawable.paint_roller_outline_28, getContext().getString(R.string.SettingsInterface)),
|
new OptionElement(R.drawable.paint_roller_outline_28, getContext().getString(R.string.SettingsInterface)),
|
||||||
new OptionElement(new PreferenceItem().setTitle(getContext().getString(R.string.SettingsInterfaceTheme)).setValueProvider(() -> getContext().getString(Prefs.getThemeMode().title)).setOnClickListener(v -> {
|
new OptionElement(new PreferenceItem().setTitle(getContext().getString(R.string.SettingsInterfaceTheme)).setValueProvider(() -> getContext().getString(Prefs.getThemeMode().title)).setOnClickListener(v -> {
|
||||||
String[] items = new String[Prefs.ThemeMode.values().length];
|
String[] items = new String[Prefs.ThemeMode.values().length];
|
||||||
@@ -107,7 +111,7 @@ public class SettingsFragment extends ProfileListFragment {
|
|||||||
BeamTheme.LIGHT.colors.put(android.R.attr.colorAccent, Prefs.getAccentColor());
|
BeamTheme.LIGHT.colors.put(android.R.attr.colorAccent, Prefs.getAccentColor());
|
||||||
BeamTheme.DARK.colors.put(android.R.attr.colorAccent, Prefs.getAccentColor());
|
BeamTheme.DARK.colors.put(android.R.attr.colorAccent, Prefs.getAccentColor());
|
||||||
ThemesRepo.invalidate((Activity) getContext());
|
ThemesRepo.invalidate((Activity) getContext());
|
||||||
recyclerView.getAdapter().notifyItemChanged(1);
|
recyclerView.getAdapter().notifyItemChanged(2 - (CloudController.hasAccountFeatures() ? 0 : 1));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.setNegativeButtonText(getContext().getString(R.string.SettingsInterfaceColorReset))
|
.setNegativeButtonText(getContext().getString(R.string.SettingsInterfaceColorReset))
|
||||||
@@ -130,7 +134,7 @@ public class SettingsFragment extends ProfileListFragment {
|
|||||||
Prefs.setRenderScale(variants[which]);
|
Prefs.setRenderScale(variants[which]);
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
// I'm too lazy to calculate real position for now
|
// I'm too lazy to calculate real position for now
|
||||||
recyclerView.getAdapter().notifyItemChanged(3);
|
recyclerView.getAdapter().notifyItemChanged(4 - (CloudController.hasAccountFeatures() ? 0 : 1));
|
||||||
})
|
})
|
||||||
.show();
|
.show();
|
||||||
})),
|
})),
|
||||||
@@ -138,18 +142,6 @@ public class SettingsFragment extends ProfileListFragment {
|
|||||||
Activity act = (Activity) getContext();
|
Activity act = (Activity) getContext();
|
||||||
act.startActivity(new Intent(act, SetupActivity.class).putExtra(SetupActivity.EXTRA_ABOUT, true));
|
act.startActivity(new Intent(act, SetupActivity.class).putExtra(SetupActivity.EXTRA_ABOUT, true));
|
||||||
}),
|
}),
|
||||||
new OptionElement(R.drawable.telegram, getContext().getString(R.string.SettingsTelegram)).setColor(R.attr.telegramColor, false).setOnClick(() -> {
|
|
||||||
Activity act = (Activity) getContext();
|
|
||||||
act.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://t.me/ytkab0bp_channel")));
|
|
||||||
}),
|
|
||||||
BeamServerData.isBoostyAvailable() ? new OptionElement(R.drawable.boosty, getContext().getString(R.string.SettingsBoosty)).setColor(R.attr.boostyColorTop, true).setOnClick(() -> {
|
|
||||||
Activity act = (Activity) getContext();
|
|
||||||
act.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://boosty.to/ytkab0bp")));
|
|
||||||
}) : null,
|
|
||||||
new OptionElement(R.drawable.k3d_logo_new_14, getContext().getString(R.string.SettingsK3D)).setColor(R.attr.k3dColor, true).setOnClick(() -> {
|
|
||||||
Activity act = (Activity) getContext();
|
|
||||||
act.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://t.me/K_3_D")));
|
|
||||||
}),
|
|
||||||
new OptionElement(R.drawable.refresh_outline_28, getContext().getString(R.string.SettingsResetToDefault)).setColor(R.attr.textColorNegative, false).setOnClick(() -> {
|
new OptionElement(R.drawable.refresh_outline_28, getContext().getString(R.string.SettingsResetToDefault)).setColor(R.attr.textColorNegative, false).setOnClick(() -> {
|
||||||
Context ctx = getContext();
|
Context ctx = getContext();
|
||||||
if (ctx instanceof Activity) {
|
if (ctx instanceof Activity) {
|
||||||
@@ -158,8 +150,8 @@ public class SettingsFragment extends ProfileListFragment {
|
|||||||
.setTitle(R.string.SettingsResetToDefaultTitle)
|
.setTitle(R.string.SettingsResetToDefaultTitle)
|
||||||
.setMessage(R.string.SettingsResetToDefaultDescription)
|
.setMessage(R.string.SettingsResetToDefaultDescription)
|
||||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||||
SliceBeam.getConfigFile().delete();
|
Santoku.getConfigFile().delete();
|
||||||
SliceBeam.CONFIG = null;
|
Santoku.CONFIG = null;
|
||||||
Prefs.getPrefs().edit().clear().apply();
|
Prefs.getPrefs().edit().clear().apply();
|
||||||
Prefs.setLastCommit();
|
Prefs.setLastCommit();
|
||||||
act.startActivity(new Intent(act, SetupActivity.class));
|
act.startActivity(new Intent(act, SetupActivity.class));
|
||||||
@@ -173,8 +165,10 @@ public class SettingsFragment extends ProfileListFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(runOnMainThread = true)
|
@EventHandler(runOnMainThread = true)
|
||||||
public void onDataUpdated(BeamServerDataUpdatedEvent e) {
|
public void onUserInfoUpdated(CloudUserInfoUpdatedEvent e) {
|
||||||
setConfigItems(getConfigItems());
|
if (CloudController.hasAccountFeatures()) {
|
||||||
|
recyclerView.getAdapter().notifyItemChanged(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
+7
-7
@@ -1,14 +1,14 @@
|
|||||||
package ru.ytkab0bp.slicebeam.navigation;
|
package com.dark98.santoku.navigation;
|
||||||
|
|
||||||
import androidx.annotation.DrawableRes;
|
import androidx.annotation.DrawableRes;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
|
import com.dark98.santoku.fragment.BedFragment;
|
||||||
import ru.ytkab0bp.slicebeam.fragment.FilamentConfigFragment;
|
import com.dark98.santoku.fragment.FilamentConfigFragment;
|
||||||
import ru.ytkab0bp.slicebeam.fragment.PrintConfigFragment;
|
import com.dark98.santoku.fragment.PrintConfigFragment;
|
||||||
import ru.ytkab0bp.slicebeam.fragment.PrinterConfigFragment;
|
import com.dark98.santoku.fragment.PrinterConfigFragment;
|
||||||
import ru.ytkab0bp.slicebeam.fragment.SettingsFragment;
|
import com.dark98.santoku.fragment.SettingsFragment;
|
||||||
|
|
||||||
public abstract class DelegateSlotImpl extends NavigationDelegate {
|
public abstract class DelegateSlotImpl extends NavigationDelegate {
|
||||||
public int getSlotCount() {
|
public int getSlotCount() {
|
||||||
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
package ru.ytkab0bp.slicebeam.navigation;
|
package com.dark98.santoku.navigation;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.CallSuper;
|
import androidx.annotation.CallSuper;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
|
|
||||||
public abstract class Fragment {
|
public abstract class Fragment {
|
||||||
private View mView;
|
private View mView;
|
||||||
+19
-7
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.navigation;
|
package com.dark98.santoku.navigation;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -23,12 +23,12 @@ import androidx.core.content.ContextCompat;
|
|||||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||||
import com.google.android.material.navigation.NavigationBarView;
|
import com.google.android.material.navigation.NavigationBarView;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
import ru.ytkab0bp.slicebeam.view.TextColorImageSpan;
|
import com.dark98.santoku.view.TextColorImageSpan;
|
||||||
import ru.ytkab0bp.slicebeam.view.ThemeBottomNavigationView;
|
import com.dark98.santoku.view.ThemeBottomNavigationView;
|
||||||
import ru.ytkab0bp.slicebeam.view.ThemeRailNavigationView;
|
import com.dark98.santoku.view.ThemeRailNavigationView;
|
||||||
|
|
||||||
public class MobileNavigationDelegate extends DelegateSlotImpl {
|
public class MobileNavigationDelegate extends DelegateSlotImpl {
|
||||||
private boolean portrait;
|
private boolean portrait;
|
||||||
@@ -107,6 +107,18 @@ public class MobileNavigationDelegate extends DelegateSlotImpl {
|
|||||||
return root = fl;
|
return root = fl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onBackPressed() {
|
||||||
|
if (super.onBackPressed()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (currentSlot != 0) {
|
||||||
|
switchSlot(0, () -> navigationView.setSelectedItemId(0));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FrameLayout getOverlayView() {
|
public FrameLayout getOverlayView() {
|
||||||
return root;
|
return root;
|
||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
package ru.ytkab0bp.slicebeam.navigation;
|
package com.dark98.santoku.navigation;
|
||||||
|
|
||||||
import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
|
import static com.dark98.santoku.utils.DebugUtils.assertTrue;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
@@ -14,7 +14,7 @@ import androidx.dynamicanimation.animation.SpringForce;
|
|||||||
|
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
|
|
||||||
public abstract class NavigationDelegate {
|
public abstract class NavigationDelegate {
|
||||||
protected Context context;
|
protected Context context;
|
||||||
@@ -0,0 +1,441 @@
|
|||||||
|
package com.dark98.santoku.print_host;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import okhttp3.MediaType;
|
||||||
|
import okhttp3.MultipartBody;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
import okhttp3.Response;
|
||||||
|
import okio.BufferedSink;
|
||||||
|
|
||||||
|
public final class ElegooLinkClient {
|
||||||
|
private static final int MAX_UPLOAD_PACKAGE_LENGTH = 1024 * 1024;
|
||||||
|
private static final MediaType OCTET_STREAM = MediaType.parse("application/octet-stream");
|
||||||
|
|
||||||
|
private ElegooLinkClient() {}
|
||||||
|
|
||||||
|
public static Result upload(File gcode, String host, String uploadName, boolean startPrint, boolean timelapse, boolean bedLeveling, int bedType) {
|
||||||
|
if (gcode == null || !gcode.exists()) {
|
||||||
|
return Result.error("G-code file not found.");
|
||||||
|
}
|
||||||
|
String finalName = (uploadName == null || uploadName.isEmpty()) ? gcode.getName() : uploadName;
|
||||||
|
String baseUrl = normalizeBaseUrl(host);
|
||||||
|
String uploadUrl = baseUrl + "/uploadFile/upload";
|
||||||
|
OkHttpClient client = new OkHttpClient.Builder()
|
||||||
|
.callTimeout(60, TimeUnit.SECONDS)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
String md5;
|
||||||
|
try {
|
||||||
|
md5 = md5File(gcode);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Result.error("Failed to compute MD5: " + e.getMessage());
|
||||||
|
}
|
||||||
|
long size = gcode.length();
|
||||||
|
String uuid = UUID.randomUUID().toString().replace("-", "");
|
||||||
|
|
||||||
|
int packageCount = (int) ((size + MAX_UPLOAD_PACKAGE_LENGTH - 1) / MAX_UPLOAD_PACKAGE_LENGTH);
|
||||||
|
for (int i = 0; i < packageCount; i++) {
|
||||||
|
long offset = (long) MAX_UPLOAD_PACKAGE_LENGTH * i;
|
||||||
|
long length = Math.min(MAX_UPLOAD_PACKAGE_LENGTH, size - offset);
|
||||||
|
Result partRes = uploadPart(client, uploadUrl, gcode, finalName, md5, uuid, size, offset, length);
|
||||||
|
if (!partRes.ok) {
|
||||||
|
return partRes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!startPrint) {
|
||||||
|
return Result.ok();
|
||||||
|
}
|
||||||
|
return startPrint(client, host, finalName, timelapse, bedLeveling, bedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Result uploadPart(OkHttpClient client, String url, File file, String uploadName, String md5, String uuid, long totalSize, long offset, long length) {
|
||||||
|
RequestBody fileBody = new FileSliceRequestBody(file, offset, length);
|
||||||
|
MultipartBody requestBody = new MultipartBody.Builder()
|
||||||
|
.setType(MultipartBody.FORM)
|
||||||
|
.addFormDataPart("Check", "1")
|
||||||
|
.addFormDataPart("S-File-MD5", md5)
|
||||||
|
.addFormDataPart("Offset", String.valueOf(offset))
|
||||||
|
.addFormDataPart("Uuid", uuid)
|
||||||
|
.addFormDataPart("TotalSize", String.valueOf(totalSize))
|
||||||
|
.addFormDataPart("File", uploadName, fileBody)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.post(requestBody)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (Response response = client.newCall(request).execute()) {
|
||||||
|
String body = response.body() != null ? response.body().string() : "";
|
||||||
|
if (!response.isSuccessful()) {
|
||||||
|
return Result.error("Upload failed: HTTP " + response.code());
|
||||||
|
}
|
||||||
|
if (!isElegooOk(body)) {
|
||||||
|
return Result.error(parseElegooError(body));
|
||||||
|
}
|
||||||
|
return Result.ok();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return Result.error("Upload failed: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Result startPrint(OkHttpClient client, String host, String filename, boolean timelapse, boolean bedLeveling, int bedType) {
|
||||||
|
WebSocketSession session = null;
|
||||||
|
String lastError = null;
|
||||||
|
for (String wsUrl : buildWebSocketCandidates(host)) {
|
||||||
|
WebSocketSession attempt = new WebSocketSession(client, wsUrl);
|
||||||
|
if (attempt.awaitOpen(10, TimeUnit.SECONDS)) {
|
||||||
|
session = attempt;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lastError = attempt.failureSummary();
|
||||||
|
if (attempt.isTooManyClients()) {
|
||||||
|
return Result.error("Printer reports too many connected clients. Close the ElegooLink web UI and other apps, then try again.");
|
||||||
|
}
|
||||||
|
attempt.close();
|
||||||
|
}
|
||||||
|
if (session == null) {
|
||||||
|
if (lastError != null && !lastError.isEmpty()) {
|
||||||
|
return Result.error("Failed to connect to ElegooLink websocket. " + lastError);
|
||||||
|
}
|
||||||
|
return Result.error("Failed to connect to ElegooLink websocket.");
|
||||||
|
}
|
||||||
|
|
||||||
|
String requestId = UUID.randomUUID().toString().replace("-", "");
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
|
String json = "{"
|
||||||
|
+ "\"Id\":\"\","
|
||||||
|
+ "\"Data\":{"
|
||||||
|
+ "\"Cmd\":128,"
|
||||||
|
+ "\"Data\":{"
|
||||||
|
+ "\"Filename\":\"/local/" + filename + "\","
|
||||||
|
+ "\"StartLayer\":0,"
|
||||||
|
+ "\"Calibration_switch\":" + (bedLeveling ? 1 : 0) + ","
|
||||||
|
+ "\"PrintPlatformType\":" + (bedType != 0 ? 1 : 0) + ","
|
||||||
|
+ "\"Tlp_Switch\":" + (timelapse ? 1 : 0)
|
||||||
|
+ "},"
|
||||||
|
+ "\"RequestID\":\"" + requestId + "\","
|
||||||
|
+ "\"MainboardID\":\"\","
|
||||||
|
+ "\"TimeStamp\":" + timestamp + ","
|
||||||
|
+ "\"From\":1"
|
||||||
|
+ "}"
|
||||||
|
+ "}";
|
||||||
|
|
||||||
|
session.sendText(json);
|
||||||
|
String response = session.receiveText(30, TimeUnit.SECONDS);
|
||||||
|
if (response == null) {
|
||||||
|
session.close();
|
||||||
|
return Result.error("Start print timeout.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
JSONObject root = new JSONObject(response);
|
||||||
|
JSONObject data = root.optJSONObject("Data");
|
||||||
|
if (data == null) {
|
||||||
|
session.close();
|
||||||
|
return Result.error("Invalid response from printer.");
|
||||||
|
}
|
||||||
|
int cmd = data.optInt("Cmd", -1);
|
||||||
|
if (cmd != 128) {
|
||||||
|
session.close();
|
||||||
|
return Result.error("Unexpected response from printer.");
|
||||||
|
}
|
||||||
|
JSONObject ackData = data.optJSONObject("Data");
|
||||||
|
int ack = ackData != null ? ackData.optInt("Ack", -1) : -1;
|
||||||
|
if (ack == 0) {
|
||||||
|
session.close();
|
||||||
|
return Result.ok();
|
||||||
|
}
|
||||||
|
String error = mapAckError(ack);
|
||||||
|
session.close();
|
||||||
|
return Result.error(error);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
session.close();
|
||||||
|
return Result.error("Invalid response from printer.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isElegooOk(String body) {
|
||||||
|
try {
|
||||||
|
JSONObject root = new JSONObject(body);
|
||||||
|
String code = root.optString("code", "");
|
||||||
|
return "000000".equals(code);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String parseElegooError(String body) {
|
||||||
|
try {
|
||||||
|
JSONObject root = new JSONObject(body);
|
||||||
|
String code = root.optString("code", "unknown");
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("ErrorCode: ").append(code);
|
||||||
|
JSONArray messages = root.optJSONArray("messages");
|
||||||
|
if (messages != null) {
|
||||||
|
for (int i = 0; i < messages.length(); i++) {
|
||||||
|
JSONObject msg = messages.optJSONObject(i);
|
||||||
|
if (msg != null) {
|
||||||
|
sb.append("\n").append(msg.optString("field", ""))
|
||||||
|
.append(":").append(msg.optString("message", ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
} catch (JSONException e) {
|
||||||
|
return "Upload failed.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String mapAckError(int ack) {
|
||||||
|
switch (ack) {
|
||||||
|
case 1:
|
||||||
|
return "The printer is busy.";
|
||||||
|
case 2:
|
||||||
|
return "The file is missing.";
|
||||||
|
case 3:
|
||||||
|
return "MD5 check failed.";
|
||||||
|
case 4:
|
||||||
|
return "File I/O error.";
|
||||||
|
case 5:
|
||||||
|
case 6:
|
||||||
|
return "File format or resolution is invalid.";
|
||||||
|
case 7:
|
||||||
|
return "File does not match the printer.";
|
||||||
|
default:
|
||||||
|
return "Unknown error. Error code: " + ack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String normalizeBaseUrl(String host) {
|
||||||
|
String value = host.trim();
|
||||||
|
if (!value.contains("://")) {
|
||||||
|
value = "http://" + value;
|
||||||
|
}
|
||||||
|
if (value.endsWith("/")) {
|
||||||
|
value = value.substring(0, value.length() - 1);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> buildWebSocketCandidates(String host) {
|
||||||
|
String h = sanitizeWebSocketHost(host);
|
||||||
|
List<String> urls = new ArrayList<>(1);
|
||||||
|
urls.add(String.format(Locale.US, "ws://%s:3030/websocket", h));
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String sanitizeWebSocketHost(String host) {
|
||||||
|
String base = normalizeBaseUrl(host);
|
||||||
|
try {
|
||||||
|
URI uri = new URI(base);
|
||||||
|
String h = uri.getHost() != null ? uri.getHost() : uri.getAuthority();
|
||||||
|
if (h != null && h.contains(":")) {
|
||||||
|
h = h.substring(0, h.indexOf(':'));
|
||||||
|
}
|
||||||
|
if (h == null || h.isEmpty()) {
|
||||||
|
h = host;
|
||||||
|
}
|
||||||
|
return wrapIpv6Host(h);
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
String h = host;
|
||||||
|
int schemeIdx = h.indexOf("://");
|
||||||
|
if (schemeIdx != -1) {
|
||||||
|
h = h.substring(schemeIdx + 3);
|
||||||
|
}
|
||||||
|
int slashIdx = h.indexOf('/');
|
||||||
|
if (slashIdx != -1) {
|
||||||
|
h = h.substring(0, slashIdx);
|
||||||
|
}
|
||||||
|
int portIdx = h.indexOf(':');
|
||||||
|
if (portIdx != -1) {
|
||||||
|
h = h.substring(0, portIdx);
|
||||||
|
}
|
||||||
|
return wrapIpv6Host(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String wrapIpv6Host(String host) {
|
||||||
|
if (host == null || host.isEmpty()) {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
if (host.indexOf(':') != -1 && !host.startsWith("[") && !host.endsWith("]")) {
|
||||||
|
return "[" + host + "]";
|
||||||
|
}
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String md5File(File file) throws IOException, NoSuchAlgorithmException {
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("MD5");
|
||||||
|
try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(file))) {
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
int read;
|
||||||
|
while ((read = in.read(buffer)) != -1) {
|
||||||
|
digest.update(buffer, 0, read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (byte b : digest.digest()) {
|
||||||
|
sb.append(String.format(Locale.US, "%02x", b));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class FileSliceRequestBody extends RequestBody {
|
||||||
|
private final File file;
|
||||||
|
private final long offset;
|
||||||
|
private final long length;
|
||||||
|
|
||||||
|
FileSliceRequestBody(File file, long offset, long length) {
|
||||||
|
this.file = file;
|
||||||
|
this.offset = offset;
|
||||||
|
this.length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MediaType contentType() {
|
||||||
|
return OCTET_STREAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long contentLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(BufferedSink sink) throws IOException {
|
||||||
|
try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
|
||||||
|
raf.seek(offset);
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
long remaining = length;
|
||||||
|
while (remaining > 0) {
|
||||||
|
int read = raf.read(buffer, 0, (int) Math.min(buffer.length, remaining));
|
||||||
|
if (read == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sink.write(buffer, 0, read);
|
||||||
|
remaining -= read;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class WebSocketSession extends okhttp3.WebSocketListener {
|
||||||
|
private final java.util.concurrent.BlockingQueue<String> messages = new java.util.concurrent.LinkedBlockingQueue<>();
|
||||||
|
private final okhttp3.WebSocket socket;
|
||||||
|
private volatile boolean opened;
|
||||||
|
private volatile String failureBody;
|
||||||
|
private volatile okhttp3.Response failureResponse;
|
||||||
|
private volatile Throwable failure;
|
||||||
|
|
||||||
|
WebSocketSession(OkHttpClient client, String url) {
|
||||||
|
Request request = new Request.Builder().url(url).build();
|
||||||
|
socket = client.newWebSocket(request, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean awaitOpen(long timeout, TimeUnit unit) {
|
||||||
|
try {
|
||||||
|
long deadline = System.nanoTime() + unit.toNanos(timeout);
|
||||||
|
while (!opened && System.nanoTime() < deadline) {
|
||||||
|
Thread.sleep(50);
|
||||||
|
}
|
||||||
|
return opened;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendText(String message) {
|
||||||
|
socket.send(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
String receiveText(long timeout, TimeUnit unit) {
|
||||||
|
try {
|
||||||
|
return messages.poll(timeout, unit);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() {
|
||||||
|
socket.close(1000, "done");
|
||||||
|
}
|
||||||
|
|
||||||
|
String failureSummary() {
|
||||||
|
if (failureResponse != null) {
|
||||||
|
return "HTTP " + failureResponse.code();
|
||||||
|
}
|
||||||
|
if (failure != null) {
|
||||||
|
return failure.getMessage();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isTooManyClients() {
|
||||||
|
return failureBody != null && failureBody.toLowerCase(Locale.US).contains("too many client");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOpen(okhttp3.WebSocket webSocket, okhttp3.Response response) {
|
||||||
|
opened = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessage(okhttp3.WebSocket webSocket, String text) {
|
||||||
|
messages.offer(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(okhttp3.WebSocket webSocket, Throwable t, okhttp3.Response response) {
|
||||||
|
failure = t;
|
||||||
|
failureResponse = response;
|
||||||
|
if (response != null && response.body() != null) {
|
||||||
|
try {
|
||||||
|
failureBody = response.body().string();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Result {
|
||||||
|
public final boolean ok;
|
||||||
|
public final String error;
|
||||||
|
|
||||||
|
private Result(boolean ok, String error) {
|
||||||
|
this.ok = ok;
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result ok() {
|
||||||
|
return new Result(true, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result error(String error) {
|
||||||
|
return new Result(false, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,314 @@
|
|||||||
|
package com.dark98.santoku.providers;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.MatrixCursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.CancellationSignal;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
|
import android.provider.DocumentsProvider;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class AppDocumentsProvider extends DocumentsProvider {
|
||||||
|
private static final String ROOT_ID = "app_files";
|
||||||
|
private static final String ROOT_DOC_ID = "app_files:";
|
||||||
|
|
||||||
|
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
|
||||||
|
DocumentsContract.Root.COLUMN_ROOT_ID,
|
||||||
|
DocumentsContract.Root.COLUMN_DOCUMENT_ID,
|
||||||
|
DocumentsContract.Root.COLUMN_TITLE,
|
||||||
|
DocumentsContract.Root.COLUMN_FLAGS,
|
||||||
|
DocumentsContract.Root.COLUMN_AVAILABLE_BYTES
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
|
||||||
|
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
|
||||||
|
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
|
||||||
|
DocumentsContract.Document.COLUMN_SIZE,
|
||||||
|
DocumentsContract.Document.COLUMN_MIME_TYPE,
|
||||||
|
DocumentsContract.Document.COLUMN_FLAGS,
|
||||||
|
DocumentsContract.Document.COLUMN_LAST_MODIFIED
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor queryRoots(String[] projection) {
|
||||||
|
final String[] proj = projection != null ? projection : DEFAULT_ROOT_PROJECTION;
|
||||||
|
MatrixCursor cursor = new MatrixCursor(proj);
|
||||||
|
|
||||||
|
File base = getBaseDir();
|
||||||
|
MatrixCursor.RowBuilder row = cursor.newRow();
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_ROOT_ID, ROOT_ID);
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, ROOT_DOC_ID);
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_TITLE, "Santoku Files");
|
||||||
|
int flags = DocumentsContract.Root.FLAG_SUPPORTS_CREATE
|
||||||
|
| DocumentsContract.Root.FLAG_LOCAL_ONLY;
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_FLAGS, flags);
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_AVAILABLE_BYTES, base.getFreeSpace());
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException {
|
||||||
|
MatrixCursor cursor = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
|
||||||
|
File file = fileForDocId(documentId);
|
||||||
|
includeFile(cursor, documentId, file);
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder)
|
||||||
|
throws FileNotFoundException {
|
||||||
|
MatrixCursor cursor = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
|
||||||
|
File parent = fileForDocId(parentDocumentId);
|
||||||
|
File[] files = parent.listFiles();
|
||||||
|
if (files != null) {
|
||||||
|
for (File file : files) {
|
||||||
|
includeFile(cursor, docIdForFile(file), file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDocumentType(String documentId) throws FileNotFoundException {
|
||||||
|
File file = fileForDocId(documentId);
|
||||||
|
return getMimeType(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public android.os.ParcelFileDescriptor openDocument(String documentId, String mode, CancellationSignal signal)
|
||||||
|
throws FileNotFoundException {
|
||||||
|
File file = fileForDocId(documentId);
|
||||||
|
int accessMode = ParcelFileDescriptorUtil.parseMode(mode);
|
||||||
|
return android.os.ParcelFileDescriptor.open(file, accessMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String createDocument(String parentDocumentId, String mimeType, String displayName)
|
||||||
|
throws FileNotFoundException {
|
||||||
|
File parent = fileForDocId(parentDocumentId);
|
||||||
|
if (!parent.isDirectory()) {
|
||||||
|
throw new FileNotFoundException("Parent is not a directory: " + parentDocumentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = displayName;
|
||||||
|
if (DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType)) {
|
||||||
|
File dir = new File(parent, name);
|
||||||
|
if (!dir.exists() && !dir.mkdirs()) {
|
||||||
|
throw new FileNotFoundException("Failed to create directory: " + name);
|
||||||
|
}
|
||||||
|
return docIdForFile(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasExtension(name)) {
|
||||||
|
String ext = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
|
||||||
|
if (ext != null && !ext.isEmpty()) {
|
||||||
|
name = name + "." + ext.toLowerCase(Locale.US);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File file = new File(parent, name);
|
||||||
|
try {
|
||||||
|
if (!file.exists() && !file.createNewFile()) {
|
||||||
|
throw new FileNotFoundException("Failed to create file: " + name);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new FileNotFoundException("Failed to create file: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return docIdForFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteDocument(String documentId) throws FileNotFoundException {
|
||||||
|
File file = fileForDocId(documentId);
|
||||||
|
deleteRecursively(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String renameDocument(String documentId, String displayName) throws FileNotFoundException {
|
||||||
|
File file = fileForDocId(documentId);
|
||||||
|
File target = new File(file.getParentFile(), displayName);
|
||||||
|
if (!file.renameTo(target)) {
|
||||||
|
throw new FileNotFoundException("Failed to rename to: " + displayName);
|
||||||
|
}
|
||||||
|
return docIdForFile(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChildDocument(String parentDocumentId, String documentId) {
|
||||||
|
try {
|
||||||
|
File parent = fileForDocId(parentDocumentId);
|
||||||
|
File doc = fileForDocId(documentId);
|
||||||
|
return isDescendant(parent, doc);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor queryRecentDocuments(String rootId, String[] projection) throws FileNotFoundException {
|
||||||
|
MatrixCursor cursor = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
|
||||||
|
File base = getBaseDir();
|
||||||
|
File[] files = base.listFiles();
|
||||||
|
if (files != null) {
|
||||||
|
for (File file : files) {
|
||||||
|
includeFile(cursor, docIdForFile(file), file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getBaseDir() {
|
||||||
|
File dir = getContext().getFilesDir();
|
||||||
|
if (dir == null) {
|
||||||
|
dir = new File(Environment.getDataDirectory(), "data");
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void includeFile(MatrixCursor cursor, String docId, File file) {
|
||||||
|
int flags = 0;
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
if (file.canWrite()) {
|
||||||
|
flags |= DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE;
|
||||||
|
flags |= DocumentsContract.Document.FLAG_SUPPORTS_DELETE;
|
||||||
|
flags |= DocumentsContract.Document.FLAG_SUPPORTS_RENAME;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (file.canWrite()) {
|
||||||
|
flags |= DocumentsContract.Document.FLAG_SUPPORTS_WRITE;
|
||||||
|
flags |= DocumentsContract.Document.FLAG_SUPPORTS_DELETE;
|
||||||
|
flags |= DocumentsContract.Document.FLAG_SUPPORTS_RENAME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String mime = getMimeType(file);
|
||||||
|
MatrixCursor.RowBuilder row = cursor.newRow();
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, docId);
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_DISPLAY_NAME, file.getName());
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_SIZE, file.isDirectory() ? null : file.length());
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_MIME_TYPE, mime);
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_FLAGS, flags);
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, file.lastModified());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMimeType(File file) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
return DocumentsContract.Document.MIME_TYPE_DIR;
|
||||||
|
}
|
||||||
|
String name = file.getName();
|
||||||
|
int dot = name.lastIndexOf('.');
|
||||||
|
if (dot != -1) {
|
||||||
|
String ext = name.substring(dot + 1).toLowerCase(Locale.US);
|
||||||
|
String type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext);
|
||||||
|
if (type != null) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String docIdForFile(File file) {
|
||||||
|
File base = getBaseDir();
|
||||||
|
String basePath = base.getAbsolutePath();
|
||||||
|
String path = file.getAbsolutePath();
|
||||||
|
if (path.equals(basePath)) {
|
||||||
|
return ROOT_DOC_ID;
|
||||||
|
}
|
||||||
|
if (path.startsWith(basePath + File.separator)) {
|
||||||
|
String rel = path.substring(basePath.length() + 1);
|
||||||
|
return ROOT_DOC_ID + rel;
|
||||||
|
}
|
||||||
|
return ROOT_DOC_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File fileForDocId(String docId) throws FileNotFoundException {
|
||||||
|
File base = getBaseDir();
|
||||||
|
if (ROOT_DOC_ID.equals(docId) || ROOT_ID.equals(docId)) {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
if (!docId.startsWith(ROOT_DOC_ID)) {
|
||||||
|
throw new FileNotFoundException("Unknown docId: " + docId);
|
||||||
|
}
|
||||||
|
String rel = docId.substring(ROOT_DOC_ID.length());
|
||||||
|
File target = new File(base, rel);
|
||||||
|
try {
|
||||||
|
File canonical = target.getCanonicalFile();
|
||||||
|
File canonicalBase = base.getCanonicalFile();
|
||||||
|
if (!isDescendant(canonicalBase, canonical)) {
|
||||||
|
throw new FileNotFoundException("Invalid path: " + docId);
|
||||||
|
}
|
||||||
|
return canonical;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new FileNotFoundException("Failed to resolve: " + docId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDescendant(File parent, File child) {
|
||||||
|
try {
|
||||||
|
String parentPath = parent.getCanonicalPath();
|
||||||
|
String childPath = child.getCanonicalPath();
|
||||||
|
return childPath.equals(parentPath) || childPath.startsWith(parentPath + File.separator);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteRecursively(File file) throws FileNotFoundException {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
File[] children = file.listFiles();
|
||||||
|
if (children != null) {
|
||||||
|
for (File child : children) {
|
||||||
|
deleteRecursively(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!file.delete()) {
|
||||||
|
throw new FileNotFoundException("Failed to delete: " + file.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasExtension(String name) {
|
||||||
|
int dot = name.lastIndexOf('.');
|
||||||
|
return dot > 0 && dot < name.length() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ParcelFileDescriptorUtil {
|
||||||
|
private ParcelFileDescriptorUtil() {}
|
||||||
|
|
||||||
|
static int parseMode(String mode) {
|
||||||
|
if ("r".equals(mode)) {
|
||||||
|
return android.os.ParcelFileDescriptor.MODE_READ_ONLY;
|
||||||
|
}
|
||||||
|
if ("w".equals(mode) || "wt".equals(mode)) {
|
||||||
|
return android.os.ParcelFileDescriptor.MODE_WRITE_ONLY
|
||||||
|
| android.os.ParcelFileDescriptor.MODE_CREATE
|
||||||
|
| android.os.ParcelFileDescriptor.MODE_TRUNCATE;
|
||||||
|
}
|
||||||
|
if ("wa".equals(mode)) {
|
||||||
|
return android.os.ParcelFileDescriptor.MODE_WRITE_ONLY
|
||||||
|
| android.os.ParcelFileDescriptor.MODE_CREATE
|
||||||
|
| android.os.ParcelFileDescriptor.MODE_APPEND;
|
||||||
|
}
|
||||||
|
if ("rw".equals(mode)) {
|
||||||
|
return android.os.ParcelFileDescriptor.MODE_READ_WRITE
|
||||||
|
| android.os.ParcelFileDescriptor.MODE_CREATE;
|
||||||
|
}
|
||||||
|
if ("rwt".equals(mode)) {
|
||||||
|
return android.os.ParcelFileDescriptor.MODE_READ_WRITE
|
||||||
|
| android.os.ParcelFileDescriptor.MODE_CREATE
|
||||||
|
| android.os.ParcelFileDescriptor.MODE_TRUNCATE;
|
||||||
|
}
|
||||||
|
return android.os.ParcelFileDescriptor.MODE_READ_ONLY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.recycler;
|
package com.dark98.santoku.recycler;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
@@ -8,8 +8,8 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
|
||||||
public class BigHeaderItem extends SimpleRecyclerItem<TextView> {
|
public class BigHeaderItem extends SimpleRecyclerItem<TextView> {
|
||||||
public String title;
|
public String title;
|
||||||
+2
-2
@@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package ru.ytkab0bp.slicebeam.recycler;
|
package com.dark98.santoku.recycler;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
@@ -29,7 +29,7 @@ import androidx.recyclerview.widget.SimpleItemAnimator;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This implementation of {@link RecyclerView.ItemAnimator} provides basic
|
* This implementation of {@link RecyclerView.ItemAnimator} provides basic
|
||||||
+3
-3
@@ -1,12 +1,12 @@
|
|||||||
package ru.ytkab0bp.slicebeam.recycler;
|
package com.dark98.santoku.recycler;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
import ru.ytkab0bp.slicebeam.view.DividerView;
|
import com.dark98.santoku.view.DividerView;
|
||||||
|
|
||||||
public class DividerItem extends SimpleRecyclerItem<DividerView> {
|
public class DividerItem extends SimpleRecyclerItem<DividerView> {
|
||||||
@Override
|
@Override
|
||||||
+39
-13
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.recycler;
|
package com.dark98.santoku.recycler;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
@@ -20,20 +20,24 @@ import androidx.appcompat.widget.AppCompatImageView;
|
|||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.theme.IThemeView;
|
import com.dark98.santoku.theme.BeamTheme;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.IThemeView;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
|
||||||
public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.PreferenceHolderView> {
|
public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.PreferenceHolderView> {
|
||||||
private Drawable mIcon;
|
private Drawable mIcon;
|
||||||
private CharSequence mTitle;
|
private CharSequence mTitle;
|
||||||
private ValueProvider mSubtitle;
|
private ValueProvider mSubtitle;
|
||||||
private View.OnClickListener onClickListener;
|
private View.OnClickListener onClickListener;
|
||||||
|
private View.OnLongClickListener onLongClickListener;
|
||||||
private int textColorRes;
|
private int textColorRes;
|
||||||
private boolean noTint;
|
private boolean noTint;
|
||||||
private ValueProvider valueProvider;
|
private ValueProvider valueProvider;
|
||||||
private float roundRadius;
|
private float roundRadius;
|
||||||
|
private int mPaddings = ViewUtils.dp(12);
|
||||||
|
private boolean mForceDark;
|
||||||
|
|
||||||
public PreferenceItem setTitle(CharSequence title) {
|
public PreferenceItem setTitle(CharSequence title) {
|
||||||
mTitle = title;
|
mTitle = title;
|
||||||
@@ -45,6 +49,16 @@ public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.Preference
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PreferenceItem setPaddings(int paddings) {
|
||||||
|
this.mPaddings = paddings;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PreferenceItem setForceDark(boolean mForceDark) {
|
||||||
|
this.mForceDark = mForceDark;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public PreferenceItem setSubtitleProvider(ValueProvider mSubtitle) {
|
public PreferenceItem setSubtitleProvider(ValueProvider mSubtitle) {
|
||||||
this.mSubtitle = mSubtitle;
|
this.mSubtitle = mSubtitle;
|
||||||
return this;
|
return this;
|
||||||
@@ -61,7 +75,7 @@ public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.Preference
|
|||||||
}
|
}
|
||||||
|
|
||||||
public PreferenceItem setIcon(int iconRes) {
|
public PreferenceItem setIcon(int iconRes) {
|
||||||
mIcon = ContextCompat.getDrawable(SliceBeam.INSTANCE, iconRes);
|
mIcon = ContextCompat.getDrawable(Santoku.INSTANCE, iconRes);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,6 +104,11 @@ public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.Preference
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PreferenceItem setOnLongClickListener(View.OnLongClickListener onLongClickListener) {
|
||||||
|
this.onLongClickListener = onLongClickListener;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PreferenceHolderView onCreateView(Context ctx) {
|
public PreferenceHolderView onCreateView(Context ctx) {
|
||||||
return new PreferenceHolderView(ctx);
|
return new PreferenceHolderView(ctx);
|
||||||
@@ -106,6 +125,8 @@ public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.Preference
|
|||||||
private TextView value;
|
private TextView value;
|
||||||
private float radius;
|
private float radius;
|
||||||
|
|
||||||
|
private PreferenceItem item;
|
||||||
|
|
||||||
public PreferenceHolderView(Context context) {
|
public PreferenceHolderView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
|
||||||
@@ -159,14 +180,14 @@ public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.Preference
|
|||||||
value.setVisibility(GONE);
|
value.setVisibility(GONE);
|
||||||
addView(value, new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
addView(value, new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
int pad = ViewUtils.dp(12);
|
|
||||||
setPadding(pad, pad, pad, pad);
|
|
||||||
setMinimumHeight(ViewUtils.dp(56));
|
setMinimumHeight(ViewUtils.dp(56));
|
||||||
setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
onApplyTheme();
|
onApplyTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
void bind(PreferenceItem item) {
|
void bind(PreferenceItem item) {
|
||||||
|
this.item = item;
|
||||||
|
setPadding(item.mPaddings, item.mPaddings, item.mPaddings, item.mPaddings);
|
||||||
title.setText(item.mTitle);
|
title.setText(item.mTitle);
|
||||||
title.setVisibility(TextUtils.isEmpty(item.mTitle) ? GONE : VISIBLE);
|
title.setVisibility(TextUtils.isEmpty(item.mTitle) ? GONE : VISIBLE);
|
||||||
|
|
||||||
@@ -189,6 +210,7 @@ public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.Preference
|
|||||||
} else {
|
} else {
|
||||||
setClickable(false);
|
setClickable(false);
|
||||||
}
|
}
|
||||||
|
setOnLongClickListener(item.onLongClickListener);
|
||||||
|
|
||||||
if (item.textColorRes != 0) {
|
if (item.textColorRes != 0) {
|
||||||
title.setTextColor(ThemesRepo.getColor(item.textColorRes));
|
title.setTextColor(ThemesRepo.getColor(item.textColorRes));
|
||||||
@@ -210,15 +232,19 @@ public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.Preference
|
|||||||
|
|
||||||
ViewGroup.LayoutParams params = icon.getLayoutParams();
|
ViewGroup.LayoutParams params = icon.getLayoutParams();
|
||||||
params.width = params.height = radius != 0 ? ViewUtils.dp(42) : ViewUtils.dp(28);
|
params.width = params.height = radius != 0 ? ViewUtils.dp(42) : ViewUtils.dp(28);
|
||||||
|
if (item.mForceDark) {
|
||||||
|
onApplyTheme();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApplyTheme() {
|
public void onApplyTheme() {
|
||||||
title.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
BeamTheme theme = item != null && item.mForceDark ? BeamTheme.DARK : ThemesRepo.getCurrent();
|
||||||
subtitle.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
title.setTextColor(theme.colors.get(android.R.attr.textColorPrimary));
|
||||||
value.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
subtitle.setTextColor(theme.colors.get(android.R.attr.textColorSecondary));
|
||||||
icon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.textColorSecondary)));
|
value.setTextColor(theme.colors.get(android.R.attr.textColorSecondary));
|
||||||
setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
|
icon.setImageTintList(ColorStateList.valueOf(theme.colors.get(android.R.attr.textColorSecondary)));
|
||||||
|
setBackground(ViewUtils.createRipple(theme.colors.get(android.R.attr.colorControlHighlight), 16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+20
-11
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.recycler;
|
package com.dark98.santoku.recycler;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
@@ -6,6 +6,7 @@ import android.graphics.drawable.Drawable;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.CompoundButton;
|
import android.widget.CompoundButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
@@ -14,12 +15,12 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.theme.IThemeView;
|
import com.dark98.santoku.theme.IThemeView;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Prefs;
|
import com.dark98.santoku.utils.Prefs;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
import ru.ytkab0bp.slicebeam.view.BeamSwitch;
|
import com.dark98.santoku.view.BeamSwitch;
|
||||||
|
|
||||||
public class PreferenceSwitchItem extends SimpleRecyclerItem<PreferenceSwitchItem.SwitchPreferenceHolderView> {
|
public class PreferenceSwitchItem extends SimpleRecyclerItem<PreferenceSwitchItem.SwitchPreferenceHolderView> {
|
||||||
private Drawable mIcon;
|
private Drawable mIcon;
|
||||||
@@ -28,6 +29,7 @@ public class PreferenceSwitchItem extends SimpleRecyclerItem<PreferenceSwitchIte
|
|||||||
private String mKey;
|
private String mKey;
|
||||||
private boolean mDefaultValue;
|
private boolean mDefaultValue;
|
||||||
private CompoundButton.OnCheckedChangeListener mChangeListener;
|
private CompoundButton.OnCheckedChangeListener mChangeListener;
|
||||||
|
private View.OnLongClickListener mLongClickListener;
|
||||||
private ValueProvider valueProvider;
|
private ValueProvider valueProvider;
|
||||||
|
|
||||||
public PreferenceSwitchItem setValueProvider(ValueProvider valueProvider) {
|
public PreferenceSwitchItem setValueProvider(ValueProvider valueProvider) {
|
||||||
@@ -46,6 +48,11 @@ public class PreferenceSwitchItem extends SimpleRecyclerItem<PreferenceSwitchIte
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PreferenceSwitchItem setLongClickListener(View.OnLongClickListener longClickListener) {
|
||||||
|
mLongClickListener = longClickListener;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public PreferenceSwitchItem setTitle(CharSequence title) {
|
public PreferenceSwitchItem setTitle(CharSequence title) {
|
||||||
mTitle = title;
|
mTitle = title;
|
||||||
return this;
|
return this;
|
||||||
@@ -57,7 +64,7 @@ public class PreferenceSwitchItem extends SimpleRecyclerItem<PreferenceSwitchIte
|
|||||||
}
|
}
|
||||||
|
|
||||||
public PreferenceSwitchItem setIcon(int iconRes) {
|
public PreferenceSwitchItem setIcon(int iconRes) {
|
||||||
mIcon = ContextCompat.getDrawable(SliceBeam.INSTANCE, iconRes);
|
mIcon = ContextCompat.getDrawable(Santoku.INSTANCE, iconRes);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,9 +96,10 @@ public class PreferenceSwitchItem extends SimpleRecyclerItem<PreferenceSwitchIte
|
|||||||
|
|
||||||
icon = new ImageView(context);
|
icon = new ImageView(context);
|
||||||
icon.setLayoutParams(new LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28)) {{
|
icon.setLayoutParams(new LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28)) {{
|
||||||
setMarginEnd(ViewUtils.dp(16));
|
setMarginStart(ViewUtils.dp(4));
|
||||||
gravity = Gravity.CENTER_VERTICAL;
|
setMarginEnd(ViewUtils.dp(8));
|
||||||
}});
|
}});
|
||||||
|
addView(icon);
|
||||||
|
|
||||||
LinearLayout innerLayout = new LinearLayout(context);
|
LinearLayout innerLayout = new LinearLayout(context);
|
||||||
innerLayout.setOrientation(VERTICAL);
|
innerLayout.setOrientation(VERTICAL);
|
||||||
@@ -154,13 +162,14 @@ public class PreferenceSwitchItem extends SimpleRecyclerItem<PreferenceSwitchIte
|
|||||||
item.mChangeListener.onCheckedChanged(matSwitch, check);
|
item.mChangeListener.onCheckedChanged(matSwitch, check);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
setOnLongClickListener(item.mLongClickListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApplyTheme() {
|
public void onApplyTheme() {
|
||||||
title.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
title.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||||
subtitle.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
subtitle.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||||
icon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.colorAccent)));
|
icon.setImageTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.textColorSecondary)));
|
||||||
setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
|
setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.recycler;
|
package com.dark98.santoku.recycler;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.recycler;
|
package com.dark98.santoku.recycler;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.recycler;
|
package com.dark98.santoku.recycler;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.widget.Space;
|
import android.widget.Space;
|
||||||
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.recycler;
|
package com.dark98.santoku.recycler;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
@@ -8,8 +8,8 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
|
||||||
public class TextHintRecyclerItem extends SimpleRecyclerItem<TextView> {
|
public class TextHintRecyclerItem extends SimpleRecyclerItem<TextView> {
|
||||||
public String title;
|
public String title;
|
||||||
+9
-7
@@ -1,9 +1,9 @@
|
|||||||
package ru.ytkab0bp.slicebeam.render;
|
package com.dark98.santoku.render;
|
||||||
|
|
||||||
import androidx.core.math.MathUtils;
|
import androidx.core.math.MathUtils;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.utils.DoubleMatrix;
|
import com.dark98.santoku.utils.DoubleMatrix;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Vec3d;
|
import com.dark98.santoku.utils.Vec3d;
|
||||||
|
|
||||||
public class Camera {
|
public class Camera {
|
||||||
private double[] viewMatrix = new double[16];
|
private double[] viewMatrix = new double[16];
|
||||||
@@ -34,14 +34,14 @@ public class Camera {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void zoom(float zoom) {
|
public void zoom(float zoom) {
|
||||||
this.zoom = MathUtils.clamp(this.zoom + zoom / 25f, 1f, 5f);
|
this.zoom = MathUtils.clamp(this.zoom + zoom / 25f, 1f, 10f);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setZoom(float zoom) {
|
public void setZoom(float zoom) {
|
||||||
this.zoom = zoom;
|
this.zoom = zoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void move(float x, float y) {
|
public Vec3d calcScreenMovement(float x, float y) {
|
||||||
x /= zoom;
|
x /= zoom;
|
||||||
y /= zoom;
|
y /= zoom;
|
||||||
|
|
||||||
@@ -61,8 +61,10 @@ public class Camera {
|
|||||||
screenX.multiply(x);
|
screenX.multiply(x);
|
||||||
screenY.multiply(y);
|
screenY.multiply(y);
|
||||||
|
|
||||||
Vec3d move = new Vec3d(screenX).add(screenY);
|
return new Vec3d(screenX).add(screenY);
|
||||||
|
}
|
||||||
|
public void move(float x, float y) {
|
||||||
|
Vec3d move = calcScreenMovement(x, y);
|
||||||
position.add(move);
|
position.add(move);
|
||||||
origin.add(move);
|
origin.add(move);
|
||||||
}
|
}
|
||||||
+12
-12
@@ -1,13 +1,13 @@
|
|||||||
package ru.ytkab0bp.slicebeam.render;
|
package com.dark98.santoku.render;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.GLModel;
|
import com.dark98.santoku.slic3r.GLModel;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.GLShaderProgram;
|
import com.dark98.santoku.slic3r.GLShaderProgram;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.GLShadersManager;
|
import com.dark98.santoku.slic3r.GLShadersManager;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Slic3rUtils;
|
import com.dark98.santoku.slic3r.Slic3rUtils;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.utils.DoubleMatrix;
|
import com.dark98.santoku.utils.DoubleMatrix;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Vec3d;
|
import com.dark98.santoku.utils.Vec3d;
|
||||||
|
|
||||||
public class CoordAxes {
|
public class CoordAxes {
|
||||||
public Vec3d origin = new Vec3d(0, 0, 0);
|
public Vec3d origin = new Vec3d(0, 0, 0);
|
||||||
@@ -39,13 +39,13 @@ public class CoordAxes {
|
|||||||
arrow.render();
|
arrow.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(double[] viewMatrix, double[] projectionMatrix, float emissionFactor, float invZoom) {
|
public void render(GLShadersManager shadersManager, double[] viewMatrix, double[] projectionMatrix, float emissionFactor, float invZoom) {
|
||||||
if (!arrow.isInitialized()) {
|
if (!arrow.isInitialized()) {
|
||||||
arrow.stilizedArrow(tipRadius, tipLength, stemRadius, stemLength);
|
arrow.stilizedArrow(tipRadius, tipLength, stemRadius, stemLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
GLShaderProgram currentShader = GLShadersManager.getCurrentShader();
|
GLShaderProgram currentShader = shadersManager.getCurrentShader();
|
||||||
GLShaderProgram shader = GLShadersManager.get(GLShadersManager.SHADER_GOURAUD_LIGHT);
|
GLShaderProgram shader = shadersManager.get(GLShadersManager.SHADER_GOURAUD_LIGHT);
|
||||||
if (currentShader != null) {
|
if (currentShader != null) {
|
||||||
currentShader.stopUsing();
|
currentShader.stopUsing();
|
||||||
}
|
}
|
||||||
+252
-38
@@ -1,8 +1,9 @@
|
|||||||
package ru.ytkab0bp.slicebeam.render;
|
package com.dark98.santoku.render;
|
||||||
|
|
||||||
import static android.opengl.GLES30.*;
|
import static android.opengl.GLES30.*;
|
||||||
import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
|
import static com.dark98.santoku.utils.DebugUtils.assertTrue;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.opengl.GLSurfaceView;
|
import android.opengl.GLSurfaceView;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -12,27 +13,29 @@ import androidx.core.graphics.ColorUtils;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
|
||||||
import javax.microedition.khronos.egl.EGLConfig;
|
import javax.microedition.khronos.egl.EGLConfig;
|
||||||
import javax.microedition.khronos.opengles.GL10;
|
import javax.microedition.khronos.opengles.GL10;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
|
import com.dark98.santoku.events.ObjectsListChangedEvent;
|
||||||
import ru.ytkab0bp.slicebeam.events.SelectedObjectChangedEvent;
|
import com.dark98.santoku.events.SelectedObjectChangedEvent;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Bed3D;
|
import com.dark98.santoku.slic3r.Bed3D;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.GCodeProcessorResult;
|
import com.dark98.santoku.slic3r.GCodeProcessorResult;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.GCodeViewer;
|
import com.dark98.santoku.slic3r.GCodeViewer;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.GLModel;
|
import com.dark98.santoku.slic3r.GLModel;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.GLShaderProgram;
|
import com.dark98.santoku.slic3r.GLShaderProgram;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.GLShadersManager;
|
import com.dark98.santoku.slic3r.GLShadersManager;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Model;
|
import com.dark98.santoku.slic3r.Model;
|
||||||
import ru.ytkab0bp.slicebeam.slic3r.Slic3rUtils;
|
import com.dark98.santoku.slic3r.Slic3rUtils;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.utils.DoubleMatrix;
|
import com.dark98.santoku.utils.DoubleMatrix;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Prefs;
|
import com.dark98.santoku.utils.Prefs;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Vec3d;
|
import com.dark98.santoku.utils.Vec3d;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
import ru.ytkab0bp.slicebeam.view.GLView;
|
import com.dark98.santoku.view.GLView;
|
||||||
|
|
||||||
public class GLRenderer implements GLSurfaceView.Renderer {
|
public class GLRenderer implements GLSurfaceView.Renderer {
|
||||||
private final static float FOV = 60f;
|
private final static float FOV = 60f;
|
||||||
@@ -51,7 +54,9 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
|||||||
|
|
||||||
// Instance values, should be released
|
// Instance values, should be released
|
||||||
private Bed3D bed;
|
private Bed3D bed;
|
||||||
|
private boolean bedVisible = true;
|
||||||
private int lastConfigUid;
|
private int lastConfigUid;
|
||||||
|
private GLShadersManager shadersManager;
|
||||||
private GLModel backgroundModel;
|
private GLModel backgroundModel;
|
||||||
private GLModel selectionModel;
|
private GLModel selectionModel;
|
||||||
private List<GLModel> glModels = new ArrayList<>();
|
private List<GLModel> glModels = new ArrayList<>();
|
||||||
@@ -76,6 +81,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
|||||||
private Vec3d bbMin = new Vec3d(), bbMax = new Vec3d();
|
private Vec3d bbMin = new Vec3d(), bbMax = new Vec3d();
|
||||||
private boolean isInFlattenMode;
|
private boolean isInFlattenMode;
|
||||||
private ArrayList<GLModel> flattenPlanes = new ArrayList<>();
|
private ArrayList<GLModel> flattenPlanes = new ArrayList<>();
|
||||||
|
private static final double TOP_VIEW_MARGIN = 1.1;
|
||||||
|
|
||||||
public Camera getCamera() {
|
public Camera getCamera() {
|
||||||
return camera;
|
return camera;
|
||||||
@@ -85,6 +91,204 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
|||||||
return bed;
|
return bed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setBedVisible(boolean visible) {
|
||||||
|
bedVisible = visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBedVisible() {
|
||||||
|
return bedVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bitmap renderToBitmap(int width, int height, boolean hideBed) {
|
||||||
|
return renderToBitmap(width, height, hideBed, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bitmap renderToBitmap(int width, int height, boolean hideBed, boolean topView) {
|
||||||
|
if (width <= 0 || height <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] fbo = new int[1];
|
||||||
|
int[] texture = new int[1];
|
||||||
|
int[] depth = new int[1];
|
||||||
|
int[] fboMsaa = new int[1];
|
||||||
|
int[] colorMsaa = new int[1];
|
||||||
|
int[] depthMsaa = new int[1];
|
||||||
|
|
||||||
|
glGenFramebuffers(1, fbo, 0);
|
||||||
|
glGenTextures(1, texture, 0);
|
||||||
|
glGenRenderbuffers(1, depth, 0);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture[0]);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
|
||||||
|
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, depth[0]);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture[0], 0);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth[0]);
|
||||||
|
|
||||||
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
glDeleteRenderbuffers(1, depth, 0);
|
||||||
|
glDeleteTextures(1, texture, 0);
|
||||||
|
glDeleteFramebuffers(1, fbo, 0);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean useMsaa = true;
|
||||||
|
if (useMsaa) {
|
||||||
|
glGenFramebuffers(1, fboMsaa, 0);
|
||||||
|
glGenRenderbuffers(1, colorMsaa, 0);
|
||||||
|
glGenRenderbuffers(1, depthMsaa, 0);
|
||||||
|
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, colorMsaa[0]);
|
||||||
|
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, width, height);
|
||||||
|
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, depthMsaa[0]);
|
||||||
|
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT16, width, height);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fboMsaa[0]);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorMsaa[0]);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthMsaa[0]);
|
||||||
|
|
||||||
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||||
|
useMsaa = false;
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
glDeleteRenderbuffers(1, depthMsaa, 0);
|
||||||
|
glDeleteRenderbuffers(1, colorMsaa, 0);
|
||||||
|
glDeleteFramebuffers(1, fboMsaa, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int prevWidth = viewportWidth;
|
||||||
|
int prevHeight = viewportHeight;
|
||||||
|
boolean prevBed = bedVisible;
|
||||||
|
CameraState prevCamera = null;
|
||||||
|
|
||||||
|
viewportWidth = width;
|
||||||
|
viewportHeight = height;
|
||||||
|
if (useMsaa) {
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fboMsaa[0]);
|
||||||
|
} else {
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
|
||||||
|
}
|
||||||
|
glViewport(0, 0, width, height);
|
||||||
|
bedVisible = !hideBed;
|
||||||
|
if (topView) {
|
||||||
|
prevCamera = applyTopViewCamera();
|
||||||
|
}
|
||||||
|
updateProjection();
|
||||||
|
|
||||||
|
onDrawFrame(null);
|
||||||
|
|
||||||
|
if (useMsaa) {
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboMsaa[0]);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[0]);
|
||||||
|
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap bitmap = readPixelsToBitmap(width, height);
|
||||||
|
|
||||||
|
if (prevCamera != null) {
|
||||||
|
restoreCamera(prevCamera);
|
||||||
|
}
|
||||||
|
bedVisible = prevBed;
|
||||||
|
viewportWidth = prevWidth;
|
||||||
|
viewportHeight = prevHeight;
|
||||||
|
glViewport(0, 0, prevWidth, prevHeight);
|
||||||
|
updateProjection();
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
if (useMsaa) {
|
||||||
|
glDeleteRenderbuffers(1, depthMsaa, 0);
|
||||||
|
glDeleteRenderbuffers(1, colorMsaa, 0);
|
||||||
|
glDeleteFramebuffers(1, fboMsaa, 0);
|
||||||
|
}
|
||||||
|
glDeleteRenderbuffers(1, depth, 0);
|
||||||
|
glDeleteTextures(1, texture, 0);
|
||||||
|
glDeleteFramebuffers(1, fbo, 0);
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Bitmap readPixelsToBitmap(int width, int height) {
|
||||||
|
int[] buffer = new int[width * height];
|
||||||
|
int[] source = new int[width * height];
|
||||||
|
IntBuffer intBuffer = IntBuffer.wrap(buffer);
|
||||||
|
intBuffer.position(0);
|
||||||
|
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, intBuffer);
|
||||||
|
int offset1, offset2;
|
||||||
|
for (int i = 0; i < height; i++) {
|
||||||
|
offset1 = i * width;
|
||||||
|
offset2 = (height - i - 1) * width;
|
||||||
|
for (int j = 0; j < width; j++) {
|
||||||
|
int texturePixel = buffer[offset1 + j];
|
||||||
|
int blue = (texturePixel >> 16) & 0xff;
|
||||||
|
int red = (texturePixel << 16) & 0x00ff0000;
|
||||||
|
source[offset2 + j] = (texturePixel & 0xff00ff00) | red | blue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Bitmap.createBitmap(source, width, height, Bitmap.Config.ARGB_8888);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CameraState applyTopViewCamera() {
|
||||||
|
if (bed == null || !bed.isValid()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Vec3d min;
|
||||||
|
Vec3d max;
|
||||||
|
if (model != null && model.getObjectsCount() > 0) {
|
||||||
|
min = model.getBoundingBoxApproxMin();
|
||||||
|
max = model.getBoundingBoxApproxMax();
|
||||||
|
} else {
|
||||||
|
min = bed.getVolumeMin();
|
||||||
|
max = bed.getVolumeMax();
|
||||||
|
}
|
||||||
|
Vec3d center = min.center(max);
|
||||||
|
double size = Math.max(max.x - min.x, max.y - min.y);
|
||||||
|
if (size <= 0) {
|
||||||
|
size = 1;
|
||||||
|
}
|
||||||
|
double fov = Math.toRadians(FOV);
|
||||||
|
double distance = (size / 2.0) / Math.tan(fov / 2.0);
|
||||||
|
distance *= TOP_VIEW_MARGIN;
|
||||||
|
|
||||||
|
CameraState state = new CameraState(camera);
|
||||||
|
camera.origin.set(center);
|
||||||
|
camera.position.set(center.x, center.y, max.z + distance);
|
||||||
|
camera.up.set(0, 1, 0);
|
||||||
|
camera.setZoom(1f);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void restoreCamera(CameraState state) {
|
||||||
|
camera.position.set(state.position);
|
||||||
|
camera.origin.set(state.origin);
|
||||||
|
camera.up.set(state.up);
|
||||||
|
camera.setZoom(state.zoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class CameraState {
|
||||||
|
final Vec3d position;
|
||||||
|
final Vec3d origin;
|
||||||
|
final Vec3d up;
|
||||||
|
final float zoom;
|
||||||
|
|
||||||
|
CameraState(Camera camera) {
|
||||||
|
position = new Vec3d(camera.position);
|
||||||
|
origin = new Vec3d(camera.origin);
|
||||||
|
up = new Vec3d(camera.up);
|
||||||
|
zoom = camera.getZoom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public double[] getProjectionMatrix() {
|
public double[] getProjectionMatrix() {
|
||||||
return projectionMatrix;
|
return projectionMatrix;
|
||||||
}
|
}
|
||||||
@@ -212,7 +416,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
|||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
GLShaderProgram shader = GLShadersManager.get(GLShadersManager.SHADER_BACKGROUND);
|
GLShaderProgram shader = shadersManager.get(GLShadersManager.SHADER_BACKGROUND);
|
||||||
shader.startUsing();
|
shader.startUsing();
|
||||||
shader.setUniformColor("top_color", ThemesRepo.getColor(R.attr.backgroundColorTop));
|
shader.setUniformColor("top_color", ThemesRepo.getColor(R.attr.backgroundColorTop));
|
||||||
shader.setUniformColor("bottom_color", ThemesRepo.getColor(R.attr.backgroundColorBottom));
|
shader.setUniformColor("bottom_color", ThemesRepo.getColor(R.attr.backgroundColorBottom));
|
||||||
@@ -221,11 +425,11 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
|||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
boolean bottom = Prefs.isOrthoProjectionEnabled() ? camera.getDirForward().z > 0 : camera.getDirToBed().z > 0;
|
boolean bottom = Prefs.isOrthoProjectionEnabled() ? camera.getDirForward().z > 0 : camera.getDirToBed().z > 0;
|
||||||
if (lastConfigUid != SliceBeam.CONFIG_UID) {
|
if (lastConfigUid != Santoku.CONFIG_UID) {
|
||||||
configureBed();
|
configureBed();
|
||||||
}
|
}
|
||||||
if (bed.isValid()) {
|
if (bed.isValid() && bedVisible) {
|
||||||
bed.render(bottom, camera.getViewModelMatrix(), projectionMatrix, 1f / camera.getZoom());
|
bed.render(shadersManager, bottom, camera.getViewModelMatrix(), projectionMatrix, 1f / camera.getZoom());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isViewerEnabled) {
|
if (isViewerEnabled) {
|
||||||
@@ -239,7 +443,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
|||||||
viewer.render(camera.getViewModelMatrix(), projectionMatrix);
|
viewer.render(camera.getViewModelMatrix(), projectionMatrix);
|
||||||
}
|
}
|
||||||
if (viewer == null && model != null) {
|
if (viewer == null && model != null) {
|
||||||
shader = GLShadersManager.get(GLShadersManager.SHADER_GOURAUD_LIGHT);
|
shader = shadersManager.get(GLShadersManager.SHADER_GOURAUD_LIGHT);
|
||||||
shader.startUsing();
|
shader.startUsing();
|
||||||
int color = ThemesRepo.getColor(android.R.attr.colorAccent);
|
int color = ThemesRepo.getColor(android.R.attr.colorAccent);
|
||||||
int hoverColor = ThemesRepo.getColor(R.attr.modelHoverColor);
|
int hoverColor = ThemesRepo.getColor(R.attr.modelHoverColor);
|
||||||
@@ -301,7 +505,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
|||||||
if (selected) {
|
if (selected) {
|
||||||
shader.stopUsing();
|
shader.stopUsing();
|
||||||
|
|
||||||
GLShaderProgram flat = GLShadersManager.get(GLShadersManager.SHADER_FLAT);
|
GLShaderProgram flat = shadersManager.get(GLShadersManager.SHADER_FLAT);
|
||||||
glLineWidth(ViewUtils.dp(1.5f));
|
glLineWidth(ViewUtils.dp(1.5f));
|
||||||
|
|
||||||
flat.startUsing();
|
flat.startUsing();
|
||||||
@@ -323,7 +527,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
|||||||
if (isInFlattenMode) {
|
if (isInFlattenMode) {
|
||||||
shader.stopUsing();
|
shader.stopUsing();
|
||||||
|
|
||||||
GLShaderProgram flat = GLShadersManager.get(GLShadersManager.SHADER_FLAT);
|
GLShaderProgram flat = shadersManager.get(GLShadersManager.SHADER_FLAT);
|
||||||
|
|
||||||
flat.startUsing();
|
flat.startUsing();
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
@@ -363,20 +567,19 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
|||||||
selX = selY = selZ = 0;
|
selX = selY = selZ = 0;
|
||||||
selRotX = selRotY = selRotZ = 0;
|
selRotX = selRotY = selRotZ = 0;
|
||||||
selScaleX = selScaleY = selScaleZ = 1;
|
selScaleX = selScaleY = selScaleZ = 1;
|
||||||
SliceBeam.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent());
|
Santoku.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.getObjectsCount() == 0) {
|
if (model.getObjectsCount() == 0) {
|
||||||
model.release();
|
model.release();
|
||||||
model = null;
|
model = null;
|
||||||
}
|
}
|
||||||
SliceBeam.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
|
Santoku.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onClick(float x, float y) {
|
public int raycastObjectIndex(float x, float y) {
|
||||||
if (model == null || isViewerEnabled) return false;
|
if (model == null) return -1;
|
||||||
|
|
||||||
double minDistance = Double.MAX_VALUE;
|
double minDistance = Double.MAX_VALUE;
|
||||||
int j = -1;
|
int j = -1;
|
||||||
for (int i = 0, c = model.getObjectsCount(); i < c; i++) {
|
for (int i = 0, c = model.getObjectsCount(); i < c; i++) {
|
||||||
@@ -394,6 +597,13 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onClick(float x, float y) {
|
||||||
|
if (model == null || isViewerEnabled) return false;
|
||||||
|
|
||||||
|
int j = raycastObjectIndex(x, y);
|
||||||
|
|
||||||
if (isInFlattenMode && (j == selectedObject || j == -1)) {
|
if (isInFlattenMode && (j == selectedObject || j == -1)) {
|
||||||
int minPlane = -1;
|
int minPlane = -1;
|
||||||
@@ -430,7 +640,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
|||||||
flattenPlanes.clear();
|
flattenPlanes.clear();
|
||||||
|
|
||||||
selectedObject = -1;
|
selectedObject = -1;
|
||||||
SliceBeam.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent());
|
Santoku.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,7 +658,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
|||||||
selRotX = selRotY = selRotZ = 0;
|
selRotX = selRotY = selRotZ = 0;
|
||||||
selScaleX = selScaleY = selScaleZ = 1;
|
selScaleX = selScaleY = selScaleZ = 1;
|
||||||
}
|
}
|
||||||
SliceBeam.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent());
|
Santoku.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent());
|
||||||
}
|
}
|
||||||
return render;
|
return render;
|
||||||
}
|
}
|
||||||
@@ -583,9 +793,9 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
|||||||
|
|
||||||
private void configureBed() {
|
private void configureBed() {
|
||||||
try {
|
try {
|
||||||
lastConfigUid = SliceBeam.CONFIG_UID;
|
lastConfigUid = Santoku.CONFIG_UID;
|
||||||
SliceBeam.genCurrentConfig();
|
Santoku.genCurrentConfig();
|
||||||
bed.configure(SliceBeam.getCurrentConfigFile());
|
bed.configure(Santoku.getCurrentConfigFile());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e("GLRenderer", "Failed to update config", e);
|
Log.e("GLRenderer", "Failed to update config", e);
|
||||||
}
|
}
|
||||||
@@ -597,6 +807,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
|||||||
|
|
||||||
backgroundModel = new GLModel();
|
backgroundModel = new GLModel();
|
||||||
backgroundModel.initBackgroundTriangles();
|
backgroundModel.initBackgroundTriangles();
|
||||||
|
shadersManager = new GLShadersManager();
|
||||||
if (!bed.isValid()) return;
|
if (!bed.isValid()) return;
|
||||||
|
|
||||||
if (cameraIsDirty) {
|
if (cameraIsDirty) {
|
||||||
@@ -619,7 +830,10 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
GLShadersManager.clearShaders();
|
if (shadersManager != null) {
|
||||||
|
shadersManager.clearShaders();
|
||||||
|
shadersManager = null;
|
||||||
|
}
|
||||||
if (backgroundModel != null) {
|
if (backgroundModel != null) {
|
||||||
backgroundModel.release();
|
backgroundModel.release();
|
||||||
backgroundModel = null;
|
backgroundModel = null;
|
||||||
+20
-18
@@ -1,17 +1,17 @@
|
|||||||
package ru.ytkab0bp.slicebeam.slic3r;
|
package com.dark98.santoku.slic3r;
|
||||||
|
|
||||||
import static android.opengl.GLES30.*;
|
import static android.opengl.GLES30.*;
|
||||||
import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
|
import static com.dark98.santoku.utils.DebugUtils.assertTrue;
|
||||||
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.render.CoordAxes;
|
import com.dark98.santoku.render.CoordAxes;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
import ru.ytkab0bp.slicebeam.utils.DoubleMatrix;
|
import com.dark98.santoku.utils.DoubleMatrix;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Vec3d;
|
import com.dark98.santoku.utils.Vec3d;
|
||||||
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
|
import com.dark98.santoku.utils.ViewUtils;
|
||||||
|
|
||||||
public class Bed3D {
|
public class Bed3D {
|
||||||
private final static float GROUND_Z = -0.02f;
|
private final static float GROUND_Z = -0.02f;
|
||||||
@@ -45,6 +45,7 @@ public class Bed3D {
|
|||||||
|
|
||||||
private void configure(String path) {
|
private void configure(String path) {
|
||||||
Native.bed_configure(pointer, path);
|
Native.bed_configure(pointer, path);
|
||||||
|
Native.bed_init_triangles_mesh(pointer, triangles.pointer);
|
||||||
boundingVolume = Native.bed_get_bounding_volume(pointer);
|
boundingVolume = Native.bed_get_bounding_volume(pointer);
|
||||||
|
|
||||||
min = max = null;
|
min = max = null;
|
||||||
@@ -78,21 +79,22 @@ public class Bed3D {
|
|||||||
return boundingVolume != null;
|
return boundingVolume != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(boolean bottom, double[] viewModelMatrix, double[] projectionMatrix, float invZoom) {
|
public GLModel.MeshRaycaster getRaycaster() {
|
||||||
|
return triangles.getRaycaster();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render(GLShadersManager shadersManager, boolean bottom, double[] viewModelMatrix, double[] projectionMatrix, float invZoom) {
|
||||||
assertTrue(viewModelMatrix.length == 16);
|
assertTrue(viewModelMatrix.length == 16);
|
||||||
assertTrue(projectionMatrix.length == 16);
|
assertTrue(projectionMatrix.length == 16);
|
||||||
|
|
||||||
DoubleMatrix.setIdentityM(modelMatrix, 0);
|
DoubleMatrix.setIdentityM(modelMatrix, 0);
|
||||||
if (!likelyDelta) {
|
|
||||||
DoubleMatrix.translateM(modelMatrix, 0, -getVolumeMin().x * 2, -getVolumeMin().y * 2, -getVolumeMin().z);
|
|
||||||
}
|
|
||||||
DoubleMatrix.multiplyMM(outModelMatrix, 0, viewModelMatrix, 0, modelMatrix, 0);
|
DoubleMatrix.multiplyMM(outModelMatrix, 0, viewModelMatrix, 0, modelMatrix, 0);
|
||||||
renderDefaultBed(bottom, outModelMatrix, projectionMatrix);
|
renderDefaultBed(shadersManager, bottom, outModelMatrix, projectionMatrix);
|
||||||
axes.render(viewModelMatrix, projectionMatrix, 0.25f, invZoom);
|
axes.render(shadersManager, viewModelMatrix, projectionMatrix, 0.25f, invZoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderDefaultBed(boolean bottom, double[] viewModelMatrix, double[] projectionMatrix) {
|
private void renderDefaultBed(GLShadersManager shadersManager, boolean bottom, double[] viewModelMatrix, double[] projectionMatrix) {
|
||||||
GLShaderProgram shader = GLShadersManager.get(GLShadersManager.SHADER_FLAT);
|
GLShaderProgram shader = shadersManager.get(GLShadersManager.SHADER_FLAT);
|
||||||
shader.startUsing();
|
shader.startUsing();
|
||||||
|
|
||||||
shader.setUniformMatrix4fv("view_model_matrix", viewModelMatrix);
|
shader.setUniformMatrix4fv("view_model_matrix", viewModelMatrix);
|
||||||
@@ -121,8 +123,8 @@ public class Bed3D {
|
|||||||
shader.stopUsing();
|
shader.stopUsing();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderTexturedBed(boolean bottom, float[] viewModelMatrix, float[] projectionMatrix) {
|
private void renderTexturedBed(GLShadersManager shadersManager, boolean bottom, float[] viewModelMatrix, float[] projectionMatrix) {
|
||||||
GLShaderProgram shader = GLShadersManager.get(GLShadersManager.SHADER_PRINTBED);
|
GLShaderProgram shader = shadersManager.get(GLShadersManager.SHADER_PRINTBED);
|
||||||
shader.startUsing();
|
shader.startUsing();
|
||||||
|
|
||||||
shader.setUniform3f("view_model_matrix", viewModelMatrix);
|
shader.setUniform3f("view_model_matrix", viewModelMatrix);
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.slic3r;
|
package com.dark98.santoku.slic3r;
|
||||||
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.slic3r;
|
package com.dark98.santoku.slic3r;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
@@ -0,0 +1,318 @@
|
|||||||
|
package com.dark98.santoku.slic3r;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import com.dark98.santoku.config.ConfigObject;
|
||||||
|
import com.dark98.santoku.view.GLView;
|
||||||
|
|
||||||
|
public final class GCodeThumbnailer {
|
||||||
|
private static final String TAG = "GCodeThumbnailer";
|
||||||
|
private static final int MAX_ROW_LENGTH = 78;
|
||||||
|
private static final int THUMBNAIL_SUPERSAMPLE = 16;
|
||||||
|
private static final int MAX_SUPERSAMPLE_PIXELS = 16_000_000;
|
||||||
|
private static final int MAX_SUPERSAMPLE_DIM = 4096;
|
||||||
|
private static final Pattern SIZE_PATTERN = Pattern.compile("^(\\d+)\\s*[xX]\\s*(\\d+)$");
|
||||||
|
|
||||||
|
private GCodeThumbnailer() {}
|
||||||
|
|
||||||
|
public static boolean addThumbnailsToGcode(File gcodeFile, ConfigObject config, GLView glView) {
|
||||||
|
if (gcodeFile == null || config == null || glView == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!gcodeFile.exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String binaryGcode = config.get("binary_gcode");
|
||||||
|
if ("1".equals(binaryGcode)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (gcodeHasThumbnail(gcodeFile)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String thumbnails = config.get("thumbnails");
|
||||||
|
if (thumbnails == null || thumbnails.trim().isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String defaultFormat = "PNG";
|
||||||
|
|
||||||
|
List<ThumbnailSpec> specs = parseThumbnailSpecs(thumbnails, defaultFormat);
|
||||||
|
if (specs.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String header = buildHeader(specs, glView);
|
||||||
|
if (header.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return prependToFile(gcodeFile, header);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Failed to add thumbnails to gcode", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<ThumbnailSpec> parseThumbnailSpecs(String thumbnails, String defaultFormat) {
|
||||||
|
List<ThumbnailSpec> specs = new ArrayList<>();
|
||||||
|
for (String raw : thumbnails.split(",")) {
|
||||||
|
String token = raw.trim();
|
||||||
|
if (token.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (token.contains("COLPIC")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String format = defaultFormat;
|
||||||
|
int slash = token.indexOf('/');
|
||||||
|
if (slash >= 0) {
|
||||||
|
format = token.substring(slash + 1).trim();
|
||||||
|
token = token.substring(0, slash).trim();
|
||||||
|
}
|
||||||
|
Matcher matcher = SIZE_PATTERN.matcher(token);
|
||||||
|
if (!matcher.matches()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int width = Integer.parseInt(matcher.group(1));
|
||||||
|
int height = Integer.parseInt(matcher.group(2));
|
||||||
|
if (width <= 0 || height <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
specs.add(new ThumbnailSpec(width, height, normalizeFormat(format)));
|
||||||
|
}
|
||||||
|
return specs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ThumbnailFormat normalizeFormat(String format) {
|
||||||
|
if (format == null) {
|
||||||
|
return ThumbnailFormat.PNG;
|
||||||
|
}
|
||||||
|
switch (format.trim().toUpperCase(Locale.US)) {
|
||||||
|
case "JPG":
|
||||||
|
case "JPEG":
|
||||||
|
return ThumbnailFormat.JPG;
|
||||||
|
case "QOI":
|
||||||
|
return ThumbnailFormat.QOI;
|
||||||
|
default:
|
||||||
|
return ThumbnailFormat.PNG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Bitmap captureSnapshot(GLView glView, int targetWidth, int targetHeight) {
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
Bitmap[] ref = new Bitmap[1];
|
||||||
|
int scale = computeSupersampleScale(targetWidth, targetHeight);
|
||||||
|
int width = targetWidth * scale;
|
||||||
|
int height = targetHeight * scale;
|
||||||
|
glView.queueEvent(() -> {
|
||||||
|
try {
|
||||||
|
ref[0] = glView.snapshotBitmap(width, height, true, true);
|
||||||
|
} catch (OutOfMemoryError e) {
|
||||||
|
Log.e(TAG, "Thumbnail snapshot OOM", e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Failed to capture GL snapshot", e);
|
||||||
|
} finally {
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
if (!latch.await(3, TimeUnit.SECONDS)) {
|
||||||
|
Log.w(TAG, "Timed out waiting for GL snapshot");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Bitmap snapshot = ref[0];
|
||||||
|
if (snapshot == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (scale <= 1) {
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
Bitmap downscaled = downscaleBitmap(snapshot, targetWidth, targetHeight);
|
||||||
|
if (downscaled != snapshot) {
|
||||||
|
snapshot.recycle();
|
||||||
|
}
|
||||||
|
return downscaled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int computeSupersampleScale(int w, int h) {
|
||||||
|
if (w <= 0 || h <= 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int scale = THUMBNAIL_SUPERSAMPLE;
|
||||||
|
while (scale > 1) {
|
||||||
|
long pixels = (long) w * h * scale * scale;
|
||||||
|
if (pixels <= MAX_SUPERSAMPLE_PIXELS && w * scale <= MAX_SUPERSAMPLE_DIM && h * scale <= MAX_SUPERSAMPLE_DIM) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
scale--;
|
||||||
|
}
|
||||||
|
return Math.max(1, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildHeader(List<ThumbnailSpec> specs, GLView glView) {
|
||||||
|
StringBuilder header = new StringBuilder();
|
||||||
|
boolean wroteThumbnail = false;
|
||||||
|
for (ThumbnailSpec spec : specs) {
|
||||||
|
Bitmap snapshot = captureSnapshot(glView, spec.width, spec.height);
|
||||||
|
if (snapshot == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ThumbnailFormat format = spec.format;
|
||||||
|
if (format == ThumbnailFormat.QOI) {
|
||||||
|
Log.w(TAG, "QOI thumbnails not supported, falling back to PNG");
|
||||||
|
format = ThumbnailFormat.PNG;
|
||||||
|
}
|
||||||
|
Bitmap scaled = snapshot;
|
||||||
|
if (snapshot.getWidth() != spec.width || snapshot.getHeight() != spec.height) {
|
||||||
|
scaled = downscaleBitmap(snapshot, spec.width, spec.height);
|
||||||
|
snapshot.recycle();
|
||||||
|
}
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
Bitmap.CompressFormat compressFormat = format == ThumbnailFormat.JPG
|
||||||
|
? Bitmap.CompressFormat.JPEG
|
||||||
|
: Bitmap.CompressFormat.PNG;
|
||||||
|
int quality = format == ThumbnailFormat.JPG ? 90 : 100;
|
||||||
|
if (!scaled.compress(compressFormat, quality, out)) {
|
||||||
|
scaled.recycle();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
scaled.recycle();
|
||||||
|
byte[] data = out.toByteArray();
|
||||||
|
if (data.length == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String tag = format == ThumbnailFormat.JPG ? "thumbnail_JPG" : "thumbnail";
|
||||||
|
if (!wroteThumbnail) {
|
||||||
|
header.append("; THUMBNAIL_BLOCK_START\n");
|
||||||
|
wroteThumbnail = true;
|
||||||
|
}
|
||||||
|
String encoded = Base64.encodeToString(data, Base64.NO_WRAP);
|
||||||
|
header.append("\n;\n; ").append(tag)
|
||||||
|
.append(" begin ").append(spec.width).append("x").append(spec.height)
|
||||||
|
.append(" ").append(encoded.length()).append("\n");
|
||||||
|
int offset = 0;
|
||||||
|
while (offset < encoded.length()) {
|
||||||
|
int end = Math.min(offset + MAX_ROW_LENGTH, encoded.length());
|
||||||
|
header.append("; ").append(encoded, offset, end).append("\n");
|
||||||
|
offset = end;
|
||||||
|
}
|
||||||
|
header.append("; ").append(tag).append(" end\n;\n");
|
||||||
|
}
|
||||||
|
if (wroteThumbnail) {
|
||||||
|
header.append("; THUMBNAIL_BLOCK_END\n");
|
||||||
|
}
|
||||||
|
return header.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Bitmap downscaleBitmap(Bitmap src, int targetWidth, int targetHeight) {
|
||||||
|
if (src.getWidth() == targetWidth && src.getHeight() == targetHeight) {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
Bitmap current = src;
|
||||||
|
Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
|
||||||
|
while (current.getWidth() / 2 >= targetWidth && current.getHeight() / 2 >= targetHeight) {
|
||||||
|
int nextWidth = Math.max(targetWidth, current.getWidth() / 2);
|
||||||
|
int nextHeight = Math.max(targetHeight, current.getHeight() / 2);
|
||||||
|
Bitmap next = Bitmap.createBitmap(nextWidth, nextHeight, Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(next);
|
||||||
|
canvas.drawBitmap(current, null, new Rect(0, 0, nextWidth, nextHeight), paint);
|
||||||
|
if (current != src) {
|
||||||
|
current.recycle();
|
||||||
|
}
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
if (current.getWidth() != targetWidth || current.getHeight() != targetHeight) {
|
||||||
|
Bitmap finalBmp = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(finalBmp);
|
||||||
|
canvas.drawBitmap(current, null, new Rect(0, 0, targetWidth, targetHeight), paint);
|
||||||
|
if (current != src) {
|
||||||
|
current.recycle();
|
||||||
|
}
|
||||||
|
current = finalBmp;
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean prependToFile(File gcodeFile, String header) throws IOException {
|
||||||
|
File tmp = new File(gcodeFile.getParentFile(), gcodeFile.getName() + ".thumbtmp");
|
||||||
|
try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(gcodeFile));
|
||||||
|
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(tmp))) {
|
||||||
|
out.write(header.getBytes(StandardCharsets.US_ASCII));
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
int read;
|
||||||
|
while ((read = in.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!gcodeFile.delete()) {
|
||||||
|
tmp.delete();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!tmp.renameTo(gcodeFile)) {
|
||||||
|
tmp.delete();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean gcodeHasThumbnail(File gcodeFile) {
|
||||||
|
try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(gcodeFile))) {
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
int total = 0;
|
||||||
|
int read;
|
||||||
|
while ((read = in.read(buffer)) != -1 && total < 262144) {
|
||||||
|
sb.append(new String(buffer, 0, read, StandardCharsets.US_ASCII));
|
||||||
|
if (sb.indexOf("thumbnail begin") != -1 || sb.indexOf("thumbnail_JPG begin") != -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
total += read;
|
||||||
|
}
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ThumbnailSpec {
|
||||||
|
final int width;
|
||||||
|
final int height;
|
||||||
|
final ThumbnailFormat format;
|
||||||
|
|
||||||
|
ThumbnailSpec(int width, int height, ThumbnailFormat format) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.format = format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum ThumbnailFormat {
|
||||||
|
PNG,
|
||||||
|
JPG,
|
||||||
|
QOI
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-4
@@ -1,6 +1,6 @@
|
|||||||
package ru.ytkab0bp.slicebeam.slic3r;
|
package com.dark98.santoku.slic3r;
|
||||||
|
|
||||||
import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
|
import static com.dark98.santoku.utils.DebugUtils.assertTrue;
|
||||||
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
|
||||||
@@ -10,8 +10,8 @@ import androidx.core.util.Pair;
|
|||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.R;
|
import com.dark98.santoku.R;
|
||||||
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
|
import com.dark98.santoku.theme.ThemesRepo;
|
||||||
|
|
||||||
public class GCodeViewer {
|
public class GCodeViewer {
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
+6
-6
@@ -1,15 +1,15 @@
|
|||||||
package ru.ytkab0bp.slicebeam.slic3r;
|
package com.dark98.santoku.slic3r;
|
||||||
|
|
||||||
import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
|
import static com.dark98.santoku.utils.DebugUtils.assertTrue;
|
||||||
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.render.Camera;
|
import com.dark98.santoku.render.Camera;
|
||||||
import ru.ytkab0bp.slicebeam.render.GLRenderer;
|
import com.dark98.santoku.render.GLRenderer;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Prefs;
|
import com.dark98.santoku.utils.Prefs;
|
||||||
import ru.ytkab0bp.slicebeam.utils.Vec3d;
|
import com.dark98.santoku.utils.Vec3d;
|
||||||
|
|
||||||
public class GLModel {
|
public class GLModel {
|
||||||
long pointer;
|
long pointer;
|
||||||
+5
-5
@@ -1,6 +1,6 @@
|
|||||||
package ru.ytkab0bp.slicebeam.slic3r;
|
package com.dark98.santoku.slic3r;
|
||||||
|
|
||||||
import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
|
import static com.dark98.santoku.utils.DebugUtils.assertTrue;
|
||||||
|
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
@@ -13,8 +13,8 @@ import java.nio.ByteBuffer;
|
|||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
import ru.ytkab0bp.slicebeam.utils.IOUtils;
|
import com.dark98.santoku.utils.IOUtils;
|
||||||
|
|
||||||
public class GLShaderProgram {
|
public class GLShaderProgram {
|
||||||
long pointer;
|
long pointer;
|
||||||
@@ -39,7 +39,7 @@ public class GLShaderProgram {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public GLShaderProgram(String name) {
|
public GLShaderProgram(String name) {
|
||||||
AssetManager assets = SliceBeam.INSTANCE.getAssets();
|
AssetManager assets = Santoku.INSTANCE.getAssets();
|
||||||
try {
|
try {
|
||||||
pointer = Native.shader_init_from_texts(name, IOUtils.readString(assets.open("shaders/" + name + ".fs")), IOUtils.readString(assets.open("shaders/" + name + ".vs")));
|
pointer = Native.shader_init_from_texts(name, IOUtils.readString(assets.open("shaders/" + name + ".fs")), IOUtils.readString(assets.open("shaders/" + name + ".vs")));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
+21
-6
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.slic3r;
|
package com.dark98.santoku.slic3r;
|
||||||
|
|
||||||
import android.opengl.GLES30;
|
import android.opengl.GLES30;
|
||||||
|
|
||||||
@@ -8,7 +8,9 @@ import androidx.annotation.StringDef;
|
|||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class GLShadersManager {
|
public class GLShadersManager {
|
||||||
@@ -52,7 +54,9 @@ public class GLShadersManager {
|
|||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface ShaderType {}
|
public @interface ShaderType {}
|
||||||
|
|
||||||
private final static Map<String, GLShaderProgram> shaders = new HashMap<String, GLShaderProgram>() {
|
private final static List<GLShadersManager> managers = new ArrayList<>();
|
||||||
|
|
||||||
|
private final Map<String, GLShaderProgram> shaders = new HashMap<String, GLShaderProgram>() {
|
||||||
@Override
|
@Override
|
||||||
public GLShaderProgram get(@Nullable Object key) {
|
public GLShaderProgram get(@Nullable Object key) {
|
||||||
GLShaderProgram shader = super.get(key);
|
GLShaderProgram shader = super.get(key);
|
||||||
@@ -61,24 +65,35 @@ public class GLShadersManager {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public static void clearShaders() {
|
public GLShadersManager() {
|
||||||
|
managers.add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearShaders() {
|
||||||
for (GLShaderProgram program : shaders.values()) {
|
for (GLShaderProgram program : shaders.values()) {
|
||||||
program.release();
|
program.release();
|
||||||
}
|
}
|
||||||
shaders.clear();
|
shaders.clear();
|
||||||
|
managers.remove(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GLShaderProgram get(@ShaderType String key) {
|
public GLShaderProgram get(@ShaderType String key) {
|
||||||
return shaders.get(key);
|
return shaders.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
private static long getCurrentShaderPointer() {
|
private static long getCurrentShaderPointer() {
|
||||||
GLShaderProgram prog = getCurrentShader();
|
GLShaderProgram prog = null;
|
||||||
|
for (GLShadersManager manager : managers) {
|
||||||
|
prog = manager.getCurrentShader();
|
||||||
|
if (prog != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
return prog != null ? prog.pointer : 0;
|
return prog != null ? prog.pointer : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GLShaderProgram getCurrentShader() {
|
public GLShaderProgram getCurrentShader() {
|
||||||
int[] idRef = {0};
|
int[] idRef = {0};
|
||||||
GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, idRef, 0);
|
GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, idRef, 0);
|
||||||
int id = idRef[0];
|
int id = idRef[0];
|
||||||
+18
-2
@@ -1,11 +1,11 @@
|
|||||||
package ru.ytkab0bp.slicebeam.slic3r;
|
package com.dark98.santoku.slic3r;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.utils.Vec3d;
|
import com.dark98.santoku.utils.Vec3d;
|
||||||
|
|
||||||
public class Model {
|
public class Model {
|
||||||
public final String key = UUID.randomUUID().toString();
|
public final String key = UUID.randomUUID().toString();
|
||||||
@@ -147,14 +147,30 @@ public class Model {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getExtruder(int i) {
|
||||||
|
return Native.model_get_extruder(pointer, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtruder(int i, int extruder) {
|
||||||
|
Native.model_set_extruder(pointer, i, extruder);
|
||||||
|
}
|
||||||
|
|
||||||
public void autoOrient(int i) {
|
public void autoOrient(int i) {
|
||||||
Native.model_auto_orient(pointer, i);
|
Native.model_auto_orient(pointer, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isBigObject(int i) {
|
||||||
|
return Native.model_is_big_object(pointer, i);
|
||||||
|
}
|
||||||
|
|
||||||
public GCodeProcessorResult slice(String configPath, String gcodePath, SliceListener listener) throws Slic3rRuntimeError {
|
public GCodeProcessorResult slice(String configPath, String gcodePath, SliceListener listener) throws Slic3rRuntimeError {
|
||||||
return new GCodeProcessorResult(Native.model_slice(pointer, configPath, gcodePath, listener));
|
return new GCodeProcessorResult(Native.model_slice(pointer, configPath, gcodePath, listener));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void export3mf(String configPath, String _3mfPath) throws Slic3rRuntimeError {
|
||||||
|
Native.model_export_3mf(pointer, configPath, _3mfPath);
|
||||||
|
}
|
||||||
|
|
||||||
public void release() {
|
public void release() {
|
||||||
if (pointer != 0) {
|
if (pointer != 0) {
|
||||||
Native.model_release(pointer);
|
Native.model_release(pointer);
|
||||||
+8
-3
@@ -1,6 +1,6 @@
|
|||||||
package ru.ytkab0bp.slicebeam.slic3r;
|
package com.dark98.santoku.slic3r;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
|
|
||||||
class Native {
|
class Native {
|
||||||
static {
|
static {
|
||||||
@@ -12,7 +12,7 @@ class Native {
|
|||||||
|
|
||||||
System.loadLibrary("slic3r");
|
System.loadLibrary("slic3r");
|
||||||
|
|
||||||
set_svg_path_prefix(SliceBeam.INSTANCE.getCacheDir().getAbsolutePath());
|
set_svg_path_prefix(Santoku.INSTANCE.getCacheDir().getAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
static native void get_print_config_def(PrintConfigDef def);
|
static native void get_print_config_def(PrintConfigDef def);
|
||||||
@@ -46,6 +46,7 @@ class Native {
|
|||||||
static native int bed_get_bounding_volume_max_size(long ptr);
|
static native int bed_get_bounding_volume_max_size(long ptr);
|
||||||
static native double[] bed_get_bounding_volume(long ptr);
|
static native double[] bed_get_bounding_volume(long ptr);
|
||||||
static native void bed_configure(long ptr, String configPath);
|
static native void bed_configure(long ptr, String configPath);
|
||||||
|
static native void bed_init_triangles_mesh(long ptr, long triangles);
|
||||||
static native boolean bed_arrange(long ptr, long modelPtr);
|
static native boolean bed_arrange(long ptr, long modelPtr);
|
||||||
static native void bed_release(long ptr);
|
static native void bed_release(long ptr);
|
||||||
|
|
||||||
@@ -72,7 +73,11 @@ class Native {
|
|||||||
static native void model_flatten_rotate(long ptr, int i, long surfacePtr);
|
static native void model_flatten_rotate(long ptr, int i, long surfacePtr);
|
||||||
static native long[] model_create_flatten_planes(long ptr, int i);
|
static native long[] model_create_flatten_planes(long ptr, int i);
|
||||||
static native void model_auto_orient(long ptr, int i);
|
static native void model_auto_orient(long ptr, int i);
|
||||||
|
static native boolean model_is_big_object(long ptr, int i);
|
||||||
|
static native int model_get_extruder(long ptr, int i);
|
||||||
|
static native void model_set_extruder(long ptr, int i, int extruder);
|
||||||
static native long model_slice(long ptr, String configPath, String path, SliceListener listener) throws Slic3rRuntimeError;
|
static native long model_slice(long ptr, String configPath, String path, SliceListener listener) throws Slic3rRuntimeError;
|
||||||
|
static native void model_export_3mf(long ptr, String configPath, String path) throws Slic3rRuntimeError;
|
||||||
static native void model_release(long ptr);
|
static native void model_release(long ptr);
|
||||||
|
|
||||||
static native long gcoderesult_load_file(String path, String name);
|
static native long gcoderesult_load_file(String path, String name);
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.slic3r;
|
package com.dark98.santoku.slic3r;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
+3
-2
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.slic3r;
|
package com.dark98.santoku.slic3r;
|
||||||
|
|
||||||
import androidx.annotation.Keep;
|
import androidx.annotation.Keep;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -16,7 +16,8 @@ public class PrintConfigDef {
|
|||||||
"tilt_up_finish_speed",
|
"tilt_up_finish_speed",
|
||||||
"tilt_down_initial_speed",
|
"tilt_down_initial_speed",
|
||||||
"tilt_down_finish_speed",
|
"tilt_down_finish_speed",
|
||||||
"tower_speed"
|
"tower_speed",
|
||||||
|
"thumbnails_format"
|
||||||
);
|
);
|
||||||
|
|
||||||
private static PrintConfigDef instance;
|
private static PrintConfigDef instance;
|
||||||
+21
-9
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.slic3r;
|
package com.dark98.santoku.slic3r;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -13,8 +13,8 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.BuildConfig;
|
import com.dark98.santoku.BuildConfig;
|
||||||
import ru.ytkab0bp.slicebeam.config.ConfigObject;
|
import com.dark98.santoku.config.ConfigObject;
|
||||||
|
|
||||||
public class Slic3rConfigWrapper {
|
public class Slic3rConfigWrapper {
|
||||||
public final static String BLACKLISTED_SYMBOLS = "<>[]:/\\|?*\"";
|
public final static String BLACKLISTED_SYMBOLS = "<>[]:/\\|?*\"";
|
||||||
@@ -80,7 +80,7 @@ public class Slic3rConfigWrapper {
|
|||||||
);
|
);
|
||||||
public final static List<String> PRINTER_CONFIG_KEYS = Arrays.asList(
|
public final static List<String> PRINTER_CONFIG_KEYS = Arrays.asList(
|
||||||
"printer_technology", "autoemit_temperature_commands",
|
"printer_technology", "autoemit_temperature_commands",
|
||||||
"bed_shape", "bed_custom_texture", "bed_custom_model", "binary_gcode", "z_offset", "gcode_flavor", "use_relative_e_distances",
|
"bed_shape", "auto_arrange_bed_clearance", "auto_arrange_rotate", "bed_custom_texture", "bed_custom_model", "binary_gcode", "z_offset", "gcode_flavor", "use_relative_e_distances",
|
||||||
"use_firmware_retraction", "use_volumetric_e", "variable_layer_height", "prefer_clockwise_movements",
|
"use_firmware_retraction", "use_volumetric_e", "variable_layer_height", "prefer_clockwise_movements",
|
||||||
//FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset.
|
//FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset.
|
||||||
"host_type", "print_host", "printhost_apikey", "printhost_cafile",
|
"host_type", "print_host", "printhost_apikey", "printhost_cafile",
|
||||||
@@ -90,12 +90,14 @@ public class Slic3rConfigWrapper {
|
|||||||
"cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "multimaterial_purging",
|
"cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "multimaterial_purging",
|
||||||
"max_print_height", "default_print_profile", "inherits",
|
"max_print_height", "default_print_profile", "inherits",
|
||||||
"remaining_times", "silent_mode",
|
"remaining_times", "silent_mode",
|
||||||
"machine_limits_usage", "thumbnails", "thumbnails_format",
|
"machine_limits_usage", "thumbnails",
|
||||||
"machine_max_acceleration_extruding", "machine_max_acceleration_retracting", "machine_max_acceleration_travel",
|
"machine_max_acceleration_extruding", "machine_max_acceleration_retracting", "machine_max_acceleration_travel",
|
||||||
"machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e",
|
"machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e",
|
||||||
"machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e",
|
"machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e",
|
||||||
"machine_min_extruding_rate", "machine_min_travel_rate",
|
"machine_min_extruding_rate", "machine_min_travel_rate",
|
||||||
"machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e"
|
"machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e",
|
||||||
|
"elegoolink_timelapse", "elegoolink_bed_leveling", "elegoolink_bed_type",
|
||||||
|
"bed_mesh_probe_distance", "bed_mesh_limit_min", "bed_mesh_limit_max", "adaptive_bed_mesh_margin"
|
||||||
);
|
);
|
||||||
public final static List<String> PHYSICAL_PRINTER_CONFIG_KEYS = Arrays.asList(
|
public final static List<String> PHYSICAL_PRINTER_CONFIG_KEYS = Arrays.asList(
|
||||||
"preset_name", // temporary option to compatibility with older Slicer
|
"preset_name", // temporary option to compatibility with older Slicer
|
||||||
@@ -199,7 +201,12 @@ public class Slic3rConfigWrapper {
|
|||||||
sb.append("[").append(key).append(":").append(cfg.getTitle()).append("]\n");
|
sb.append("[").append(key).append(":").append(cfg.getTitle()).append("]\n");
|
||||||
|
|
||||||
for (Map.Entry<String, String> en : cfg.values.entrySet()) {
|
for (Map.Entry<String, String> en : cfg.values.entrySet()) {
|
||||||
sb.append(en.getKey()).append(" = ").append(en.getValue().replace("\n", "\\n")).append("\n");
|
String value = en.getValue();
|
||||||
|
if (value != null) {
|
||||||
|
value = value.replace("\r\n", "\n").replace("\r", "\n");
|
||||||
|
value = value.replace("\n", "\\n");
|
||||||
|
}
|
||||||
|
sb.append(en.getKey()).append(" = ").append(value).append("\n");
|
||||||
}
|
}
|
||||||
sb.append("\n");
|
sb.append("\n");
|
||||||
}
|
}
|
||||||
@@ -207,7 +214,7 @@ public class Slic3rConfigWrapper {
|
|||||||
|
|
||||||
public String serialize() {
|
public String serialize() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("# generated by Slice Beam ").append(BuildConfig.VERSION_NAME).append("\n\n");
|
sb.append("# generated by Santoku ").append(BuildConfig.VERSION_NAME).append("\n\n");
|
||||||
serializeList(sb, "printer", printerConfigs);
|
serializeList(sb, "printer", printerConfigs);
|
||||||
serializeList(sb, "print", printConfigs);
|
serializeList(sb, "print", printConfigs);
|
||||||
serializeList(sb, "filament", filamentConfigs);
|
serializeList(sb, "filament", filamentConfigs);
|
||||||
@@ -215,7 +222,12 @@ public class Slic3rConfigWrapper {
|
|||||||
if (presets != null) {
|
if (presets != null) {
|
||||||
sb.append("[presets]\n");
|
sb.append("[presets]\n");
|
||||||
for (Map.Entry<String, String> en : presets.values.entrySet()) {
|
for (Map.Entry<String, String> en : presets.values.entrySet()) {
|
||||||
sb.append(en.getKey()).append(" = ").append(en.getValue().replace("\n", "\\n")).append("\n");
|
String value = en.getValue();
|
||||||
|
if (value != null) {
|
||||||
|
value = value.replace("\r\n", "\n").replace("\r", "\n");
|
||||||
|
value = value.replace("\n", "\\n");
|
||||||
|
}
|
||||||
|
sb.append(en.getKey()).append(" = ").append(value).append("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+4
-4
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.slic3r;
|
package com.dark98.santoku.slic3r;
|
||||||
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ import java.util.HashMap;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.SliceBeam;
|
import com.dark98.santoku.Santoku;
|
||||||
|
|
||||||
public class Slic3rLocalization {
|
public class Slic3rLocalization {
|
||||||
private static Map<String, Slic3rLocalization> localesMap = new HashMap<String, Slic3rLocalization>() {
|
private static Map<String, Slic3rLocalization> localesMap = new HashMap<String, Slic3rLocalization>() {
|
||||||
@@ -38,7 +38,7 @@ public class Slic3rLocalization {
|
|||||||
private Map<String, String> map = new HashMap<>();
|
private Map<String, String> map = new HashMap<>();
|
||||||
|
|
||||||
public Slic3rLocalization(String key) throws IOException {
|
public Slic3rLocalization(String key) throws IOException {
|
||||||
InputStream in = SliceBeam.INSTANCE.getAssets().open("localization/" + key + ".po");
|
InputStream in = Santoku.INSTANCE.getAssets().open("localization/" + key + ".po");
|
||||||
BufferedReader r = new BufferedReader(new InputStreamReader(in));
|
BufferedReader r = new BufferedReader(new InputStreamReader(in));
|
||||||
String line;
|
String line;
|
||||||
StringBuilder msgId = null;
|
StringBuilder msgId = null;
|
||||||
@@ -71,7 +71,7 @@ public class Slic3rLocalization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String replaceStr(String val) {
|
private static String replaceStr(String val) {
|
||||||
return val.replace("\\n", "\n").replaceAll("\\\\(.)", "$1").replace("Slic3r", "Slice Beam").replace("PrusaSlicer", "Slice Beam");
|
return val.replace("\\n", "\n").replaceAll("\\\\(.)", "$1").replace("Slic3r", "Santoku").replace("PrusaSlicer", "Santoku");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getString(String key) {
|
public static String getString(String key) {
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.slic3r;
|
package com.dark98.santoku.slic3r;
|
||||||
|
|
||||||
public class Slic3rRuntimeError extends Exception {
|
public class Slic3rRuntimeError extends Exception {
|
||||||
public Slic3rRuntimeError() {
|
public Slic3rRuntimeError() {
|
||||||
+3
-3
@@ -1,10 +1,10 @@
|
|||||||
package ru.ytkab0bp.slicebeam.slic3r;
|
package com.dark98.santoku.slic3r;
|
||||||
|
|
||||||
import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
|
import static com.dark98.santoku.utils.DebugUtils.assertTrue;
|
||||||
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import ru.ytkab0bp.slicebeam.utils.Vec3d;
|
import com.dark98.santoku.utils.Vec3d;
|
||||||
|
|
||||||
public class Slic3rUtils {
|
public class Slic3rUtils {
|
||||||
public static void calcViewNormalMatrix(double[] viewMatrix, double[] worldMatrix, double[] normalMatrix) {
|
public static void calcViewNormalMatrix(double[] viewMatrix, double[] worldMatrix, double[] normalMatrix) {
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package ru.ytkab0bp.slicebeam.slic3r;
|
package com.dark98.santoku.slic3r;
|
||||||
|
|
||||||
public interface SliceListener {
|
public interface SliceListener {
|
||||||
void onProgress(int progress, String text);
|
void onProgress(int progress, String text);
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user