16 Commits

Author SHA1 Message Date
Dark98 9f4ae6e38f Gradle startBackend Fixes 2026-03-08 04:58:53 +00:00
Dark98 7e723fce4e Enable Gradle Configuration Cache 2026-02-23 03:18:21 +00:00
Dark98 2b8c182516 Updated Config Handling 2026-02-15 22:19:31 +00:00
Dark98 213e723276 Update App Icon Again 2026-02-15 22:15:28 +00:00
Dark98 bb07740d43 PlayStore & Debug Build Changes
*Updated Icon
*Upgraded NDK to 29.0.14206865
*Rebuilt Libs To 16KB Page Sizr Compatible
*Updated Debug APK Name And Package
2026-02-11 21:44:30 +00:00
Dark98 d37c099060 Cloud/Backend Overhaul
Reverse Engineered a Backend
Removed Boosty Stuff
Removed Socials Stuff
Removed Subscription Stuff
Probably Some More
2026-02-05 21:41:21 +00:00
Dark98 197e46eabc Implement Support For Klippers Adaptive Bed Meshing 2026-01-30 21:01:36 +00:00
Dark98 3b67c4cdac Add Klipper (Moonraker) Host Support 2026-01-30 10:39:50 +00:00
Dark98 254ab77439 Android SAF Impl 2026-01-30 09:59:13 +00:00
Dark98 1e02fbf071 Config File Saving Updates
*Save Config On Exit
*Handle New Lines A Bit Better
2026-01-30 09:57:26 +00:00
Dark98 aab6387f26 Add CrossHatch Infill 2026-01-30 06:50:58 +00:00
Dark98 d692d3e061 Srat Refactoring To Santoku 2026-01-30 04:28:58 +00:00
Dark98 5f2f0829dd Auto Arrange Rotation & Bed Edge Clearance 2026-01-25 03:59:24 +00:00
Dark98 a5e0d8a9d8 Create Build Workflow 2026-01-25 01:38:23 +00:00
Dark98 20010bfbd5 Add Model Cloning Support 2026-01-23 00:56:45 +00:00
Dark98 f352a02b9f Initial ElegooLink Support (Only Centauri Carbon Tested) 2026-01-22 07:46:13 +00:00
205 changed files with 4003 additions and 2932 deletions
+38 -16
View File
@@ -2,27 +2,49 @@ 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
- uses: actions/checkout@v4
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build
- name: set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: gradle
- name: Upload a Debug APK
uses: actions/upload-artifact@v6.0.0
with:
path: app/build/outputs/apk/debug/*.apk
- 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
+15 -5
View File
@@ -8,7 +8,7 @@ include(CheckLibraryExists)
include(GenerateExportHeader)
include(CheckCSourceCompiles)
project(SliceBeam)
project(Santoku)
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_CXX_STANDARD 17)
@@ -17,6 +17,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(IS_CLANG_CL TRUE)
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_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,common-page-size=16384 -Wl,-z,max-page-size=16384")
add_compile_options(-fsigned-char)
# Suppress all warnings
@@ -782,6 +783,8 @@ add_library(slic3r
src/main/jni/libslic3r/Fill/FillBase.hpp
src/main/jni/libslic3r/Fill/FillConcentric.cpp
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.hpp
src/main/jni/libslic3r/Fill/FillHoneycomb.cpp
@@ -1241,9 +1244,9 @@ add_library(slic3r
src/main/jni/bbl/Orient.cpp
src/main/jni/slicebeam/beam_native.cpp
src/main/jni/slicebeam/GLModel.cpp
src/main/jni/slicebeam/GLShader.cpp
src/main/jni/santoku/beam_native.cpp
src/main/jni/santoku/GLModel.cpp
src/main/jni/santoku/GLShader.cpp
# $<TARGET_OBJECTS:simd>
)
@@ -1349,4 +1352,11 @@ target_link_libraries(slic3r PRIVATE
boost_wave
boost_wserialization
${OCCT_LIBS}
)
)
if(ANDROID)
target_link_options(slic3r PRIVATE
"-Wl,-z,common-page-size=16384"
"-Wl,-z,max-page-size=16384"
"-Wl,--rosegment")
endif()
+245 -124
View File
@@ -6,26 +6,30 @@ plugins {
}
def commit = getGitCommitHash(file('.'))
def prodCloudBaseUrl = "https://santoku.dark98.co.uk/v1/"
def prodBeamBaseUrl = "https://santoku.dark98.co.uk"
android {
namespace 'ru.ytkab0bp.slicebeam'
namespace 'com.dark98.santoku'
compileSdk 35
defaultConfig {
applicationId "ru.ytkab0bp.slicebeam"
applicationId "com.dark98.santoku"
minSdk 21
targetSdk 35
versionCode 8
versionName "0.3.0"
versionCode 1
versionName "0.0.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField "String", "CLOUD_BASE_URL_PROD", "\"" + prodCloudBaseUrl + "\""
buildConfigField "String", "BEAM_BASE_URL_PROD", "\"" + prodBeamBaseUrl + "\""
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_shared', '-DANDROID_PLATFORM=21',
'-DCMAKE_BUILD_TYPE=Release', // Disabling this results in drastically degradation of slicing times on debug builds
"-DSLIC3R_VERSION=\"${defaultConfig.versionName}\"",
"-DSLIC3R_BUILD_ID=\"${defaultConfig.versionCode}\""
"-DSLIC3R_VERSION=${defaultConfig.versionName}",
"-DSLIC3R_BUILD_ID=${defaultConfig.versionCode}"
}
}
}
@@ -39,7 +43,6 @@ android {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
buildConfigField "boolean", "IS_GOOGLE_PLAY", "false"
buildConfigField "String", "COMMIT", "\"" + commit + "\""
ndk {
//noinspection ChromeOsAbiSupport
@@ -47,7 +50,8 @@ android {
}
}
debug {
buildConfigField "boolean", "IS_GOOGLE_PLAY", "false"
applicationIdSuffix ".debug"
resValue "string", "AppName", "Santoku (Debug)"
buildConfigField "String", "COMMIT", "\"" + commit + "\""
ndk {
debugSymbolLevel 'NONE'
@@ -65,7 +69,7 @@ android {
cmake {
path file('CMakeLists.txt')
}
ndkVersion "23.1.7779620"
ndkVersion "29.0.14206865"
}
buildFeatures {
@@ -74,7 +78,9 @@ android {
applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "SliceBeam_" + commit + ".apk"
def isDebug = variant.buildType.name == "debug"
def suffix = isDebug ? "_debug" : ""
outputFileName = "Santoku_" + commit + suffix + ".apk"
}
}
}
@@ -88,11 +94,62 @@ tasks.withType(JavaCompile).configureEach {
static String getGitCommitHash(File dir) {
try {
return Runtime.getRuntime().exec("git rev-parse HEAD", null, dir).inputStream.readLines().get(0).substring(0, 10)
} catch (Exception e){
println("Unable to get git commit hash:")
e.printStackTrace()
return "non-git build"
File gitDir = new File(dir, ".git")
if (!gitDir.exists()) {
return "non-git"
}
// 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"
}
}
@@ -110,6 +167,7 @@ dependencies {
implementation 'com.google.android.material:material:1.12.0'
implementation 'com.loopj.android:android-async-http:1.4.11'
implementation 'androidx.activity:activity:1.10.1'
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
}
def loadLocalProperties() {
@@ -139,10 +197,17 @@ def cmakeDir = cmakeRoot.listFiles()?.findAll { it.isDirectory() }?.sort { a, b
if (!cmakeDir) {
throw new GradleException("No CMake found under ${sdkDir}/cmake")
}
def cmakeExe = "${cmakeDir}/bin/cmake.exe"
def ninjaExe = "${cmakeDir}/bin/ninja.exe"
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"
@@ -159,40 +224,64 @@ 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 thirdPartyDir = file("${rootDir}/third_party")
def rootPath = rootPathForTasks
def thirdPartyDir = new File(rootPath, "third_party")
if (!thirdPartyDir.exists()) {
thirdPartyDir.mkdirs()
}
def repos = [
[path: "${rootDir}/third_party/Boost-for-Android", url: "https://github.com/moritz-wundke/Boost-for-Android.git"],
[path: "${rootDir}/third_party/openvdb-android", url: "https://github.com/syoyo/openvdb-android.git"],
[path: "${rootDir}/third_party/occt", url: "https://github.com/Open-Cascade-SAS/OCCT.git"]
[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"]
]
repos.each { repo ->
if (!file(repo.path).exists()) {
exec {
workingDir rootDir
commandLine "git", "clone", "--depth", "1", repo.url, repo.path
}
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(' ')}")
}
}
def vdbRoot = file("${rootDir}/third_party/openvdb-android")
if (file("${vdbRoot}/.gitmodules").exists() && !file("${vdbRoot}/tbb-aarch64/CMakeLists.txt").exists()) {
exec {
workingDir vdbRoot
commandLine "git", "submodule", "update", "--init", "--recursive"
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 scriptFile = file("${boostDir}/build-android.sh")
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
@@ -232,7 +321,7 @@ tasks.register("patchBoostForAndroid") {
}
}
def commonFile = file("${boostDir}/configs/user-config-ndk23-1_85_0-common.jam")
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
@@ -256,7 +345,7 @@ tasks.register("patchBoostForAndroid") {
}
}
def arm64File = file("${boostDir}/configs/user-config-ndk23-1_85_0-arm64-v8a.jam")
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>")) {
@@ -265,7 +354,7 @@ tasks.register("patchBoostForAndroid") {
}
}
def armv7File = file("${boostDir}/configs/user-config-ndk23-1_85_0-armeabi-v7a.jam")
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>")) {
@@ -279,10 +368,11 @@ tasks.register("patchBoostForAndroid") {
tasks.register("buildTbbArm64") {
dependsOn("ensureThirdParty")
onlyIf {
!file("${projectDir}/src/main/jniImports/oneTBB/lib/arm64-v8a/libtbb.a").exists() ||
!file("${projectDir}/src/main/jniImports/oneTBB/lib/arm64-v8a/libtbbmalloc.a").exists() ||
!file("${projectDir}/src/main/jniLibs/arm64-v8a/libtbb.so").exists() ||
!file("${projectDir}/src/main/jniLibs/arm64-v8a/libtbbmalloc.so").exists()
forceNativeRebuild ||
!new File(tbbArm64LibA).exists() ||
!new File(tbbArm64MallocA).exists() ||
!new File(tbbArm64So).exists() ||
!new File(tbbArm64MallocSo).exists()
}
doLast {
exec {
@@ -290,11 +380,13 @@ tasks.register("buildTbbArm64") {
"-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"
}
@@ -307,10 +399,11 @@ tasks.register("buildTbbArm64") {
tasks.register("buildTbbArmv7") {
dependsOn("ensureThirdParty")
onlyIf {
!file("${projectDir}/src/main/jniImports/oneTBB/lib/armeabi-v7a/libtbb.a").exists() ||
!file("${projectDir}/src/main/jniImports/oneTBB/lib/armeabi-v7a/libtbbmalloc.a").exists() ||
!file("${projectDir}/src/main/jniLibs/armeabi-v7a/libtbb.so").exists() ||
!file("${projectDir}/src/main/jniLibs/armeabi-v7a/libtbbmalloc.so").exists()
forceNativeRebuild ||
!new File(tbbArmv7LibA).exists() ||
!new File(tbbArmv7MallocA).exists() ||
!new File(tbbArmv7So).exists() ||
!new File(tbbArmv7MallocSo).exists()
}
doLast {
exec {
@@ -318,11 +411,13 @@ tasks.register("buildTbbArmv7") {
"-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"
}
@@ -335,10 +430,11 @@ tasks.register("buildTbbArmv7") {
tasks.register("copyTbbArm64", Copy) {
dependsOn("buildTbbArm64")
onlyIf {
!file("${projectDir}/src/main/jniImports/oneTBB/lib/arm64-v8a/libtbb.a").exists() ||
!file("${projectDir}/src/main/jniImports/oneTBB/lib/arm64-v8a/libtbbmalloc.a").exists() ||
!file("${projectDir}/src/main/jniLibs/arm64-v8a/libtbb.so").exists() ||
!file("${projectDir}/src/main/jniLibs/arm64-v8a/libtbbmalloc.so").exists()
forceNativeRebuild ||
!new File(tbbArm64LibA).exists() ||
!new File(tbbArm64MallocA).exists() ||
!new File(tbbArm64So).exists() ||
!new File(tbbArm64MallocSo).exists()
}
from("${tbbInstallArm64}/lib") {
include "libtbb_static.a"
@@ -373,10 +469,11 @@ tasks.register("copyTbbArm64", Copy) {
tasks.register("copyTbbArmv7", Copy) {
dependsOn("buildTbbArmv7")
onlyIf {
!file("${projectDir}/src/main/jniImports/oneTBB/lib/armeabi-v7a/libtbb.a").exists() ||
!file("${projectDir}/src/main/jniImports/oneTBB/lib/armeabi-v7a/libtbbmalloc.a").exists() ||
!file("${projectDir}/src/main/jniLibs/armeabi-v7a/libtbb.so").exists() ||
!file("${projectDir}/src/main/jniLibs/armeabi-v7a/libtbbmalloc.so").exists()
forceNativeRebuild ||
!new File(tbbArmv7LibA).exists() ||
!new File(tbbArmv7MallocA).exists() ||
!new File(tbbArmv7So).exists() ||
!new File(tbbArmv7MallocSo).exists()
}
from("${tbbInstallArmv7}/lib") {
include "libtbb_static.a"
@@ -410,7 +507,7 @@ tasks.register("copyTbbArmv7", Copy) {
tasks.register("copyTbbHeaders") {
dependsOn("buildTbbArm64")
onlyIf { !file("${projectDir}/src/main/jniImports/oneTBB/include/tbb/tbb.h").exists() }
onlyIf { !new File(tbbHeaderOut).exists() }
doLast {
copy {
from "${tbbInstallArm64}/include/tbb"
@@ -425,39 +522,59 @@ tasks.register("copyTbbHeaders") {
tasks.register("buildBoostArm64") {
dependsOn("patchBoostForAndroid")
onlyIf { !file("${projectDir}/src/main/jniImports/boost/lib/arm64-v8a/lib/libboost_atomic-clang-mt-a64-1_85.a").exists() }
onlyIf { !new File(boostArm64OutLib).exists() }
doLast {
if (!file(wslExe).exists()) {
throw new GradleException("WSL is required to build Boost. Install WSL or provide prebuilt Boost libs.")
}
def ndkWsl = toWslPath(ndkDir)
def boostWsl = toWslPath(boostDir)
def binWsl = toWslPath("${ndkDir}/toolchains/llvm/prebuilt/windows-x86_64/bin")
exec {
commandLine wslExe, "bash", "-lc", "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"
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 { !file("${projectDir}/src/main/jniImports/boost/lib/armeabi-v7a/lib/libboost_atomic-clang-mt-a32-1_85.a").exists() }
onlyIf { !new File(boostArmv7OutLib).exists() }
doLast {
if (!file(wslExe).exists()) {
throw new GradleException("WSL is required to build Boost. Install WSL or provide prebuilt Boost libs.")
}
def ndkWsl = toWslPath(ndkDir)
def boostWsl = toWslPath(boostDir)
def binWsl = toWslPath("${ndkDir}/toolchains/llvm/prebuilt/windows-x86_64/bin")
exec {
commandLine wslExe, "bash", "-lc", "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"
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 { !file("${projectDir}/src/main/jniImports/boost/lib/arm64-v8a/lib/libboost_atomic-clang-mt-a64-1_85.a").exists() }
onlyIf { !new File(boostArm64OutLib).exists() }
from("${boostOutArm64}/lib") {
include "libboost_*.a"
}
@@ -466,7 +583,7 @@ tasks.register("copyBoostArm64", Copy) {
tasks.register("copyBoostArmv7", Copy) {
dependsOn("buildBoostArmv7")
onlyIf { !file("${projectDir}/src/main/jniImports/boost/lib/armeabi-v7a/lib/libboost_atomic-clang-mt-a32-1_85.a").exists() }
onlyIf { !new File(boostArmv7OutLib).exists() }
from("${boostOutArmv7}/lib") {
include "libboost_*.a"
}
@@ -475,72 +592,76 @@ tasks.register("copyBoostArmv7", Copy) {
tasks.register("copyBoostHeaders", Copy) {
dependsOn("ensureThirdParty")
onlyIf { !file("${projectDir}/src/main/jniImports/boost/include/boost/variant.hpp").exists() }
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 { !file("${projectDir}/src/main/occt/jniLibs/arm64-v8a/libTKDESTEP.so").exists() }
onlyIf { forceNativeRebuild || !new File(occtArm64So).exists() }
doLast {
exec {
commandLine 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",
"-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}"
}
exec {
commandLine cmakeExe, "--build", occtBuildArm64, "--target", "install", "--config", "Release", "--", "-j2"
}
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 { !file("${projectDir}/src/main/occt/jniLibs/armeabi-v7a/libTKDESTEP.so").exists() }
onlyIf { forceNativeRebuild || !new File(occtArmv7So).exists() }
doLast {
exec {
commandLine 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",
"-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}"
}
exec {
commandLine cmakeExe, "--build", occtBuildArmv7, "--target", "install", "--config", "Release", "--", "-j2"
}
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 { !file("${projectDir}/src/main/occt/jniLibs/arm64-v8a/libTKDESTEP.so").exists() }
onlyIf { forceNativeRebuild || !new File(occtArm64So).exists() }
from("${occtDistArm64}/libs/arm64-v8a") {
include "*.so"
}
@@ -549,7 +670,7 @@ tasks.register("copyOcctArm64", Copy) {
tasks.register("copyOcctArmv7", Copy) {
dependsOn("buildOcctArmv7")
onlyIf { !file("${projectDir}/src/main/occt/jniLibs/armeabi-v7a/libTKDESTEP.so").exists() }
onlyIf { forceNativeRebuild || !new File(occtArmv7So).exists() }
from("${occtDistArmv7}/libs/armeabi-v7a") {
include "*.so"
}
@@ -558,14 +679,14 @@ tasks.register("copyOcctArmv7", Copy) {
tasks.register("copyOcctHeadersArm64", Copy) {
dependsOn("buildOcctArm64")
onlyIf { !file("${projectDir}/src/main/occt/include/arm64-v8a/STEPCAFControl_Reader.hxx").exists() }
onlyIf { !new File(occtHeaderArm64Out).exists() }
from("${occtDistArm64}/inc")
into("${projectDir}/src/main/occt/include/arm64-v8a")
}
tasks.register("copyOcctHeadersArmv7", Copy) {
dependsOn("buildOcctArmv7")
onlyIf { !file("${projectDir}/src/main/occt/include/armeabi-v7a/STEPCAFControl_Reader.hxx").exists() }
onlyIf { !new File(occtHeaderArmv7Out).exists() }
from("${occtDistArmv7}/inc")
into("${projectDir}/src/main/occt/include/armeabi-v7a")
}
+5 -1
View File
@@ -18,4 +18,8 @@
# If you keep the line number information, uncomment this to
# 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.** { *; }
+24 -9
View File
@@ -8,9 +8,6 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<queries>
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
<!-- WebView fails sometime if not queried, idk why -->
<package android:name="com.google.android.webview"/>
</queries>
@@ -19,11 +16,11 @@
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/icon"
android:icon="@mipmap/ic_launcher"
android:label="@string/AppName"
android:supportsRtl="true"
android:theme="@style/Theme.SliceBeam"
android:name=".SliceBeam"
android:theme="@style/Theme.Santoku"
android:name=".Santoku"
android:usesCleartextTraffic="true"
android:largeHeap="true"
android:isGame="false"
@@ -35,7 +32,7 @@
android:windowSoftInputMode="adjustPan"
android:configChanges="uiMode"
android:exported="true"
android:theme="@style/Theme.SliceBeam">
android:theme="@style/Theme.Santoku">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -46,6 +43,13 @@
<category android:name="android.intent.category.DEFAULT" />
<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="content" />
<data android:mimeType="*/*" />
@@ -55,7 +59,7 @@
<activity android:name=".SetupActivity" android:exported="false"/>
<activity android:name=".SafeStartActivity" android:exported="false"/>
<provider
android:authorities="ru.ytkab0bp.slicebeam.provider"
android:authorities="${applicationId}.provider"
android:name="androidx.core.content.FileProvider"
android:grantUriPermissions="true"
android:exported="false">
@@ -64,6 +68,17 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</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>
</manifest>
</manifest>
+3 -3
View File
@@ -47,13 +47,13 @@ var dark = $['is_dark_theme'];
document.getElementsByClassName('md-option')[dark ? 1 : 0].click();
beginSaveFile = function(filename) {
SliceBeam.beginDownload(filename);
Santoku.beginDownload(filename);
}
writeToFile = function(data) {
SliceBeam.writeData(btoa(unescape(encodeURIComponent(data))));
Santoku.writeData(btoa(unescape(encodeURIComponent(data))));
}
finishFile = function() {
SliceBeam.finishDownload();
Santoku.finishDownload();
}
+3 -3
View File
@@ -47,7 +47,7 @@ var dark = $['is_dark_theme'];
document.getElementsByClassName('md-option')[dark ? 1 : 0].click();
saveTextAsFile = function(filename, text) {
SliceBeam.beginDownload(filename);
SliceBeam.writeData(btoa(unescape(encodeURIComponent(text))));
SliceBeam.finishDownload();
Santoku.beginDownload(filename);
Santoku.writeData(btoa(unescape(encodeURIComponent(text))));
Santoku.finishDownload();
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

@@ -1,20 +1,14 @@
package ru.ytkab0bp.slicebeam;
package com.dark98.santoku;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Process;
import android.provider.MediaStore;
import android.util.Base64;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
@@ -32,7 +26,6 @@ import org.json.JSONArray;
import org.json.JSONObject;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -48,36 +41,32 @@ import java.util.Objects;
import java.util.UUID;
import java.util.zip.ZipFile;
import ru.ytkab0bp.sapil.APICallback;
import ru.ytkab0bp.slicebeam.cloud.CloudAPI;
import ru.ytkab0bp.slicebeam.cloud.CloudController;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.components.ChangeLogBottomSheet;
import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.events.NeedDismissAIGeneratorMenu;
import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import ru.ytkab0bp.slicebeam.navigation.Fragment;
import ru.ytkab0bp.slicebeam.navigation.MobileNavigationDelegate;
import ru.ytkab0bp.slicebeam.navigation.NavigationDelegate;
import ru.ytkab0bp.slicebeam.slic3r.Model;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rRuntimeError;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.IOUtils;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
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,
REQUEST_CODE_AI_GENERATOR_TAKE_PHOTO = 6, REQUEST_CODE_AI_GENERATOR_CHOOSE_PHOTO = 7;
REQUEST_CODE_EXPORT_3MF = 5;
private static MainActivity activeInstance;
@@ -85,9 +74,6 @@ public class MainActivity extends AppCompatActivity {
public static List<ConfigObject> EXPORTING_FILAMENTS;
public static List<ConfigObject> EXPORTING_PRINTERS;
public static boolean IS_GENERATING_AI_MODEL;
public static File aiTempFile;
private static SparseArray<NavigationDelegate> liveDelegate = new SparseArray<>();
private static int lastId;
@@ -105,7 +91,7 @@ public class MainActivity extends AppCompatActivity {
finish();
return;
}
if (SliceBeam.CONFIG == null) {
if (Santoku.CONFIG == null) {
Prefs.setLastCommit();
startActivity(new Intent(this, SetupActivity.class));
finish();
@@ -154,8 +140,13 @@ public class MainActivity extends AppCompatActivity {
setContentView(v);
if (getIntent() != null && getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_VIEW)) {
loadFile(getIntent().getData());
setIntent(null);
Uri data = getIntent().getData();
if (data != null && "santoku".equalsIgnoreCase(data.getScheme())) {
setIntent(null);
} else {
loadFile(data);
setIntent(null);
}
}
DisplayMetrics dm = getResources().getDisplayMetrics();
@@ -194,9 +185,8 @@ public class MainActivity extends AppCompatActivity {
}
}
if (!Objects.equals(Prefs.getLastCommit(), BuildConfig.COMMIT) && SliceBeam.hasUpdateInfo) {
if (!Objects.equals(Prefs.getLastCommit(), BuildConfig.COMMIT) && Santoku.hasUpdateInfo) {
Prefs.setLastCommit();
BeamServerData.load();
new ChangeLogBottomSheet(this).show();
}
}
@@ -209,7 +199,11 @@ public class MainActivity extends AppCompatActivity {
@Override
protected void onNewIntent(@NonNull Intent intent) {
super.onNewIntent(intent);
loadFile(intent.getData());
Uri data = intent.getData();
if (data != null && "santoku".equalsIgnoreCase(data.getScheme())) {
return;
}
loadFile(data);
setIntent(null);
}
@@ -226,8 +220,8 @@ public class MainActivity extends AppCompatActivity {
OutputStream out = getContentResolver().openOutputStream(data.getData());
Model model = ((BedFragment) fragment).getGlView().getRenderer().getModel();
File tempFile = File.createTempFile("temp_project", ".3mf");
SliceBeam.genCurrentConfig();
File cfg = SliceBeam.getCurrentConfigFile();
Santoku.genCurrentConfig();
File cfg = Santoku.getCurrentConfigFile();
model.export3mf(cfg.getAbsolutePath(), tempFile.getAbsolutePath());
InputStream in = new FileInputStream(tempFile);
@@ -240,7 +234,7 @@ public class MainActivity extends AppCompatActivity {
out.close();
tempFile.delete();
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileExport3mfSuccess));
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileExport3mfSuccess));
} catch (IOException | Slic3rRuntimeError e) {
throw new RuntimeException(e);
}
@@ -273,21 +267,21 @@ public class MainActivity extends AppCompatActivity {
EXPORTING_PRINTERS = null;
w.presets = new ConfigObject();
if (w.findPrint(SliceBeam.CONFIG.presets.get("print")) != null) {
w.presets.put("print", SliceBeam.CONFIG.presets.get("print"));
if (w.findPrint(Santoku.CONFIG.presets.get("print")) != null) {
w.presets.put("print", Santoku.CONFIG.presets.get("print"));
}
if (w.findFilament(SliceBeam.CONFIG.presets.get("filament")) != null) {
w.presets.put("filament", SliceBeam.CONFIG.presets.get("filament"));
if (w.findFilament(Santoku.CONFIG.presets.get("filament")) != null) {
w.presets.put("filament", Santoku.CONFIG.presets.get("filament"));
}
if (w.findPrinter(SliceBeam.CONFIG.presets.get("printer")) != null) {
w.presets.put("printer", SliceBeam.CONFIG.presets.get("printer"));
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();
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileExportProfilesSuccess));
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileExportProfilesSuccess));
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -327,31 +321,14 @@ public class MainActivity extends AppCompatActivity {
.setPositiveButton(android.R.string.ok, null)
.show();
}
} else if (requestCode == REQUEST_CODE_AI_GENERATOR_TAKE_PHOTO) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissAIGeneratorMenu());
Bitmap bm = BitmapFactory.decodeFile(aiTempFile.getAbsolutePath());
generateAiModel(bm);
aiTempFile.delete();
aiTempFile = null;
} else if (requestCode == REQUEST_CODE_AI_GENERATOR_CHOOSE_PHOTO) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissAIGeneratorMenu());
try {
InputStream in = getContentResolver().openInputStream(data.getData());
Bitmap bm = BitmapFactory.decodeStream(in);
generateAiModel(bm);
} catch (Exception e) {
Log.e("ai_generator", "Failed to write to downloads", e);
}
}
}
}
private void loadConvertedProfile(Uri uri) {
String tag = UUID.randomUUID().toString();
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.OrcaConversionPleaseWait).tag(tag));
File f = new File(SliceBeam.getModelCacheDir(), "orca_conv.zip");
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);
@@ -369,7 +346,7 @@ public class MainActivity extends AppCompatActivity {
if (!bundle.get("bundle_type").equals("printer config bundle")) {
zf.close();
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileImportProfilesFailed)
.setMessage(R.string.OrcaConversionNotAConfigBundle)
@@ -470,17 +447,17 @@ public class MainActivity extends AppCompatActivity {
zf.close();
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
loadIniForImport(new ByteArrayInputStream(w.serialize().getBytes(StandardCharsets.UTF_8)));
} catch (IOUtils.MissingProfileException ep) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
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) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileImportProfilesFailed)
.setMessage(e.toString())
@@ -490,110 +467,6 @@ public class MainActivity extends AppCompatActivity {
});
}
private void generateAiModel(Bitmap bm) {
IS_GENERATING_AI_MODEL = true;
String uploadTag = UUID.randomUUID().toString();
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileAIGeneratorUploading).tag(uploadTag));
IOUtils.IO_POOL.submit(()->{
Bitmap scaled;
if (bm.getWidth() > 1024 || bm.getHeight() > 1024) {
if (bm.getWidth() > bm.getHeight()) {
int w = 1024;
int h = (int) ((float) w * bm.getHeight() / bm.getWidth());
scaled = Bitmap.createScaledBitmap(bm, w, h, true);
} else {
int h = 1024;
int w = (int) ((float) h * bm.getWidth() / bm.getHeight());
scaled = Bitmap.createScaledBitmap(bm, w, h, true);
}
bm.recycle();
} else {
scaled = bm;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
scaled.compress(Bitmap.CompressFormat.PNG, 100, out);
scaled.recycle();
String processTag = UUID.randomUUID().toString();
CloudAPI.INSTANCE.modelsGenerate(Base64.encodeToString(out.toByteArray(), Base64.NO_WRAP), "image/png", new APICallback<InputStream>() {
@Override
public void onResponse(InputStream in) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(processTag));
String downloadTag = UUID.randomUUID().toString();
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileAIGeneratorDownloading).tag(downloadTag));
String fileName = "generated_" + UUID.randomUUID() + ".stl";
File f = new File(SliceBeam.getModelCacheDir(), fileName);
try {
FileOutputStream fos = new FileOutputStream(f);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "application/vnd.ms-pki.stl");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
Uri uri = getContentResolver().insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues);
if (uri != null) {
try {
OutputStream out = getContentResolver().openOutputStream(uri);
byte[] buf = new byte[10240];
int c;
while ((c = in.read(buf)) != -1) {
out.write(buf, 0, c);
fos.write(buf, 0, c);
}
out.close();
} catch (IOException e) {
Log.e("ai_generator", "Failed to write to downloads", e);
}
}
} else {
File downloadsDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
File file = new File(downloadsDirectory, fileName);
try {
FileOutputStream out = new FileOutputStream(file);
byte[] buf = new byte[10240];
int c;
while ((c = in.read(buf)) != -1) {
out.write(buf, 0, c);
fos.write(buf, 0, c);
}
out.close();
} catch (IOException e) {
Log.e("ai_generator", "Failed to write to downloads", e);
}
}
fos.close();
} catch (Exception e) {
Log.e("ai_generator", "Failed to write to downloads", e);
}
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(downloadTag));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileAIGeneratorSavedAs, fileName));
loadFile(f, true);
CloudController.checkGeneratorRemaining();
IS_GENERATING_AI_MODEL = false;
}
@Override
public void onException(Exception e) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(processTag));
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(MainActivity.this)
.setTitle(R.string.MenuFileAIGeneratorError)
.setMessage(e.toString())
.setPositiveButton(android.R.string.ok, null)
.show());
IS_GENERATING_AI_MODEL = false;
}
});
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(uploadTag));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileAIGeneratorProcessing).tag(processTag));
});
}
private void loadIniForImport(InputStream in) {
IOUtils.IO_POOL.submit(()->{
try {
@@ -633,20 +506,20 @@ public class MainActivity extends AppCompatActivity {
Runnable finish = () -> {
for (int i = 0; i < enabledPrints.length; i++) {
if (enabledPrints[i]) {
SliceBeam.CONFIG.importPrint(w.printConfigs.get(i));
Santoku.CONFIG.importPrint(w.printConfigs.get(i));
}
}
for (int i = 0; i < enabledFilaments.length; i++) {
if (enabledFilaments[i]) {
SliceBeam.CONFIG.importFilament(w.filamentConfigs.get(i));
Santoku.CONFIG.importFilament(w.filamentConfigs.get(i));
}
}
for (int i = 0; i < enabledPrinters.length; i++) {
if (enabledPrinters[i]) {
SliceBeam.CONFIG.importPrinter(w.printerConfigs.get(i));
Santoku.CONFIG.importPrinter(w.printerConfigs.get(i));
}
}
SliceBeam.saveConfig();
Santoku.saveConfig();
};
Runnable printersRun = () -> {
if (printers.length == 0) {
@@ -698,7 +571,7 @@ public class MainActivity extends AppCompatActivity {
private void loadFile(File f, boolean autoorient) {
String tag = UUID.randomUUID().toString();
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileOpenFileLoading).tag(tag));
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) {
@@ -720,7 +593,7 @@ public class MainActivity extends AppCompatActivity {
}
if (!gcode) {
SliceBeam.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
Santoku.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
}
int i = model.getObjectsCount() - 1;
if (autoorient) {
@@ -728,10 +601,10 @@ public class MainActivity extends AppCompatActivity {
fragment.getGlView().getRenderer().invalidateGlModel(i);
fragment.getGlView().requestRender();
}
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileOpenFileLoaded));
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileOpenFileLoaded));
if (model.isBigObject(i)) {
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.WARNING, R.string.MenuFileOpenFileBigObject));
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.WARNING, R.string.MenuFileOpenFileBigObject));
}
}
});
@@ -739,7 +612,7 @@ public class MainActivity extends AppCompatActivity {
Log.e("MainActivity", "Failed to load model", e);
f.delete();
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileOpenFileFailed)
.setMessage(e.toString())
@@ -780,7 +653,7 @@ public class MainActivity extends AppCompatActivity {
return;
}
File f = new File(SliceBeam.getModelCacheDir(), fileName);
File f = new File(Santoku.getModelCacheDir(), fileName);
// TODO: Check if file already exists
IOUtils.IO_POOL.submit(()->{
try {
@@ -879,6 +752,12 @@ public class MainActivity extends AppCompatActivity {
delegate.onPause();
}
@Override
protected void onStop() {
super.onStop();
Santoku.saveConfig();
}
@Override
protected void onDestroy() {
super.onDestroy();
@@ -889,4 +768,4 @@ public class MainActivity extends AppCompatActivity {
delegate.onDestroy();
}
}
}
}
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam;
package com.dark98.santoku;
import android.app.Activity;
import android.content.Intent;
@@ -17,10 +17,10 @@ import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.BeamButton;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.Prefs;
import com.dark98.santoku.utils.ViewUtils;
import com.dark98.santoku.view.BeamButton;
public class SafeStartActivity extends Activity {
@Override
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam;
package com.dark98.santoku;
import android.annotation.SuppressLint;
import android.app.Application;
@@ -17,31 +17,29 @@ import java.util.Arrays;
import java.util.Map;
import ru.ytkab0bp.eventbus.EventBus;
import ru.ytkab0bp.slicebeam.boot.AppBoot;
import ru.ytkab0bp.slicebeam.boot.BeamServerDataTask;
import ru.ytkab0bp.slicebeam.boot.CheckUpdateJsonTask;
import ru.ytkab0bp.slicebeam.boot.ClearModelCacheTask;
import ru.ytkab0bp.slicebeam.boot.CloudInitTask;
import ru.ytkab0bp.slicebeam.boot.EventBusTask;
import ru.ytkab0bp.slicebeam.boot.LoadSlic3rConfigTask;
import ru.ytkab0bp.slicebeam.boot.PrefsTask;
import ru.ytkab0bp.slicebeam.boot.PrintConfigWarmupTask;
import ru.ytkab0bp.slicebeam.boot.TrueTimeTask;
import ru.ytkab0bp.slicebeam.boot.VibrationUtilsTask;
import ru.ytkab0bp.slicebeam.cloud.CloudController;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.slic3r.ConfigOptionDef;
import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
import ru.ytkab0bp.slicebeam.utils.Prefs;
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 SliceBeam extends Application {
public static SliceBeam INSTANCE;
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 BeamServerData SERVER_DATA;
public static boolean hasUpdateInfo;
@SuppressLint("ApplySharedPref")
@@ -54,7 +52,6 @@ public class SliceBeam extends Application {
new PrefsTask(),
new VibrationUtilsTask(),
new TrueTimeTask(),
new BeamServerDataTask(),
new PrintConfigWarmupTask(),
new CheckUpdateJsonTask(),
new ClearModelCacheTask(),
@@ -75,13 +72,40 @@ public class SliceBeam extends Application {
}
public static void saveConfig() {
SliceBeam.CONFIG_UID++;
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 {
FileOutputStream fos = new FileOutputStream(f);
fos.write(CONFIG.serialize().getBytes(StandardCharsets.UTF_8));
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);
@@ -101,11 +125,11 @@ public class SliceBeam extends Application {
public static ConfigObject buildCurrentConfigObject() {
ConfigObject singleObject = new ConfigObject();
ConfigObject printerConfig = SliceBeam.CONFIG.findPrinter(SliceBeam.CONFIG.presets.get("printer"));
ConfigObject printerConfig = Santoku.CONFIG.findPrinter(Santoku.CONFIG.presets.get("printer"));
if (printerConfig != null) {
singleObject.values.putAll(printerConfig.values);
}
ConfigObject printConfig = SliceBeam.CONFIG.findPrint(SliceBeam.CONFIG.presets.get("print"));
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())) {
@@ -114,7 +138,7 @@ public class SliceBeam extends Application {
}
}
// TODO: MMU. Detect by printerConfig#getExtruderCount()
ConfigObject filamentConfig = SliceBeam.CONFIG.findFilament(SliceBeam.CONFIG.presets.get("filament"));
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())) {
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam;
package com.dark98.santoku;
import static android.opengl.GLES30.GL_COLOR_BUFFER_BIT;
import static android.opengl.GLES30.GL_DEPTH_BUFFER_BIT;
@@ -24,6 +24,7 @@ import android.net.Uri;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.os.Bundle;
import android.text.InputType;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.Log;
@@ -35,6 +36,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.EditText;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -60,6 +63,7 @@ import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import ru.ytkab0bp.sapil.APICallback;
import org.json.JSONArray;
import org.json.JSONException;
@@ -89,39 +93,35 @@ import javax.microedition.khronos.opengles.GL10;
import cz.msebera.android.httpclient.Header;
import ru.ytkab0bp.eventbus.EventHandler;
import ru.ytkab0bp.slicebeam.cloud.CloudAPI;
import ru.ytkab0bp.slicebeam.cloud.CloudController;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.components.CloudManageBottomSheet;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.events.BeamServerDataUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.CloudFeaturesUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.CloudLoginStateUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.CloudSyncFinishedEvent;
import ru.ytkab0bp.slicebeam.recycler.BigHeaderItem;
import ru.ytkab0bp.slicebeam.recycler.PreferenceItem;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerAdapter;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import ru.ytkab0bp.slicebeam.recycler.TextHintRecyclerItem;
import ru.ytkab0bp.slicebeam.slic3r.GLModel;
import ru.ytkab0bp.slicebeam.slic3r.GLShaderProgram;
import ru.ytkab0bp.slicebeam.slic3r.GLShadersManager;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rUtils;
import ru.ytkab0bp.slicebeam.theme.BeamTheme;
import ru.ytkab0bp.slicebeam.theme.IThemeView;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.BeamSwitch;
import ru.ytkab0bp.slicebeam.view.BoostySubsView;
import ru.ytkab0bp.slicebeam.view.FadeRecyclerView;
import ru.ytkab0bp.slicebeam.view.MiniColorView;
import ru.ytkab0bp.slicebeam.view.TextColorImageSpan;
import com.dark98.santoku.cloud.CloudAPI;
import com.dark98.santoku.cloud.CloudController;
import com.dark98.santoku.components.BeamAlertDialogBuilder;
import com.dark98.santoku.components.CloudManageBottomSheet;
import com.dark98.santoku.config.ConfigObject;
import com.dark98.santoku.events.CloudLoginStateUpdatedEvent;
import com.dark98.santoku.events.CloudSyncFinishedEvent;
import com.dark98.santoku.recycler.BigHeaderItem;
import com.dark98.santoku.recycler.PreferenceItem;
import com.dark98.santoku.recycler.SimpleRecyclerAdapter;
import com.dark98.santoku.recycler.SimpleRecyclerItem;
import com.dark98.santoku.recycler.TextHintRecyclerItem;
import com.dark98.santoku.slic3r.GLModel;
import com.dark98.santoku.slic3r.GLShaderProgram;
import com.dark98.santoku.slic3r.GLShadersManager;
import com.dark98.santoku.slic3r.Slic3rConfigWrapper;
import com.dark98.santoku.slic3r.Slic3rUtils;
import com.dark98.santoku.theme.BeamTheme;
import com.dark98.santoku.theme.IThemeView;
import com.dark98.santoku.theme.ThemesRepo;
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 final static String EXTRA_ABOUT = "about";
public final static String EXTRA_BOOSTY_ONLY = "boosty_only";
public final static String EXTRA_CLOUD_PROFILE = "cloud_profile";
public final static String EXTRA_CLOUD_IMPORT_FROM_SETUP = "cloud_import_from_setup";
@@ -129,12 +129,11 @@ public class SetupActivity extends AppCompatActivity {
private final static List<String> REPOS_URLS = Arrays.asList(
"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 PROFILES_INDEX = 2;
private static int BOOSTY_INDEX = 3;
private final static int TYPE_PRINTER = 0, TYPE_PRINT_CONFIG = 1, TYPE_FILAMENT = 2;
@@ -148,7 +147,6 @@ public class SetupActivity extends AppCompatActivity {
private int titleY;
private float backgroundProgress;
private float boostyProgress;
private SpringAnimation fakeScroller;
@@ -166,14 +164,13 @@ public class SetupActivity extends AppCompatActivity {
private Map<ProfilesRepo, List<Slic3rConfigWrapper>> profilesMap = new HashMap<>();
private boolean isProfilesLoaded;
private boolean about;
private boolean boostyOnly;
private boolean cloudProfile;
private boolean cloudImport;
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.setLoggingEnabled(false);
}
@@ -182,14 +179,13 @@ public class SetupActivity extends AppCompatActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
SliceBeam.EVENT_BUS.registerListener(this);
Santoku.EVENT_BUS.registerListener(this);
about = getIntent().getBooleanExtra(EXTRA_ABOUT, false);
boostyOnly = getIntent().getBooleanExtra(EXTRA_BOOSTY_ONLY, false);
cloudProfile = getIntent().getBooleanExtra(EXTRA_CLOUD_PROFILE, false);
cloudImport = getIntent().getBooleanExtra(EXTRA_CLOUD_IMPORT_FROM_SETUP, false);
if (!about && !boostyOnly && !cloudProfile) {
if (!about && !cloudProfile) {
new BeamAlertDialogBuilder(this)
.setTitle(R.string.IntroEarlyAccess)
.setMessage(R.string.IntroEarlyAccessMessage)
@@ -197,7 +193,7 @@ public class SetupActivity extends AppCompatActivity {
.show();
}
if (boostyOnly || cloudProfile) {
if (cloudProfile) {
backgroundProgress = 1f;
}
@@ -205,7 +201,7 @@ public class SetupActivity extends AppCompatActivity {
adapter = new SimpleRecyclerAdapter() {
@Override
public int getItemCount() {
return about || boostyOnly || cloudProfile ? 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();
@@ -236,25 +232,12 @@ public class SetupActivity extends AppCompatActivity {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (position == 0 && !boostyOnly && !cloudProfile) {
if (position == 0 && !cloudProfile) {
backgroundProgress = positionOffset;
} else {
backgroundProgress = 1f;
}
if (boostyOnly) {
boostyProgress = 1f;
} else 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 (!isReposLoaded && !isLoading) {
loadRepos(true);
@@ -334,7 +317,7 @@ public class SetupActivity extends AppCompatActivity {
Log.e(TAG, "Failed to load vendor file " + iniUrl, error);
isLoading = false;
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);
pager.setUserInputEnabled(true);
});
@@ -361,7 +344,7 @@ public class SetupActivity extends AppCompatActivity {
isLoading = false;
Log.e(TAG, "Failed to load repo", error);
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);
pager.setUserInputEnabled(true);
});
@@ -434,17 +417,13 @@ public class SetupActivity extends AppCompatActivity {
shader.startUsing();
int topColor = ThemesRepo.getColor(android.R.attr.colorAccent);
int bottomColor = ThemesRepo.getColor(android.R.attr.windowBackground);
if (boostyProgress != 0f) {
topColor = ColorUtils.blendARGB(bottomColor, ThemesRepo.getColor(R.attr.boostyColorTop), boostyProgress);
bottomColor = ColorUtils.blendARGB(bottomColor, ThemesRepo.getColor(R.attr.boostyColorBottom), boostyProgress);
}
if (cloudProfile) {
bottomColor = ColorUtils.blendARGB(bottomColor, topColor, 0.5f);
}
shader.setUniformColor("top_color", topColor);
shader.setUniformColor("bottom_color", bottomColor);
shader.setUniform("progress", backgroundProgress - (cloudProfile ? 1.4f : 0) - (boostyProgress != 0 ? 1.2f : 0));
shader.setUniform("progress", backgroundProgress - (cloudProfile ? 1.4f : 0));
shader.setUniform("time", time);
backgroundModel.render();
shader.stopUsing();
@@ -489,7 +468,7 @@ public class SetupActivity extends AppCompatActivity {
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 - boostyProgress);
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));
}
@@ -498,17 +477,7 @@ public class SetupActivity extends AppCompatActivity {
protected void onDestroy() {
super.onDestroy();
SliceBeam.EVENT_BUS.unregisterListener(this);
}
@EventHandler(runOnMainThread = true)
public void onDataUpdated(BeamServerDataUpdatedEvent e) {
if (!about && !boostyOnly && !cloudProfile) {
boolean wasBoosty = BOOSTY_INDEX != -1;
if (wasBoosty != BeamServerData.isBoostyAvailable()) {
setItems();
}
}
Santoku.EVENT_BUS.unregisterListener(this);
}
@SuppressLint("NotifyDataSetChanged")
@@ -517,7 +486,7 @@ public class SetupActivity extends AppCompatActivity {
if (cloudProfile && Prefs.getCloudAPIToken() != null && cloudImport) {
finish();
}
if (!about && !boostyOnly && !cloudProfile) {
if (!about && !cloudProfile) {
if (Prefs.getCloudAPIToken() != null) {
limitRepoFragmentCount = false;
limitProfileFragmentCount = false;
@@ -531,13 +500,8 @@ public class SetupActivity extends AppCompatActivity {
public void onCloudAuthStateUpdated(CloudLoginStateUpdatedEvent e) {
if (cloudProfile) {
cloudItem.bindLoginButton(true);
cloudItem.bindFeatures();
}
}
@EventHandler(runOnMainThread = true)
public void onCloudFeaturesUpdated(CloudFeaturesUpdatedEvent e) {
if (!about && !boostyOnly && !cloudProfile) {
if (!about && !cloudProfile && reposItem != null) {
reposItem.onCloudInfoUpdated();
}
}
@@ -545,8 +509,6 @@ public class SetupActivity extends AppCompatActivity {
private void setItems() {
if (cloudProfile){
adapter.setItems(Collections.singletonList(cloudItem = new CloudProfileItem()));
} else if (boostyOnly) {
adapter.setItems(Collections.singletonList(new BoostyItem()));
} else if (about) {
adapter.setItems(Collections.singletonList(new AboutItem()));
} else {
@@ -555,13 +517,6 @@ public class SetupActivity extends AppCompatActivity {
reposItem = new ReposItem(),
profilesItem = new ProfilesItem()));
if (BeamServerData.isBoostyAvailable()) {
BOOSTY_INDEX = items.size();
items.add(new BoostyItem());
} else {
BOOSTY_INDEX = -1;
}
items.add(new FinishItem());
adapter.setItems(items);
}
@@ -634,7 +589,7 @@ public class SetupActivity extends AppCompatActivity {
Log.e(TAG, "Failed to load repos", error);
if (fromPage) {
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);
pager.setUserInputEnabled(true);
});
@@ -685,7 +640,9 @@ public class SetupActivity extends AppCompatActivity {
private FrameLayout buttonView;
private TextView buttonText;
private ProgressBar buttonProgress;
private FadeRecyclerView recyclerView;
private TextView titleView;
private TextView signUpButton;
private boolean signUpInProgress;
@Override
public View onCreateView(Context ctx) {
@@ -693,23 +650,16 @@ public class SetupActivity extends AppCompatActivity {
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.SettingsCloudManageDescription);
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
title.setGravity(Gravity.CENTER);
title.setPadding(ViewUtils.dp(12), 0, ViewUtils.dp(12), 0);
ll.addView(title);
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();
FrameLayout fl = new FrameLayout(ctx);
recyclerView = new FadeRecyclerView(ctx);
recyclerView.setBitmapMode();
recyclerView.setAdapter(adapter = new SimpleRecyclerAdapter());
recyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
fl.addView(recyclerView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
bindFeatures();
ll.addView(fl, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
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(" ");
@@ -724,7 +674,7 @@ public class SetupActivity extends AppCompatActivity {
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("https://beam3d.ru/slicebeam_cloud_tos.html"))));
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);
@@ -744,27 +694,30 @@ public class SetupActivity extends AppCompatActivity {
buttonProgress.setIndeterminateTintList(ColorStateList.valueOf(ThemesRepo.getColor(R.attr.textColorOnAccent)));
buttonView.addView(buttonProgress, new FrameLayout.LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28), Gravity.CENTER));
bindLoginButton(false);
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 bindFeatures() {
List<SimpleRecyclerItem> items = new ArrayList<>();
if (CloudController.getUserFeatures() != null) {
for (CloudAPI.SubscriptionLevel lvl : CloudController.getUserFeatures().levels) {
items.add(new CloudSubscriptionLevel(lvl));
}
}
adapter.setItems(items);
}
private void bindLoginButton(boolean animate) {
boolean loggedIn = Prefs.getCloudAPIToken() != null;
boolean loading = !loggedIn && CloudController.isLoggingIn();
@@ -819,6 +772,7 @@ public class SetupActivity extends AppCompatActivity {
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())
@@ -833,177 +787,150 @@ public class SetupActivity extends AppCompatActivity {
CloudController.beginLogin();
}
});
}
}
private final static class CloudSubscriptionLevel extends SimpleRecyclerItem<CloudSubscriptionLevel.LevelHolderView> {
private CloudAPI.SubscriptionLevel level;
private CloudSubscriptionLevel(CloudAPI.SubscriptionLevel level) {
this.level = level;
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);
}
}
@Override
public LevelHolderView onCreateView(Context ctx) {
return new LevelHolderView(ctx);
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);
}
}
@Override
public void onBindView(LevelHolderView view) {
view.bind(this);
}
public final static class LevelHolderView extends LinearLayout implements IThemeView {
private ImageView icon;
private TextView title;
private TextView price;
private RecyclerView featuresLayout;
private SimpleRecyclerAdapter featuresAdapter;
public LevelHolderView(@NonNull Context context) {
super(context);
setOrientation(VERTICAL);
setPadding(0, ViewUtils.dp(16), 0, ViewUtils.dp(8));
LinearLayout inner = new LinearLayout(context);
inner.setOrientation(HORIZONTAL);
inner.setGravity(Gravity.CENTER_VERTICAL);
inner.setPadding(ViewUtils.dp(28), 0, ViewUtils.dp(28), 0);
addView(inner, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
bottomMargin = ViewUtils.dp(8);
}});
icon = new ImageView(context);
inner.addView(icon, new LayoutParams(ViewUtils.dp(26), ViewUtils.dp(26)));
title = new TextView(context);
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
inner.addView(title, new LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f) {{
leftMargin = ViewUtils.dp(12);
}});
price = new TextView(context);
price.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
price.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
inner.addView(price);
featuresLayout = new RecyclerView(context) {
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return false;
}
@Override
protected boolean dispatchHoverEvent(MotionEvent event) {
return false;
}
};
featuresLayout.setLayoutManager(new LinearLayoutManager(context));
featuresLayout.setAdapter(featuresAdapter = new SimpleRecyclerAdapter());
addView(featuresLayout, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
topMargin = ViewUtils.dp(3);
leftMargin = rightMargin = ViewUtils.dp(16);
bottomMargin = ViewUtils.dp(8);
}});
setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
leftMargin = rightMargin = ViewUtils.dp(12);
topMargin = ViewUtils.dp(12);
}});
onApplyTheme();
private void showSignUpDialog(Context ctx) {
if (signUpInProgress) {
return;
}
public void bind(CloudSubscriptionLevel item) {
CloudAPI.SubscriptionLevel lvl = item.level;
title.setText(lvl.title);
price.setText(lvl.price);
if (lvl.level <= 0) {
icon.setImageResource(R.drawable.zero_ruble_outline_28);
price.setText(R.string.SettingsCloudManageFree);
} else if (lvl.level == 1) {
icon.setImageResource(R.drawable.stars_outline_28);
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 {
icon.setImageResource(R.drawable.cloud_plus_outline_28);
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);
}});
List<SimpleRecyclerItem> items = new ArrayList<>();
CloudAPI.UserFeatures features = CloudController.getUserFeatures();
CloudAPI.UserInfo info = CloudController.getUserInfo();
Context ctx = getContext();
if (!BuildConfig.IS_GOOGLE_PLAY && features.earlyAccessLevel != -1 && lvl.level >= features.earlyAccessLevel) {
items.add(new PreferenceItem()
.setForceDark(true)
.setPaddings(ViewUtils.dp(8))
.setIcon(R.drawable.clock_circle_dashed_outline_24)
.setTitle(ctx.getString(R.string.SettingsCloudManageFeatureEarlyAccess))
.setSubtitle(ctx.getString(R.string.SettingsCloudManageFeatureEarlyAccessDescription)));
}
if (features.syncRequiredLevel != -1 && lvl.level >= features.syncRequiredLevel) {
items.add(new PreferenceItem()
.setForceDark(true)
.setPaddings(ViewUtils.dp(8))
.setIcon(R.drawable.sync_outline_28)
.setTitle(ctx.getString(R.string.SettingsCloudManageFeatureCloudSync))
.setSubtitle(ctx.getString(R.string.SettingsCloudManageFeatureCloudSyncDescription)));
}
if (features.aiGeneratorRequiredLevel != -1 && lvl.level >= features.aiGeneratorRequiredLevel) {
items.add(new PreferenceItem()
.setForceDark(true)
.setPaddings(ViewUtils.dp(8))
.setIcon(R.drawable.brain_outline_28)
.setTitle(ctx.getString(R.string.SettingsCloudManageFeatureAIGenerator))
.setSubtitle(ctx.getString(R.string.SettingsCloudManageFeatureAIGeneratorDescription, features.aiGeneratorModelsPerMonth)));
}
if (lvl.level > 0) {
items.add(new PreferenceItem()
.setForceDark(true)
.setPaddings(ViewUtils.dp(8))
.setIcon(R.drawable.box_heart_outline_28)
.setTitle(ctx.getString(R.string.SettingsCloudManageFeatureFreeForAll))
.setSubtitle(ctx.getString(R.string.SettingsCloudManageFeatureFreeForAllDescription)));
}
featuresAdapter.setItems(items);
featuresLayout.setVisibility(items.isEmpty() ? View.GONE : View.VISIBLE);
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));
boolean subscribed = lvl.level > 0 && info != null && lvl.level == info.currentLevel;
boolean allowSubscribe = lvl.level > 0 && (info == null || lvl.level > info.currentLevel);
if (subscribed) {
price.setText(R.string.SettingsCloudManageSubscribed);
}
price.setVisibility(allowSubscribe || subscribed ? View.VISIBLE : View.GONE);
setOnClickListener(v -> {
if (subscribed) {
v.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(lvl.manageUrl)));
} else {
new BeamAlertDialogBuilder(getContext())
.setTitle(lvl.title)
.setMessage(R.string.SettingsCloudManageLevelRedirectMessage)
.setPositiveButton(android.R.string.ok, (dialog, which) -> v.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(lvl.subscribeOrUpgradeUrl))))
.setNegativeButton(R.string.SettingsCloudManageLevelRedirectAlreadySubscribed, (dialog, which) -> v.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(features.alreadySubscribedInfoUrl))))
.show();
}
});
setClickable(allowSubscribe || subscribed);
onApplyTheme();
}
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 onApplyTheme() {
int accent = ThemesRepo.getColor(android.R.attr.colorAccent);
if (ColorUtils.calculateLuminance(accent) >= 0.6f) {
accent = ColorUtils.blendARGB(accent, Color.BLACK, 0.075f);
}
boolean tooLight = ColorUtils.calculateLuminance(accent) >= 0.6f;
title.setTextColor(0xffffffff);
price.setTextColor(0xffffffff);
icon.setImageTintList(ColorStateList.valueOf(0xffffffff));
featuresLayout.setBackground(ViewUtils.createRipple(0, tooLight ? 0x33ffffff : 0x21ffffff, 24));
setBackground(ViewUtils.createRipple(0x21000000, ColorUtils.blendARGB(0xffffffff, accent, tooLight ? 0.9f : 0.75f), 32));
}
@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> {
@@ -1235,16 +1162,16 @@ public class SetupActivity extends AppCompatActivity {
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(BeamServerData.isCloudAvailable() ? View.VISIBLE : View.GONE);
cloudImportView.setVisibility(Prefs.getCloudAPIToken() != null ? View.VISIBLE : View.GONE);
cloudOrView.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
cloudOrView.setVisibility(BeamServerData.isCloudAvailable() ? View.VISIBLE : View.GONE);
cloudOrView.setVisibility(Prefs.getCloudAPIToken() != null ? View.VISIBLE : View.GONE);
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));
if (adapter.getItemCount() == 0 && isReposLoaded) {
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);
} else {
adapter.notifyDataSetChanged();
@@ -1264,14 +1191,14 @@ public class SetupActivity extends AppCompatActivity {
public void onCloudInfoUpdated() {
if (cloudImportView != null) {
cloudImportView.setVisibility(BeamServerData.isCloudAvailable() ? View.VISIBLE : View.GONE);
cloudOrView.setVisibility(BeamServerData.isCloudAvailable() ? View.VISIBLE : View.GONE);
cloudImportView.setVisibility(Prefs.getCloudAPIToken() != null ? View.VISIBLE : View.GONE);
cloudOrView.setVisibility(Prefs.getCloudAPIToken() != null ? View.VISIBLE : View.GONE);
}
}
public void onReposLoaded() {
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);
new SpringAnimation(new FloatValueHolder(0))
.setMinimumVisibleChange(1 / 256f)
@@ -1425,80 +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);
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(ctx.getString(R.string.IntroBoostySupport)).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);
subscribeButton.setText(sb);
subscribeButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
subscribeButton.setTextColor(Color.WHITE);
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.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
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, ViewUtils.dp(52)) {{
leftMargin = rightMargin = ViewUtils.dp(16);
bottomMargin = ViewUtils.dp(8);
}});
TextView buttonView = new TextView(ctx);
if (boostyOnly) {
buttonView.setText(android.R.string.ok);
} else {
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-> {
if (boostyOnly) {
finish();
return;
}
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 TextView buttonView;
@@ -1629,10 +1482,10 @@ public class SetupActivity extends AppCompatActivity {
}
}
try {
if (Prefs.getCloudAPIToken() == null || SliceBeam.CONFIG == null) {
SliceBeam.getCurrentConfigFile().delete();
SliceBeam.CONFIG = cfg;
FileOutputStream fos = new FileOutputStream(SliceBeam.getConfigFile());
if (Prefs.getCloudAPIToken() == null || Santoku.CONFIG == null) {
Santoku.getCurrentConfigFile().delete();
Santoku.CONFIG = cfg;
FileOutputStream fos = new FileOutputStream(Santoku.getConfigFile());
fos.write(cfg.serialize().getBytes(StandardCharsets.UTF_8));
fos.close();
}
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.boot;
package com.dark98.santoku.boot;
import android.util.Log;
import android.util.SparseBooleanArray;
@@ -11,7 +11,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import ru.ytkab0bp.slicebeam.BuildConfig;
import com.dark98.santoku.BuildConfig;
public class AppBoot {
private final static String TAG = "boot";
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.boot;
package com.dark98.santoku.boot;
import java.util.Collections;
import java.util.List;
@@ -1,17 +1,17 @@
package ru.ytkab0bp.slicebeam.boot;
package com.dark98.santoku.boot;
import java.io.IOException;
import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.Santoku;
public class CheckUpdateJsonTask extends BootTask {
public CheckUpdateJsonTask() {
super(() -> {
try {
SliceBeam.INSTANCE.getAssets().open("update.json").close();
SliceBeam.hasUpdateInfo = true;
Santoku.INSTANCE.getAssets().open("update.json").close();
Santoku.hasUpdateInfo = true;
} catch (IOException e) {
SliceBeam.hasUpdateInfo = false;
Santoku.hasUpdateInfo = false;
}
});
onWorker();
@@ -1,14 +1,14 @@
package ru.ytkab0bp.slicebeam.boot;
package com.dark98.santoku.boot;
import java.io.File;
import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.Santoku;
public class ClearModelCacheTask extends BootTask {
@SuppressWarnings("ResultOfMethodCallIgnored")
public ClearModelCacheTask() {
super(()->{
File cache = SliceBeam.getModelCacheDir();
File cache = Santoku.getModelCacheDir();
if (cache.exists()) {
for (File f : cache.listFiles()) {
f.delete();
@@ -1,8 +1,8 @@
package ru.ytkab0bp.slicebeam.boot;
package com.dark98.santoku.boot;
import java.util.Collections;
import ru.ytkab0bp.slicebeam.cloud.CloudController;
import com.dark98.santoku.cloud.CloudController;
public class CloudCachedInitTask extends BootTask {
public CloudCachedInitTask() {
@@ -1,8 +1,8 @@
package ru.ytkab0bp.slicebeam.boot;
package com.dark98.santoku.boot;
import java.util.Arrays;
import ru.ytkab0bp.slicebeam.cloud.CloudController;
import com.dark98.santoku.cloud.CloudController;
public class CloudInitTask extends BootTask {
public CloudInitTask() {
@@ -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();
}
}
@@ -1,20 +1,20 @@
package ru.ytkab0bp.slicebeam.boot;
package com.dark98.santoku.boot;
import java.io.File;
import java.io.IOException;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.slic3r.Slic3rConfigWrapper;
@SuppressWarnings("ResultOfMethodCallIgnored")
public class LoadSlic3rConfigTask extends BootTask {
public LoadSlic3rConfigTask() {
super(() -> {
File cfgFile = SliceBeam.getConfigFile();
SliceBeam.getCurrentConfigFile().delete();
File cfgFile = Santoku.getConfigFile();
Santoku.getCurrentConfigFile().delete();
if (cfgFile.exists()) {
try {
SliceBeam.CONFIG = new Slic3rConfigWrapper(cfgFile);
Santoku.CONFIG = new Slic3rConfigWrapper(cfgFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -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));
}
}
@@ -1,6 +1,6 @@
package ru.ytkab0bp.slicebeam.boot;
package com.dark98.santoku.boot;
import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
import com.dark98.santoku.slic3r.PrintConfigDef;
public class PrintConfigWarmupTask extends BootTask {
public PrintConfigWarmupTask() {
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.boot;
package com.dark98.santoku.boot;
import androidx.annotation.NonNull;
@@ -13,13 +13,13 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import kotlinx.coroutines.Dispatchers;
import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.Santoku;
public class TrueTimeTask extends BootTask {
public TrueTimeTask() {
super(() -> {
CountDownLatch latch = new CountDownLatch(1);
SliceBeam.TRUE_TIME = new TrueTimeImpl(new TrueTimeParameters.Builder().buildParams(), Dispatchers.getIO(), new TrueTimeEventListener() {
Santoku.TRUE_TIME = new TrueTimeImpl(new TrueTimeParameters.Builder().buildParams(), Dispatchers.getIO(), new TrueTimeEventListener() {
@Override
public void initialize(@NonNull TrueTimeParameters trueTimeParameters) {}
@@ -64,7 +64,7 @@ public class TrueTimeTask extends BootTask {
@Override
public void returningDeviceTime() {}
});
SliceBeam.TRUE_TIME.sync();
Santoku.TRUE_TIME.sync();
try {
latch.await(300, TimeUnit.MILLISECONDS);
} catch (InterruptedException ignored) {}
@@ -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();
}
}
@@ -1,13 +1,8 @@
package ru.ytkab0bp.slicebeam.cloud;
package com.dark98.santoku.cloud;
import androidx.annotation.Nullable;
import com.google.gson.Gson;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import ru.ytkab0bp.sapil.APICallback;
@@ -18,8 +13,8 @@ import ru.ytkab0bp.sapil.Arg;
import ru.ytkab0bp.sapil.Header;
import ru.ytkab0bp.sapil.Method;
import ru.ytkab0bp.sapil.RequestType;
import ru.ytkab0bp.slicebeam.BuildConfig;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import com.dark98.santoku.BuildConfig;
import com.dark98.santoku.utils.Prefs;
public interface CloudAPI extends APIRunner {
CloudAPI INSTANCE = APILibrary.newRunner(CloudAPI.class, new RunnerConfig() {
@@ -27,12 +22,12 @@ public interface CloudAPI extends APIRunner {
@Override
public String getBaseURL() {
return "https://api.beam3d.ru/v1/";
return BuildConfig.CLOUD_BASE_URL_PROD;
}
@Override
public String getDefaultUserAgent() {
return "SliceBeam v" + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_CODE;
return "Santoku v" + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_CODE;
}
@Override
@@ -43,6 +38,11 @@ public interface CloudAPI extends APIRunner {
}
return headers;
}
@Override
public ru.ytkab0bp.sapil.util.Pair<String, String> getNamingTransformPolicy() {
return null;
}
});
/**
@@ -72,10 +72,16 @@ public interface CloudAPI extends APIRunner {
void userGetInfo(APICallback<UserInfo> callback);
/**
* Gets user features
* Creates a new account (email/password)
*/
@Method("user/getFeatures")
void userGetFeatures(APICallback<UserFeatures> callback);
@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
@@ -103,24 +109,6 @@ public interface CloudAPI extends APIRunner {
@Method("sync/get")
void syncGet(APICallback<String> callback);
/**
* Generates 3D model from image
* <p>
* @param image Base64 encoded image
* <p>
* Requires authorization
*/
@Method(requestType = RequestType.POST, value = "models/generate")
void modelsGenerate(@Arg("") String image, @Header("Content-Type") String type, APICallback<InputStream> callback);
/**
* Gets remaining model generations count
* <p>
* Requires authorization
*/
@Method("models/getRemainingCount")
void modelsGetRemainingCount(APICallback<ModelsRemainingCount> callback);
/**
* Destroys token
* <p>
@@ -158,66 +146,13 @@ public interface CloudAPI extends APIRunner {
public String bearer;
}
final class UserFeatures {
final class AuthToken {
/**
* Which level is required for early access
* Bearer token
*/
public int earlyAccessLevel;
/**
* Which level is required for data sync
*/
public int syncRequiredLevel;
/**
* Which level is required for AI model generator
*/
public int aiGeneratorRequiredLevel;
/**
* Models per month max
*/
public int aiGeneratorModelsPerMonth;
/**
* Url at which user should be redirected for info about how to restore a subscription
*/
public String alreadySubscribedInfoUrl;
/**
* List of subscription levels
*/
public List<SubscriptionLevel> levels = new ArrayList<>();
public String bearer;
}
final class SubscriptionLevel {
/**
* Int representation
*/
public int level;
/**
* Title of this level
*/
public String title;
/**
* Price of this level
*/
public String price;
/**
* Url at which user should be redirected for purchase
*/
public String subscribeOrUpgradeUrl;
/**
* Url at which user should be redirected for managing the subscription
*/
public String manageUrl;
}
final class UserInfo {
/**
* User's id
@@ -235,10 +170,6 @@ public interface CloudAPI extends APIRunner {
@Nullable
public String avatarUrl;
/**
* Current subscription level
*/
public int currentLevel;
}
@@ -259,15 +190,4 @@ public interface CloudAPI extends APIRunner {
public long maxSize;
}
final class ModelsRemainingCount {
/**
* Used generations
*/
public int used;
/**
* Max available generations
*/
public int max;
}
}
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.cloud;
package com.dark98.santoku.cloud;
import android.content.Intent;
import android.net.Uri;
@@ -18,48 +18,47 @@ import java.util.Locale;
import ru.ytkab0bp.sapil.APICallback;
import ru.ytkab0bp.sapil.APIRequestHandle;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.events.CloudFeaturesUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.CloudLoginStateUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.CloudModelsRemainingCountUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.CloudSyncFinishedEvent;
import ru.ytkab0bp.slicebeam.events.CloudUserInfoUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
import ru.ytkab0bp.slicebeam.utils.IOUtils;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
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 USER_INFO_AI_GEN_TAG = "ai_gen_user_info";
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 final static long MIN_SYNC_FEATURES_DELTA = 12 * 60 * 60 * 1000L; // Once in 12 hours
private static boolean isSyncInProgress;
private static CloudAPI.UserInfo userInfo;
private static CloudAPI.UserFeatures userFeatures;
private static int modelsUsed;
private static int modelsMaxGenerations;
private static boolean isLoggingIn;
private static APIRequestHandle beginLoginHandle;
private static String loginSessionId;
private static Runnable loginAutoCancel = () -> {
loginSessionId = null;
isLoggingIn = false;
SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
};
private static Runnable loginCheck = new Runnable() {
@Override
public void run() {
CloudAPI.INSTANCE.loginCheck(loginSessionId, new APICallback<CloudAPI.LoginState>() {
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) {
@@ -85,32 +84,31 @@ public class CloudController {
private static Gson gson = new Gson();
public static void initCached() {
if (Prefs.getCloudCachedUserFeatures() != null) {
userFeatures = gson.fromJson(Prefs.getCloudCachedUserFeatures(), CloudAPI.UserFeatures.class);
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);
modelsUsed = Prefs.getCloudCachedUsedModels();
modelsMaxGenerations = Prefs.getCloudCachedMaxModels();
}
}
}
public static void init() {
long now = SliceBeam.TRUE_TIME.now().getTime();
boolean needSyncInfo = userFeatures == null || now - Prefs.getCloudLastFeaturesSync() > MIN_SYNC_FEATURES_DELTA;
if (needSyncInfo) {
checkUserFeatures();
}
if (Prefs.getCloudAPIToken() != null) {
if (needSyncInfo || userInfo == null) {
long now = Santoku.TRUE_TIME.now().getTime();
if (userInfo == null) {
loadUserInfo();
}
if (!needSyncInfo && userInfo != null && isSyncAvailable() && Prefs.isCloudProfileSyncEnabled()) {
if (userInfo != null && isSyncAvailable() && Prefs.isCloudProfileSyncEnabled()) {
if (now - Prefs.getCloudLastSync() > MIN_SYNC_DELTA) {
syncData();
}
@@ -119,7 +117,11 @@ public class CloudController {
}
private static void loadUserInfo() {
CloudAPI.INSTANCE.userGetInfo(new APICallback<CloudAPI.UserInfo>() {
CloudAPI api = getApiSafe();
if (api == null) {
return;
}
api.userGetInfo(new APICallback<CloudAPI.UserInfo>() {
@Override
public void onResponse(CloudAPI.UserInfo response) {
userInfo = response;
@@ -128,29 +130,26 @@ public class CloudController {
userInfo = null;
Prefs.setCloudAPIToken(null);
Prefs.setCloudCachedUserInfo(null);
SliceBeam.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent());
Santoku.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent());
if (isLoggingIn) {
isLoggingIn = false;
SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
}
} else {
Prefs.setCloudCachedUserInfo(gson.toJson(userInfo));
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(USER_INFO_AI_GEN_TAG));
SliceBeam.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent());
Santoku.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent());
if (isLoggingIn) {
isLoggingIn = false;
SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
}
if (isSyncAvailable() && Prefs.isCloudProfileSyncEnabled()) {
syncData();
}
checkGeneratorRemaining();
}
Prefs.setCloudLastFeaturesSync(SliceBeam.TRUE_TIME.now().getTime());
}
@Override
@@ -166,14 +165,20 @@ public class CloudController {
}
private static void beginLogin0() {
beginLoginHandle = CloudAPI.INSTANCE.loginBegin(new APICallback<CloudAPI.LoginData>() {
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 - SliceBeam.TRUE_TIME.now().getTime());
ViewUtils.postOnMainThread(loginAutoCancel, response.expiresAt * 1000L - Santoku.TRUE_TIME.now().getTime());
ViewUtils.postOnMainThread(loginCheck, 5000);
ViewUtils.postOnMainThread(() -> SliceBeam.INSTANCE.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(response.url)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)));
ViewUtils.postOnMainThread(() -> Santoku.INSTANCE.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(response.url)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)));
}
@Override
@@ -185,15 +190,18 @@ public class CloudController {
public static void beginLogin() {
isLoggingIn = true;
SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
beginLogin0();
}
public static void cancelLogin() {
isLoggingIn = false;
SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
if (loginSessionId != null) {
CloudAPI.INSTANCE.loginCancel(loginSessionId, response -> {});
CloudAPI api = getApiSafe();
if (api != null) {
api.loginCancel(loginSessionId, response -> {});
}
}
if (beginLoginHandle != null && beginLoginHandle.isRunning()) {
beginLoginHandle.cancel();
@@ -205,107 +213,61 @@ public class CloudController {
}
public static void logout() {
CloudAPI api = getApiSafe();
if (api != null) {
api.logout(response -> {});
}
Prefs.setCloudAPIToken(null);
userInfo = null;
SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
SliceBeam.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent());
CloudAPI.INSTANCE.logout(response -> {});
}
public static void checkGeneratorRemaining() {
CloudAPI.INSTANCE.modelsGetRemainingCount(new APICallback<CloudAPI.ModelsRemainingCount>() {
@Override
public void onResponse(CloudAPI.ModelsRemainingCount response) {
modelsUsed = response.used;
modelsMaxGenerations = response.max;
Prefs.setCloudCachedUsedMaxModels(modelsUsed, modelsMaxGenerations);
SliceBeam.EVENT_BUS.fireEvent(new CloudModelsRemainingCountUpdatedEvent());
}
@Override
public void onException(Exception e) {
Log.e(TAG, "Failed to check remaining models", e);
ViewUtils.postOnMainThread(CloudController::checkGeneratorRemaining, 15000);
}
});
}
public static void checkUserFeatures() {
CloudAPI.INSTANCE.userGetFeatures(new APICallback<CloudAPI.UserFeatures>() {
@Override
public void onResponse(CloudAPI.UserFeatures response) {
userFeatures = response;
Prefs.setCloudCachedUserFeatures(gson.toJson(userFeatures));
if (Prefs.getCloudAPIToken() == null) {
Prefs.setCloudLastFeaturesSync(SliceBeam.TRUE_TIME.now().getTime());
}
SliceBeam.EVENT_BUS.fireEvent(new CloudFeaturesUpdatedEvent());
}
@Override
public void onException(Exception e) {
Log.e(TAG, "Failed to get user features", e);
ViewUtils.postOnMainThread(CloudController::checkUserFeatures, 15000);
}
});
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
Santoku.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent());
}
public static CloudAPI.UserInfo getUserInfo() {
return userInfo;
}
public static CloudAPI.UserFeatures getUserFeatures() {
return userFeatures;
}
public static boolean hasAccountFeatures() {
return userFeatures != null && userFeatures.levels != null && !userFeatures.levels.isEmpty();
return true;
}
public static boolean isSyncAvailable() {
return Prefs.getCloudAPIToken() != null && userInfo != null && userFeatures != null && userInfo.currentLevel >= userFeatures.syncRequiredLevel;
}
public static boolean needShowAIGenerator() {
return userFeatures != null && userFeatures.aiGeneratorRequiredLevel >= 0;
}
public static int getGeneratedModels() {
return modelsUsed;
}
public static int getMaxGeneratedModels() {
return modelsMaxGenerations;
return Prefs.getCloudAPIToken() != null && userInfo != null;
}
private static void downloadData(long lastModified) {
CloudAPI.INSTANCE.syncGet(new APICallback<String>() {
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 = SliceBeam.getConfigFile();
File f = Santoku.getConfigFile();
byte[] data = Base64.decode(response, 0);
FileOutputStream fos = new FileOutputStream(f);
fos.write(data);
fos.close();
SliceBeam.CONFIG = new Slic3rConfigWrapper(f);
Santoku.CONFIG = new Slic3rConfigWrapper(f);
Prefs.setCloudLocalLastModified(lastModified);
Prefs.setCloudLocalLastSentModified(lastModified);
Prefs.setCloudRemoteLastModified(lastModified);
Prefs.setCloudLastSync(SliceBeam.TRUE_TIME.now().getTime());
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.CloudSyncSuccess));
SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
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;
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
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());
}
});
}
@@ -315,9 +277,9 @@ public class CloudController {
Log.e(TAG, "Failed to download data", e);
isSyncInProgress = false;
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
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());
}
});
}
@@ -326,20 +288,24 @@ public class CloudController {
if (isSyncInProgress) {
return;
}
CloudAPI api = getApiSafe();
if (api == null) {
return;
}
long modified = Prefs.getCloudLocalLastModified();
isSyncInProgress = true;
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.CloudSyncInProgress).tag(CLOUD_SYNC_TAG));
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.CloudSyncInProgress).tag(CLOUD_SYNC_TAG));
CloudAPI.INSTANCE.syncGetState(new APICallback<CloudAPI.SyncState>() {
api.syncGetState(new APICallback<CloudAPI.SyncState>() {
@Override
public void onResponse(CloudAPI.SyncState response) {
if (SliceBeam.CONFIG == null && response.usedSize != 0) {
if (Santoku.CONFIG == null && response.usedSize != 0) {
// Setup screen, no config yet
downloadData(response.lastUpdatedDate);
} else if (response.usedSize == 0) {
if (SliceBeam.CONFIG == null) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
if (Santoku.CONFIG == null) {
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
return;
}
@@ -351,8 +317,8 @@ public class CloudController {
downloadData(response.lastUpdatedDate);
} else {
// Modified on client and on server
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.WARNING, R.string.CloudSyncConflict).button(R.string.CloudSyncConflictResolve, v -> {
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)
@@ -368,8 +334,8 @@ public class CloudController {
uploadData(modified);
} else {
// Not modified on server and on client
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
}
}
}
@@ -379,8 +345,8 @@ public class CloudController {
Log.e(TAG, "Failed to get sync state", e);
isSyncInProgress = false;
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
}
});
}
@@ -388,7 +354,7 @@ public class CloudController {
private static void uploadData(long modified) {
IOUtils.IO_POOL.submit(() -> {
try {
File f = SliceBeam.getConfigFile();
File f = Santoku.getConfigFile();
FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[10240];
@@ -399,7 +365,12 @@ public class CloudController {
bos.close();
fis.close();
CloudAPI.INSTANCE.syncUpload(Base64.encodeToString(bos.toByteArray(), Base64.NO_WRAP), "application/ini", new APICallback<CloudAPI.SyncState>() {
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;
@@ -409,10 +380,10 @@ public class CloudController {
}
Prefs.setCloudRemoteLastModified(response.lastUpdatedDate);
Prefs.setCloudLocalLastSentModified(modified);
Prefs.setCloudLastSync(SliceBeam.TRUE_TIME.now().getTime());
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.CloudSyncSuccess));
SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
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
@@ -420,24 +391,24 @@ public class CloudController {
Log.e(TAG, "Failed to upload sync data", e);
isSyncInProgress = false;
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
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;
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
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 = SliceBeam.TRUE_TIME.now().getTime();
long now = Santoku.TRUE_TIME.now().getTime();
Prefs.setCloudLocalLastModified(now);
if (!isSyncAvailable() || !Prefs.isCloudProfileSyncEnabled()) {
return;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.components;
package com.dark98.santoku.components;
import android.annotation.SuppressLint;
import android.content.Context;
@@ -21,9 +21,9 @@ import androidx.appcompat.widget.AppCompatCheckedTextView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.R;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils;
public class BeamAlertDialogBuilder extends MaterialAlertDialogBuilder {
public BeamAlertDialogBuilder(@NonNull Context context) {
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.components;
package com.dark98.santoku.components;
import android.app.Dialog;
import android.content.Context;
@@ -8,9 +8,9 @@ import android.widget.TextView;
import com.mrudultora.colorpicker.ColorPickerPopUp;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.R;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils;
public class BeamColorPickerPopUp extends ColorPickerPopUp {
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);
}
}
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.components;
package com.dark98.santoku.components;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -21,12 +21,12 @@ import java.util.ArrayList;
import java.util.List;
import ru.ytkab0bp.eventbus.EventHandler;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.events.SlicingProgressEvent;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
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.events.SlicingProgressEvent;
import com.dark98.santoku.slic3r.Slic3rLocalization;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils;
public class SliceProgressBottomSheet extends BottomSheetDialog {
private RecyclerView recyclerView;
@@ -122,12 +122,12 @@ public class SliceProgressBottomSheet extends BottomSheetDialog {
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
SliceBeam.EVENT_BUS.registerListener(this);
Santoku.EVENT_BUS.registerListener(this);
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
SliceBeam.EVENT_BUS.unregisterListener(this);
Santoku.EVENT_BUS.unregisterListener(this);
}
}
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.components;
package com.dark98.santoku.components;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -18,10 +18,10 @@ import androidx.dynamicanimation.animation.FloatValueHolder;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.MirrorView;
import com.dark98.santoku.fragment.BedFragment;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils;
import com.dark98.santoku.view.MirrorView;
public abstract class UnfoldMenu {
protected BedFragment fragment;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.components;
package com.dark98.santoku.components;
import android.annotation.SuppressLint;
import android.content.Context;
@@ -23,14 +23,14 @@ import java.io.File;
import java.io.FileOutputStream;
import java.util.Locale;
import ru.ytkab0bp.slicebeam.BuildConfig;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.events.NeedDismissCalibrationsMenu;
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.DividerView;
import com.dark98.santoku.BuildConfig;
import com.dark98.santoku.R;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.events.NeedDismissCalibrationsMenu;
import com.dark98.santoku.fragment.BedFragment;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils;
import com.dark98.santoku.view.DividerView;
public class WebViewMenu extends UnfoldMenu {
private final Uri uri;
@@ -93,10 +93,10 @@ public class WebViewMenu extends UnfoldMenu {
return super.dispatchTouchEvent(ev);
}
};
webView.addJavascriptInterface(new Bridge(), "SliceBeam");
webView.addJavascriptInterface(new Bridge(), "Santoku");
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.setDomStorageEnabled(true);
settings.setDatabaseEnabled(true);
@@ -131,7 +131,7 @@ public class WebViewMenu extends UnfoldMenu {
@JavascriptInterface
public void beginDownload(String filename) {
cacheFile = new File(SliceBeam.getModelCacheDir(), filename);
cacheFile = new File(Santoku.getModelCacheDir(), filename);
try {
fileStream = new FileOutputStream(cacheFile);
} catch (Exception e) {
@@ -155,7 +155,7 @@ public class WebViewMenu extends UnfoldMenu {
ViewUtils.postOnMainThread(() -> {
dismiss(true);
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissCalibrationsMenu());
Santoku.EVENT_BUS.fireEvent(new NeedDismissCalibrationsMenu());
ViewUtils.postOnMainThread(() -> fragment.loadGCode(cacheFile), 200);
});
} catch (Exception e) {
@@ -1,12 +1,12 @@
package ru.ytkab0bp.slicebeam.components.bed_menu;
package com.dark98.santoku.components.bed_menu;
import android.content.Context;
import android.view.View;
import androidx.annotation.CallSuper;
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import ru.ytkab0bp.slicebeam.slic3r.Bed3D;
import com.dark98.santoku.fragment.BedFragment;
import com.dark98.santoku.slic3r.Bed3D;
public abstract class BedMenu {
private View view;
@@ -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.res.ColorStateList;
@@ -31,13 +31,13 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import ru.ytkab0bp.slicebeam.theme.IThemeView;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.RandomUtils;
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 final int titleRes;
@@ -169,7 +169,7 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
canvas.save();
if (sparkles == null) sparkles = new ArrayList<>();
if (sparkleDrawable == null) {
sparkleDrawable = ContextCompat.getDrawable(SliceBeam.INSTANCE, R.drawable.sparkle_28);
sparkleDrawable = ContextCompat.getDrawable(Santoku.INSTANCE, R.drawable.sparkle_28);
sparkleDrawable.setColorFilter(new PorterDuffColorFilter(ThemesRepo.getColor(android.R.attr.colorAccent), PorterDuff.Mode.SRC_IN));
}
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.components.bed_menu;
package com.dark98.santoku.components.bed_menu;
import android.content.Context;
import android.widget.Toast;
@@ -10,16 +10,16 @@ import androidx.dynamicanimation.animation.SpringForce;
import java.util.Arrays;
import java.util.List;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
import ru.ytkab0bp.slicebeam.render.Camera;
import ru.ytkab0bp.slicebeam.slic3r.Bed3D;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.Vec3d;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.GLView;
import com.dark98.santoku.R;
import com.dark98.santoku.components.BeamAlertDialogBuilder;
import com.dark98.santoku.recycler.SimpleRecyclerItem;
import com.dark98.santoku.recycler.SpaceItem;
import com.dark98.santoku.render.Camera;
import com.dark98.santoku.slic3r.Bed3D;
import com.dark98.santoku.utils.Prefs;
import com.dark98.santoku.utils.Vec3d;
import com.dark98.santoku.utils.ViewUtils;
import com.dark98.santoku.view.GLView;
public class CameraMenu extends ListBedMenu {
private boolean checkInvalidBed() {
@@ -1,10 +1,9 @@
package ru.ytkab0bp.slicebeam.components.bed_menu;
package com.dark98.santoku.components.bed_menu;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
@@ -15,7 +14,6 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.core.content.FileProvider;
import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.RecyclerView;
@@ -33,40 +31,31 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import ru.ytkab0bp.eventbus.EventHandler;
import ru.ytkab0bp.slicebeam.BeamServerData;
import ru.ytkab0bp.slicebeam.BuildConfig;
import ru.ytkab0bp.slicebeam.MainActivity;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SetupActivity;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.cloud.CloudController;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
import ru.ytkab0bp.slicebeam.components.WebViewMenu;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.events.CloudFeaturesUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.CloudModelsRemainingCountUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.NeedDismissAIGeneratorMenu;
import ru.ytkab0bp.slicebeam.events.NeedDismissCalibrationsMenu;
import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
import ru.ytkab0bp.slicebeam.events.SelectedObjectChangedEvent;
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import ru.ytkab0bp.slicebeam.recycler.PreferenceItem;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerAdapter;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
import ru.ytkab0bp.slicebeam.slic3r.Bed3D;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rRuntimeError;
import ru.ytkab0bp.slicebeam.theme.BeamTheme;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.DividerView;
import ru.ytkab0bp.slicebeam.view.FadeRecyclerView;
import ru.ytkab0bp.slicebeam.view.SegmentsView;
import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
import com.dark98.santoku.MainActivity;
import com.dark98.santoku.R;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.components.BeamAlertDialogBuilder;
import com.dark98.santoku.components.UnfoldMenu;
import com.dark98.santoku.components.WebViewMenu;
import com.dark98.santoku.config.ConfigObject;
import com.dark98.santoku.events.NeedDismissCalibrationsMenu;
import com.dark98.santoku.events.NeedDismissSnackbarEvent;
import com.dark98.santoku.events.NeedSnackbarEvent;
import com.dark98.santoku.events.ObjectsListChangedEvent;
import com.dark98.santoku.events.SelectedObjectChangedEvent;
import com.dark98.santoku.fragment.BedFragment;
import com.dark98.santoku.recycler.PreferenceItem;
import com.dark98.santoku.recycler.SimpleRecyclerAdapter;
import com.dark98.santoku.recycler.SimpleRecyclerItem;
import com.dark98.santoku.recycler.SpaceItem;
import com.dark98.santoku.slic3r.Bed3D;
import com.dark98.santoku.slic3r.Slic3rRuntimeError;
import com.dark98.santoku.theme.BeamTheme;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils;
import com.dark98.santoku.view.DividerView;
import com.dark98.santoku.view.FadeRecyclerView;
import com.dark98.santoku.view.SnackbarsLayout;
public class FileMenu extends ListBedMenu {
private final static List<String> K3D_SUPPORTED_LANGUAGES = Arrays.asList("en", "ru");
@@ -126,29 +115,6 @@ public class FileMenu extends ListBedMenu {
}
}),
new SpaceItem(portrait ? ViewUtils.dp(3) : 0, portrait ? 0 : ViewUtils.dp(3))));
if (BeamServerData.isBoostyAvailable() && CloudController.needShowAIGenerator()) {
list.add(new BedMenuItem(R.string.MenuFileAIGenerator, R.drawable.picture_stack_outline_28).setShiny(true).onClick(view -> {
if (Prefs.getCloudAPIToken() == null || CloudController.getUserInfo() != null && CloudController.getMaxGeneratedModels() == 0) {
Context ctx = view.getContext();
ctx.startActivity(new Intent(ctx, SetupActivity.class).putExtra(SetupActivity.EXTRA_CLOUD_PROFILE, true));
return;
}
if (CloudController.getUserInfo() == null) {
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileAIGeneratorPleaseWaitSetup).tag(CloudController.USER_INFO_AI_GEN_TAG));
ViewUtils.postOnMainThread(() -> {
if (CloudController.getUserInfo() == null) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CloudController.USER_INFO_AI_GEN_TAG));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.MenuFileAIGeneratorErrorNotLoadedUserAccount));
} else {
fragment.showUnfoldMenu(new AIGeneratorMenu(), view);
}
}, 2500);
return;
}
fragment.showUnfoldMenu(new AIGeneratorMenu(), view);
}));
}
list.addAll(Arrays.asList(
new BedMenuItem(R.string.MenuFileCalibrations, R.drawable.wrench_outline_28).setSingleLine(true).onClick(v -> {
if (!fragment.getGlView().getRenderer().getBed().isValid()) {
@@ -169,24 +135,24 @@ public class FileMenu extends ListBedMenu {
}
}),
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];
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;
}
CharSequence[] filaments = new CharSequence[SliceBeam.CONFIG.filamentConfigs.size()];
CharSequence[] filaments = new CharSequence[Santoku.CONFIG.filamentConfigs.size()];
boolean[] enabledFilaments = new boolean[filaments.length];
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;
}
CharSequence[] printers = new CharSequence[SliceBeam.CONFIG.printerConfigs.size()];
CharSequence[] printers = new CharSequence[Santoku.CONFIG.printerConfigs.size()];
boolean[] enabledPrinters = new boolean[printers.length];
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;
}
@@ -205,21 +171,21 @@ public class FileMenu extends ListBedMenu {
for (int i = 0; i < enabledPrints.length; i++) {
if (enabledPrints[i]) {
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<>();
for (int i = 0; i < enabledFilaments.length; i++) {
if (enabledFilaments[i]) {
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<>();
for (int i = 0; i < enabledPrinters.length; i++) {
if (enabledPrinters[i]) {
hasEnabled = true;
MainActivity.EXPORTING_PRINTERS.add(SliceBeam.CONFIG.printerConfigs.get(i));
MainActivity.EXPORTING_PRINTERS.add(Santoku.CONFIG.printerConfigs.get(i));
}
}
if (!hasEnabled) {
@@ -235,7 +201,7 @@ public class FileMenu extends ListBedMenu {
Activity act = (Activity) fragment.getContext();
Intent i = new Intent(Intent.ACTION_CREATE_DOCUMENT);
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);
}
})
@@ -251,7 +217,7 @@ public class FileMenu extends ListBedMenu {
Activity act = (Activity) fragment.getContext();
Intent i = new Intent(Intent.ACTION_CREATE_DOCUMENT);
i.setType("application/3mf");
i.putExtra(Intent.EXTRA_TITLE, "SliceBeam_project.3mf");
i.putExtra(Intent.EXTRA_TITLE, "Santoku_project.3mf");
act.startActivityForResult(i, MainActivity.REQUEST_CODE_EXPORT_3MF);
}
})
@@ -263,10 +229,7 @@ public class FileMenu extends ListBedMenu {
public void onObjectsChanged(ObjectsListChangedEvent e) {
((BedMenuItem) adapter.getItems().get(1)).setEnabled(hasSelection());
adapter.notifyItemChanged(1);
int i = 8 - (BeamServerData.isBoostyAvailable() && CloudController.needShowAIGenerator() ? 0 : 1);
((BedMenuItem) adapter.getItems().get(i)).setEnabled(hasModel());
adapter.notifyItemChanged(i);
updateModelItems();
}
@EventHandler(runOnMainThread = true)
@@ -275,147 +238,17 @@ public class FileMenu extends ListBedMenu {
adapter.notifyItemChanged(1);
}
@EventHandler(runOnMainThread = true)
public void onFeaturedUpdated(CloudFeaturesUpdatedEvent e) {
adapter.setItems(onCreateItems(wasPortrait));
}
public final static class AIGeneratorMenu extends UnfoldMenu {
private TextView remainingView;
private SegmentsView segmentsView;
@Override
public int getRequestedSize(FrameLayout into, boolean portrait) {
return (int) (portrait ? ViewUtils.dp(52) + ViewUtils.dp(60) * 2 + ViewUtils.dp(28) + ViewUtils.dp(18) + ViewUtils.dp(2) : into.getWidth() * 0.6f);
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;
}
}
@Override
protected View onCreateView(Context ctx, boolean portrait) {
LinearLayout ll = new LinearLayout(ctx);
ll.setOrientation(LinearLayout.VERTICAL);
RecyclerView rv = new FadeRecyclerView(ctx);
rv.setOverScrollMode(View.OVER_SCROLL_NEVER);
SimpleRecyclerAdapter adapter = new SimpleRecyclerAdapter();
adapter.setItems(Arrays.asList(
new PreferenceItem().setIcon(R.drawable.camera_outline_28).setTitle(ctx.getString(R.string.MenuFileAIGeneratorFromCamera)).setOnClickListener(v -> {
if (CloudController.getGeneratedModels() >= CloudController.getMaxGeneratedModels()) {
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.MenuFileAIGeneratorNoGenerationsLeft));
return;
}
if (MainActivity.IS_GENERATING_AI_MODEL) {
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.WARNING, R.string.MenuFileAIGeneratorAlreadyGenerating));
return;
}
if (ctx instanceof MainActivity) {
try {
MainActivity.aiTempFile = File.createTempFile("ai_capture", ".jpg");
Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(ctx, BuildConfig.APPLICATION_ID + ".provider", MainActivity.aiTempFile));
i.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
((MainActivity) ctx).startActivityForResult(i, MainActivity.REQUEST_CODE_AI_GENERATOR_TAKE_PHOTO);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}),
new PreferenceItem().setIcon(R.drawable.picture_outline_28).setTitle(ctx.getString(R.string.MenuFileAIGeneratorFromGallery)).setOnClickListener(v -> {
if (CloudController.getGeneratedModels() >= CloudController.getMaxGeneratedModels()) {
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.MenuFileAIGeneratorNoGenerationsLeft));
return;
}
if (MainActivity.IS_GENERATING_AI_MODEL) {
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.WARNING, R.string.MenuFileAIGeneratorAlreadyGenerating));
return;
}
if (ctx instanceof MainActivity) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
((MainActivity) ctx).startActivityForResult(Intent.createChooser(intent, ""), MainActivity.REQUEST_CODE_AI_GENERATOR_CHOOSE_PHOTO);
}
})
));
rv.setAdapter(adapter);
ll.addView(rv, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
ll.addView(new DividerView(ctx), new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(1f)));
remainingView = new TextView(ctx);
remainingView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
remainingView.setGravity(Gravity.CENTER);
remainingView.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
ll.addView(remainingView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(18)) {{
topMargin = ViewUtils.dp(8);
}});
segmentsView = new SegmentsView(ctx) {
@Override
protected int onGetColor(int i) {
return i == 1 ? ThemesRepo.getColor(android.R.attr.textColorSecondary) : ThemesRepo.getColor(android.R.attr.colorAccent);
}
};
ll.addView(segmentsView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(12)) {{
leftMargin = rightMargin = ViewUtils.dp(12);
topMargin = bottomMargin = ViewUtils.dp(8);
}});
updateRemaining();
ll.addView(new DividerView(ctx), new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(1f)));
LinearLayout toolbar = new LinearLayout(ctx);
toolbar.setPadding(ViewUtils.dp(12), 0, ViewUtils.dp(12), 0);
toolbar.setOrientation(LinearLayout.HORIZONTAL);
toolbar.setGravity(Gravity.CENTER_VERTICAL);
toolbar.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 0));
toolbar.setOnClickListener(v -> dismiss());
ImageView icon = new ImageView(ctx);
icon.setImageResource(R.drawable.arrow_left_outline_28);
icon.setColorFilter(ThemesRepo.getColor(android.R.attr.textColorSecondary));
toolbar.addView(icon, new LinearLayout.LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28)));
TextView title = new TextView(ctx);
title.setText(R.string.MenuOrientationPositionBack);
title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
title.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
toolbar.addView(title, new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f) {{
leftMargin = ViewUtils.dp(12);
}});
ll.addView(toolbar, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)));
return ll;
}
@Override
protected void onCreate() {
super.onCreate();
SliceBeam.EVENT_BUS.registerListener(this);
ViewUtils.postOnMainThread(() -> segmentsView.startAnimation(), 50);
}
@EventHandler(runOnMainThread = true)
public void onDismiss(NeedDismissAIGeneratorMenu e) {
dismiss();
}
@EventHandler(runOnMainThread = true)
public void onRemainingUpdated(CloudModelsRemainingCountUpdatedEvent e) {
updateRemaining();
}
@Override
protected void onDestroy() {
super.onDestroy();
SliceBeam.EVENT_BUS.unregisterListener(this);
}
private void updateRemaining() {
int rev = CloudController.getMaxGeneratedModels() - CloudController.getGeneratedModels();
remainingView.setText(SliceBeam.INSTANCE.getString(R.string.MenuFileAIGeneratorRemaining, rev, CloudController.getMaxGeneratedModels()));
segmentsView.setValues(new float[]{0, rev / (float) CloudController.getMaxGeneratedModels(), 1});
if (idx != -1) {
((BedMenuItem) adapter.getItems().get(idx)).setEnabled(hasModel());
adapter.notifyItemChanged(idx);
}
}
@@ -427,7 +260,7 @@ public class FileMenu extends ListBedMenu {
private String loadJSLoader(String key) {
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();
byte[] buffer = new byte[10240]; int c;
while ((c = in.read(buffer)) != -1) {
@@ -436,7 +269,7 @@ public class FileMenu extends ListBedMenu {
bos.close();
in.close();
ConfigObject cfg = SliceBeam.buildCurrentConfigObject();
ConfigObject cfg = Santoku.buildCurrentConfigObject();
Bed3D bed = FileMenu.this.fragment.getGlView().getRenderer().getBed();
double bedX = bed.getVolumeMax().x - bed.getVolumeMin().x;
double bedY = bed.getVolumeMax().y - bed.getVolumeMin().y;
@@ -562,14 +395,14 @@ public class FileMenu extends ListBedMenu {
protected void onCreate() {
super.onCreate();
SliceBeam.EVENT_BUS.registerListener(this);
Santoku.EVENT_BUS.registerListener(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
SliceBeam.EVENT_BUS.unregisterListener(this);
Santoku.EVENT_BUS.unregisterListener(this);
}
}
@@ -577,10 +410,10 @@ public class FileMenu extends ListBedMenu {
private void loadModel(String key) {
BedFragment fragment = this.fragment;
ViewUtils.postOnMainThread(() -> {
File f = new File(SliceBeam.getModelCacheDir(), "calibration_" + key + ".stl");
File f = new File(Santoku.getModelCacheDir(), "calibration_" + key + ".stl");
new Thread(()->{
try {
InputStream in = SliceBeam.INSTANCE.getAssets().open("models/" + key + ".stl");
InputStream in = Santoku.INSTANCE.getAssets().open("models/" + key + ".stl");
FileOutputStream fos = new FileOutputStream(f);
byte[] buffer = new byte[10240]; int c;
while ((c = in.read(buffer)) != -1) {
@@ -595,9 +428,9 @@ public class FileMenu extends ListBedMenu {
fragment.loadGCode(f);
} else {
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) {
f.delete();
@@ -618,7 +451,7 @@ public class FileMenu extends ListBedMenu {
}
}).start();
}, 200);
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissCalibrationsMenu());
Santoku.EVENT_BUS.fireEvent(new NeedDismissCalibrationsMenu());
dismiss(true);
}
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.components.bed_menu;
package com.dark98.santoku.components.bed_menu;
import android.content.Context;
import android.graphics.Rect;
@@ -11,11 +11,11 @@ import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerAdapter;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.fragment.BedFragment;
import com.dark98.santoku.recycler.SimpleRecyclerAdapter;
import com.dark98.santoku.recycler.SimpleRecyclerItem;
import com.dark98.santoku.utils.ViewUtils;
public abstract class ListBedMenu extends BedMenu {
protected BedFragment fragment;
@@ -69,13 +69,13 @@ public abstract class ListBedMenu extends BedMenu {
@Override
public void onViewCreated(View v) {
super.onViewCreated(v);
SliceBeam.EVENT_BUS.registerListener(ListBedMenu.this);
Santoku.EVENT_BUS.registerListener(ListBedMenu.this);
}
@Override
public void onViewDestroyed() {
super.onViewDestroyed();
SliceBeam.EVENT_BUS.unregisterListener(ListBedMenu.this);
Santoku.EVENT_BUS.unregisterListener(ListBedMenu.this);
}
protected abstract List<SimpleRecyclerItem> onCreateItems(boolean portrait);
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.components.bed_menu;
package com.dark98.santoku.components.bed_menu;
import android.content.Context;
import android.graphics.drawable.Drawable;
@@ -26,25 +26,25 @@ import java.util.List;
import java.util.Locale;
import ru.ytkab0bp.eventbus.EventHandler;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
import ru.ytkab0bp.slicebeam.events.FlattenModeResetEvent;
import ru.ytkab0bp.slicebeam.events.LongClickTranslationEvent;
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
import ru.ytkab0bp.slicebeam.events.SelectedObjectChangedEvent;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
import ru.ytkab0bp.slicebeam.slic3r.Model;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.Vec3d;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.BeamButton;
import ru.ytkab0bp.slicebeam.view.DividerView;
import ru.ytkab0bp.slicebeam.view.PositionScrollView;
import ru.ytkab0bp.slicebeam.view.TextColorImageSpan;
import com.dark98.santoku.R;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.components.BeamAlertDialogBuilder;
import com.dark98.santoku.components.UnfoldMenu;
import com.dark98.santoku.events.FlattenModeResetEvent;
import com.dark98.santoku.events.LongClickTranslationEvent;
import com.dark98.santoku.events.NeedSnackbarEvent;
import com.dark98.santoku.events.ObjectsListChangedEvent;
import com.dark98.santoku.events.SelectedObjectChangedEvent;
import com.dark98.santoku.recycler.SimpleRecyclerItem;
import com.dark98.santoku.recycler.SpaceItem;
import com.dark98.santoku.slic3r.Model;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.Vec3d;
import com.dark98.santoku.utils.ViewUtils;
import com.dark98.santoku.view.BeamButton;
import com.dark98.santoku.view.DividerView;
import com.dark98.santoku.view.PositionScrollView;
import com.dark98.santoku.view.TextColorImageSpan;
public class OrientationMenu extends ListBedMenu {
private boolean hasSelection() {
@@ -61,7 +61,7 @@ public class OrientationMenu extends ListBedMenu {
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),
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 -> {
@@ -76,7 +76,7 @@ public class OrientationMenu extends ListBedMenu {
fragment.getGlView().getRenderer().invalidateGlModel(i);
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) -> {
fragment.getGlView().getRenderer().setInFlattenMode(isChecked);
@@ -200,10 +200,10 @@ public class OrientationMenu extends ListBedMenu {
}
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");
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.setBounds(0, 0, size, size);
sb.setSpan(new TextColorImageSpan(dr, 0), sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
@@ -355,7 +355,7 @@ public class OrientationMenu extends ListBedMenu {
protected void onCreate() {
super.onCreate();
SliceBeam.EVENT_BUS.registerListener(this);
Santoku.EVENT_BUS.registerListener(this);
setSelectionValues();
}
@@ -363,7 +363,7 @@ public class OrientationMenu extends ListBedMenu {
protected void onDestroy() {
super.onDestroy();
SliceBeam.EVENT_BUS.unregisterListener(this);
Santoku.EVENT_BUS.unregisterListener(this);
stopScroll();
}
@@ -501,10 +501,10 @@ public class OrientationMenu extends ListBedMenu {
}
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");
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.setBounds(0, 0, size, size);
sb.setSpan(new TextColorImageSpan(dr, 0), sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
@@ -666,7 +666,7 @@ public class OrientationMenu extends ListBedMenu {
protected void onCreate() {
super.onCreate();
SliceBeam.EVENT_BUS.registerListener(this);
Santoku.EVENT_BUS.registerListener(this);
setSelectionValues();
}
@@ -674,7 +674,7 @@ public class OrientationMenu extends ListBedMenu {
protected void onDestroy() {
super.onDestroy();
SliceBeam.EVENT_BUS.unregisterListener(this);
Santoku.EVENT_BUS.unregisterListener(this);
stopScroll();
}
@@ -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.app.Activity;
@@ -46,27 +46,28 @@ import java.util.UUID;
import cz.msebera.android.httpclient.Header;
import cz.msebera.android.httpclient.entity.ContentType;
import cz.msebera.android.httpclient.message.BasicHeader;
import ru.ytkab0bp.slicebeam.BuildConfig;
import ru.ytkab0bp.slicebeam.MainActivity;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import ru.ytkab0bp.slicebeam.slic3r.GCodeProcessorResult;
import ru.ytkab0bp.slicebeam.slic3r.GCodeViewer;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
import ru.ytkab0bp.slicebeam.theme.IThemeView;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.DividerView;
import ru.ytkab0bp.slicebeam.view.PositionScrollView;
import ru.ytkab0bp.slicebeam.view.SegmentsView;
import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
import com.dark98.santoku.BuildConfig;
import com.dark98.santoku.MainActivity;
import com.dark98.santoku.R;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.components.BeamAlertDialogBuilder;
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.fragment.BedFragment;
import com.dark98.santoku.print_host.ElegooLinkClient;
import com.dark98.santoku.recycler.SimpleRecyclerItem;
import com.dark98.santoku.slic3r.GCodeProcessorResult;
import com.dark98.santoku.slic3r.GCodeViewer;
import com.dark98.santoku.slic3r.Slic3rLocalization;
import com.dark98.santoku.theme.IThemeView;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils;
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 {
private AsyncHttpClient client = new AsyncHttpClient();
@@ -76,12 +77,12 @@ public class SliceMenu extends ListBedMenu {
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;
@Override
protected List<SimpleRecyclerItem> onCreateItems(boolean portrait) {
lastUid = SliceBeam.CONFIG_UID;
lastUid = Santoku.CONFIG_UID;
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.MenuSliceLayers, R.drawable.square_stack_up_outline_28).onClick(v -> fragment.showUnfoldMenu(new LayersMenu(), v)),
@@ -113,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);
String type = obj.get("host_type");
if (type == null) type = "octoprint";
@@ -121,22 +122,23 @@ public class SliceMenu extends ListBedMenu {
String apiKey = obj.get("printhost_apikey");
if (SUPPORTED_SEND.contains(type) && !TextUtils.isEmpty(host)) {
String finalType = type;
items.add(new BedMenuItem(R.string.MenuSliceSendToPrinter, R.drawable.send_outline_28).onClick(v -> upload(finalType, host, apiKey, false)));
items.add(new BedMenuItem(R.string.MenuSliceSendToPrinterAndPrint, R.drawable.send_28).onClick(v -> upload(finalType, host, apiKey, true)));
ConfigObject finalObj = obj;
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;
}
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();
switch (type) {
default:
case "octoprint":
if (!host.startsWith("http://")) {
case "klipper":
case "moonraker": {
if (!host.startsWith("http://") && !host.startsWith("https://")) {
host = "http://" + host;
}
String tag = UUID.randomUUID().toString();
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuSliceSendToPrinterLoading).tag(tag));
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 {
@@ -147,16 +149,27 @@ public class SliceMenu extends ListBedMenu {
params.put("select", 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
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
try {
JSONObject obj = new JSONObject(new String(responseBody));
if (!obj.has("action") && !obj.has("files")) {
throw new JSONException(obj.toString());
// Moonraker responses may wrap data in "result"
JSONObject payload = obj;
if (obj.has("result") && obj.opt("result") instanceof JSONObject) {
payload = obj.optJSONObject("result");
}
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(print ? SnackbarsLayout.Type.INFO : SnackbarsLayout.Type.DONE, 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) {
onFailure(statusCode, headers, responseBody, e);
}
@@ -164,7 +177,7 @@ public class SliceMenu extends ListBedMenu {
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(fragment.getContext())
.setTitle(R.string.MenuSliceSendToPrinterFailed)
.setMessage(error.toString())
@@ -173,6 +186,86 @@ public class SliceMenu extends ListBedMenu {
}
});
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;
}
}
}
@@ -182,7 +275,7 @@ public class SliceMenu extends ListBedMenu {
v.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(@NonNull View v) {
if (lastUid != SliceBeam.CONFIG_UID) {
if (lastUid != Santoku.CONFIG_UID) {
adapter.setItems(onCreateItems(v.getWidth() < v.getHeight()));
}
}
@@ -350,9 +443,9 @@ public class SliceMenu extends ListBedMenu {
private static String formatComplex(double weight, double length, float time) {
StringBuilder sb = new StringBuilder();
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));
return sb.toString();
}
@@ -365,17 +458,17 @@ public class SliceMenu extends ListBedMenu {
StringBuilder sb = new StringBuilder();
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 (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 (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();
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.components.bed_menu;
package com.dark98.santoku.components.bed_menu;
import android.content.Context;
import android.graphics.drawable.Drawable;
@@ -16,6 +16,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Space;
import android.widget.TextView;
import android.widget.Toast;
import androidx.core.content.ContextCompat;
@@ -25,23 +26,24 @@ import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
import ru.ytkab0bp.eventbus.EventHandler;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
import ru.ytkab0bp.slicebeam.events.SelectedObjectChangedEvent;
import ru.ytkab0bp.slicebeam.recycler.PreferenceSwitchItem;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import ru.ytkab0bp.slicebeam.slic3r.Model;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.DoubleMatrix;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.Vec3d;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.DividerView;
import ru.ytkab0bp.slicebeam.view.PositionScrollView;
import ru.ytkab0bp.slicebeam.view.TextColorImageSpan;
import com.dark98.santoku.R;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.components.BeamAlertDialogBuilder;
import com.dark98.santoku.components.UnfoldMenu;
import com.dark98.santoku.events.ObjectsListChangedEvent;
import com.dark98.santoku.events.SelectedObjectChangedEvent;
import com.dark98.santoku.recycler.PreferenceSwitchItem;
import com.dark98.santoku.recycler.SimpleRecyclerItem;
import com.dark98.santoku.render.GLRenderer;
import com.dark98.santoku.slic3r.Model;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.DoubleMatrix;
import com.dark98.santoku.utils.Prefs;
import com.dark98.santoku.utils.Vec3d;
import com.dark98.santoku.utils.ViewUtils;
import com.dark98.santoku.view.DividerView;
import com.dark98.santoku.view.PositionScrollView;
import com.dark98.santoku.view.TextColorImageSpan;
public class TransformMenu extends ListBedMenu {
private double[] tempMatrix = new double[16];
@@ -55,6 +57,77 @@ public class TransformMenu extends ListBedMenu {
protected List<SimpleRecyclerItem> onCreateItems(boolean portrait) {
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.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 -> {
Context ctx = fragment.getContext();
new BeamAlertDialogBuilder(ctx)
@@ -105,6 +178,9 @@ public class TransformMenu extends ListBedMenu {
((BedMenuItem) adapter.getItems().get(1)).setEnabled(hasSelection());
adapter.notifyItemChanged(1);
((BedMenuItem) adapter.getItems().get(2)).setEnabled(hasSelection());
adapter.notifyItemChanged(2);
}
@EventHandler(runOnMainThread = true)
@@ -114,6 +190,9 @@ public class TransformMenu extends ListBedMenu {
((BedMenuItem) adapter.getItems().get(1)).setEnabled(hasSelection());
adapter.notifyItemChanged(1);
((BedMenuItem) adapter.getItems().get(2)).setEnabled(hasSelection());
adapter.notifyItemChanged(2);
}
public final class ScaleMenu extends UnfoldMenu {
@@ -251,10 +330,10 @@ public class TransformMenu extends ListBedMenu {
}
private CharSequence formatTrackTitle(int res, double value, double mm) {
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(SliceBeam.INSTANCE.getString(res, value, mm));
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(Santoku.INSTANCE.getString(res, value, mm));
sb.append(" d");
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.setBounds(0, 0, size, size);
sb.setSpan(new TextColorImageSpan(dr, 0), sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
@@ -509,7 +588,7 @@ public class TransformMenu extends ListBedMenu {
protected void onCreate() {
super.onCreate();
SliceBeam.EVENT_BUS.registerListener(this);
Santoku.EVENT_BUS.registerListener(this);
setSelectionValues();
}
@@ -517,7 +596,7 @@ public class TransformMenu extends ListBedMenu {
protected void onDestroy() {
super.onDestroy();
SliceBeam.EVENT_BUS.unregisterListener(this);
Santoku.EVENT_BUS.unregisterListener(this);
stopScroll();
}
@@ -1,12 +1,12 @@
package ru.ytkab0bp.slicebeam.config;
package com.dark98.santoku.config;
import java.util.HashMap;
import java.util.Map;
import ru.ytkab0bp.slicebeam.BuildConfig;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.fragment.ProfileListFragment;
import com.dark98.santoku.BuildConfig;
import com.dark98.santoku.R;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.fragment.ProfileListFragment;
/** @noinspection CopyConstructorMissesField*/
public class ConfigObject implements ProfileListFragment.ProfileListItem {
@@ -70,28 +70,35 @@ public class ConfigObject implements ProfileListFragment.ProfileListItem {
public boolean isSelected() {
switch (profileListType) {
case PROFILE_LIST_PRINT:
return getTitle().equals(SliceBeam.CONFIG.presets.get("print"));
return getTitle().equals(Santoku.CONFIG.presets.get("print"));
case PROFILE_LIST_FILAMENT:
return getTitle().equals(SliceBeam.CONFIG.presets.get("filament"));
return getTitle().equals(Santoku.CONFIG.presets.get("filament"));
case PROFILE_LIST_PRINTER:
return getTitle().equals(SliceBeam.CONFIG.presets.get("printer"));
return getTitle().equals(Santoku.CONFIG.presets.get("printer"));
}
return false;
}
public String serialize() {
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()) {
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();
}
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("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("gcode_flavor", "marlin");
custom.put("max_print_height", "200");
@@ -124,6 +131,10 @@ public class ConfigObject implements ProfileListFragment.ProfileListItem {
custom.put("machine_min_extruding_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("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");
@@ -131,7 +142,7 @@ public class ConfigObject implements ProfileListFragment.ProfileListItem {
}
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.put("first_layer_bed_temperature", "60");
genericFilament.put("bed_temperature", "60");
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.events;
package com.dark98.santoku.events;
import ru.ytkab0bp.eventbus.Event;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.events;
package com.dark98.santoku.events;
import ru.ytkab0bp.eventbus.Event;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.events;
package com.dark98.santoku.events;
import ru.ytkab0bp.eventbus.Event;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.events;
package com.dark98.santoku.events;
import ru.ytkab0bp.eventbus.Event;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.events;
package com.dark98.santoku.events;
import ru.ytkab0bp.eventbus.Event;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.events;
package com.dark98.santoku.events;
import ru.ytkab0bp.eventbus.Event;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.events;
package com.dark98.santoku.events;
import ru.ytkab0bp.eventbus.Event;
@@ -1,10 +1,10 @@
package ru.ytkab0bp.slicebeam.events;
package com.dark98.santoku.events;
import android.view.View;
import ru.ytkab0bp.eventbus.Event;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.view.SnackbarsLayout;
@Event
public class NeedSnackbarEvent {
@@ -25,12 +25,12 @@ public class NeedSnackbarEvent {
}
public NeedSnackbarEvent(int title, Object... args) {
this.title = SliceBeam.INSTANCE.getString(title, args);
this.title = Santoku.INSTANCE.getString(title, args);
}
public NeedSnackbarEvent(SnackbarsLayout.Type type, int title, Object... args) {
this.type = type;
this.title = SliceBeam.INSTANCE.getString(title, args);
this.title = Santoku.INSTANCE.getString(title, args);
}
public NeedSnackbarEvent tag(String tag) {
@@ -39,7 +39,7 @@ public class NeedSnackbarEvent {
}
public NeedSnackbarEvent button(int title, View.OnClickListener click) {
this.buttonTitle = SliceBeam.INSTANCE.getString(title);
this.buttonTitle = Santoku.INSTANCE.getString(title);
this.buttonClick = click;
return this;
}
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.events;
package com.dark98.santoku.events;
import ru.ytkab0bp.eventbus.Event;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.events;
package com.dark98.santoku.events;
import ru.ytkab0bp.eventbus.Event;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.events;
package com.dark98.santoku.events;
public class SlicingProgressEvent {
public final int progress;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.fragment;
package com.dark98.santoku.fragment;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -32,37 +32,37 @@ import com.google.android.material.navigation.NavigationBarView;
import java.io.File;
import ru.ytkab0bp.eventbus.EventHandler;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.components.SliceProgressBottomSheet;
import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
import ru.ytkab0bp.slicebeam.components.bed_menu.BedMenu;
import ru.ytkab0bp.slicebeam.components.bed_menu.CameraMenu;
import ru.ytkab0bp.slicebeam.components.bed_menu.FileMenu;
import ru.ytkab0bp.slicebeam.components.bed_menu.OrientationMenu;
import ru.ytkab0bp.slicebeam.components.bed_menu.SliceMenu;
import ru.ytkab0bp.slicebeam.components.bed_menu.TransformMenu;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.events.FlattenModeResetEvent;
import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import ru.ytkab0bp.slicebeam.events.SlicingProgressEvent;
import ru.ytkab0bp.slicebeam.navigation.Fragment;
import ru.ytkab0bp.slicebeam.slic3r.Bed3D;
import ru.ytkab0bp.slicebeam.slic3r.GCodeProcessorResult;
import ru.ytkab0bp.slicebeam.slic3r.GCodeThumbnailer;
import ru.ytkab0bp.slicebeam.slic3r.Model;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rRuntimeError;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.Vec3d;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.BedSwipeDownLayout;
import ru.ytkab0bp.slicebeam.view.DividerView;
import ru.ytkab0bp.slicebeam.view.GLView;
import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
import ru.ytkab0bp.slicebeam.view.ThemeBottomNavigationView;
import ru.ytkab0bp.slicebeam.view.ThemeRailNavigationView;
import com.dark98.santoku.R;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.components.BeamAlertDialogBuilder;
import com.dark98.santoku.components.SliceProgressBottomSheet;
import com.dark98.santoku.components.UnfoldMenu;
import com.dark98.santoku.components.bed_menu.BedMenu;
import com.dark98.santoku.components.bed_menu.CameraMenu;
import com.dark98.santoku.components.bed_menu.FileMenu;
import com.dark98.santoku.components.bed_menu.OrientationMenu;
import com.dark98.santoku.components.bed_menu.SliceMenu;
import com.dark98.santoku.components.bed_menu.TransformMenu;
import com.dark98.santoku.config.ConfigObject;
import com.dark98.santoku.events.FlattenModeResetEvent;
import com.dark98.santoku.events.NeedDismissSnackbarEvent;
import com.dark98.santoku.events.NeedSnackbarEvent;
import com.dark98.santoku.events.SlicingProgressEvent;
import com.dark98.santoku.navigation.Fragment;
import com.dark98.santoku.slic3r.Bed3D;
import com.dark98.santoku.slic3r.GCodeProcessorResult;
import com.dark98.santoku.slic3r.GCodeThumbnailer;
import com.dark98.santoku.slic3r.Model;
import com.dark98.santoku.slic3r.Slic3rRuntimeError;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.Vec3d;
import com.dark98.santoku.utils.ViewUtils;
import com.dark98.santoku.view.BedSwipeDownLayout;
import com.dark98.santoku.view.DividerView;
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 {
private final static boolean DEBUG_VIEWER = false;
@@ -129,14 +129,14 @@ public class BedFragment extends Fragment {
}
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
public void onCreate() {
super.onCreate();
SliceBeam.EVENT_BUS.registerListener(this);
Santoku.EVENT_BUS.registerListener(this);
}
@EventHandler(runOnMainThread = true)
@@ -210,7 +210,7 @@ public class BedFragment extends Fragment {
public void onDestroy() {
super.onDestroy();
SliceBeam.EVENT_BUS.unregisterListener(this);
Santoku.EVENT_BUS.unregisterListener(this);
for (int i = 0; i < menuMap.size(); i++) {
menuMap.valueAt(i).onViewDestroyed();
@@ -232,7 +232,7 @@ public class BedFragment extends Fragment {
public void onResume() {
super.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")) && panelWebView != null;
swipeDownLayout.setEnableTop(enable);
if (enable) {
@@ -388,7 +388,7 @@ public class BedFragment extends Fragment {
.show();
} else {
tempExportingFile = null;
File cfg = SliceBeam.getCurrentConfigFile();
File cfg = Santoku.getCurrentConfigFile();
File gcode = getTempGCodePath();
if (!DEBUG_VIEWER) {
@@ -399,12 +399,12 @@ public class BedFragment extends Fragment {
Process.setThreadPriority(-20);
try {
SliceBeam.genCurrentConfig();
Santoku.genCurrentConfig();
} catch (Exception e) {
Log.e("BedFragment", "Failed to write config", e);
ViewUtils.postOnMainThread(()->{
SliceBeam.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, ""));
Santoku.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, ""));
new BeamAlertDialogBuilder(ctx)
.setTitle(R.string.SliceFailed)
.setMessage(e.getMessage())
@@ -414,12 +414,12 @@ public class BedFragment extends Fragment {
}
if (!DEBUG_VIEWER) {
gCodeResult = glView.getRenderer().getModel().slice(cfg.getAbsolutePath(), gcode.getAbsolutePath(), (progress, text) -> SliceBeam.EVENT_BUS.fireEvent(new SlicingProgressEvent(progress, text)));
GCodeThumbnailer.addThumbnailsToGcode(gcode, SliceBeam.buildCurrentConfigObject(), glView);
SliceBeam.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, ""));
gCodeResult = glView.getRenderer().getModel().slice(cfg.getAbsolutePath(), gcode.getAbsolutePath(), (progress, text) -> Santoku.EVENT_BUS.fireEvent(new SlicingProgressEvent(progress, text)));
GCodeThumbnailer.addThumbnailsToGcode(gcode, Santoku.buildCurrentConfigObject(), glView);
Santoku.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, ""));
} else {
gCodeResult = new GCodeProcessorResult(gcode);
GCodeThumbnailer.addThumbnailsToGcode(gcode, SliceBeam.buildCurrentConfigObject(), glView);
GCodeThumbnailer.addThumbnailsToGcode(gcode, Santoku.buildCurrentConfigObject(), glView);
}
ViewUtils.postOnMainThread(()-> {
glView.queueEvent(()->{
@@ -438,7 +438,7 @@ public class BedFragment extends Fragment {
} catch (Exception e) {
Log.e("BedFragment", "Slice failed", e);
ViewUtils.postOnMainThread(()->{
SliceBeam.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, ""));
Santoku.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, ""));
new BeamAlertDialogBuilder(ctx)
.setTitle(R.string.SliceFailed)
.setMessage(e.getMessage())
@@ -491,7 +491,7 @@ public class BedFragment extends Fragment {
private void selectMenu(Context ctx, boolean portrait, int slot) {
if (glView.getRenderer().resetFlattenMode()) {
glView.requestRender();
SliceBeam.EVENT_BUS.fireEvent(new FlattenModeResetEvent());
Santoku.EVENT_BUS.fireEvent(new FlattenModeResetEvent());
}
isAnimatingMenu = true;
@@ -1,18 +1,18 @@
package ru.ytkab0bp.slicebeam.fragment;
package com.dark98.santoku.fragment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rUtils;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.R;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.config.ConfigObject;
import com.dark98.santoku.recycler.SpaceItem;
import com.dark98.santoku.slic3r.PrintConfigDef;
import com.dark98.santoku.slic3r.Slic3rLocalization;
import com.dark98.santoku.slic3r.Slic3rUtils;
import com.dark98.santoku.utils.ViewUtils;
public class FilamentConfigFragment extends ProfileListFragment {
private List<ProfileListItem> compatItems;
@@ -29,18 +29,18 @@ public class FilamentConfigFragment extends ProfileListFragment {
@Override
protected List<ProfileListItem> getItems(boolean filter) {
List<ConfigObject> list = SliceBeam.CONFIG.filamentConfigs;
List<ConfigObject> list = Santoku.CONFIG.filamentConfigs;
if (filter) {
String printer = SliceBeam.CONFIG.presets.get("printer");
String print = SliceBeam.CONFIG.presets.get("print");
if (Objects.equals(lastPrinter, printer) && Objects.equals(lastPrint, print) && compatItems != null && lastUid == SliceBeam.CONFIG_UID) {
String printer = Santoku.CONFIG.presets.get("printer");
String print = Santoku.CONFIG.presets.get("print");
if (Objects.equals(lastPrinter, printer) && Objects.equals(lastPrint, print) && compatItems != null && lastUid == Santoku.CONFIG_UID) {
return compatItems;
}
List<ConfigObject> nList = new ArrayList<>(list.size());
Slic3rUtils.ConfigChecker checker = new Slic3rUtils.ConfigChecker(SliceBeam.CONFIG.findPrinter(printer).serialize());
if (SliceBeam.CONFIG.findPrint(print) != null) {
Slic3rUtils.ConfigChecker printChecker = new Slic3rUtils.ConfigChecker(SliceBeam.CONFIG.findPrint(print).serialize());
Slic3rUtils.ConfigChecker checker = new Slic3rUtils.ConfigChecker(Santoku.CONFIG.findPrinter(printer).serialize());
if (Santoku.CONFIG.findPrint(print) != null) {
Slic3rUtils.ConfigChecker printChecker = new Slic3rUtils.ConfigChecker(Santoku.CONFIG.findPrint(print).serialize());
for (ConfigObject obj : list) {
if (checker.checkCompatibility(obj.get("compatible_printers_condition")) && printChecker.checkCompatibility(obj.get("compatible_prints_condition"))) {
nList.add(obj);
@@ -51,7 +51,7 @@ public class FilamentConfigFragment extends ProfileListFragment {
checker.release();
lastPrinter = printer;
lastPrint = print;
lastUid = SliceBeam.CONFIG_UID;
lastUid = Santoku.CONFIG_UID;
return compatItems = (List) nList;
}
return (List) list;
@@ -218,14 +218,14 @@ public class FilamentConfigFragment extends ProfileListFragment {
@Override
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);
currentConfig = new ConfigObject(obj);
SliceBeam.CONFIG.filamentConfigs.add(obj);
SliceBeam.CONFIG.presets.put("filament", obj.getTitle());
SliceBeam.saveConfig();
SliceBeam.getCurrentConfigFile().delete();
Santoku.CONFIG.filamentConfigs.add(obj);
Santoku.CONFIG.presets.put("filament", obj.getTitle());
Santoku.saveConfig();
Santoku.getCurrentConfigFile().delete();
currentConfig = new ConfigObject(obj);
dropdownView.setTitle(getCurrentConfig().getTitle());
@@ -235,7 +235,7 @@ public class FilamentConfigFragment extends ProfileListFragment {
@Override
protected void deleteCurrentProfile() {
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));
dropdownView.setTitle(getCurrentConfig().getTitle());
@@ -244,21 +244,25 @@ public class FilamentConfigFragment extends ProfileListFragment {
@Override
protected void onApplyConfig(String title) {
compatItems = null;
ConfigObject obj = SliceBeam.CONFIG.findFilament(currentConfig.getTitle());
ConfigObject obj = Santoku.CONFIG.findFilament(currentConfig.getTitle());
obj.setTitle(title);
obj.values.putAll(currentConfig.values);
currentConfig.setTitle(title);
SliceBeam.CONFIG.presets.put("filament", title);
SliceBeam.saveConfig();
SliceBeam.getCurrentConfigFile().delete();
Santoku.CONFIG.presets.put("filament", title);
Santoku.saveConfig();
Santoku.getCurrentConfigFile().delete();
dropdownView.setTitle(title);
}
@Override
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
@@ -274,7 +278,7 @@ public class FilamentConfigFragment extends ProfileListFragment {
@Override
protected void selectItem(ProfileListItem item) {
currentConfig = new ConfigObject((ConfigObject) item);
SliceBeam.CONFIG.presets.put("filament", item.getTitle());
SliceBeam.saveConfig();
Santoku.CONFIG.presets.put("filament", item.getTitle());
Santoku.saveConfig();
}
}
@@ -1,17 +1,17 @@
package ru.ytkab0bp.slicebeam.fragment;
package com.dark98.santoku.fragment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rUtils;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.R;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.config.ConfigObject;
import com.dark98.santoku.recycler.SpaceItem;
import com.dark98.santoku.slic3r.PrintConfigDef;
import com.dark98.santoku.slic3r.Slic3rUtils;
import com.dark98.santoku.utils.ViewUtils;
public class PrintConfigFragment extends ProfileListFragment {
private List<ProfileListItem> compatItems;
@@ -28,15 +28,15 @@ public class PrintConfigFragment extends ProfileListFragment {
@Override
protected List<ProfileListItem> getItems(boolean filter) {
List<ConfigObject> list = SliceBeam.CONFIG.printConfigs;
List<ConfigObject> list = Santoku.CONFIG.printConfigs;
if (filter) {
String printer = SliceBeam.CONFIG.presets.get("printer");
if (Objects.equals(lastPrinter, printer) && compatItems != null && lastUid == SliceBeam.CONFIG_UID) {
String printer = Santoku.CONFIG.presets.get("printer");
if (Objects.equals(lastPrinter, printer) && compatItems != null && lastUid == Santoku.CONFIG_UID) {
return compatItems;
}
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) {
if (checker.checkCompatibility(obj.get("compatible_printers_condition"))) {
nList.add(obj);
@@ -44,7 +44,7 @@ public class PrintConfigFragment extends ProfileListFragment {
}
checker.release();
lastPrinter = printer;
lastUid = SliceBeam.CONFIG_UID;
lastUid = Santoku.CONFIG_UID;
return compatItems = (List) nList;
}
return (List) list;
@@ -354,14 +354,14 @@ public class PrintConfigFragment extends ProfileListFragment {
@Override
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);
currentConfig = new ConfigObject(obj);
SliceBeam.CONFIG.printConfigs.add(obj);
SliceBeam.CONFIG.presets.put("print", obj.getTitle());
SliceBeam.saveConfig();
SliceBeam.getCurrentConfigFile().delete();
Santoku.CONFIG.printConfigs.add(obj);
Santoku.CONFIG.presets.put("print", obj.getTitle());
Santoku.saveConfig();
Santoku.getCurrentConfigFile().delete();
currentConfig = new ConfigObject(obj);
dropdownView.setTitle(getCurrentConfig().getTitle());
@@ -371,7 +371,7 @@ public class PrintConfigFragment extends ProfileListFragment {
@Override
protected void deleteCurrentProfile() {
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));
dropdownView.setTitle(getCurrentConfig().getTitle());
@@ -380,28 +380,28 @@ public class PrintConfigFragment extends ProfileListFragment {
@Override
protected void onApplyConfig(String title) {
compatItems = null;
ConfigObject obj = SliceBeam.CONFIG.findPrint(currentConfig.getTitle());
ConfigObject obj = Santoku.CONFIG.findPrint(currentConfig.getTitle());
obj.setTitle(title);
obj.values.putAll(currentConfig.values);
currentConfig.setTitle(title);
SliceBeam.CONFIG.presets.put("print", title);
SliceBeam.saveConfig();
SliceBeam.getCurrentConfigFile().delete();
Santoku.CONFIG.presets.put("print", title);
Santoku.saveConfig();
Santoku.getCurrentConfigFile().delete();
dropdownView.setTitle(title);
}
@Override
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) {
currentConfig = new ConfigObject(print);
} else {
currentConfig = new ConfigObject(SliceBeam.INSTANCE.getString(R.string.IntroCustomProfileName));
SliceBeam.CONFIG.printConfigs.add(new ConfigObject(currentConfig));
SliceBeam.saveConfig();
SliceBeam.getCurrentConfigFile().delete();
currentConfig = new ConfigObject(Santoku.INSTANCE.getString(R.string.IntroCustomProfileName));
Santoku.CONFIG.printConfigs.add(new ConfigObject(currentConfig));
Santoku.saveConfig();
Santoku.getCurrentConfigFile().delete();
}
}
@@ -418,7 +418,7 @@ public class PrintConfigFragment extends ProfileListFragment {
@Override
protected void selectItem(ProfileListItem item) {
currentConfig = new ConfigObject((ConfigObject) item);
SliceBeam.CONFIG.presets.put("print", item.getTitle());
SliceBeam.saveConfig();
Santoku.CONFIG.presets.put("print", item.getTitle());
Santoku.saveConfig();
}
}
@@ -1,16 +1,17 @@
package ru.ytkab0bp.slicebeam.fragment;
package com.dark98.santoku.fragment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.R;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.config.ConfigObject;
import com.dark98.santoku.recycler.SpaceItem;
import com.dark98.santoku.slic3r.PrintConfigDef;
import com.dark98.santoku.slic3r.ConfigOptionDef;
import com.dark98.santoku.slic3r.Slic3rLocalization;
import com.dark98.santoku.utils.ViewUtils;
public class PrinterConfigFragment extends ProfileListFragment {
private ConfigObject currentConfig;
@@ -23,7 +24,7 @@ public class PrinterConfigFragment extends ProfileListFragment {
@Override
protected List<ProfileListItem> getItems(boolean filter) {
return (List) SliceBeam.CONFIG.printerConfigs;
return (List) Santoku.CONFIG.printerConfigs;
}
@Override
@@ -164,6 +165,11 @@ public class PrinterConfigFragment extends ProfileListFragment {
));
}
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(new SubHeader("Notes")),
new OptionElement(def.options.get("printer_notes")),
@@ -175,19 +181,56 @@ public class PrinterConfigFragment extends ProfileListFragment {
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;
}
@Override
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);
currentConfig = new ConfigObject(obj);
SliceBeam.CONFIG.printerConfigs.add(obj);
SliceBeam.CONFIG.presets.put("printer", obj.getTitle());
SliceBeam.saveConfig();
SliceBeam.getCurrentConfigFile().delete();
Santoku.CONFIG.printerConfigs.add(obj);
Santoku.CONFIG.presets.put("printer", obj.getTitle());
Santoku.saveConfig();
Santoku.getCurrentConfigFile().delete();
currentConfig = new ConfigObject(obj);
dropdownView.setTitle(getCurrentConfig().getTitle());
@@ -195,28 +238,28 @@ public class PrinterConfigFragment extends ProfileListFragment {
@Override
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));
dropdownView.setTitle(getCurrentConfig().getTitle());
}
@Override
protected void onApplyConfig(String title) {
ConfigObject obj = SliceBeam.CONFIG.findPrinter(currentConfig.getTitle());
ConfigObject obj = Santoku.CONFIG.findPrinter(currentConfig.getTitle());
obj.setTitle(title);
obj.values.putAll(currentConfig.values);
currentConfig.setTitle(title);
SliceBeam.CONFIG.presets.put("printer", title);
SliceBeam.saveConfig();
SliceBeam.getCurrentConfigFile().delete();
Santoku.CONFIG.presets.put("printer", title);
Santoku.saveConfig();
Santoku.getCurrentConfigFile().delete();
dropdownView.setTitle(title);
}
@Override
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
@@ -232,9 +275,23 @@ public class PrinterConfigFragment extends ProfileListFragment {
@Override
protected void selectItem(ProfileListItem 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?
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();
}
}
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.fragment;
package com.dark98.santoku.fragment;
import android.annotation.SuppressLint;
import android.content.Context;
@@ -49,30 +49,30 @@ import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.cloud.CloudAPI;
import ru.ytkab0bp.slicebeam.cloud.CloudController;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.components.BeamColorPickerPopUp;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.navigation.Fragment;
import ru.ytkab0bp.slicebeam.recycler.CubicBezierItemAnimator;
import ru.ytkab0bp.slicebeam.recycler.PreferenceItem;
import ru.ytkab0bp.slicebeam.recycler.PreferenceSwitchItem;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import ru.ytkab0bp.slicebeam.slic3r.ConfigOptionDef;
import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
import ru.ytkab0bp.slicebeam.theme.IThemeView;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.BeamButton;
import ru.ytkab0bp.slicebeam.view.DividerView;
import ru.ytkab0bp.slicebeam.view.FadeRecyclerView;
import ru.ytkab0bp.slicebeam.view.ProfileDropdownView;
import com.dark98.santoku.R;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.cloud.CloudAPI;
import com.dark98.santoku.cloud.CloudController;
import com.dark98.santoku.components.BeamAlertDialogBuilder;
import com.dark98.santoku.components.BeamColorPickerPopUp;
import com.dark98.santoku.config.ConfigObject;
import com.dark98.santoku.navigation.Fragment;
import com.dark98.santoku.recycler.CubicBezierItemAnimator;
import com.dark98.santoku.recycler.PreferenceItem;
import com.dark98.santoku.recycler.PreferenceSwitchItem;
import com.dark98.santoku.recycler.SimpleRecyclerItem;
import com.dark98.santoku.slic3r.ConfigOptionDef;
import com.dark98.santoku.slic3r.PrintConfigDef;
import com.dark98.santoku.slic3r.Slic3rConfigWrapper;
import com.dark98.santoku.slic3r.Slic3rLocalization;
import com.dark98.santoku.theme.IThemeView;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.Prefs;
import com.dark98.santoku.utils.ViewUtils;
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 final static int SPECIAL_TYPE_CLOUD_HEADER = 0;
@@ -507,18 +507,19 @@ public abstract class ProfileListFragment extends Fragment {
@Override
public void onCreate() {
super.onCreate();
SliceBeam.EVENT_BUS.registerListener(this);
Santoku.EVENT_BUS.registerListener(this);
}
@Override
public void onDestroy() {
super.onDestroy();
SliceBeam.EVENT_BUS.unregisterListener(this);
Santoku.EVENT_BUS.unregisterListener(this);
unfolded.clear();
}
@SuppressLint("NotifyDataSetChanged")
protected void setConfigItems(List<OptionElement> items) {
categoryElements.clear();
List<OptionWrapper> list = new ArrayList<>();
int j = 0;
for (int i = 0; i < items.size(); i++) {
@@ -539,7 +540,21 @@ public abstract class ProfileListFragment extends Fragment {
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();
}
@@ -676,8 +691,8 @@ public abstract class ProfileListFragment extends Fragment {
String[] labels;
String[] values;
if (Objects.equals("host_type", def.key)) {
labels = new String[]{"OctoPrint"};
values = new String[]{"octoprint"};
labels = new String[]{"OctoPrint", "Klipper (Moonraker)", "ElegooLink"};
values = new String[]{"octoprint", "moonraker", "elegoolink"};
} else {
labels = new String[def.enumLabels.length];
values = def.enumValues;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.fragment;
package com.dark98.santoku.fragment;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -20,19 +20,18 @@ import java.util.Collections;
import java.util.List;
import ru.ytkab0bp.eventbus.EventHandler;
import ru.ytkab0bp.slicebeam.BeamServerData;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SetupActivity;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import ru.ytkab0bp.slicebeam.components.BeamColorPickerPopUp;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.events.BeamServerDataUpdatedEvent;
import ru.ytkab0bp.slicebeam.events.CloudUserInfoUpdatedEvent;
import ru.ytkab0bp.slicebeam.recycler.PreferenceItem;
import ru.ytkab0bp.slicebeam.theme.BeamTheme;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import com.dark98.santoku.R;
import com.dark98.santoku.SetupActivity;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.components.BeamAlertDialogBuilder;
import com.dark98.santoku.components.BeamColorPickerPopUp;
import com.dark98.santoku.config.ConfigObject;
import com.dark98.santoku.events.CloudUserInfoUpdatedEvent;
import com.dark98.santoku.cloud.CloudController;
import com.dark98.santoku.recycler.PreferenceItem;
import com.dark98.santoku.theme.BeamTheme;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.Prefs;
public class SettingsFragment extends ProfileListFragment {
@@ -46,7 +45,7 @@ public class SettingsFragment extends ProfileListFragment {
@Override
protected List<OptionElement> getConfigItems() {
return Arrays.asList(
BeamServerData.isCloudAvailable() ? new OptionElement(SPECIAL_TYPE_CLOUD_HEADER).setOnClick(() -> {
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,
@@ -112,7 +111,7 @@ public class SettingsFragment extends ProfileListFragment {
BeamTheme.LIGHT.colors.put(android.R.attr.colorAccent, Prefs.getAccentColor());
BeamTheme.DARK.colors.put(android.R.attr.colorAccent, Prefs.getAccentColor());
ThemesRepo.invalidate((Activity) getContext());
recyclerView.getAdapter().notifyItemChanged(2 - (BeamServerData.isCloudAvailable() ? 0 : 1));
recyclerView.getAdapter().notifyItemChanged(2 - (CloudController.hasAccountFeatures() ? 0 : 1));
}
})
.setNegativeButtonText(getContext().getString(R.string.SettingsInterfaceColorReset))
@@ -135,7 +134,7 @@ public class SettingsFragment extends ProfileListFragment {
Prefs.setRenderScale(variants[which]);
dialog.dismiss();
// I'm too lazy to calculate real position for now
recyclerView.getAdapter().notifyItemChanged(4 - (BeamServerData.isCloudAvailable() ? 0 : 1));
recyclerView.getAdapter().notifyItemChanged(4 - (CloudController.hasAccountFeatures() ? 0 : 1));
})
.show();
})),
@@ -143,18 +142,6 @@ public class SettingsFragment extends ProfileListFragment {
Activity act = (Activity) getContext();
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(act, SetupActivity.class).putExtra(SetupActivity.EXTRA_BOOSTY_ONLY, true));
}) : 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(() -> {
Context ctx = getContext();
if (ctx instanceof Activity) {
@@ -163,8 +150,8 @@ public class SettingsFragment extends ProfileListFragment {
.setTitle(R.string.SettingsResetToDefaultTitle)
.setMessage(R.string.SettingsResetToDefaultDescription)
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
SliceBeam.getConfigFile().delete();
SliceBeam.CONFIG = null;
Santoku.getConfigFile().delete();
Santoku.CONFIG = null;
Prefs.getPrefs().edit().clear().apply();
Prefs.setLastCommit();
act.startActivity(new Intent(act, SetupActivity.class));
@@ -177,14 +164,9 @@ public class SettingsFragment extends ProfileListFragment {
);
}
@EventHandler(runOnMainThread = true)
public void onDataUpdated(BeamServerDataUpdatedEvent e) {
setConfigItems(getConfigItems());
}
@EventHandler(runOnMainThread = true)
public void onUserInfoUpdated(CloudUserInfoUpdatedEvent e) {
if (BeamServerData.isCloudAvailable()) {
if (CloudController.hasAccountFeatures()) {
recyclerView.getAdapter().notifyItemChanged(0);
}
}
@@ -1,14 +1,14 @@
package ru.ytkab0bp.slicebeam.navigation;
package com.dark98.santoku.navigation;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import ru.ytkab0bp.slicebeam.fragment.FilamentConfigFragment;
import ru.ytkab0bp.slicebeam.fragment.PrintConfigFragment;
import ru.ytkab0bp.slicebeam.fragment.PrinterConfigFragment;
import ru.ytkab0bp.slicebeam.fragment.SettingsFragment;
import com.dark98.santoku.R;
import com.dark98.santoku.fragment.BedFragment;
import com.dark98.santoku.fragment.FilamentConfigFragment;
import com.dark98.santoku.fragment.PrintConfigFragment;
import com.dark98.santoku.fragment.PrinterConfigFragment;
import com.dark98.santoku.fragment.SettingsFragment;
public abstract class DelegateSlotImpl extends NavigationDelegate {
public int getSlotCount() {
@@ -1,11 +1,11 @@
package ru.ytkab0bp.slicebeam.navigation;
package com.dark98.santoku.navigation;
import android.content.Context;
import android.view.View;
import androidx.annotation.CallSuper;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.theme.ThemesRepo;
public abstract class Fragment {
private View mView;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.navigation;
package com.dark98.santoku.navigation;
import android.annotation.SuppressLint;
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.navigation.NavigationBarView;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.TextColorImageSpan;
import ru.ytkab0bp.slicebeam.view.ThemeBottomNavigationView;
import ru.ytkab0bp.slicebeam.view.ThemeRailNavigationView;
import com.dark98.santoku.R;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils;
import com.dark98.santoku.view.TextColorImageSpan;
import com.dark98.santoku.view.ThemeBottomNavigationView;
import com.dark98.santoku.view.ThemeRailNavigationView;
public class MobileNavigationDelegate extends DelegateSlotImpl {
private boolean portrait;
@@ -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.util.SparseArray;
@@ -14,7 +14,7 @@ import androidx.dynamicanimation.animation.SpringForce;
import java.util.Stack;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.theme.ThemesRepo;
public abstract class NavigationDelegate {
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;
}
}
}
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.recycler;
package com.dark98.santoku.recycler;
import android.content.Context;
import android.util.TypedValue;
@@ -8,8 +8,8 @@ import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils;
public class BigHeaderItem extends SimpleRecyclerItem<TextView> {
public String title;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ru.ytkab0bp.slicebeam.recycler;
package com.dark98.santoku.recycler;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -29,7 +29,7 @@ import androidx.recyclerview.widget.SimpleItemAnimator;
import java.util.ArrayList;
import java.util.List;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.utils.ViewUtils;
/**
* This implementation of {@link RecyclerView.ItemAnimator} provides basic
@@ -1,12 +1,12 @@
package ru.ytkab0bp.slicebeam.recycler;
package com.dark98.santoku.recycler;
import android.content.Context;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.DividerView;
import com.dark98.santoku.utils.ViewUtils;
import com.dark98.santoku.view.DividerView;
public class DividerItem extends SimpleRecyclerItem<DividerView> {
@Override
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.recycler;
package com.dark98.santoku.recycler;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -20,11 +20,11 @@ import androidx.appcompat.widget.AppCompatImageView;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.theme.BeamTheme;
import ru.ytkab0bp.slicebeam.theme.IThemeView;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.theme.BeamTheme;
import com.dark98.santoku.theme.IThemeView;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils;
public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.PreferenceHolderView> {
private Drawable mIcon;
@@ -75,7 +75,7 @@ public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.Preference
}
public PreferenceItem setIcon(int iconRes) {
mIcon = ContextCompat.getDrawable(SliceBeam.INSTANCE, iconRes);
mIcon = ContextCompat.getDrawable(Santoku.INSTANCE, iconRes);
return this;
}
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.recycler;
package com.dark98.santoku.recycler;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -15,12 +15,12 @@ import android.widget.TextView;
import androidx.core.content.ContextCompat;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.theme.IThemeView;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.BeamSwitch;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.theme.IThemeView;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.Prefs;
import com.dark98.santoku.utils.ViewUtils;
import com.dark98.santoku.view.BeamSwitch;
public class PreferenceSwitchItem extends SimpleRecyclerItem<PreferenceSwitchItem.SwitchPreferenceHolderView> {
private Drawable mIcon;
@@ -64,7 +64,7 @@ public class PreferenceSwitchItem extends SimpleRecyclerItem<PreferenceSwitchIte
}
public PreferenceSwitchItem setIcon(int iconRes) {
mIcon = ContextCompat.getDrawable(SliceBeam.INSTANCE, iconRes);
mIcon = ContextCompat.getDrawable(Santoku.INSTANCE, iconRes);
return this;
}
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.recycler;
package com.dark98.santoku.recycler;
import android.annotation.SuppressLint;
import android.view.ViewGroup;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.recycler;
package com.dark98.santoku.recycler;
import android.content.Context;
import android.view.View;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.recycler;
package com.dark98.santoku.recycler;
import android.content.Context;
import android.widget.Space;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.recycler;
package com.dark98.santoku.recycler;
import android.content.Context;
import android.util.TypedValue;
@@ -8,8 +8,8 @@ import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils;
public class TextHintRecyclerItem extends SimpleRecyclerItem<TextView> {
public String title;
@@ -1,9 +1,9 @@
package ru.ytkab0bp.slicebeam.render;
package com.dark98.santoku.render;
import androidx.core.math.MathUtils;
import ru.ytkab0bp.slicebeam.utils.DoubleMatrix;
import ru.ytkab0bp.slicebeam.utils.Vec3d;
import com.dark98.santoku.utils.DoubleMatrix;
import com.dark98.santoku.utils.Vec3d;
public class Camera {
private double[] viewMatrix = new double[16];
@@ -1,13 +1,13 @@
package ru.ytkab0bp.slicebeam.render;
package com.dark98.santoku.render;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.slic3r.GLModel;
import ru.ytkab0bp.slicebeam.slic3r.GLShaderProgram;
import ru.ytkab0bp.slicebeam.slic3r.GLShadersManager;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rUtils;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.DoubleMatrix;
import ru.ytkab0bp.slicebeam.utils.Vec3d;
import com.dark98.santoku.R;
import com.dark98.santoku.slic3r.GLModel;
import com.dark98.santoku.slic3r.GLShaderProgram;
import com.dark98.santoku.slic3r.GLShadersManager;
import com.dark98.santoku.slic3r.Slic3rUtils;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.DoubleMatrix;
import com.dark98.santoku.utils.Vec3d;
public class CoordAxes {
public Vec3d origin = new Vec3d(0, 0, 0);
@@ -1,7 +1,7 @@
package ru.ytkab0bp.slicebeam.render;
package com.dark98.santoku.render;
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;
@@ -18,24 +18,24 @@ import java.nio.IntBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
import ru.ytkab0bp.slicebeam.events.SelectedObjectChangedEvent;
import ru.ytkab0bp.slicebeam.slic3r.Bed3D;
import ru.ytkab0bp.slicebeam.slic3r.GCodeProcessorResult;
import ru.ytkab0bp.slicebeam.slic3r.GCodeViewer;
import ru.ytkab0bp.slicebeam.slic3r.GLModel;
import ru.ytkab0bp.slicebeam.slic3r.GLShaderProgram;
import ru.ytkab0bp.slicebeam.slic3r.GLShadersManager;
import ru.ytkab0bp.slicebeam.slic3r.Model;
import ru.ytkab0bp.slicebeam.slic3r.Slic3rUtils;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.DoubleMatrix;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.Vec3d;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.GLView;
import com.dark98.santoku.R;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.events.ObjectsListChangedEvent;
import com.dark98.santoku.events.SelectedObjectChangedEvent;
import com.dark98.santoku.slic3r.Bed3D;
import com.dark98.santoku.slic3r.GCodeProcessorResult;
import com.dark98.santoku.slic3r.GCodeViewer;
import com.dark98.santoku.slic3r.GLModel;
import com.dark98.santoku.slic3r.GLShaderProgram;
import com.dark98.santoku.slic3r.GLShadersManager;
import com.dark98.santoku.slic3r.Model;
import com.dark98.santoku.slic3r.Slic3rUtils;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.DoubleMatrix;
import com.dark98.santoku.utils.Prefs;
import com.dark98.santoku.utils.Vec3d;
import com.dark98.santoku.utils.ViewUtils;
import com.dark98.santoku.view.GLView;
public class GLRenderer implements GLSurfaceView.Renderer {
private final static float FOV = 60f;
@@ -425,7 +425,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
glEnable(GL_DEPTH_TEST);
boolean bottom = Prefs.isOrthoProjectionEnabled() ? camera.getDirForward().z > 0 : camera.getDirToBed().z > 0;
if (lastConfigUid != SliceBeam.CONFIG_UID) {
if (lastConfigUid != Santoku.CONFIG_UID) {
configureBed();
}
if (bed.isValid() && bedVisible) {
@@ -567,14 +567,14 @@ public class GLRenderer implements GLSurfaceView.Renderer {
selX = selY = selZ = 0;
selRotX = selRotY = selRotZ = 0;
selScaleX = selScaleY = selScaleZ = 1;
SliceBeam.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent());
Santoku.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent());
}
if (model.getObjectsCount() == 0) {
model.release();
model = null;
}
SliceBeam.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
Santoku.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
return true;
}
@@ -640,7 +640,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
flattenPlanes.clear();
selectedObject = -1;
SliceBeam.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent());
Santoku.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent());
return true;
}
@@ -658,7 +658,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
selRotX = selRotY = selRotZ = 0;
selScaleX = selScaleY = selScaleZ = 1;
}
SliceBeam.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent());
Santoku.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent());
}
return render;
}
@@ -793,9 +793,9 @@ public class GLRenderer implements GLSurfaceView.Renderer {
private void configureBed() {
try {
lastConfigUid = SliceBeam.CONFIG_UID;
SliceBeam.genCurrentConfig();
bed.configure(SliceBeam.getCurrentConfigFile());
lastConfigUid = Santoku.CONFIG_UID;
Santoku.genCurrentConfig();
bed.configure(Santoku.getCurrentConfigFile());
} catch (Exception e) {
Log.e("GLRenderer", "Failed to update config", e);
}
@@ -1,17 +1,17 @@
package ru.ytkab0bp.slicebeam.slic3r;
package com.dark98.santoku.slic3r;
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 ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.render.CoordAxes;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.DoubleMatrix;
import ru.ytkab0bp.slicebeam.utils.Vec3d;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.R;
import com.dark98.santoku.render.CoordAxes;
import com.dark98.santoku.theme.ThemesRepo;
import com.dark98.santoku.utils.DoubleMatrix;
import com.dark98.santoku.utils.Vec3d;
import com.dark98.santoku.utils.ViewUtils;
public class Bed3D {
private final static float GROUND_Z = -0.02f;
@@ -88,9 +88,6 @@ public class Bed3D {
assertTrue(projectionMatrix.length == 16);
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);
renderDefaultBed(shadersManager, bottom, outModelMatrix, projectionMatrix);
axes.render(shadersManager, viewModelMatrix, projectionMatrix, 0.25f, invZoom);
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.slic3r;
package com.dark98.santoku.slic3r;
import android.text.TextUtils;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.slic3r;
package com.dark98.santoku.slic3r;
import java.io.File;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.slic3r;
package com.dark98.santoku.slic3r;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -23,8 +23,8 @@ import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import ru.ytkab0bp.slicebeam.view.GLView;
import com.dark98.santoku.config.ConfigObject;
import com.dark98.santoku.view.GLView;
public final class GCodeThumbnailer {
private static final String TAG = "GCodeThumbnailer";
@@ -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;
@@ -10,8 +10,8 @@ import androidx.core.util.Pair;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.R;
import com.dark98.santoku.theme.ThemesRepo;
public class GCodeViewer {
@Retention(RetentionPolicy.SOURCE)
@@ -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 java.util.ArrayList;
import ru.ytkab0bp.slicebeam.render.Camera;
import ru.ytkab0bp.slicebeam.render.GLRenderer;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.Vec3d;
import com.dark98.santoku.render.Camera;
import com.dark98.santoku.render.GLRenderer;
import com.dark98.santoku.utils.Prefs;
import com.dark98.santoku.utils.Vec3d;
public class GLModel {
long pointer;
@@ -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.graphics.Color;
@@ -13,8 +13,8 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.utils.IOUtils;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.utils.IOUtils;
public class GLShaderProgram {
long pointer;
@@ -39,7 +39,7 @@ public class GLShaderProgram {
};
public GLShaderProgram(String name) {
AssetManager assets = SliceBeam.INSTANCE.getAssets();
AssetManager assets = Santoku.INSTANCE.getAssets();
try {
pointer = Native.shader_init_from_texts(name, IOUtils.readString(assets.open("shaders/" + name + ".fs")), IOUtils.readString(assets.open("shaders/" + name + ".vs")));
} catch (IOException e) {
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.slic3r;
package com.dark98.santoku.slic3r;
import android.opengl.GLES30;
@@ -1,11 +1,11 @@
package ru.ytkab0bp.slicebeam.slic3r;
package com.dark98.santoku.slic3r;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import ru.ytkab0bp.slicebeam.utils.Vec3d;
import com.dark98.santoku.utils.Vec3d;
public class Model {
public final String key = UUID.randomUUID().toString();
@@ -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 {
static {
@@ -12,7 +12,7 @@ class Native {
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);
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.slic3r;
package com.dark98.santoku.slic3r;
import java.util.Arrays;
import java.util.List;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.slic3r;
package com.dark98.santoku.slic3r;
import androidx.annotation.Keep;
import androidx.annotation.Nullable;
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.slic3r;
package com.dark98.santoku.slic3r;
import java.io.BufferedReader;
import java.io.File;
@@ -13,8 +13,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import ru.ytkab0bp.slicebeam.BuildConfig;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import com.dark98.santoku.BuildConfig;
import com.dark98.santoku.config.ConfigObject;
public class Slic3rConfigWrapper {
public final static String BLACKLISTED_SYMBOLS = "<>[]:/\\|?*\"";
@@ -80,7 +80,7 @@ public class Slic3rConfigWrapper {
);
public final static List<String> PRINTER_CONFIG_KEYS = Arrays.asList(
"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",
//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",
@@ -95,7 +95,9 @@ public class Slic3rConfigWrapper {
"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_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(
"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");
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");
}
@@ -207,7 +214,7 @@ public class Slic3rConfigWrapper {
public String serialize() {
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, "print", printConfigs);
serializeList(sb, "filament", filamentConfigs);
@@ -215,7 +222,12 @@ public class Slic3rConfigWrapper {
if (presets != null) {
sb.append("[presets]\n");
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");
}
}
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.slic3r;
package com.dark98.santoku.slic3r;
import android.text.TextUtils;
@@ -12,7 +12,7 @@ import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.Santoku;
public class 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<>();
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));
String line;
StringBuilder msgId = null;
@@ -71,7 +71,7 @@ public class Slic3rLocalization {
}
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) {
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.slic3r;
package com.dark98.santoku.slic3r;
public class Slic3rRuntimeError extends Exception {
public Slic3rRuntimeError() {
@@ -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 ru.ytkab0bp.slicebeam.utils.Vec3d;
import com.dark98.santoku.utils.Vec3d;
public class Slic3rUtils {
public static void calcViewNormalMatrix(double[] viewMatrix, double[] worldMatrix, double[] normalMatrix) {
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.slic3r;
package com.dark98.santoku.slic3r;
public interface SliceListener {
void onProgress(int progress, String text);
@@ -1,11 +1,11 @@
package ru.ytkab0bp.slicebeam.theme;
package com.dark98.santoku.theme;
import android.util.SparseIntArray;
import androidx.annotation.StringRes;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import com.dark98.santoku.R;
import com.dark98.santoku.utils.Prefs;
public class BeamTheme {
public final static BeamTheme LIGHT = new BeamTheme() {{
@@ -20,10 +20,6 @@ public class BeamTheme {
colors.put(R.attr.dividerContrastColor, 0xffcccccc);
colors.put(R.attr.dialogBackground, 0xffffffff);
colors.put(R.attr.switchThumbUncheckedColor, 0xffeef2f3);
colors.put(R.attr.boostyColorTop, 0xfff06e2a);
colors.put(R.attr.boostyColorBottom, 0xff884725);
colors.put(R.attr.telegramColor, 0xff27a7e7);
colors.put(R.attr.k3dColor, 0xff039045);
colors.put(R.attr.modelHoverColor, 0xffffffff);
colors.put(R.attr.textColorNegative, 0xffff464a);
@@ -73,7 +69,6 @@ public class BeamTheme {
colors.put(R.attr.bedContourlinesColor, 0x40ffffff);
colors.put(R.attr.backgroundColorTop, 0xff292929);
colors.put(R.attr.backgroundColorBottom, 0xff181818);
colors.put(R.attr.boostyColorBottom, 0xff884725);
colors.put(R.attr.xTrackColor, 0xffee0000);
colors.put(R.attr.yTrackColor, 0xff00ee00);
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.theme;
package com.dark98.santoku.theme;
public interface IThemeView {
void onApplyTheme();
@@ -1,4 +1,4 @@
package ru.ytkab0bp.slicebeam.theme;
package com.dark98.santoku.theme;
import android.annotation.SuppressLint;
import android.app.Activity;
@@ -8,9 +8,9 @@ import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import ru.ytkab0bp.slicebeam.MainActivity;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import com.dark98.santoku.MainActivity;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.utils.Prefs;
public class ThemesRepo {
private static Boolean resolvedSystemMode;
@@ -18,7 +18,7 @@ public class ThemesRepo {
public static BeamTheme getCurrent() {
if (Prefs.getThemeMode() == Prefs.ThemeMode.SYSTEM) {
if (resolvedSystemMode == null) {
resolvedSystemMode = (SliceBeam.INSTANCE.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
resolvedSystemMode = (Santoku.INSTANCE.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
}
return resolvedSystemMode ? BeamTheme.DARK : BeamTheme.LIGHT;
}
@@ -1,6 +1,6 @@
package ru.ytkab0bp.slicebeam.utils;
package com.dark98.santoku.utils;
import ru.ytkab0bp.slicebeam.BuildConfig;
import com.dark98.santoku.BuildConfig;
public class DebugUtils {
public static void assertTrue(boolean value) {
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package ru.ytkab0bp.slicebeam.utils;
package com.dark98.santoku.utils;
import androidx.annotation.NonNull;
@@ -1,9 +1,10 @@
package ru.ytkab0bp.slicebeam.utils;
package com.dark98.santoku.utils;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.text.TextUtils;
import org.json.JSONArray;
@@ -23,27 +24,46 @@ import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.config.ConfigObject;
import com.dark98.santoku.Santoku;
import com.dark98.santoku.config.ConfigObject;
public class IOUtils {
public static ExecutorService IO_POOL = Executors.newCachedThreadPool();
public static String getDisplayName(Uri uri) {
ContentResolver resolver = SliceBeam.INSTANCE.getContentResolver();
ContentResolver resolver = Santoku.INSTANCE.getContentResolver();
String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME};
Cursor metaCursor = resolver.query(uri, projection, null, null, null);
String fileName = null;
if (metaCursor != null) {
try {
if (metaCursor.moveToFirst()) {
fileName = metaCursor.getString(0);
}
} finally {
Cursor metaCursor = null;
try {
String[] projection = {OpenableColumns.DISPLAY_NAME};
metaCursor = resolver.query(uri, projection, null, null, null);
if (metaCursor != null && metaCursor.moveToFirst()) {
fileName = metaCursor.getString(0);
}
} catch (Exception ignored) {
// Some providers throw for query; fall back below.
} finally {
if (metaCursor != null) {
metaCursor.close();
}
}
if (fileName == null) {
try {
String path = uri.getPath();
if (path != null) {
int idx = path.lastIndexOf('/');
if (idx != -1 && idx + 1 < path.length()) {
fileName = path.substring(idx + 1);
} else if (!path.isEmpty()) {
fileName = path;
}
}
} catch (Exception ignored) {
}
}
return fileName;
}
public static String readString(InputStream in) throws IOException {

Some files were not shown because too many files have changed in this diff Show More