1 Commits

Author SHA1 Message Date
Josh Shippam c584dd6324 Create Debug Workflow 2026-01-22 04:57:55 +00:00
205 changed files with 2929 additions and 4000 deletions
+2 -24
View File
@@ -2,15 +2,14 @@ name: Android CI
on: on:
workflow_dispatch: workflow_dispatch:
push:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: set up JDK 21 - name: set up JDK 21
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
@@ -18,33 +17,12 @@ jobs:
distribution: 'temurin' distribution: 'temurin'
cache: gradle cache: gradle
- name: Install NDK 23.1.7779620
run: |
echo "y" | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --install "ndk;23.1.7779620"
- name: Cache Build Deps
id: native-dirs-cache
uses: actions/cache@v4
with:
path: |
app/src/main/jniImports/boost/
app/src/main/jniImports/oneTBB/
app/src/main/occt/
key: ${{ runner.os }}-native-dirs-${{ hashFiles('**/*.gradle*', '**/gradle.properties', '**/gradle-wrapper.properties', 'gradle/libs.versions.toml', 'settings.gradle*') }}
restore-keys: |
${{ runner.os }}-native-dirs-
- name: Native cache status
run: echo "Native cache hit? ${{ steps.native-dirs-cache.outputs.cache-hit }}"
- name: Grant execute permission for gradlew - name: Grant execute permission for gradlew
run: chmod +x gradlew run: chmod +x gradlew
- name: Build with Gradle - name: Build with Gradle
run: ./gradlew build run: ./gradlew build
- name: Upload Debug Build Artifact - name: Upload a Debug APK
uses: actions/upload-artifact@v6.0.0 uses: actions/upload-artifact@v6.0.0
with: with:
name: Build-${{ github.run_id }}
path: app/build/outputs/apk/debug/*.apk path: app/build/outputs/apk/debug/*.apk
+4 -14
View File
@@ -8,7 +8,7 @@ include(CheckLibraryExists)
include(GenerateExportHeader) include(GenerateExportHeader)
include(CheckCSourceCompiles) include(CheckCSourceCompiles)
project(Santoku) project(SliceBeam)
cmake_minimum_required(VERSION 3.4.1) cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
@@ -17,7 +17,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(IS_CLANG_CL TRUE) set(IS_CLANG_CL TRUE)
set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-imsvc") set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-imsvc")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-old-style-cast -Wno-reserved-id-macro -Wno-c++98-compat-pedantic -funwind-tables -fopenmp -static-openmp -Wl,--no-merge-exidx-entries") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-old-style-cast -Wno-reserved-id-macro -Wno-c++98-compat-pedantic -funwind-tables -fopenmp -static-openmp -Wl,--no-merge-exidx-entries")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,common-page-size=16384 -Wl,-z,max-page-size=16384")
add_compile_options(-fsigned-char) add_compile_options(-fsigned-char)
# Suppress all warnings # Suppress all warnings
@@ -783,8 +782,6 @@ add_library(slic3r
src/main/jni/libslic3r/Fill/FillBase.hpp src/main/jni/libslic3r/Fill/FillBase.hpp
src/main/jni/libslic3r/Fill/FillConcentric.cpp src/main/jni/libslic3r/Fill/FillConcentric.cpp
src/main/jni/libslic3r/Fill/FillConcentric.hpp src/main/jni/libslic3r/Fill/FillConcentric.hpp
src/main/jni/libslic3r/Fill/FillCrossHatch.cpp
src/main/jni/libslic3r/Fill/FillCrossHatch.hpp
src/main/jni/libslic3r/Fill/FillEnsuring.cpp src/main/jni/libslic3r/Fill/FillEnsuring.cpp
src/main/jni/libslic3r/Fill/FillEnsuring.hpp src/main/jni/libslic3r/Fill/FillEnsuring.hpp
src/main/jni/libslic3r/Fill/FillHoneycomb.cpp src/main/jni/libslic3r/Fill/FillHoneycomb.cpp
@@ -1244,9 +1241,9 @@ add_library(slic3r
src/main/jni/bbl/Orient.cpp src/main/jni/bbl/Orient.cpp
src/main/jni/santoku/beam_native.cpp src/main/jni/slicebeam/beam_native.cpp
src/main/jni/santoku/GLModel.cpp src/main/jni/slicebeam/GLModel.cpp
src/main/jni/santoku/GLShader.cpp src/main/jni/slicebeam/GLShader.cpp
# $<TARGET_OBJECTS:simd> # $<TARGET_OBJECTS:simd>
) )
@@ -1353,10 +1350,3 @@ target_link_libraries(slic3r PRIVATE
boost_wserialization boost_wserialization
${OCCT_LIBS} ${OCCT_LIBS}
) )
if(ANDROID)
target_link_options(slic3r PRIVATE
"-Wl,-z,common-page-size=16384"
"-Wl,-z,max-page-size=16384"
"-Wl,--rosegment")
endif()
+85 -206
View File
@@ -6,30 +6,26 @@ plugins {
} }
def commit = getGitCommitHash(file('.')) def commit = getGitCommitHash(file('.'))
def prodCloudBaseUrl = "https://santoku.dark98.co.uk/v1/"
def prodBeamBaseUrl = "https://santoku.dark98.co.uk"
android { android {
namespace 'com.dark98.santoku' namespace 'ru.ytkab0bp.slicebeam'
compileSdk 35 compileSdk 35
defaultConfig { defaultConfig {
applicationId "com.dark98.santoku" applicationId "ru.ytkab0bp.slicebeam"
minSdk 21 minSdk 21
targetSdk 35 targetSdk 35
versionCode 1 versionCode 8
versionName "0.0.1" versionName "0.3.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField "String", "CLOUD_BASE_URL_PROD", "\"" + prodCloudBaseUrl + "\""
buildConfigField "String", "BEAM_BASE_URL_PROD", "\"" + prodBeamBaseUrl + "\""
externalNativeBuild { externalNativeBuild {
cmake { cmake {
arguments '-DANDROID_STL=c++_shared', '-DANDROID_PLATFORM=21', arguments '-DANDROID_STL=c++_shared', '-DANDROID_PLATFORM=21',
'-DCMAKE_BUILD_TYPE=Release', // Disabling this results in drastically degradation of slicing times on debug builds '-DCMAKE_BUILD_TYPE=Release', // Disabling this results in drastically degradation of slicing times on debug builds
"-DSLIC3R_VERSION=${defaultConfig.versionName}", "-DSLIC3R_VERSION=\"${defaultConfig.versionName}\"",
"-DSLIC3R_BUILD_ID=${defaultConfig.versionCode}" "-DSLIC3R_BUILD_ID=\"${defaultConfig.versionCode}\""
} }
} }
} }
@@ -43,6 +39,7 @@ android {
release { release {
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
buildConfigField "boolean", "IS_GOOGLE_PLAY", "false"
buildConfigField "String", "COMMIT", "\"" + commit + "\"" buildConfigField "String", "COMMIT", "\"" + commit + "\""
ndk { ndk {
//noinspection ChromeOsAbiSupport //noinspection ChromeOsAbiSupport
@@ -50,8 +47,7 @@ android {
} }
} }
debug { debug {
applicationIdSuffix ".debug" buildConfigField "boolean", "IS_GOOGLE_PLAY", "false"
resValue "string", "AppName", "Santoku (Debug)"
buildConfigField "String", "COMMIT", "\"" + commit + "\"" buildConfigField "String", "COMMIT", "\"" + commit + "\""
ndk { ndk {
debugSymbolLevel 'NONE' debugSymbolLevel 'NONE'
@@ -69,7 +65,7 @@ android {
cmake { cmake {
path file('CMakeLists.txt') path file('CMakeLists.txt')
} }
ndkVersion "29.0.14206865" ndkVersion "23.1.7779620"
} }
buildFeatures { buildFeatures {
@@ -78,9 +74,7 @@ android {
applicationVariants.all { variant -> applicationVariants.all { variant ->
variant.outputs.all { variant.outputs.all {
def isDebug = variant.buildType.name == "debug" outputFileName = "SliceBeam_" + commit + ".apk"
def suffix = isDebug ? "_debug" : ""
outputFileName = "Santoku_" + commit + suffix + ".apk"
} }
} }
} }
@@ -94,62 +88,11 @@ tasks.withType(JavaCompile).configureEach {
static String getGitCommitHash(File dir) { static String getGitCommitHash(File dir) {
try { try {
File gitDir = new File(dir, ".git") return Runtime.getRuntime().exec("git rev-parse HEAD", null, dir).inputStream.readLines().get(0).substring(0, 10)
if (!gitDir.exists()) { } catch (Exception e){
return "non-git" println("Unable to get git commit hash:")
} e.printStackTrace()
return "non-git build"
// Worktree/submodule support: .git may be a file with "gitdir: <path>".
if (gitDir.isFile()) {
String pointer = gitDir.text.trim()
if (pointer.startsWith("gitdir:")) {
gitDir = new File(dir, pointer.substring("gitdir:".length()).trim())
}
}
if (!gitDir.exists() || !gitDir.isDirectory()) {
return "non-git"
}
File headFile = new File(gitDir, "HEAD")
if (!headFile.exists()) {
return "non-git"
}
String head = headFile.text.trim()
String fullHash = null
if (head.startsWith("ref:")) {
String refPath = head.substring("ref:".length()).trim()
File refFile = new File(gitDir, refPath)
if (refFile.exists()) {
fullHash = refFile.text.trim()
} else {
File packedRefs = new File(gitDir, "packed-refs")
if (packedRefs.exists()) {
String prefix = refPath + " "
for (String line : packedRefs.readLines()) {
if (line.startsWith("#") || line.startsWith("^") || line.trim().isEmpty()) {
continue
}
int space = line.indexOf(' ')
if (space > 0 && line.substring(space + 1).trim() == refPath) {
fullHash = line.substring(0, space).trim()
break
}
}
}
}
} else {
// Detached HEAD.
fullHash = head
}
if (!fullHash || fullHash.length() < 10) {
return "non-git"
}
return fullHash.substring(0, 10)
} catch (Exception ignored) {
return "non-git"
} }
} }
@@ -167,7 +110,6 @@ dependencies {
implementation 'com.google.android.material:material:1.12.0' implementation 'com.google.android.material:material:1.12.0'
implementation 'com.loopj.android:android-async-http:1.4.11' implementation 'com.loopj.android:android-async-http:1.4.11'
implementation 'androidx.activity:activity:1.10.1' implementation 'androidx.activity:activity:1.10.1'
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
} }
def loadLocalProperties() { def loadLocalProperties() {
@@ -197,17 +139,10 @@ def cmakeDir = cmakeRoot.listFiles()?.findAll { it.isDirectory() }?.sort { a, b
if (!cmakeDir) { if (!cmakeDir) {
throw new GradleException("No CMake found under ${sdkDir}/cmake") throw new GradleException("No CMake found under ${sdkDir}/cmake")
} }
def osName = System.getProperty("os.name").toLowerCase() def cmakeExe = "${cmakeDir}/bin/cmake.exe"
def isWindows = osName.contains("windows") def ninjaExe = "${cmakeDir}/bin/ninja.exe"
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 toolchainFile = "${ndkDir}/build/cmake/android.toolchain.cmake"
def wslExe = "${System.getenv("SystemRoot") ?: "C:/Windows"}/System32/wsl.exe" 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 tbbSrc = "${rootDir}/third_party/openvdb-android/tbb-aarch64"
def tbbBuildArm64 = "${rootDir}/third_party/openvdb-android/build-tbb-android-21-arm64" def tbbBuildArm64 = "${rootDir}/third_party/openvdb-android/build-tbb-android-21-arm64"
@@ -224,55 +159,32 @@ def occtDistArmv7 = "${rootDir}/third_party/occt/dist/android-armeabi-v7a"
def boostDir = "${rootDir}/third_party/Boost-for-Android" def boostDir = "${rootDir}/third_party/Boost-for-Android"
def boostOutArm64 = "${boostDir}/build/out/arm64-v8a" def boostOutArm64 = "${boostDir}/build/out/arm64-v8a"
def boostOutArmv7 = "${boostDir}/build/out/armeabi-v7a" 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") { tasks.register("ensureThirdParty") {
doLast { doLast {
def rootPath = rootPathForTasks def thirdPartyDir = file("${rootDir}/third_party")
def thirdPartyDir = new File(rootPath, "third_party")
if (!thirdPartyDir.exists()) { if (!thirdPartyDir.exists()) {
thirdPartyDir.mkdirs() thirdPartyDir.mkdirs()
} }
def repos = [ def repos = [
[path: "${rootPath}/third_party/Boost-for-Android", url: "https://github.com/moritz-wundke/Boost-for-Android.git"], [path: "${rootDir}/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: "${rootDir}/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"] [path: "${rootDir}/third_party/occt", url: "https://github.com/Open-Cascade-SAS/OCCT.git"]
] ]
def runCommand = { List<String> cmd, File workDir -> repos.each { repo ->
Process process = new ProcessBuilder(cmd) if (!file(repo.path).exists()) {
.directory(workDir) exec {
.inheritIO() workingDir rootDir
.start() commandLine "git", "clone", "--depth", "1", repo.url, repo.path
int exitCode = process.waitFor()
if (exitCode != 0) {
throw new GradleException("Command failed (${exitCode}): ${cmd.join(' ')}")
} }
} }
for (def repo : repos) {
if (!new File(repo.path).exists()) {
runCommand(["git", "clone", "--depth", "1", repo.url, repo.path], new File(rootPath))
} }
def vdbRoot = 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"
} }
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)
} }
} }
} }
@@ -280,8 +192,7 @@ tasks.register("ensureThirdParty") {
tasks.register("patchBoostForAndroid") { tasks.register("patchBoostForAndroid") {
dependsOn("ensureThirdParty") dependsOn("ensureThirdParty")
doLast { doLast {
def boostRoot = new File(rootPathForTasks, "third_party/Boost-for-Android") def scriptFile = file("${boostDir}/build-android.sh")
def scriptFile = new File(boostRoot, "build-android.sh")
if (scriptFile.exists()) { if (scriptFile.exists()) {
def scriptText = scriptFile.getText("UTF-8") def scriptText = scriptFile.getText("UTF-8")
def changed = false def changed = false
@@ -321,7 +232,7 @@ tasks.register("patchBoostForAndroid") {
} }
} }
def commonFile = new File(boostRoot, "configs/user-config-ndk23-1_85_0-common.jam") def commonFile = file("${boostDir}/configs/user-config-ndk23-1_85_0-common.jam")
if (commonFile.exists()) { if (commonFile.exists()) {
def commonText = commonFile.getText("UTF-8") def commonText = commonFile.getText("UTF-8")
def commonChanged = false def commonChanged = false
@@ -345,7 +256,7 @@ tasks.register("patchBoostForAndroid") {
} }
} }
def arm64File = new File(boostRoot, "configs/user-config-ndk23-1_85_0-arm64-v8a.jam") def arm64File = file("${boostDir}/configs/user-config-ndk23-1_85_0-arm64-v8a.jam")
if (arm64File.exists()) { if (arm64File.exists()) {
def text = arm64File.getText("UTF-8") def text = arm64File.getText("UTF-8")
if (!text.contains("<arch>")) { if (!text.contains("<arch>")) {
@@ -354,7 +265,7 @@ tasks.register("patchBoostForAndroid") {
} }
} }
def armv7File = new File(boostRoot, "configs/user-config-ndk23-1_85_0-armeabi-v7a.jam") def armv7File = file("${boostDir}/configs/user-config-ndk23-1_85_0-armeabi-v7a.jam")
if (armv7File.exists()) { if (armv7File.exists()) {
def text = armv7File.getText("UTF-8") def text = armv7File.getText("UTF-8")
if (!text.contains("<arch>")) { if (!text.contains("<arch>")) {
@@ -368,11 +279,10 @@ tasks.register("patchBoostForAndroid") {
tasks.register("buildTbbArm64") { tasks.register("buildTbbArm64") {
dependsOn("ensureThirdParty") dependsOn("ensureThirdParty")
onlyIf { onlyIf {
forceNativeRebuild || !file("${projectDir}/src/main/jniImports/oneTBB/lib/arm64-v8a/libtbb.a").exists() ||
!new File(tbbArm64LibA).exists() || !file("${projectDir}/src/main/jniImports/oneTBB/lib/arm64-v8a/libtbbmalloc.a").exists() ||
!new File(tbbArm64MallocA).exists() || !file("${projectDir}/src/main/jniLibs/arm64-v8a/libtbb.so").exists() ||
!new File(tbbArm64So).exists() || !file("${projectDir}/src/main/jniLibs/arm64-v8a/libtbbmalloc.so").exists()
!new File(tbbArm64MallocSo).exists()
} }
doLast { doLast {
exec { exec {
@@ -380,13 +290,11 @@ tasks.register("buildTbbArm64") {
"-S", tbbSrc, "-S", tbbSrc,
"-B", tbbBuildArm64, "-B", tbbBuildArm64,
"-DCMAKE_MAKE_PROGRAM=${ninjaExe}", "-DCMAKE_MAKE_PROGRAM=${ninjaExe}",
"-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
"-DCMAKE_TOOLCHAIN_FILE=${toolchainFile}", "-DCMAKE_TOOLCHAIN_FILE=${toolchainFile}",
"-DANDROID_ABI=arm64-v8a", "-DANDROID_ABI=arm64-v8a",
"-DANDROID_PLATFORM=android-21", "-DANDROID_PLATFORM=android-21",
"-DANDROID_STL=c++_shared", "-DANDROID_STL=c++_shared",
"-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_BUILD_TYPE=Release",
"-DCMAKE_SHARED_LINKER_FLAGS=${pageSizeLinkerFlags}",
"-DCMAKE_INSTALL_PREFIX=${tbbInstallArm64}", "-DCMAKE_INSTALL_PREFIX=${tbbInstallArm64}",
"-DTBB_BUILD_TESTS=Off" "-DTBB_BUILD_TESTS=Off"
} }
@@ -399,11 +307,10 @@ tasks.register("buildTbbArm64") {
tasks.register("buildTbbArmv7") { tasks.register("buildTbbArmv7") {
dependsOn("ensureThirdParty") dependsOn("ensureThirdParty")
onlyIf { onlyIf {
forceNativeRebuild || !file("${projectDir}/src/main/jniImports/oneTBB/lib/armeabi-v7a/libtbb.a").exists() ||
!new File(tbbArmv7LibA).exists() || !file("${projectDir}/src/main/jniImports/oneTBB/lib/armeabi-v7a/libtbbmalloc.a").exists() ||
!new File(tbbArmv7MallocA).exists() || !file("${projectDir}/src/main/jniLibs/armeabi-v7a/libtbb.so").exists() ||
!new File(tbbArmv7So).exists() || !file("${projectDir}/src/main/jniLibs/armeabi-v7a/libtbbmalloc.so").exists()
!new File(tbbArmv7MallocSo).exists()
} }
doLast { doLast {
exec { exec {
@@ -411,13 +318,11 @@ tasks.register("buildTbbArmv7") {
"-S", tbbSrc, "-S", tbbSrc,
"-B", tbbBuildArmv7, "-B", tbbBuildArmv7,
"-DCMAKE_MAKE_PROGRAM=${ninjaExe}", "-DCMAKE_MAKE_PROGRAM=${ninjaExe}",
"-DCMAKE_POLICY_VERSION_MINIMUM=3.5",
"-DCMAKE_TOOLCHAIN_FILE=${toolchainFile}", "-DCMAKE_TOOLCHAIN_FILE=${toolchainFile}",
"-DANDROID_ABI=armeabi-v7a", "-DANDROID_ABI=armeabi-v7a",
"-DANDROID_PLATFORM=android-21", "-DANDROID_PLATFORM=android-21",
"-DANDROID_STL=c++_shared", "-DANDROID_STL=c++_shared",
"-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_BUILD_TYPE=Release",
"-DCMAKE_SHARED_LINKER_FLAGS=${pageSizeLinkerFlags}",
"-DCMAKE_INSTALL_PREFIX=${tbbInstallArmv7}", "-DCMAKE_INSTALL_PREFIX=${tbbInstallArmv7}",
"-DTBB_BUILD_TESTS=Off" "-DTBB_BUILD_TESTS=Off"
} }
@@ -430,11 +335,10 @@ tasks.register("buildTbbArmv7") {
tasks.register("copyTbbArm64", Copy) { tasks.register("copyTbbArm64", Copy) {
dependsOn("buildTbbArm64") dependsOn("buildTbbArm64")
onlyIf { onlyIf {
forceNativeRebuild || !file("${projectDir}/src/main/jniImports/oneTBB/lib/arm64-v8a/libtbb.a").exists() ||
!new File(tbbArm64LibA).exists() || !file("${projectDir}/src/main/jniImports/oneTBB/lib/arm64-v8a/libtbbmalloc.a").exists() ||
!new File(tbbArm64MallocA).exists() || !file("${projectDir}/src/main/jniLibs/arm64-v8a/libtbb.so").exists() ||
!new File(tbbArm64So).exists() || !file("${projectDir}/src/main/jniLibs/arm64-v8a/libtbbmalloc.so").exists()
!new File(tbbArm64MallocSo).exists()
} }
from("${tbbInstallArm64}/lib") { from("${tbbInstallArm64}/lib") {
include "libtbb_static.a" include "libtbb_static.a"
@@ -469,11 +373,10 @@ tasks.register("copyTbbArm64", Copy) {
tasks.register("copyTbbArmv7", Copy) { tasks.register("copyTbbArmv7", Copy) {
dependsOn("buildTbbArmv7") dependsOn("buildTbbArmv7")
onlyIf { onlyIf {
forceNativeRebuild || !file("${projectDir}/src/main/jniImports/oneTBB/lib/armeabi-v7a/libtbb.a").exists() ||
!new File(tbbArmv7LibA).exists() || !file("${projectDir}/src/main/jniImports/oneTBB/lib/armeabi-v7a/libtbbmalloc.a").exists() ||
!new File(tbbArmv7MallocA).exists() || !file("${projectDir}/src/main/jniLibs/armeabi-v7a/libtbb.so").exists() ||
!new File(tbbArmv7So).exists() || !file("${projectDir}/src/main/jniLibs/armeabi-v7a/libtbbmalloc.so").exists()
!new File(tbbArmv7MallocSo).exists()
} }
from("${tbbInstallArmv7}/lib") { from("${tbbInstallArmv7}/lib") {
include "libtbb_static.a" include "libtbb_static.a"
@@ -507,7 +410,7 @@ tasks.register("copyTbbArmv7", Copy) {
tasks.register("copyTbbHeaders") { tasks.register("copyTbbHeaders") {
dependsOn("buildTbbArm64") dependsOn("buildTbbArm64")
onlyIf { !new File(tbbHeaderOut).exists() } onlyIf { !file("${projectDir}/src/main/jniImports/oneTBB/include/tbb/tbb.h").exists() }
doLast { doLast {
copy { copy {
from "${tbbInstallArm64}/include/tbb" from "${tbbInstallArm64}/include/tbb"
@@ -522,59 +425,39 @@ tasks.register("copyTbbHeaders") {
tasks.register("buildBoostArm64") { tasks.register("buildBoostArm64") {
dependsOn("patchBoostForAndroid") dependsOn("patchBoostForAndroid")
onlyIf { !new File(boostArm64OutLib).exists() } onlyIf { !file("${projectDir}/src/main/jniImports/boost/lib/arm64-v8a/lib/libboost_atomic-clang-mt-a64-1_85.a").exists() }
doLast { doLast {
if (isWindows) { if (!file(wslExe).exists()) {
if (!new File(wslExe).exists()) { throw new GradleException("WSL is required to build Boost. Install WSL or provide prebuilt Boost libs.")
throw new GradleException("WSL is required to build Boost on Windows. Install WSL or provide prebuilt Boost libs.")
} }
def ndkWsl = toWslPath(ndkDir) def ndkWsl = toWslPath(ndkDir)
def boostWsl = toWslPath(boostDir) def boostWsl = toWslPath(boostDir)
def binWsl = toWslPath(llvmBinDir) def binWsl = toWslPath("${ndkDir}/toolchains/llvm/prebuilt/windows-x86_64/bin")
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" exec {
Process p = new ProcessBuilder([wslExe, "bash", "-lc", cmd]).inheritIO().start() 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 (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") { tasks.register("buildBoostArmv7") {
dependsOn("patchBoostForAndroid") dependsOn("patchBoostForAndroid")
onlyIf { !new File(boostArmv7OutLib).exists() } onlyIf { !file("${projectDir}/src/main/jniImports/boost/lib/armeabi-v7a/lib/libboost_atomic-clang-mt-a32-1_85.a").exists() }
doLast { doLast {
if (isWindows) { if (!file(wslExe).exists()) {
if (!new File(wslExe).exists()) { throw new GradleException("WSL is required to build Boost. Install WSL or provide prebuilt Boost libs.")
throw new GradleException("WSL is required to build Boost on Windows. Install WSL or provide prebuilt Boost libs.")
} }
def ndkWsl = toWslPath(ndkDir) def ndkWsl = toWslPath(ndkDir)
def boostWsl = toWslPath(boostDir) def boostWsl = toWslPath(boostDir)
def binWsl = toWslPath(llvmBinDir) def binWsl = toWslPath("${ndkDir}/toolchains/llvm/prebuilt/windows-x86_64/bin")
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" exec {
Process p = new ProcessBuilder([wslExe, "bash", "-lc", cmd]).inheritIO().start() 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 (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) { tasks.register("copyBoostArm64", Copy) {
dependsOn("buildBoostArm64") dependsOn("buildBoostArm64")
onlyIf { !new File(boostArm64OutLib).exists() } onlyIf { !file("${projectDir}/src/main/jniImports/boost/lib/arm64-v8a/lib/libboost_atomic-clang-mt-a64-1_85.a").exists() }
from("${boostOutArm64}/lib") { from("${boostOutArm64}/lib") {
include "libboost_*.a" include "libboost_*.a"
} }
@@ -583,7 +466,7 @@ tasks.register("copyBoostArm64", Copy) {
tasks.register("copyBoostArmv7", Copy) { tasks.register("copyBoostArmv7", Copy) {
dependsOn("buildBoostArmv7") dependsOn("buildBoostArmv7")
onlyIf { !new File(boostArmv7OutLib).exists() } onlyIf { !file("${projectDir}/src/main/jniImports/boost/lib/armeabi-v7a/lib/libboost_atomic-clang-mt-a32-1_85.a").exists() }
from("${boostOutArmv7}/lib") { from("${boostOutArmv7}/lib") {
include "libboost_*.a" include "libboost_*.a"
} }
@@ -592,17 +475,17 @@ tasks.register("copyBoostArmv7", Copy) {
tasks.register("copyBoostHeaders", Copy) { tasks.register("copyBoostHeaders", Copy) {
dependsOn("ensureThirdParty") dependsOn("ensureThirdParty")
onlyIf { !new File(boostHeadersOut).exists() } onlyIf { !file("${projectDir}/src/main/jniImports/boost/include/boost/variant.hpp").exists() }
from("${boostDir}/boost_1_85_0/boost") from("${boostDir}/boost_1_85_0/boost")
into("${projectDir}/src/main/jniImports/boost/include/boost") into("${projectDir}/src/main/jniImports/boost/include/boost")
} }
tasks.register("buildOcctArm64") { tasks.register("buildOcctArm64") {
dependsOn("ensureThirdParty") dependsOn("ensureThirdParty")
onlyIf { forceNativeRebuild || !new File(occtArm64So).exists() } onlyIf { !file("${projectDir}/src/main/occt/jniLibs/arm64-v8a/libTKDESTEP.so").exists() }
doLast { doLast {
List<String> cfgCmd = [ exec {
cmakeExe, "-G", "Ninja", commandLine cmakeExe, "-G", "Ninja",
"-S", occtSrc, "-S", occtSrc,
"-B", occtBuildArm64, "-B", occtBuildArm64,
"-DCMAKE_MAKE_PROGRAM=${ninjaExe}", "-DCMAKE_MAKE_PROGRAM=${ninjaExe}",
@@ -611,7 +494,6 @@ tasks.register("buildOcctArm64") {
"-DANDROID_PLATFORM=android-21", "-DANDROID_PLATFORM=android-21",
"-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_BUILD_TYPE=Release",
"-DBUILD_LIBRARY_TYPE=Shared", "-DBUILD_LIBRARY_TYPE=Shared",
"-DCMAKE_SHARED_LINKER_FLAGS=${pageSizeLinkerFlags}",
"-DINSTALL_DIR_LIB=libs/arm64-v8a", "-DINSTALL_DIR_LIB=libs/arm64-v8a",
"-DINSTALL_DIR_INCLUDE=inc", "-DINSTALL_DIR_INCLUDE=inc",
"-DBUILD_DOC_Overview=OFF", "-DBUILD_DOC_Overview=OFF",
@@ -620,20 +502,19 @@ tasks.register("buildOcctArm64") {
"-DUSE_RAPIDJSON=OFF", "-DUSE_RAPIDJSON=OFF",
"-DUSE_DRACO=OFF", "-DUSE_DRACO=OFF",
"-DCMAKE_INSTALL_PREFIX=${occtDistArm64}" "-DCMAKE_INSTALL_PREFIX=${occtDistArm64}"
] }
Process p1 = new ProcessBuilder(cfgCmd).inheritIO().start() exec {
if (p1.waitFor() != 0) throw new GradleException("OCCT arm64 configure failed") commandLine cmakeExe, "--build", occtBuildArm64, "--target", "install", "--config", "Release", "--", "-j2"
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") { tasks.register("buildOcctArmv7") {
dependsOn("ensureThirdParty") dependsOn("ensureThirdParty")
onlyIf { forceNativeRebuild || !new File(occtArmv7So).exists() } onlyIf { !file("${projectDir}/src/main/occt/jniLibs/armeabi-v7a/libTKDESTEP.so").exists() }
doLast { doLast {
List<String> cfgCmd = [ exec {
cmakeExe, "-G", "Ninja", commandLine cmakeExe, "-G", "Ninja",
"-S", occtSrc, "-S", occtSrc,
"-B", occtBuildArmv7, "-B", occtBuildArmv7,
"-DCMAKE_MAKE_PROGRAM=${ninjaExe}", "-DCMAKE_MAKE_PROGRAM=${ninjaExe}",
@@ -642,7 +523,6 @@ tasks.register("buildOcctArmv7") {
"-DANDROID_PLATFORM=android-21", "-DANDROID_PLATFORM=android-21",
"-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_BUILD_TYPE=Release",
"-DBUILD_LIBRARY_TYPE=Shared", "-DBUILD_LIBRARY_TYPE=Shared",
"-DCMAKE_SHARED_LINKER_FLAGS=${pageSizeLinkerFlags}",
"-DINSTALL_DIR_LIB=libs/armeabi-v7a", "-DINSTALL_DIR_LIB=libs/armeabi-v7a",
"-DINSTALL_DIR_INCLUDE=inc", "-DINSTALL_DIR_INCLUDE=inc",
"-DBUILD_DOC_Overview=OFF", "-DBUILD_DOC_Overview=OFF",
@@ -651,17 +531,16 @@ tasks.register("buildOcctArmv7") {
"-DUSE_RAPIDJSON=OFF", "-DUSE_RAPIDJSON=OFF",
"-DUSE_DRACO=OFF", "-DUSE_DRACO=OFF",
"-DCMAKE_INSTALL_PREFIX=${occtDistArmv7}" "-DCMAKE_INSTALL_PREFIX=${occtDistArmv7}"
] }
Process p1 = new ProcessBuilder(cfgCmd).inheritIO().start() exec {
if (p1.waitFor() != 0) throw new GradleException("OCCT armv7 configure failed") commandLine cmakeExe, "--build", occtBuildArmv7, "--target", "install", "--config", "Release", "--", "-j2"
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) { tasks.register("copyOcctArm64", Copy) {
dependsOn("buildOcctArm64") dependsOn("buildOcctArm64")
onlyIf { forceNativeRebuild || !new File(occtArm64So).exists() } onlyIf { !file("${projectDir}/src/main/occt/jniLibs/arm64-v8a/libTKDESTEP.so").exists() }
from("${occtDistArm64}/libs/arm64-v8a") { from("${occtDistArm64}/libs/arm64-v8a") {
include "*.so" include "*.so"
} }
@@ -670,7 +549,7 @@ tasks.register("copyOcctArm64", Copy) {
tasks.register("copyOcctArmv7", Copy) { tasks.register("copyOcctArmv7", Copy) {
dependsOn("buildOcctArmv7") dependsOn("buildOcctArmv7")
onlyIf { forceNativeRebuild || !new File(occtArmv7So).exists() } onlyIf { !file("${projectDir}/src/main/occt/jniLibs/armeabi-v7a/libTKDESTEP.so").exists() }
from("${occtDistArmv7}/libs/armeabi-v7a") { from("${occtDistArmv7}/libs/armeabi-v7a") {
include "*.so" include "*.so"
} }
@@ -679,14 +558,14 @@ tasks.register("copyOcctArmv7", Copy) {
tasks.register("copyOcctHeadersArm64", Copy) { tasks.register("copyOcctHeadersArm64", Copy) {
dependsOn("buildOcctArm64") dependsOn("buildOcctArm64")
onlyIf { !new File(occtHeaderArm64Out).exists() } onlyIf { !file("${projectDir}/src/main/occt/include/arm64-v8a/STEPCAFControl_Reader.hxx").exists() }
from("${occtDistArm64}/inc") from("${occtDistArm64}/inc")
into("${projectDir}/src/main/occt/include/arm64-v8a") into("${projectDir}/src/main/occt/include/arm64-v8a")
} }
tasks.register("copyOcctHeadersArmv7", Copy) { tasks.register("copyOcctHeadersArmv7", Copy) {
dependsOn("buildOcctArmv7") dependsOn("buildOcctArmv7")
onlyIf { !new File(occtHeaderArmv7Out).exists() } onlyIf { !file("${projectDir}/src/main/occt/include/armeabi-v7a/STEPCAFControl_Reader.hxx").exists() }
from("${occtDistArmv7}/inc") from("${occtDistArmv7}/inc")
into("${projectDir}/src/main/occt/include/armeabi-v7a") into("${projectDir}/src/main/occt/include/armeabi-v7a")
} }
-4
View File
@@ -19,7 +19,3 @@
# If you keep the line number information, uncomment this to # If you keep the line number information, uncomment this to
# hide the original source file name. # hide the original source file name.
#-renamesourcefileattribute SourceFile #-renamesourcefileattribute SourceFile
# Keep cloud API and SAPIL runtime proxies.
-keep class com.dark98.santoku.cloud.** { *; }
-keep class ru.ytkab0bp.sapil.** { *; }
+8 -23
View File
@@ -8,6 +8,9 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<queries> <queries>
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
<!-- WebView fails sometime if not queried, idk why --> <!-- WebView fails sometime if not queried, idk why -->
<package android:name="com.google.android.webview"/> <package android:name="com.google.android.webview"/>
</queries> </queries>
@@ -16,11 +19,11 @@
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/icon"
android:label="@string/AppName" android:label="@string/AppName"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.Santoku" android:theme="@style/Theme.SliceBeam"
android:name=".Santoku" android:name=".SliceBeam"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
android:largeHeap="true" android:largeHeap="true"
android:isGame="false" android:isGame="false"
@@ -32,7 +35,7 @@
android:windowSoftInputMode="adjustPan" android:windowSoftInputMode="adjustPan"
android:configChanges="uiMode" android:configChanges="uiMode"
android:exported="true" android:exported="true"
android:theme="@style/Theme.Santoku"> android:theme="@style/Theme.SliceBeam">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@@ -43,13 +46,6 @@
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="santoku" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" /> <data android:scheme="file" />
<data android:scheme="content" /> <data android:scheme="content" />
<data android:mimeType="*/*" /> <data android:mimeType="*/*" />
@@ -59,7 +55,7 @@
<activity android:name=".SetupActivity" android:exported="false"/> <activity android:name=".SetupActivity" android:exported="false"/>
<activity android:name=".SafeStartActivity" android:exported="false"/> <activity android:name=".SafeStartActivity" android:exported="false"/>
<provider <provider
android:authorities="${applicationId}.provider" android:authorities="ru.ytkab0bp.slicebeam.provider"
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:grantUriPermissions="true" android:grantUriPermissions="true"
android:exported="false"> android:exported="false">
@@ -68,17 +64,6 @@
android:name="android.support.FILE_PROVIDER_PATHS" android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" /> android:resource="@xml/provider_paths" />
</provider> </provider>
<provider
android:name=".providers.AppDocumentsProvider"
android:authorities="${applicationId}.documents"
android:exported="true"
android:grantUriPermissions="true"
android:permission="android.permission.MANAGE_DOCUMENTS">
<intent-filter>
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
</intent-filter>
</provider>
</application> </application>
</manifest> </manifest>
+3 -3
View File
@@ -47,13 +47,13 @@ var dark = $['is_dark_theme'];
document.getElementsByClassName('md-option')[dark ? 1 : 0].click(); document.getElementsByClassName('md-option')[dark ? 1 : 0].click();
beginSaveFile = function(filename) { beginSaveFile = function(filename) {
Santoku.beginDownload(filename); SliceBeam.beginDownload(filename);
} }
writeToFile = function(data) { writeToFile = function(data) {
Santoku.writeData(btoa(unescape(encodeURIComponent(data)))); SliceBeam.writeData(btoa(unescape(encodeURIComponent(data))));
} }
finishFile = function() { finishFile = function() {
Santoku.finishDownload(); SliceBeam.finishDownload();
} }
+3 -3
View File
@@ -47,7 +47,7 @@ var dark = $['is_dark_theme'];
document.getElementsByClassName('md-option')[dark ? 1 : 0].click(); document.getElementsByClassName('md-option')[dark ? 1 : 0].click();
saveTextAsFile = function(filename, text) { saveTextAsFile = function(filename, text) {
Santoku.beginDownload(filename); SliceBeam.beginDownload(filename);
Santoku.writeData(btoa(unescape(encodeURIComponent(text)))); SliceBeam.writeData(btoa(unescape(encodeURIComponent(text))));
Santoku.finishDownload(); SliceBeam.finishDownload();
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

@@ -1,18 +0,0 @@
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,10 +0,0 @@
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,12 +0,0 @@
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,115 +0,0 @@
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);
}
}
@@ -1,136 +0,0 @@
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,441 +0,0 @@
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);
}
}
}
@@ -1,314 +0,0 @@
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;
}
}
}
@@ -0,0 +1,98 @@
package ru.ytkab0bp.slicebeam;
import android.util.Log;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import cz.msebera.android.httpclient.Header;
import ru.ytkab0bp.slicebeam.cloud.CloudController;
import ru.ytkab0bp.slicebeam.events.BeamServerDataUpdatedEvent;
import ru.ytkab0bp.slicebeam.utils.Prefs;
public class BeamServerData {
private final static String TAG = "BeamServerData";
private final static String DATA_URL = "https://beam3d.ru/slicebeam.php?act=get_data";
private final static String RUSSIA_CHECK_URL = "https://beam3d.ru/check_russia.txt";
private static AsyncHttpClient client = new AsyncHttpClient();
static {
client.setUserAgent(String.format(Locale.ROOT, "SliceBeam/%s-%d", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
client.setEnableRedirects(true);
client.setLoggingEnabled(false);
}
public List<String> boostySubscribers = new ArrayList<>();
public BeamServerData(JSONObject obj) {
JSONArray arr = obj.optJSONArray("boosty_subscribers");
if (arr != null) {
for (int i = 0; i < arr.length(); i++) {
boostySubscribers.add(arr.optString(i));
}
}
}
public static boolean isBoostyAvailable() {
return !BuildConfig.IS_GOOGLE_PLAY || Prefs.isRussianIP();
}
public static boolean isCloudAvailable() {
return isBoostyAvailable() && CloudController.hasAccountFeatures();
}
public static void load() {
client.get(DATA_URL, new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
String str = new String(responseBody, StandardCharsets.UTF_8);
Prefs.setBeamServerData(str);
Prefs.setLastCheckedInfo();
try {
SliceBeam.SERVER_DATA = new BeamServerData(new JSONObject(str));
} catch (JSONException e) {
throw new RuntimeException(e);
}
// Disable Boosty only for Google Play builds on non-Russian IP's
if (BuildConfig.IS_GOOGLE_PLAY) {
client.get(RUSSIA_CHECK_URL, new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
setIsRussia(new String(responseBody).equals("true"));
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
if (statusCode == 403) {
setIsRussia(false);
}
}
private void setIsRussia(boolean v) {
Prefs.setRussianIP(v);
SliceBeam.EVENT_BUS.fireEvent(new BeamServerDataUpdatedEvent());
}
});
} else {
SliceBeam.EVENT_BUS.fireEvent(new BeamServerDataUpdatedEvent());
}
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
Log.e(TAG, "Failed to update server data", error);
}
});
}
}
@@ -1,14 +1,20 @@
package com.dark98.santoku; package ru.ytkab0bp.slicebeam;
import android.app.Activity; import android.app.Activity;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color; import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.os.Process; import android.os.Process;
import android.provider.MediaStore;
import android.util.Base64;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
@@ -26,6 +32,7 @@ import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@@ -41,32 +48,36 @@ import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
import com.dark98.santoku.cloud.CloudController; import ru.ytkab0bp.sapil.APICallback;
import com.dark98.santoku.components.BeamAlertDialogBuilder; import ru.ytkab0bp.slicebeam.cloud.CloudAPI;
import com.dark98.santoku.components.ChangeLogBottomSheet; import ru.ytkab0bp.slicebeam.cloud.CloudController;
import com.dark98.santoku.components.UnfoldMenu; import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import com.dark98.santoku.config.ConfigObject; import ru.ytkab0bp.slicebeam.components.ChangeLogBottomSheet;
import com.dark98.santoku.events.NeedDismissSnackbarEvent; import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
import com.dark98.santoku.events.NeedSnackbarEvent; import ru.ytkab0bp.slicebeam.config.ConfigObject;
import com.dark98.santoku.events.ObjectsListChangedEvent; import ru.ytkab0bp.slicebeam.events.NeedDismissAIGeneratorMenu;
import com.dark98.santoku.fragment.BedFragment; import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import com.dark98.santoku.navigation.Fragment; import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import com.dark98.santoku.navigation.MobileNavigationDelegate; import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
import com.dark98.santoku.navigation.NavigationDelegate; import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import com.dark98.santoku.slic3r.Model; import ru.ytkab0bp.slicebeam.navigation.Fragment;
import com.dark98.santoku.slic3r.Slic3rConfigWrapper; import ru.ytkab0bp.slicebeam.navigation.MobileNavigationDelegate;
import com.dark98.santoku.slic3r.Slic3rRuntimeError; import ru.ytkab0bp.slicebeam.navigation.NavigationDelegate;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.slic3r.Model;
import com.dark98.santoku.utils.IOUtils; import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
import com.dark98.santoku.utils.Prefs; import ru.ytkab0bp.slicebeam.slic3r.Slic3rRuntimeError;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.view.SnackbarsLayout; import ru.ytkab0bp.slicebeam.utils.IOUtils;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
// Activity result // Activity result
public final static int REQUEST_CODE_OPEN_FILE = 1, REQUEST_CODE_EXPORT_GCODE = 2, 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_IMPORT_PROFILES = 3, REQUEST_CODE_EXPORT_PROFILES = 4,
REQUEST_CODE_EXPORT_3MF = 5; REQUEST_CODE_EXPORT_3MF = 5,
REQUEST_CODE_AI_GENERATOR_TAKE_PHOTO = 6, REQUEST_CODE_AI_GENERATOR_CHOOSE_PHOTO = 7;
private static MainActivity activeInstance; private static MainActivity activeInstance;
@@ -74,6 +85,9 @@ public class MainActivity extends AppCompatActivity {
public static List<ConfigObject> EXPORTING_FILAMENTS; public static List<ConfigObject> EXPORTING_FILAMENTS;
public static List<ConfigObject> EXPORTING_PRINTERS; 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 SparseArray<NavigationDelegate> liveDelegate = new SparseArray<>();
private static int lastId; private static int lastId;
@@ -91,7 +105,7 @@ public class MainActivity extends AppCompatActivity {
finish(); finish();
return; return;
} }
if (Santoku.CONFIG == null) { if (SliceBeam.CONFIG == null) {
Prefs.setLastCommit(); Prefs.setLastCommit();
startActivity(new Intent(this, SetupActivity.class)); startActivity(new Intent(this, SetupActivity.class));
finish(); finish();
@@ -140,13 +154,8 @@ public class MainActivity extends AppCompatActivity {
setContentView(v); setContentView(v);
if (getIntent() != null && getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_VIEW)) { if (getIntent() != null && getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_VIEW)) {
Uri data = getIntent().getData(); loadFile(getIntent().getData());
if (data != null && "santoku".equalsIgnoreCase(data.getScheme())) {
setIntent(null); setIntent(null);
} else {
loadFile(data);
setIntent(null);
}
} }
DisplayMetrics dm = getResources().getDisplayMetrics(); DisplayMetrics dm = getResources().getDisplayMetrics();
@@ -185,8 +194,9 @@ public class MainActivity extends AppCompatActivity {
} }
} }
if (!Objects.equals(Prefs.getLastCommit(), BuildConfig.COMMIT) && Santoku.hasUpdateInfo) { if (!Objects.equals(Prefs.getLastCommit(), BuildConfig.COMMIT) && SliceBeam.hasUpdateInfo) {
Prefs.setLastCommit(); Prefs.setLastCommit();
BeamServerData.load();
new ChangeLogBottomSheet(this).show(); new ChangeLogBottomSheet(this).show();
} }
} }
@@ -199,11 +209,7 @@ public class MainActivity extends AppCompatActivity {
@Override @Override
protected void onNewIntent(@NonNull Intent intent) { protected void onNewIntent(@NonNull Intent intent) {
super.onNewIntent(intent); super.onNewIntent(intent);
Uri data = intent.getData(); loadFile(intent.getData());
if (data != null && "santoku".equalsIgnoreCase(data.getScheme())) {
return;
}
loadFile(data);
setIntent(null); setIntent(null);
} }
@@ -220,8 +226,8 @@ public class MainActivity extends AppCompatActivity {
OutputStream out = getContentResolver().openOutputStream(data.getData()); OutputStream out = getContentResolver().openOutputStream(data.getData());
Model model = ((BedFragment) fragment).getGlView().getRenderer().getModel(); Model model = ((BedFragment) fragment).getGlView().getRenderer().getModel();
File tempFile = File.createTempFile("temp_project", ".3mf"); File tempFile = File.createTempFile("temp_project", ".3mf");
Santoku.genCurrentConfig(); SliceBeam.genCurrentConfig();
File cfg = Santoku.getCurrentConfigFile(); File cfg = SliceBeam.getCurrentConfigFile();
model.export3mf(cfg.getAbsolutePath(), tempFile.getAbsolutePath()); model.export3mf(cfg.getAbsolutePath(), tempFile.getAbsolutePath());
InputStream in = new FileInputStream(tempFile); InputStream in = new FileInputStream(tempFile);
@@ -234,7 +240,7 @@ public class MainActivity extends AppCompatActivity {
out.close(); out.close();
tempFile.delete(); tempFile.delete();
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileExport3mfSuccess)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileExport3mfSuccess));
} catch (IOException | Slic3rRuntimeError e) { } catch (IOException | Slic3rRuntimeError e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -267,21 +273,21 @@ public class MainActivity extends AppCompatActivity {
EXPORTING_PRINTERS = null; EXPORTING_PRINTERS = null;
w.presets = new ConfigObject(); w.presets = new ConfigObject();
if (w.findPrint(Santoku.CONFIG.presets.get("print")) != null) { if (w.findPrint(SliceBeam.CONFIG.presets.get("print")) != null) {
w.presets.put("print", Santoku.CONFIG.presets.get("print")); w.presets.put("print", SliceBeam.CONFIG.presets.get("print"));
} }
if (w.findFilament(Santoku.CONFIG.presets.get("filament")) != null) { if (w.findFilament(SliceBeam.CONFIG.presets.get("filament")) != null) {
w.presets.put("filament", Santoku.CONFIG.presets.get("filament")); w.presets.put("filament", SliceBeam.CONFIG.presets.get("filament"));
} }
if (w.findPrinter(Santoku.CONFIG.presets.get("printer")) != null) { if (w.findPrinter(SliceBeam.CONFIG.presets.get("printer")) != null) {
w.presets.put("printer", Santoku.CONFIG.presets.get("printer")); w.presets.put("printer", SliceBeam.CONFIG.presets.get("printer"));
} }
OutputStream out = getContentResolver().openOutputStream(data.getData()); OutputStream out = getContentResolver().openOutputStream(data.getData());
out.write(w.serialize().getBytes(StandardCharsets.UTF_8)); out.write(w.serialize().getBytes(StandardCharsets.UTF_8));
out.close(); out.close();
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileExportProfilesSuccess)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileExportProfilesSuccess));
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -321,14 +327,31 @@ public class MainActivity extends AppCompatActivity {
.setPositiveButton(android.R.string.ok, null) .setPositiveButton(android.R.string.ok, null)
.show(); .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) { private void loadConvertedProfile(Uri uri) {
String tag = UUID.randomUUID().toString(); String tag = UUID.randomUUID().toString();
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.OrcaConversionPleaseWait).tag(tag)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.OrcaConversionPleaseWait).tag(tag));
File f = new File(Santoku.getModelCacheDir(), "orca_conv.zip"); File f = new File(SliceBeam.getModelCacheDir(), "orca_conv.zip");
IOUtils.IO_POOL.submit(()->{ IOUtils.IO_POOL.submit(()->{
try { try {
InputStream in = getContentResolver().openInputStream(uri); InputStream in = getContentResolver().openInputStream(uri);
@@ -346,7 +369,7 @@ public class MainActivity extends AppCompatActivity {
if (!bundle.get("bundle_type").equals("printer config bundle")) { if (!bundle.get("bundle_type").equals("printer config bundle")) {
zf.close(); zf.close();
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag)); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this) ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileImportProfilesFailed) .setTitle(R.string.MenuFileImportProfilesFailed)
.setMessage(R.string.OrcaConversionNotAConfigBundle) .setMessage(R.string.OrcaConversionNotAConfigBundle)
@@ -447,17 +470,17 @@ public class MainActivity extends AppCompatActivity {
zf.close(); zf.close();
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag)); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
loadIniForImport(new ByteArrayInputStream(w.serialize().getBytes(StandardCharsets.UTF_8))); loadIniForImport(new ByteArrayInputStream(w.serialize().getBytes(StandardCharsets.UTF_8)));
} catch (IOUtils.MissingProfileException ep) { } catch (IOUtils.MissingProfileException ep) {
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag)); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this) ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileImportProfilesFailed) .setTitle(R.string.MenuFileImportProfilesFailed)
.setMessage(getString(R.string.MenuFileImportProfilesFailedBaseProfileNotFound, ep.profile)) .setMessage(getString(R.string.MenuFileImportProfilesFailedBaseProfileNotFound, ep.profile))
.setPositiveButton(android.R.string.ok, null) .setPositiveButton(android.R.string.ok, null)
.show()); .show());
} catch (Exception e) { } catch (Exception e) {
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag)); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this) ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileImportProfilesFailed) .setTitle(R.string.MenuFileImportProfilesFailed)
.setMessage(e.toString()) .setMessage(e.toString())
@@ -467,6 +490,110 @@ 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) { private void loadIniForImport(InputStream in) {
IOUtils.IO_POOL.submit(()->{ IOUtils.IO_POOL.submit(()->{
try { try {
@@ -506,20 +633,20 @@ public class MainActivity extends AppCompatActivity {
Runnable finish = () -> { Runnable finish = () -> {
for (int i = 0; i < enabledPrints.length; i++) { for (int i = 0; i < enabledPrints.length; i++) {
if (enabledPrints[i]) { if (enabledPrints[i]) {
Santoku.CONFIG.importPrint(w.printConfigs.get(i)); SliceBeam.CONFIG.importPrint(w.printConfigs.get(i));
} }
} }
for (int i = 0; i < enabledFilaments.length; i++) { for (int i = 0; i < enabledFilaments.length; i++) {
if (enabledFilaments[i]) { if (enabledFilaments[i]) {
Santoku.CONFIG.importFilament(w.filamentConfigs.get(i)); SliceBeam.CONFIG.importFilament(w.filamentConfigs.get(i));
} }
} }
for (int i = 0; i < enabledPrinters.length; i++) { for (int i = 0; i < enabledPrinters.length; i++) {
if (enabledPrinters[i]) { if (enabledPrinters[i]) {
Santoku.CONFIG.importPrinter(w.printerConfigs.get(i)); SliceBeam.CONFIG.importPrinter(w.printerConfigs.get(i));
} }
} }
Santoku.saveConfig(); SliceBeam.saveConfig();
}; };
Runnable printersRun = () -> { Runnable printersRun = () -> {
if (printers.length == 0) { if (printers.length == 0) {
@@ -571,7 +698,7 @@ public class MainActivity extends AppCompatActivity {
private void loadFile(File f, boolean autoorient) { private void loadFile(File f, boolean autoorient) {
String tag = UUID.randomUUID().toString(); String tag = UUID.randomUUID().toString();
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileOpenFileLoading).tag(tag)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileOpenFileLoading).tag(tag));
IOUtils.IO_POOL.submit(() -> { IOUtils.IO_POOL.submit(() -> {
Process.setThreadPriority(-20); Process.setThreadPriority(-20);
if (delegate.getCurrentFragment() instanceof BedFragment) { if (delegate.getCurrentFragment() instanceof BedFragment) {
@@ -593,7 +720,7 @@ public class MainActivity extends AppCompatActivity {
} }
if (!gcode) { if (!gcode) {
Santoku.EVENT_BUS.fireEvent(new ObjectsListChangedEvent()); SliceBeam.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
} }
int i = model.getObjectsCount() - 1; int i = model.getObjectsCount() - 1;
if (autoorient) { if (autoorient) {
@@ -601,10 +728,10 @@ public class MainActivity extends AppCompatActivity {
fragment.getGlView().getRenderer().invalidateGlModel(i); fragment.getGlView().getRenderer().invalidateGlModel(i);
fragment.getGlView().requestRender(); fragment.getGlView().requestRender();
} }
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag)); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileOpenFileLoaded)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileOpenFileLoaded));
if (model.isBigObject(i)) { if (model.isBigObject(i)) {
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.WARNING, R.string.MenuFileOpenFileBigObject)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.WARNING, R.string.MenuFileOpenFileBigObject));
} }
} }
}); });
@@ -612,7 +739,7 @@ public class MainActivity extends AppCompatActivity {
Log.e("MainActivity", "Failed to load model", e); Log.e("MainActivity", "Failed to load model", e);
f.delete(); f.delete();
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag)); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this) ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(this)
.setTitle(R.string.MenuFileOpenFileFailed) .setTitle(R.string.MenuFileOpenFileFailed)
.setMessage(e.toString()) .setMessage(e.toString())
@@ -653,7 +780,7 @@ public class MainActivity extends AppCompatActivity {
return; return;
} }
File f = new File(Santoku.getModelCacheDir(), fileName); File f = new File(SliceBeam.getModelCacheDir(), fileName);
// TODO: Check if file already exists // TODO: Check if file already exists
IOUtils.IO_POOL.submit(()->{ IOUtils.IO_POOL.submit(()->{
try { try {
@@ -752,12 +879,6 @@ public class MainActivity extends AppCompatActivity {
delegate.onPause(); delegate.onPause();
} }
@Override
protected void onStop() {
super.onStop();
Santoku.saveConfig();
}
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
@@ -1,4 +1,4 @@
package com.dark98.santoku; package ru.ytkab0bp.slicebeam;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
@@ -17,10 +17,10 @@ import android.widget.TextView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils; import androidx.core.graphics.ColorUtils;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.Prefs; import ru.ytkab0bp.slicebeam.utils.Prefs;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.view.BeamButton; import ru.ytkab0bp.slicebeam.view.BeamButton;
public class SafeStartActivity extends Activity { public class SafeStartActivity extends Activity {
@Override @Override
@@ -1,4 +1,4 @@
package com.dark98.santoku; package ru.ytkab0bp.slicebeam;
import static android.opengl.GLES30.GL_COLOR_BUFFER_BIT; import static android.opengl.GLES30.GL_COLOR_BUFFER_BIT;
import static android.opengl.GLES30.GL_DEPTH_BUFFER_BIT; import static android.opengl.GLES30.GL_DEPTH_BUFFER_BIT;
@@ -24,7 +24,6 @@ import android.net.Uri;
import android.opengl.GLSurfaceView; import android.opengl.GLSurfaceView;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.text.InputType;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@@ -36,8 +35,6 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.EditText;
import android.widget.CheckBox;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
@@ -63,7 +60,6 @@ import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import com.loopj.android.http.AsyncHttpClient; import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler; import com.loopj.android.http.AsyncHttpResponseHandler;
import ru.ytkab0bp.sapil.APICallback;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
@@ -93,35 +89,39 @@ import javax.microedition.khronos.opengles.GL10;
import cz.msebera.android.httpclient.Header; import cz.msebera.android.httpclient.Header;
import ru.ytkab0bp.eventbus.EventHandler; import ru.ytkab0bp.eventbus.EventHandler;
import com.dark98.santoku.cloud.CloudAPI; import ru.ytkab0bp.slicebeam.cloud.CloudAPI;
import com.dark98.santoku.cloud.CloudController; import ru.ytkab0bp.slicebeam.cloud.CloudController;
import com.dark98.santoku.components.BeamAlertDialogBuilder; import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import com.dark98.santoku.components.CloudManageBottomSheet; import ru.ytkab0bp.slicebeam.components.CloudManageBottomSheet;
import com.dark98.santoku.config.ConfigObject; import ru.ytkab0bp.slicebeam.config.ConfigObject;
import com.dark98.santoku.events.CloudLoginStateUpdatedEvent; import ru.ytkab0bp.slicebeam.events.BeamServerDataUpdatedEvent;
import com.dark98.santoku.events.CloudSyncFinishedEvent; import ru.ytkab0bp.slicebeam.events.CloudFeaturesUpdatedEvent;
import com.dark98.santoku.recycler.BigHeaderItem; import ru.ytkab0bp.slicebeam.events.CloudLoginStateUpdatedEvent;
import com.dark98.santoku.recycler.PreferenceItem; import ru.ytkab0bp.slicebeam.events.CloudSyncFinishedEvent;
import com.dark98.santoku.recycler.SimpleRecyclerAdapter; import ru.ytkab0bp.slicebeam.recycler.BigHeaderItem;
import com.dark98.santoku.recycler.SimpleRecyclerItem; import ru.ytkab0bp.slicebeam.recycler.PreferenceItem;
import com.dark98.santoku.recycler.TextHintRecyclerItem; import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerAdapter;
import com.dark98.santoku.slic3r.GLModel; import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import com.dark98.santoku.slic3r.GLShaderProgram; import ru.ytkab0bp.slicebeam.recycler.TextHintRecyclerItem;
import com.dark98.santoku.slic3r.GLShadersManager; import ru.ytkab0bp.slicebeam.slic3r.GLModel;
import com.dark98.santoku.slic3r.Slic3rConfigWrapper; import ru.ytkab0bp.slicebeam.slic3r.GLShaderProgram;
import com.dark98.santoku.slic3r.Slic3rUtils; import ru.ytkab0bp.slicebeam.slic3r.GLShadersManager;
import com.dark98.santoku.theme.BeamTheme; import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
import com.dark98.santoku.theme.IThemeView; import ru.ytkab0bp.slicebeam.slic3r.Slic3rUtils;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.BeamTheme;
import com.dark98.santoku.utils.Prefs; import ru.ytkab0bp.slicebeam.theme.IThemeView;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.view.BeamSwitch; import ru.ytkab0bp.slicebeam.utils.Prefs;
import com.dark98.santoku.view.FadeRecyclerView; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.view.MiniColorView; import ru.ytkab0bp.slicebeam.view.BeamSwitch;
import com.dark98.santoku.view.TextColorImageSpan; import ru.ytkab0bp.slicebeam.view.BoostySubsView;
import ru.ytkab0bp.slicebeam.view.FadeRecyclerView;
import ru.ytkab0bp.slicebeam.view.MiniColorView;
import ru.ytkab0bp.slicebeam.view.TextColorImageSpan;
public class SetupActivity extends AppCompatActivity { public class SetupActivity extends AppCompatActivity {
public final static String EXTRA_ABOUT = "about"; public final static String EXTRA_ABOUT = "about";
public final static String EXTRA_BOOSTY_ONLY = "boosty_only";
public final static String EXTRA_CLOUD_PROFILE = "cloud_profile"; public final static String EXTRA_CLOUD_PROFILE = "cloud_profile";
public final static String EXTRA_CLOUD_IMPORT_FROM_SETUP = "cloud_import_from_setup"; public final static String EXTRA_CLOUD_IMPORT_FROM_SETUP = "cloud_import_from_setup";
@@ -129,11 +129,12 @@ public class SetupActivity extends AppCompatActivity {
private final static List<String> REPOS_URLS = Arrays.asList( private final static List<String> REPOS_URLS = Arrays.asList(
"https://preset-repo-api.prusa3d.com/v1/repos", "https://preset-repo-api.prusa3d.com/v1/repos",
"https://raw.githubusercontent.com/Dark98/SliceBeam/refs/heads/master/.profiledumpsrepo/manifest.json" "https://raw.githubusercontent.com/utkabobr/SliceBeam/refs/heads/master/.profiledumpsrepo/manifest.json"
); );
private final static int REPOS_INDEX = 1; private final static int REPOS_INDEX = 1;
private final static int PROFILES_INDEX = 2; private final static int PROFILES_INDEX = 2;
private static int BOOSTY_INDEX = 3;
private final static int TYPE_PRINTER = 0, TYPE_PRINT_CONFIG = 1, TYPE_FILAMENT = 2; private final static int TYPE_PRINTER = 0, TYPE_PRINT_CONFIG = 1, TYPE_FILAMENT = 2;
@@ -147,6 +148,7 @@ public class SetupActivity extends AppCompatActivity {
private int titleY; private int titleY;
private float backgroundProgress; private float backgroundProgress;
private float boostyProgress;
private SpringAnimation fakeScroller; private SpringAnimation fakeScroller;
@@ -164,13 +166,14 @@ public class SetupActivity extends AppCompatActivity {
private Map<ProfilesRepo, List<Slic3rConfigWrapper>> profilesMap = new HashMap<>(); private Map<ProfilesRepo, List<Slic3rConfigWrapper>> profilesMap = new HashMap<>();
private boolean isProfilesLoaded; private boolean isProfilesLoaded;
private boolean about; private boolean about;
private boolean boostyOnly;
private boolean cloudProfile; private boolean cloudProfile;
private boolean cloudImport; private boolean cloudImport;
private List<ConfigObject> enabledPrinters = new ArrayList<>(); private List<ConfigObject> enabledPrinters = new ArrayList<>();
{ {
client.setUserAgent(String.format(Locale.ROOT, "Santoku/%s-%d", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)); client.setUserAgent(String.format(Locale.ROOT, "SliceBeam/%s-%d", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
client.setEnableRedirects(true); client.setEnableRedirects(true);
client.setLoggingEnabled(false); client.setLoggingEnabled(false);
} }
@@ -179,13 +182,14 @@ public class SetupActivity extends AppCompatActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
EdgeToEdge.enable(this); EdgeToEdge.enable(this);
Santoku.EVENT_BUS.registerListener(this); SliceBeam.EVENT_BUS.registerListener(this);
about = getIntent().getBooleanExtra(EXTRA_ABOUT, false); about = getIntent().getBooleanExtra(EXTRA_ABOUT, false);
boostyOnly = getIntent().getBooleanExtra(EXTRA_BOOSTY_ONLY, false);
cloudProfile = getIntent().getBooleanExtra(EXTRA_CLOUD_PROFILE, false); cloudProfile = getIntent().getBooleanExtra(EXTRA_CLOUD_PROFILE, false);
cloudImport = getIntent().getBooleanExtra(EXTRA_CLOUD_IMPORT_FROM_SETUP, false); cloudImport = getIntent().getBooleanExtra(EXTRA_CLOUD_IMPORT_FROM_SETUP, false);
if (!about && !cloudProfile) { if (!about && !boostyOnly && !cloudProfile) {
new BeamAlertDialogBuilder(this) new BeamAlertDialogBuilder(this)
.setTitle(R.string.IntroEarlyAccess) .setTitle(R.string.IntroEarlyAccess)
.setMessage(R.string.IntroEarlyAccessMessage) .setMessage(R.string.IntroEarlyAccessMessage)
@@ -193,7 +197,7 @@ public class SetupActivity extends AppCompatActivity {
.show(); .show();
} }
if (cloudProfile) { if (boostyOnly || cloudProfile) {
backgroundProgress = 1f; backgroundProgress = 1f;
} }
@@ -201,7 +205,7 @@ public class SetupActivity extends AppCompatActivity {
adapter = new SimpleRecyclerAdapter() { adapter = new SimpleRecyclerAdapter() {
@Override @Override
public int getItemCount() { public int getItemCount() {
return about || cloudProfile ? 1 : limitRepoFragmentCount ? REPOS_INDEX + 1 : limitProfileFragmentCount ? PROFILES_INDEX + 1 : super.getItemCount(); return about || boostyOnly || cloudProfile ? 1 : limitRepoFragmentCount ? REPOS_INDEX + 1 : limitProfileFragmentCount ? PROFILES_INDEX + 1 : super.getItemCount();
} }
}; };
setItems(); setItems();
@@ -232,12 +236,25 @@ public class SetupActivity extends AppCompatActivity {
@Override @Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (position == 0 && !cloudProfile) { if (position == 0 && !boostyOnly && !cloudProfile) {
backgroundProgress = positionOffset; backgroundProgress = positionOffset;
} else { } else {
backgroundProgress = 1f; 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 (position == REPOS_INDEX) {
if (!isReposLoaded && !isLoading) { if (!isReposLoaded && !isLoading) {
loadRepos(true); loadRepos(true);
@@ -317,7 +334,7 @@ public class SetupActivity extends AppCompatActivity {
Log.e(TAG, "Failed to load vendor file " + iniUrl, error); Log.e(TAG, "Failed to load vendor file " + iniUrl, error);
isLoading = false; isLoading = false;
ViewUtils.postOnMainThread(() -> { ViewUtils.postOnMainThread(() -> {
Toast.makeText(Santoku.INSTANCE, R.string.IntroFailedToLoadRepos, Toast.LENGTH_SHORT).show(); Toast.makeText(SliceBeam.INSTANCE, R.string.IntroFailedToLoadRepos, Toast.LENGTH_SHORT).show();
fakeScroll(-1); fakeScroll(-1);
pager.setUserInputEnabled(true); pager.setUserInputEnabled(true);
}); });
@@ -344,7 +361,7 @@ public class SetupActivity extends AppCompatActivity {
isLoading = false; isLoading = false;
Log.e(TAG, "Failed to load repo", error); Log.e(TAG, "Failed to load repo", error);
ViewUtils.postOnMainThread(() -> { ViewUtils.postOnMainThread(() -> {
Toast.makeText(Santoku.INSTANCE, R.string.IntroFailedToLoadRepos, Toast.LENGTH_SHORT).show(); Toast.makeText(SliceBeam.INSTANCE, R.string.IntroFailedToLoadRepos, Toast.LENGTH_SHORT).show();
fakeScroll(-1); fakeScroll(-1);
pager.setUserInputEnabled(true); pager.setUserInputEnabled(true);
}); });
@@ -417,13 +434,17 @@ public class SetupActivity extends AppCompatActivity {
shader.startUsing(); shader.startUsing();
int topColor = ThemesRepo.getColor(android.R.attr.colorAccent); int topColor = ThemesRepo.getColor(android.R.attr.colorAccent);
int bottomColor = ThemesRepo.getColor(android.R.attr.windowBackground); int bottomColor = ThemesRepo.getColor(android.R.attr.windowBackground);
if (boostyProgress != 0f) {
topColor = ColorUtils.blendARGB(bottomColor, ThemesRepo.getColor(R.attr.boostyColorTop), boostyProgress);
bottomColor = ColorUtils.blendARGB(bottomColor, ThemesRepo.getColor(R.attr.boostyColorBottom), boostyProgress);
}
if (cloudProfile) { if (cloudProfile) {
bottomColor = ColorUtils.blendARGB(bottomColor, topColor, 0.5f); bottomColor = ColorUtils.blendARGB(bottomColor, topColor, 0.5f);
} }
shader.setUniformColor("top_color", topColor); shader.setUniformColor("top_color", topColor);
shader.setUniformColor("bottom_color", bottomColor); shader.setUniformColor("bottom_color", bottomColor);
shader.setUniform("progress", backgroundProgress - (cloudProfile ? 1.4f : 0)); shader.setUniform("progress", backgroundProgress - (cloudProfile ? 1.4f : 0) - (boostyProgress != 0 ? 1.2f : 0));
shader.setUniform("time", time); shader.setUniform("time", time);
backgroundModel.render(); backgroundModel.render();
shader.stopUsing(); shader.stopUsing();
@@ -468,7 +489,7 @@ public class SetupActivity extends AppCompatActivity {
title.setPivotY(0); title.setPivotY(0);
title.setScaleX(sc); title.setScaleX(sc);
title.setScaleY(sc); title.setScaleY(sc);
int color = ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.textColorOnAccent), ThemesRepo.getColor(android.R.attr.colorAccent), cloudProfile ? 0f : backgroundProgress); int color = ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.textColorOnAccent), ThemesRepo.getColor(android.R.attr.colorAccent), cloudProfile ? 0f : backgroundProgress - boostyProgress);
title.setTextColor(color); title.setTextColor(color);
title.setTranslationY(ViewUtils.lerp(titleY, (ViewUtils.dp(52) - title.getHeight() * title.getScaleY()) / 2f, backgroundProgress)); title.setTranslationY(ViewUtils.lerp(titleY, (ViewUtils.dp(52) - title.getHeight() * title.getScaleY()) / 2f, backgroundProgress));
} }
@@ -477,7 +498,17 @@ public class SetupActivity extends AppCompatActivity {
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
Santoku.EVENT_BUS.unregisterListener(this); 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();
}
}
} }
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
@@ -486,7 +517,7 @@ public class SetupActivity extends AppCompatActivity {
if (cloudProfile && Prefs.getCloudAPIToken() != null && cloudImport) { if (cloudProfile && Prefs.getCloudAPIToken() != null && cloudImport) {
finish(); finish();
} }
if (!about && !cloudProfile) { if (!about && !boostyOnly && !cloudProfile) {
if (Prefs.getCloudAPIToken() != null) { if (Prefs.getCloudAPIToken() != null) {
limitRepoFragmentCount = false; limitRepoFragmentCount = false;
limitProfileFragmentCount = false; limitProfileFragmentCount = false;
@@ -500,8 +531,13 @@ public class SetupActivity extends AppCompatActivity {
public void onCloudAuthStateUpdated(CloudLoginStateUpdatedEvent e) { public void onCloudAuthStateUpdated(CloudLoginStateUpdatedEvent e) {
if (cloudProfile) { if (cloudProfile) {
cloudItem.bindLoginButton(true); cloudItem.bindLoginButton(true);
cloudItem.bindFeatures();
} }
if (!about && !cloudProfile && reposItem != null) { }
@EventHandler(runOnMainThread = true)
public void onCloudFeaturesUpdated(CloudFeaturesUpdatedEvent e) {
if (!about && !boostyOnly && !cloudProfile) {
reposItem.onCloudInfoUpdated(); reposItem.onCloudInfoUpdated();
} }
} }
@@ -509,6 +545,8 @@ public class SetupActivity extends AppCompatActivity {
private void setItems() { private void setItems() {
if (cloudProfile){ if (cloudProfile){
adapter.setItems(Collections.singletonList(cloudItem = new CloudProfileItem())); adapter.setItems(Collections.singletonList(cloudItem = new CloudProfileItem()));
} else if (boostyOnly) {
adapter.setItems(Collections.singletonList(new BoostyItem()));
} else if (about) { } else if (about) {
adapter.setItems(Collections.singletonList(new AboutItem())); adapter.setItems(Collections.singletonList(new AboutItem()));
} else { } else {
@@ -517,6 +555,13 @@ public class SetupActivity extends AppCompatActivity {
reposItem = new ReposItem(), reposItem = new ReposItem(),
profilesItem = new ProfilesItem())); profilesItem = new ProfilesItem()));
if (BeamServerData.isBoostyAvailable()) {
BOOSTY_INDEX = items.size();
items.add(new BoostyItem());
} else {
BOOSTY_INDEX = -1;
}
items.add(new FinishItem()); items.add(new FinishItem());
adapter.setItems(items); adapter.setItems(items);
} }
@@ -589,7 +634,7 @@ public class SetupActivity extends AppCompatActivity {
Log.e(TAG, "Failed to load repos", error); Log.e(TAG, "Failed to load repos", error);
if (fromPage) { if (fromPage) {
ViewUtils.postOnMainThread(() -> { ViewUtils.postOnMainThread(() -> {
Toast.makeText(Santoku.INSTANCE, R.string.IntroFailedToLoadRepos, Toast.LENGTH_SHORT).show(); Toast.makeText(SliceBeam.INSTANCE, R.string.IntroFailedToLoadRepos, Toast.LENGTH_SHORT).show();
fakeScroll(-1); fakeScroll(-1);
pager.setUserInputEnabled(true); pager.setUserInputEnabled(true);
}); });
@@ -640,9 +685,7 @@ public class SetupActivity extends AppCompatActivity {
private FrameLayout buttonView; private FrameLayout buttonView;
private TextView buttonText; private TextView buttonText;
private ProgressBar buttonProgress; private ProgressBar buttonProgress;
private TextView titleView; private FadeRecyclerView recyclerView;
private TextView signUpButton;
private boolean signUpInProgress;
@Override @Override
public View onCreateView(Context ctx) { public View onCreateView(Context ctx) {
@@ -650,16 +693,23 @@ public class SetupActivity extends AppCompatActivity {
ll.setOrientation(LinearLayout.VERTICAL); ll.setOrientation(LinearLayout.VERTICAL);
ll.setPadding(0, ViewUtils.dp(42), 0, 0); ll.setPadding(0, ViewUtils.dp(42), 0, 0);
titleView = new TextView(ctx); TextView title = new TextView(ctx);
titleView.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent)); title.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); title.setText(R.string.SettingsCloudManageDescription);
titleView.setGravity(Gravity.CENTER); title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
titleView.setPadding(ViewUtils.dp(12), 0, ViewUtils.dp(12), 0); title.setGravity(Gravity.CENTER);
ll.addView(titleView); title.setPadding(ViewUtils.dp(12), 0, ViewUtils.dp(12), 0);
bindHeader(); ll.addView(title);
View spacer = new View(ctx); FrameLayout fl = new FrameLayout(ctx);
ll.addView(spacer, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f)); 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));
TextView tosButton = new TextView(ctx); TextView tosButton = new TextView(ctx);
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(ctx.getString(R.string.SettingsCloudManageTermsOfService)).append(" "); SpannableStringBuilder sb = SpannableStringBuilder.valueOf(ctx.getString(R.string.SettingsCloudManageTermsOfService)).append(" ");
@@ -674,7 +724,7 @@ public class SetupActivity extends AppCompatActivity {
tosButton.setGravity(Gravity.CENTER); tosButton.setGravity(Gravity.CENTER);
tosButton.setPadding(ViewUtils.dp(12), ViewUtils.dp(8), ViewUtils.dp(12), ViewUtils.dp(8)); 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.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
tosButton.setOnClickListener(v -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(BuildConfig.BEAM_BASE_URL_PROD + "/tos")))); tosButton.setOnClickListener(v -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://beam3d.ru/slicebeam_cloud_tos.html"))));
ll.addView(tosButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{ ll.addView(tosButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{
leftMargin = rightMargin = ViewUtils.dp(16); leftMargin = rightMargin = ViewUtils.dp(16);
bottomMargin = ViewUtils.dp(8); bottomMargin = ViewUtils.dp(8);
@@ -694,30 +744,27 @@ public class SetupActivity extends AppCompatActivity {
buttonProgress.setIndeterminateTintList(ColorStateList.valueOf(ThemesRepo.getColor(R.attr.textColorOnAccent))); buttonProgress.setIndeterminateTintList(ColorStateList.valueOf(ThemesRepo.getColor(R.attr.textColorOnAccent)));
buttonView.addView(buttonProgress, new FrameLayout.LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28), Gravity.CENTER)); 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)) {{ ll.addView(buttonView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{
leftMargin = rightMargin = ViewUtils.dp(16); 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); bottomMargin = ViewUtils.dp(16);
}}); }});
bindLoginButton(false);
ll.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); ll.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return ll; 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) { private void bindLoginButton(boolean animate) {
boolean loggedIn = Prefs.getCloudAPIToken() != null; boolean loggedIn = Prefs.getCloudAPIToken() != null;
boolean loading = !loggedIn && CloudController.isLoggingIn(); boolean loading = !loggedIn && CloudController.isLoggingIn();
@@ -772,7 +819,6 @@ public class SetupActivity extends AppCompatActivity {
buttonText.setVisibility(loading ? View.GONE : View.VISIBLE); buttonText.setVisibility(loading ? View.GONE : View.VISIBLE);
} }
buttonText.setText(loggedIn ? R.string.SettingsCloudManageButtonManage : R.string.SettingsCloudManageButtonLogIn); buttonText.setText(loggedIn ? R.string.SettingsCloudManageButtonManage : R.string.SettingsCloudManageButtonLogIn);
bindHeader();
buttonView.setOnClickListener(v-> { buttonView.setOnClickListener(v-> {
if (loading) { if (loading) {
new BeamAlertDialogBuilder(v.getContext()) new BeamAlertDialogBuilder(v.getContext())
@@ -787,150 +833,177 @@ public class SetupActivity extends AppCompatActivity {
CloudController.beginLogin(); CloudController.beginLogin();
} }
}); });
if (signUpButton != null) {
boolean showSignUp = !loggedIn && !loading;
signUpButton.setVisibility(showSignUp ? View.VISIBLE : View.GONE);
signUpButton.setEnabled(showSignUp && !signUpInProgress);
signUpButton.setAlpha(signUpButton.isEnabled() ? 1f : 0.6f);
} }
} }
private void bindHeader() { private final static class CloudSubscriptionLevel extends SimpleRecyclerItem<CloudSubscriptionLevel.LevelHolderView> {
if (titleView == null) return; private CloudAPI.SubscriptionLevel level;
private CloudSubscriptionLevel(CloudAPI.SubscriptionLevel level) {
this.level = level;
}
@Override
public LevelHolderView onCreateView(Context ctx) {
return new LevelHolderView(ctx);
}
@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();
}
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);
} else {
icon.setImageResource(R.drawable.cloud_plus_outline_28);
}
List<SimpleRecyclerItem> items = new ArrayList<>();
CloudAPI.UserFeatures features = CloudController.getUserFeatures();
CloudAPI.UserInfo info = CloudController.getUserInfo(); CloudAPI.UserInfo info = CloudController.getUserInfo();
if (Prefs.getCloudAPIToken() != null && info != null && info.displayName != null) { Context ctx = getContext();
titleView.setText(titleView.getContext().getString(R.string.SettingsCloudManageLoggedInAs, info.displayName)); 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);
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 { } else {
titleView.setText(R.string.SettingsCloudManageDescription); 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();
} }
}
private void showSignUpDialog(Context ctx) {
if (signUpInProgress) {
return;
}
LinearLayout ll = new LinearLayout(ctx);
ll.setOrientation(LinearLayout.VERTICAL);
ll.setPadding(ViewUtils.dp(16), ViewUtils.dp(8), ViewUtils.dp(16), 0);
TextView emailLabel = new TextView(ctx);
emailLabel.setText(R.string.SettingsCloudManageSignUpEmail);
emailLabel.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
emailLabel.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
ll.addView(emailLabel, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
EditText emailInput = new EditText(ctx);
emailInput.setHint(R.string.SettingsCloudManageSignUpEmail);
emailInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
emailInput.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
emailInput.setHintTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
ll.addView(emailInput, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
TextView displayNameLabel = new TextView(ctx);
displayNameLabel.setText(R.string.SettingsCloudManageSignUpDisplayName);
displayNameLabel.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
displayNameLabel.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
ll.addView(displayNameLabel, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
topMargin = ViewUtils.dp(12);
}});
EditText displayNameInput = new EditText(ctx);
displayNameInput.setHint(R.string.SettingsCloudManageSignUpDisplayName);
displayNameInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PERSON_NAME);
displayNameInput.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
displayNameInput.setHintTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
ll.addView(displayNameInput, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
topMargin = ViewUtils.dp(8);
}});
TextView passwordLabel = new TextView(ctx);
passwordLabel.setText(R.string.SettingsCloudManageSignUpPassword);
passwordLabel.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
passwordLabel.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
ll.addView(passwordLabel, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
topMargin = ViewUtils.dp(12);
}});
EditText passwordInput = new EditText(ctx);
passwordInput.setHint(R.string.SettingsCloudManageSignUpPassword);
passwordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
passwordInput.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
passwordInput.setHintTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
ll.addView(passwordInput, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
topMargin = ViewUtils.dp(8);
}});
CheckBox showPassword = new CheckBox(ctx);
showPassword.setText(R.string.SettingsCloudManageSignUpShowPassword);
showPassword.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
showPassword.setOnCheckedChangeListener((buttonView, isChecked) -> {
int selection = passwordInput.getSelectionEnd();
if (isChecked) {
passwordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
} else {
passwordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
}
passwordInput.setSelection(Math.max(selection, 0));
}); });
ll.addView(showPassword, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{ setClickable(allowSubscribe || subscribed);
topMargin = ViewUtils.dp(4); onApplyTheme();
}});
TextView dialogTitle = new TextView(ctx);
dialogTitle.setText(R.string.SettingsCloudManageButtonSignUp);
dialogTitle.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
dialogTitle.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 28);
dialogTitle.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
dialogTitle.setGravity(Gravity.CENTER);
dialogTitle.setPadding(0, ViewUtils.dp(4), 0, ViewUtils.dp(8));
ll.addView(dialogTitle, 0, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
new BeamAlertDialogBuilder(ctx)
.setView(ll)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.SettingsCloudManageButtonSignUp, (dialog, which) -> {
String email = emailInput.getText().toString().trim();
String displayName = displayNameInput.getText().toString().trim();
String password = passwordInput.getText().toString();
if (TextUtils.isEmpty(email) || TextUtils.isEmpty(displayName) || TextUtils.isEmpty(password)) {
Toast.makeText(ctx, R.string.SettingsCloudManageSignUpMissingFields, Toast.LENGTH_SHORT).show();
return;
}
CloudAPI api = CloudController.getApiSafe();
if (api == null) {
Toast.makeText(ctx, R.string.SettingsCloudManageSignUpFailed, Toast.LENGTH_SHORT).show();
return;
}
signUpInProgress = true;
bindLoginButton(true);
api.signup(email, password, displayName, new APICallback<CloudAPI.AuthToken>() {
@Override
public void onResponse(CloudAPI.AuthToken response) {
Prefs.setCloudAPIToken(response.bearer);
signUpInProgress = false;
CloudController.init();
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
} }
@Override @Override
public void onException(Exception e) { public void onApplyTheme() {
signUpInProgress = false; int accent = ThemesRepo.getColor(android.R.attr.colorAccent);
ViewUtils.postOnMainThread(() -> { if (ColorUtils.calculateLuminance(accent) >= 0.6f) {
new BeamAlertDialogBuilder(ctx) accent = ColorUtils.blendARGB(accent, Color.BLACK, 0.075f);
.setTitle(R.string.SettingsCloudManageSignUpFailed) }
.setMessage(e.toString()) boolean tooLight = ColorUtils.calculateLuminance(accent) >= 0.6f;
.setPositiveButton(android.R.string.ok, null) title.setTextColor(0xffffffff);
.show(); price.setTextColor(0xffffffff);
bindLoginButton(true); 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));
} }
});
})
.show();
} }
} }
private final class AboutItem extends SimpleRecyclerItem<View> { private final class AboutItem extends SimpleRecyclerItem<View> {
@@ -1162,16 +1235,16 @@ public class SetupActivity extends AppCompatActivity {
progressBar.setIndeterminateTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.colorAccent))); progressBar.setIndeterminateTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.colorAccent)));
cloudImportView.setTextColor(ThemesRepo.getColor(android.R.attr.colorAccent)); cloudImportView.setTextColor(ThemesRepo.getColor(android.R.attr.colorAccent));
cloudImportView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16)); cloudImportView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
cloudImportView.setVisibility(Prefs.getCloudAPIToken() != null ? View.VISIBLE : View.GONE); cloudImportView.setVisibility(BeamServerData.isCloudAvailable() ? View.VISIBLE : View.GONE);
cloudOrView.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary)); cloudOrView.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
cloudOrView.setVisibility(Prefs.getCloudAPIToken() != null ? View.VISIBLE : View.GONE); cloudOrView.setVisibility(BeamServerData.isCloudAvailable() ? View.VISIBLE : View.GONE);
customProfileView.setTextColor(ThemesRepo.getColor(android.R.attr.colorAccent)); customProfileView.setTextColor(ThemesRepo.getColor(android.R.attr.colorAccent));
customProfileView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16)); customProfileView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
buttonView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), ThemesRepo.getColor(android.R.attr.colorAccent), 16)); buttonView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), ThemesRepo.getColor(android.R.attr.colorAccent), 16));
if (adapter.getItemCount() == 0 && isReposLoaded) { if (adapter.getItemCount() == 0 && isReposLoaded) {
List<SimpleRecyclerItem> items = new ArrayList<>(repos); List<SimpleRecyclerItem> items = new ArrayList<>(repos);
items.add(new TextHintRecyclerItem(Santoku.INSTANCE.getString(R.string.IntroSelectRepos))); items.add(new TextHintRecyclerItem(SliceBeam.INSTANCE.getString(R.string.IntroSelectRepos)));
adapter.setItems(items); adapter.setItems(items);
} else { } else {
adapter.notifyDataSetChanged(); adapter.notifyDataSetChanged();
@@ -1191,14 +1264,14 @@ public class SetupActivity extends AppCompatActivity {
public void onCloudInfoUpdated() { public void onCloudInfoUpdated() {
if (cloudImportView != null) { if (cloudImportView != null) {
cloudImportView.setVisibility(Prefs.getCloudAPIToken() != null ? View.VISIBLE : View.GONE); cloudImportView.setVisibility(BeamServerData.isCloudAvailable() ? View.VISIBLE : View.GONE);
cloudOrView.setVisibility(Prefs.getCloudAPIToken() != null ? View.VISIBLE : View.GONE); cloudOrView.setVisibility(BeamServerData.isCloudAvailable() ? View.VISIBLE : View.GONE);
} }
} }
public void onReposLoaded() { public void onReposLoaded() {
List<SimpleRecyclerItem> items = new ArrayList<>(repos); List<SimpleRecyclerItem> items = new ArrayList<>(repos);
items.add(new TextHintRecyclerItem(Santoku.INSTANCE.getString(R.string.IntroSelectRepos))); items.add(new TextHintRecyclerItem(SliceBeam.INSTANCE.getString(R.string.IntroSelectRepos)));
adapter.setItems(items); adapter.setItems(items);
new SpringAnimation(new FloatValueHolder(0)) new SpringAnimation(new FloatValueHolder(0))
.setMinimumVisibleChange(1 / 256f) .setMinimumVisibleChange(1 / 256f)
@@ -1352,6 +1425,80 @@ 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 final class FinishItem extends SimpleRecyclerItem<View> {
private TextView buttonView; private TextView buttonView;
@@ -1482,10 +1629,10 @@ public class SetupActivity extends AppCompatActivity {
} }
} }
try { try {
if (Prefs.getCloudAPIToken() == null || Santoku.CONFIG == null) { if (Prefs.getCloudAPIToken() == null || SliceBeam.CONFIG == null) {
Santoku.getCurrentConfigFile().delete(); SliceBeam.getCurrentConfigFile().delete();
Santoku.CONFIG = cfg; SliceBeam.CONFIG = cfg;
FileOutputStream fos = new FileOutputStream(Santoku.getConfigFile()); FileOutputStream fos = new FileOutputStream(SliceBeam.getConfigFile());
fos.write(cfg.serialize().getBytes(StandardCharsets.UTF_8)); fos.write(cfg.serialize().getBytes(StandardCharsets.UTF_8));
fos.close(); fos.close();
} }
@@ -1,4 +1,4 @@
package com.dark98.santoku; package ru.ytkab0bp.slicebeam;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Application; import android.app.Application;
@@ -17,29 +17,31 @@ import java.util.Arrays;
import java.util.Map; import java.util.Map;
import ru.ytkab0bp.eventbus.EventBus; import ru.ytkab0bp.eventbus.EventBus;
import com.dark98.santoku.boot.AppBoot; import ru.ytkab0bp.slicebeam.boot.AppBoot;
import com.dark98.santoku.boot.CheckUpdateJsonTask; import ru.ytkab0bp.slicebeam.boot.BeamServerDataTask;
import com.dark98.santoku.boot.ClearModelCacheTask; import ru.ytkab0bp.slicebeam.boot.CheckUpdateJsonTask;
import com.dark98.santoku.boot.CloudInitTask; import ru.ytkab0bp.slicebeam.boot.ClearModelCacheTask;
import com.dark98.santoku.boot.EventBusTask; import ru.ytkab0bp.slicebeam.boot.CloudInitTask;
import com.dark98.santoku.boot.LoadSlic3rConfigTask; import ru.ytkab0bp.slicebeam.boot.EventBusTask;
import com.dark98.santoku.boot.PrefsTask; import ru.ytkab0bp.slicebeam.boot.LoadSlic3rConfigTask;
import com.dark98.santoku.boot.PrintConfigWarmupTask; import ru.ytkab0bp.slicebeam.boot.PrefsTask;
import com.dark98.santoku.boot.TrueTimeTask; import ru.ytkab0bp.slicebeam.boot.PrintConfigWarmupTask;
import com.dark98.santoku.boot.VibrationUtilsTask; import ru.ytkab0bp.slicebeam.boot.TrueTimeTask;
import com.dark98.santoku.cloud.CloudController; import ru.ytkab0bp.slicebeam.boot.VibrationUtilsTask;
import com.dark98.santoku.config.ConfigObject; import ru.ytkab0bp.slicebeam.cloud.CloudController;
import com.dark98.santoku.slic3r.ConfigOptionDef; import ru.ytkab0bp.slicebeam.config.ConfigObject;
import com.dark98.santoku.slic3r.PrintConfigDef; import ru.ytkab0bp.slicebeam.slic3r.ConfigOptionDef;
import com.dark98.santoku.slic3r.Slic3rConfigWrapper; import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
import com.dark98.santoku.utils.Prefs; import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
import ru.ytkab0bp.slicebeam.utils.Prefs;
public class Santoku extends Application { public class SliceBeam extends Application {
public static Santoku INSTANCE; public static SliceBeam INSTANCE;
public static EventBus EVENT_BUS = EventBus.newBus("main"); public static EventBus EVENT_BUS = EventBus.newBus("main");
public static TrueTimeImpl TRUE_TIME; public static TrueTimeImpl TRUE_TIME;
public static Slic3rConfigWrapper CONFIG; public static Slic3rConfigWrapper CONFIG;
public static int CONFIG_UID = 0; public static int CONFIG_UID = 0;
public static BeamServerData SERVER_DATA;
public static boolean hasUpdateInfo; public static boolean hasUpdateInfo;
@SuppressLint("ApplySharedPref") @SuppressLint("ApplySharedPref")
@@ -52,6 +54,7 @@ public class Santoku extends Application {
new PrefsTask(), new PrefsTask(),
new VibrationUtilsTask(), new VibrationUtilsTask(),
new TrueTimeTask(), new TrueTimeTask(),
new BeamServerDataTask(),
new PrintConfigWarmupTask(), new PrintConfigWarmupTask(),
new CheckUpdateJsonTask(), new CheckUpdateJsonTask(),
new ClearModelCacheTask(), new ClearModelCacheTask(),
@@ -72,40 +75,13 @@ public class Santoku extends Application {
} }
public static void saveConfig() { public static void saveConfig() {
if (CONFIG == null) { SliceBeam.CONFIG_UID++;
return;
}
Santoku.CONFIG_UID++;
File f = getConfigFile(); 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 { try {
File tmp = new File(f.getParentFile(), f.getName() + ".tmp"); FileOutputStream fos = new FileOutputStream(f);
FileOutputStream fos = new FileOutputStream(tmp); fos.write(CONFIG.serialize().getBytes(StandardCharsets.UTF_8));
fos.write(serialized.getBytes(StandardCharsets.UTF_8));
fos.getFD().sync();
fos.close(); 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(); getCurrentConfigFile().delete();
} catch (Exception e) { } catch (Exception e) {
Log.e("Config", "Failed to save config", e); Log.e("Config", "Failed to save config", e);
@@ -125,11 +101,11 @@ public class Santoku extends Application {
public static ConfigObject buildCurrentConfigObject() { public static ConfigObject buildCurrentConfigObject() {
ConfigObject singleObject = new ConfigObject(); ConfigObject singleObject = new ConfigObject();
ConfigObject printerConfig = Santoku.CONFIG.findPrinter(Santoku.CONFIG.presets.get("printer")); ConfigObject printerConfig = SliceBeam.CONFIG.findPrinter(SliceBeam.CONFIG.presets.get("printer"));
if (printerConfig != null) { if (printerConfig != null) {
singleObject.values.putAll(printerConfig.values); singleObject.values.putAll(printerConfig.values);
} }
ConfigObject printConfig = Santoku.CONFIG.findPrint(Santoku.CONFIG.presets.get("print")); ConfigObject printConfig = SliceBeam.CONFIG.findPrint(SliceBeam.CONFIG.presets.get("print"));
if (printConfig != null) { if (printConfig != null) {
for (Map.Entry<String, String> en : printConfig.values.entrySet()) { for (Map.Entry<String, String> en : printConfig.values.entrySet()) {
if (!Slic3rConfigWrapper.PRINTER_CONFIG_KEYS.contains(en.getKey())) { if (!Slic3rConfigWrapper.PRINTER_CONFIG_KEYS.contains(en.getKey())) {
@@ -138,7 +114,7 @@ public class Santoku extends Application {
} }
} }
// TODO: MMU. Detect by printerConfig#getExtruderCount() // TODO: MMU. Detect by printerConfig#getExtruderCount()
ConfigObject filamentConfig = Santoku.CONFIG.findFilament(Santoku.CONFIG.presets.get("filament")); ConfigObject filamentConfig = SliceBeam.CONFIG.findFilament(SliceBeam.CONFIG.presets.get("filament"));
if (filamentConfig != null) { if (filamentConfig != null) {
for (Map.Entry<String, String> en : filamentConfig.values.entrySet()) { for (Map.Entry<String, String> en : filamentConfig.values.entrySet()) {
if (!Slic3rConfigWrapper.PRINTER_CONFIG_KEYS.contains(en.getKey())) { if (!Slic3rConfigWrapper.PRINTER_CONFIG_KEYS.contains(en.getKey())) {
@@ -1,4 +1,4 @@
package com.dark98.santoku.boot; package ru.ytkab0bp.slicebeam.boot;
import android.util.Log; import android.util.Log;
import android.util.SparseBooleanArray; import android.util.SparseBooleanArray;
@@ -11,7 +11,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import com.dark98.santoku.BuildConfig; import ru.ytkab0bp.slicebeam.BuildConfig;
public class AppBoot { public class AppBoot {
private final static String TAG = "boot"; private final static String TAG = "boot";
@@ -0,0 +1,25 @@
package ru.ytkab0bp.slicebeam.boot;
import org.json.JSONException;
import org.json.JSONObject;
import ru.ytkab0bp.slicebeam.BeamServerData;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
public class BeamServerDataTask extends BootTask {
public BeamServerDataTask() {
super(() -> {
try {
SliceBeam.SERVER_DATA = new BeamServerData(new JSONObject(Prefs.getBeamServerData()));
} catch (JSONException e) {
throw new RuntimeException(e);
}
if (System.currentTimeMillis() - Prefs.getLastCheckedInfo() >= 86400000L) {
ViewUtils.postOnMainThread(BeamServerData::load);
}
});
onWorker();
}
}
@@ -1,4 +1,4 @@
package com.dark98.santoku.boot; package ru.ytkab0bp.slicebeam.boot;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -1,17 +1,17 @@
package com.dark98.santoku.boot; package ru.ytkab0bp.slicebeam.boot;
import java.io.IOException; import java.io.IOException;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
public class CheckUpdateJsonTask extends BootTask { public class CheckUpdateJsonTask extends BootTask {
public CheckUpdateJsonTask() { public CheckUpdateJsonTask() {
super(() -> { super(() -> {
try { try {
Santoku.INSTANCE.getAssets().open("update.json").close(); SliceBeam.INSTANCE.getAssets().open("update.json").close();
Santoku.hasUpdateInfo = true; SliceBeam.hasUpdateInfo = true;
} catch (IOException e) { } catch (IOException e) {
Santoku.hasUpdateInfo = false; SliceBeam.hasUpdateInfo = false;
} }
}); });
onWorker(); onWorker();
@@ -1,14 +1,14 @@
package com.dark98.santoku.boot; package ru.ytkab0bp.slicebeam.boot;
import java.io.File; import java.io.File;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
public class ClearModelCacheTask extends BootTask { public class ClearModelCacheTask extends BootTask {
@SuppressWarnings("ResultOfMethodCallIgnored") @SuppressWarnings("ResultOfMethodCallIgnored")
public ClearModelCacheTask() { public ClearModelCacheTask() {
super(()->{ super(()->{
File cache = Santoku.getModelCacheDir(); File cache = SliceBeam.getModelCacheDir();
if (cache.exists()) { if (cache.exists()) {
for (File f : cache.listFiles()) { for (File f : cache.listFiles()) {
f.delete(); f.delete();
@@ -1,8 +1,8 @@
package com.dark98.santoku.boot; package ru.ytkab0bp.slicebeam.boot;
import java.util.Collections; import java.util.Collections;
import com.dark98.santoku.cloud.CloudController; import ru.ytkab0bp.slicebeam.cloud.CloudController;
public class CloudCachedInitTask extends BootTask { public class CloudCachedInitTask extends BootTask {
public CloudCachedInitTask() { public CloudCachedInitTask() {
@@ -1,8 +1,8 @@
package com.dark98.santoku.boot; package ru.ytkab0bp.slicebeam.boot;
import java.util.Arrays; import java.util.Arrays;
import com.dark98.santoku.cloud.CloudController; import ru.ytkab0bp.slicebeam.cloud.CloudController;
public class CloudInitTask extends BootTask { public class CloudInitTask extends BootTask {
public CloudInitTask() { public CloudInitTask() {
@@ -0,0 +1,12 @@
package ru.ytkab0bp.slicebeam.boot;
import ru.ytkab0bp.eventbus.EventBus;
import ru.ytkab0bp.slicebeam.BuildConfig;
public class EventBusTask extends BootTask {
public EventBusTask() {
super(() -> EventBus.registerImpl(BuildConfig.APPLICATION_ID));
onWorker();
}
}
@@ -1,20 +1,20 @@
package com.dark98.santoku.boot; package ru.ytkab0bp.slicebeam.boot;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.slic3r.Slic3rConfigWrapper; import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
@SuppressWarnings("ResultOfMethodCallIgnored") @SuppressWarnings("ResultOfMethodCallIgnored")
public class LoadSlic3rConfigTask extends BootTask { public class LoadSlic3rConfigTask extends BootTask {
public LoadSlic3rConfigTask() { public LoadSlic3rConfigTask() {
super(() -> { super(() -> {
File cfgFile = Santoku.getConfigFile(); File cfgFile = SliceBeam.getConfigFile();
Santoku.getCurrentConfigFile().delete(); SliceBeam.getCurrentConfigFile().delete();
if (cfgFile.exists()) { if (cfgFile.exists()) {
try { try {
Santoku.CONFIG = new Slic3rConfigWrapper(cfgFile); SliceBeam.CONFIG = new Slic3rConfigWrapper(cfgFile);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -0,0 +1,10 @@
package ru.ytkab0bp.slicebeam.boot;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.utils.Prefs;
public class PrefsTask extends BootTask {
public PrefsTask() {
super(()->Prefs.init(SliceBeam.INSTANCE));
}
}
@@ -1,6 +1,6 @@
package com.dark98.santoku.boot; package ru.ytkab0bp.slicebeam.boot;
import com.dark98.santoku.slic3r.PrintConfigDef; import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
public class PrintConfigWarmupTask extends BootTask { public class PrintConfigWarmupTask extends BootTask {
public PrintConfigWarmupTask() { public PrintConfigWarmupTask() {
@@ -1,4 +1,4 @@
package com.dark98.santoku.boot; package ru.ytkab0bp.slicebeam.boot;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -13,13 +13,13 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import kotlinx.coroutines.Dispatchers; import kotlinx.coroutines.Dispatchers;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
public class TrueTimeTask extends BootTask { public class TrueTimeTask extends BootTask {
public TrueTimeTask() { public TrueTimeTask() {
super(() -> { super(() -> {
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
Santoku.TRUE_TIME = new TrueTimeImpl(new TrueTimeParameters.Builder().buildParams(), Dispatchers.getIO(), new TrueTimeEventListener() { SliceBeam.TRUE_TIME = new TrueTimeImpl(new TrueTimeParameters.Builder().buildParams(), Dispatchers.getIO(), new TrueTimeEventListener() {
@Override @Override
public void initialize(@NonNull TrueTimeParameters trueTimeParameters) {} public void initialize(@NonNull TrueTimeParameters trueTimeParameters) {}
@@ -64,7 +64,7 @@ public class TrueTimeTask extends BootTask {
@Override @Override
public void returningDeviceTime() {} public void returningDeviceTime() {}
}); });
Santoku.TRUE_TIME.sync(); SliceBeam.TRUE_TIME.sync();
try { try {
latch.await(300, TimeUnit.MILLISECONDS); latch.await(300, TimeUnit.MILLISECONDS);
} catch (InterruptedException ignored) {} } catch (InterruptedException ignored) {}
@@ -0,0 +1,12 @@
package ru.ytkab0bp.slicebeam.boot;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.utils.VibrationUtils;
public class VibrationUtilsTask extends BootTask {
public VibrationUtilsTask() {
super(() -> VibrationUtils.init(SliceBeam.INSTANCE));
onWorker();
}
}
@@ -1,8 +1,13 @@
package com.dark98.santoku.cloud; package ru.ytkab0bp.slicebeam.cloud;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.gson.Gson;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import ru.ytkab0bp.sapil.APICallback; import ru.ytkab0bp.sapil.APICallback;
@@ -13,8 +18,8 @@ import ru.ytkab0bp.sapil.Arg;
import ru.ytkab0bp.sapil.Header; import ru.ytkab0bp.sapil.Header;
import ru.ytkab0bp.sapil.Method; import ru.ytkab0bp.sapil.Method;
import ru.ytkab0bp.sapil.RequestType; import ru.ytkab0bp.sapil.RequestType;
import com.dark98.santoku.BuildConfig; import ru.ytkab0bp.slicebeam.BuildConfig;
import com.dark98.santoku.utils.Prefs; import ru.ytkab0bp.slicebeam.utils.Prefs;
public interface CloudAPI extends APIRunner { public interface CloudAPI extends APIRunner {
CloudAPI INSTANCE = APILibrary.newRunner(CloudAPI.class, new RunnerConfig() { CloudAPI INSTANCE = APILibrary.newRunner(CloudAPI.class, new RunnerConfig() {
@@ -22,12 +27,12 @@ public interface CloudAPI extends APIRunner {
@Override @Override
public String getBaseURL() { public String getBaseURL() {
return BuildConfig.CLOUD_BASE_URL_PROD; return "https://api.beam3d.ru/v1/";
} }
@Override @Override
public String getDefaultUserAgent() { public String getDefaultUserAgent() {
return "Santoku v" + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_CODE; return "SliceBeam v" + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_CODE;
} }
@Override @Override
@@ -38,11 +43,6 @@ public interface CloudAPI extends APIRunner {
} }
return headers; return headers;
} }
@Override
public ru.ytkab0bp.sapil.util.Pair<String, String> getNamingTransformPolicy() {
return null;
}
}); });
/** /**
@@ -72,16 +72,10 @@ public interface CloudAPI extends APIRunner {
void userGetInfo(APICallback<UserInfo> callback); void userGetInfo(APICallback<UserInfo> callback);
/** /**
* Creates a new account (email/password) * Gets user features
*/ */
@Method(requestType = RequestType.POST, value = "signup") @Method("user/getFeatures")
void signup(@Arg("email") String email, @Arg("password") String password, @Arg("displayName") String displayName, APICallback<AuthToken> callback); void userGetFeatures(APICallback<UserFeatures> 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 * Fetches sync state
@@ -109,6 +103,24 @@ public interface CloudAPI extends APIRunner {
@Method("sync/get") @Method("sync/get")
void syncGet(APICallback<String> callback); 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 * Destroys token
* <p> * <p>
@@ -146,13 +158,66 @@ public interface CloudAPI extends APIRunner {
public String bearer; public String bearer;
} }
final class AuthToken { final class UserFeatures {
/** /**
* Bearer token * Which level is required for early access
*/ */
public String bearer; 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<>();
} }
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 { final class UserInfo {
/** /**
* User's id * User's id
@@ -170,6 +235,10 @@ public interface CloudAPI extends APIRunner {
@Nullable @Nullable
public String avatarUrl; public String avatarUrl;
/**
* Current subscription level
*/
public int currentLevel;
} }
@@ -190,4 +259,15 @@ public interface CloudAPI extends APIRunner {
public long maxSize; public long maxSize;
} }
final class ModelsRemainingCount {
/**
* Used generations
*/
public int used;
/**
* Max available generations
*/
public int max;
}
} }
@@ -1,4 +1,4 @@
package com.dark98.santoku.cloud; package ru.ytkab0bp.slicebeam.cloud;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
@@ -18,47 +18,48 @@ import java.util.Locale;
import ru.ytkab0bp.sapil.APICallback; import ru.ytkab0bp.sapil.APICallback;
import ru.ytkab0bp.sapil.APIRequestHandle; import ru.ytkab0bp.sapil.APIRequestHandle;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.components.BeamAlertDialogBuilder; import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import com.dark98.santoku.events.CloudLoginStateUpdatedEvent; import ru.ytkab0bp.slicebeam.events.CloudFeaturesUpdatedEvent;
import com.dark98.santoku.events.CloudSyncFinishedEvent; import ru.ytkab0bp.slicebeam.events.CloudLoginStateUpdatedEvent;
import com.dark98.santoku.events.CloudUserInfoUpdatedEvent; import ru.ytkab0bp.slicebeam.events.CloudModelsRemainingCountUpdatedEvent;
import com.dark98.santoku.events.NeedDismissSnackbarEvent; import ru.ytkab0bp.slicebeam.events.CloudSyncFinishedEvent;
import com.dark98.santoku.events.NeedSnackbarEvent; import ru.ytkab0bp.slicebeam.events.CloudUserInfoUpdatedEvent;
import com.dark98.santoku.slic3r.Slic3rConfigWrapper; import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import com.dark98.santoku.utils.IOUtils; import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import com.dark98.santoku.utils.Prefs; import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.IOUtils;
import com.dark98.santoku.view.SnackbarsLayout; import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
public class CloudController { 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"; public final static String CLOUD_SYNC_TAG = "cloud_sync";
private final static String TAG = "cloud"; 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_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 boolean isSyncInProgress;
private static CloudAPI.UserInfo userInfo; private static CloudAPI.UserInfo userInfo;
private static CloudAPI.UserFeatures userFeatures;
private static int modelsUsed;
private static int modelsMaxGenerations;
private static boolean isLoggingIn; private static boolean isLoggingIn;
private static APIRequestHandle beginLoginHandle; private static APIRequestHandle beginLoginHandle;
private static String loginSessionId; private static String loginSessionId;
private static Runnable loginAutoCancel = () -> { private static Runnable loginAutoCancel = () -> {
loginSessionId = null; loginSessionId = null;
isLoggingIn = false; isLoggingIn = false;
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent()); SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
}; };
private static Runnable loginCheck = new Runnable() { private static Runnable loginCheck = new Runnable() {
@Override @Override
public void run() { public void run() {
CloudAPI api = getApiSafe(); CloudAPI.INSTANCE.loginCheck(loginSessionId, new APICallback<CloudAPI.LoginState>() {
if (api == null) {
loginSessionId = null;
isLoggingIn = false;
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
return;
}
api.loginCheck(loginSessionId, new APICallback<CloudAPI.LoginState>() {
@Override @Override
public void onResponse(CloudAPI.LoginState response) { public void onResponse(CloudAPI.LoginState response) {
if (response.loggedIn) { if (response.loggedIn) {
@@ -84,31 +85,32 @@ public class CloudController {
private static Gson gson = new Gson(); private static Gson gson = new Gson();
public static CloudAPI getApiSafe() {
try {
return CloudAPI.INSTANCE;
} catch (Throwable t) {
Log.e(TAG, "Cloud API unavailable", t);
return null;
}
}
public static void initCached() { public static void initCached() {
if (Prefs.getCloudCachedUserFeatures() != null) {
userFeatures = gson.fromJson(Prefs.getCloudCachedUserFeatures(), CloudAPI.UserFeatures.class);
}
if (Prefs.getCloudAPIToken() != null) { if (Prefs.getCloudAPIToken() != null) {
if (Prefs.getCloudCachedUserInfo() != null) { if (Prefs.getCloudCachedUserInfo() != null) {
userInfo = gson.fromJson(Prefs.getCloudCachedUserInfo(), CloudAPI.UserInfo.class); userInfo = gson.fromJson(Prefs.getCloudCachedUserInfo(), CloudAPI.UserInfo.class);
modelsUsed = Prefs.getCloudCachedUsedModels();
modelsMaxGenerations = Prefs.getCloudCachedMaxModels();
} }
} }
} }
public static void init() { 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 (Prefs.getCloudAPIToken() != null) {
long now = Santoku.TRUE_TIME.now().getTime(); if (needSyncInfo || userInfo == null) {
if (userInfo == null) {
loadUserInfo(); loadUserInfo();
} }
if (userInfo != null && isSyncAvailable() && Prefs.isCloudProfileSyncEnabled()) { if (!needSyncInfo && userInfo != null && isSyncAvailable() && Prefs.isCloudProfileSyncEnabled()) {
if (now - Prefs.getCloudLastSync() > MIN_SYNC_DELTA) { if (now - Prefs.getCloudLastSync() > MIN_SYNC_DELTA) {
syncData(); syncData();
} }
@@ -117,11 +119,7 @@ public class CloudController {
} }
private static void loadUserInfo() { private static void loadUserInfo() {
CloudAPI api = getApiSafe(); CloudAPI.INSTANCE.userGetInfo(new APICallback<CloudAPI.UserInfo>() {
if (api == null) {
return;
}
api.userGetInfo(new APICallback<CloudAPI.UserInfo>() {
@Override @Override
public void onResponse(CloudAPI.UserInfo response) { public void onResponse(CloudAPI.UserInfo response) {
userInfo = response; userInfo = response;
@@ -130,26 +128,29 @@ public class CloudController {
userInfo = null; userInfo = null;
Prefs.setCloudAPIToken(null); Prefs.setCloudAPIToken(null);
Prefs.setCloudCachedUserInfo(null); Prefs.setCloudCachedUserInfo(null);
Santoku.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent()); SliceBeam.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent());
if (isLoggingIn) { if (isLoggingIn) {
isLoggingIn = false; isLoggingIn = false;
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent()); SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
} }
} else { } else {
Prefs.setCloudCachedUserInfo(gson.toJson(userInfo)); Prefs.setCloudCachedUserInfo(gson.toJson(userInfo));
Santoku.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent()); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(USER_INFO_AI_GEN_TAG));
SliceBeam.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent());
if (isLoggingIn) { if (isLoggingIn) {
isLoggingIn = false; isLoggingIn = false;
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent()); SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
} }
if (isSyncAvailable() && Prefs.isCloudProfileSyncEnabled()) { if (isSyncAvailable() && Prefs.isCloudProfileSyncEnabled()) {
syncData(); syncData();
} }
checkGeneratorRemaining();
} }
Prefs.setCloudLastFeaturesSync(SliceBeam.TRUE_TIME.now().getTime());
} }
@Override @Override
@@ -165,20 +166,14 @@ public class CloudController {
} }
private static void beginLogin0() { private static void beginLogin0() {
CloudAPI api = getApiSafe(); beginLoginHandle = CloudAPI.INSTANCE.loginBegin(new APICallback<CloudAPI.LoginData>() {
if (api == null) {
isLoggingIn = false;
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
return;
}
beginLoginHandle = api.loginBegin(new APICallback<CloudAPI.LoginData>() {
@Override @Override
public void onResponse(CloudAPI.LoginData response) { public void onResponse(CloudAPI.LoginData response) {
loginSessionId = response.sessionId; loginSessionId = response.sessionId;
ViewUtils.postOnMainThread(loginAutoCancel, response.expiresAt * 1000L - Santoku.TRUE_TIME.now().getTime()); ViewUtils.postOnMainThread(loginAutoCancel, response.expiresAt * 1000L - SliceBeam.TRUE_TIME.now().getTime());
ViewUtils.postOnMainThread(loginCheck, 5000); ViewUtils.postOnMainThread(loginCheck, 5000);
ViewUtils.postOnMainThread(() -> Santoku.INSTANCE.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(response.url)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))); ViewUtils.postOnMainThread(() -> SliceBeam.INSTANCE.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(response.url)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)));
} }
@Override @Override
@@ -190,18 +185,15 @@ public class CloudController {
public static void beginLogin() { public static void beginLogin() {
isLoggingIn = true; isLoggingIn = true;
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent()); SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
beginLogin0(); beginLogin0();
} }
public static void cancelLogin() { public static void cancelLogin() {
isLoggingIn = false; isLoggingIn = false;
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent()); SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
if (loginSessionId != null) { if (loginSessionId != null) {
CloudAPI api = getApiSafe(); CloudAPI.INSTANCE.loginCancel(loginSessionId, response -> {});
if (api != null) {
api.loginCancel(loginSessionId, response -> {});
}
} }
if (beginLoginHandle != null && beginLoginHandle.isRunning()) { if (beginLoginHandle != null && beginLoginHandle.isRunning()) {
beginLoginHandle.cancel(); beginLoginHandle.cancel();
@@ -213,61 +205,107 @@ public class CloudController {
} }
public static void logout() { public static void logout() {
CloudAPI api = getApiSafe();
if (api != null) {
api.logout(response -> {});
}
Prefs.setCloudAPIToken(null); Prefs.setCloudAPIToken(null);
userInfo = null; userInfo = null;
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent()); SliceBeam.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
Santoku.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent()); 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);
}
});
} }
public static CloudAPI.UserInfo getUserInfo() { public static CloudAPI.UserInfo getUserInfo() {
return userInfo; return userInfo;
} }
public static CloudAPI.UserFeatures getUserFeatures() {
return userFeatures;
}
public static boolean hasAccountFeatures() { public static boolean hasAccountFeatures() {
return true; return userFeatures != null && userFeatures.levels != null && !userFeatures.levels.isEmpty();
} }
public static boolean isSyncAvailable() { public static boolean isSyncAvailable() {
return Prefs.getCloudAPIToken() != null && userInfo != null; 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;
} }
private static void downloadData(long lastModified) { private static void downloadData(long lastModified) {
CloudAPI api = getApiSafe(); CloudAPI.INSTANCE.syncGet(new APICallback<String>() {
if (api == null) {
isSyncInProgress = false;
return;
}
api.syncGet(new APICallback<String>() {
@Override @Override
public void onResponse(String response) { public void onResponse(String response) {
IOUtils.IO_POOL.submit(() -> { IOUtils.IO_POOL.submit(() -> {
try { try {
File f = Santoku.getConfigFile(); File f = SliceBeam.getConfigFile();
byte[] data = Base64.decode(response, 0); byte[] data = Base64.decode(response, 0);
FileOutputStream fos = new FileOutputStream(f); FileOutputStream fos = new FileOutputStream(f);
fos.write(data); fos.write(data);
fos.close(); fos.close();
Santoku.CONFIG = new Slic3rConfigWrapper(f); SliceBeam.CONFIG = new Slic3rConfigWrapper(f);
Prefs.setCloudLocalLastModified(lastModified); Prefs.setCloudLocalLastModified(lastModified);
Prefs.setCloudLocalLastSentModified(lastModified); Prefs.setCloudLocalLastSentModified(lastModified);
Prefs.setCloudRemoteLastModified(lastModified); Prefs.setCloudRemoteLastModified(lastModified);
Prefs.setCloudLastSync(Santoku.TRUE_TIME.now().getTime()); Prefs.setCloudLastSync(SliceBeam.TRUE_TIME.now().getTime());
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG)); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.CloudSyncSuccess)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.CloudSyncSuccess));
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent()); SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "Failed to write data", e); Log.e(TAG, "Failed to write data", e);
isSyncInProgress = false; isSyncInProgress = false;
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG)); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent()); SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
} }
}); });
} }
@@ -277,9 +315,9 @@ public class CloudController {
Log.e(TAG, "Failed to download data", e); Log.e(TAG, "Failed to download data", e);
isSyncInProgress = false; isSyncInProgress = false;
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG)); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent()); SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
} }
}); });
} }
@@ -288,24 +326,20 @@ public class CloudController {
if (isSyncInProgress) { if (isSyncInProgress) {
return; return;
} }
CloudAPI api = getApiSafe();
if (api == null) {
return;
}
long modified = Prefs.getCloudLocalLastModified(); long modified = Prefs.getCloudLocalLastModified();
isSyncInProgress = true; isSyncInProgress = true;
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.CloudSyncInProgress).tag(CLOUD_SYNC_TAG)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.CloudSyncInProgress).tag(CLOUD_SYNC_TAG));
api.syncGetState(new APICallback<CloudAPI.SyncState>() { CloudAPI.INSTANCE.syncGetState(new APICallback<CloudAPI.SyncState>() {
@Override @Override
public void onResponse(CloudAPI.SyncState response) { public void onResponse(CloudAPI.SyncState response) {
if (Santoku.CONFIG == null && response.usedSize != 0) { if (SliceBeam.CONFIG == null && response.usedSize != 0) {
// Setup screen, no config yet // Setup screen, no config yet
downloadData(response.lastUpdatedDate); downloadData(response.lastUpdatedDate);
} else if (response.usedSize == 0) { } else if (response.usedSize == 0) {
if (Santoku.CONFIG == null) { if (SliceBeam.CONFIG == null) {
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG)); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent()); SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
return; return;
} }
@@ -317,8 +351,8 @@ public class CloudController {
downloadData(response.lastUpdatedDate); downloadData(response.lastUpdatedDate);
} else { } else {
// Modified on client and on server // Modified on client and on server
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG)); SliceBeam.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 -> { SliceBeam.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()); SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy HH:mm", Locale.getDefault());
new BeamAlertDialogBuilder(v.getContext()) new BeamAlertDialogBuilder(v.getContext())
.setTitle(R.string.CloudSyncConflict) .setTitle(R.string.CloudSyncConflict)
@@ -334,8 +368,8 @@ public class CloudController {
uploadData(modified); uploadData(modified);
} else { } else {
// Not modified on server and on client // Not modified on server and on client
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG)); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent()); SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
} }
} }
} }
@@ -345,8 +379,8 @@ public class CloudController {
Log.e(TAG, "Failed to get sync state", e); Log.e(TAG, "Failed to get sync state", e);
isSyncInProgress = false; isSyncInProgress = false;
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG)); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
} }
}); });
} }
@@ -354,7 +388,7 @@ public class CloudController {
private static void uploadData(long modified) { private static void uploadData(long modified) {
IOUtils.IO_POOL.submit(() -> { IOUtils.IO_POOL.submit(() -> {
try { try {
File f = Santoku.getConfigFile(); File f = SliceBeam.getConfigFile();
FileInputStream fis = new FileInputStream(f); FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[10240]; byte[] buffer = new byte[10240];
@@ -365,12 +399,7 @@ public class CloudController {
bos.close(); bos.close();
fis.close(); fis.close();
CloudAPI api = getApiSafe(); CloudAPI.INSTANCE.syncUpload(Base64.encodeToString(bos.toByteArray(), Base64.NO_WRAP), "application/ini", new APICallback<CloudAPI.SyncState>() {
if (api == null) {
isSyncInProgress = false;
return;
}
api.syncUpload(Base64.encodeToString(bos.toByteArray(), Base64.NO_WRAP), "application/ini", new APICallback<CloudAPI.SyncState>() {
@Override @Override
public void onResponse(CloudAPI.SyncState response) { public void onResponse(CloudAPI.SyncState response) {
isSyncInProgress = false; isSyncInProgress = false;
@@ -380,10 +409,10 @@ public class CloudController {
} }
Prefs.setCloudRemoteLastModified(response.lastUpdatedDate); Prefs.setCloudRemoteLastModified(response.lastUpdatedDate);
Prefs.setCloudLocalLastSentModified(modified); Prefs.setCloudLocalLastSentModified(modified);
Prefs.setCloudLastSync(Santoku.TRUE_TIME.now().getTime()); Prefs.setCloudLastSync(SliceBeam.TRUE_TIME.now().getTime());
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG)); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.CloudSyncSuccess)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.CloudSyncSuccess));
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent()); SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
} }
@Override @Override
@@ -391,24 +420,24 @@ public class CloudController {
Log.e(TAG, "Failed to upload sync data", e); Log.e(TAG, "Failed to upload sync data", e);
isSyncInProgress = false; isSyncInProgress = false;
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG)); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent()); SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
} }
}); });
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "Failed to read sync data", e); Log.e(TAG, "Failed to read sync data", e);
isSyncInProgress = false; isSyncInProgress = false;
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG)); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CLOUD_SYNC_TAG));
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.CloudSyncError));
Santoku.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent()); SliceBeam.EVENT_BUS.fireEvent(new CloudSyncFinishedEvent());
} }
}); });
} }
public static void notifyDataChanged() { public static void notifyDataChanged() {
long now = Santoku.TRUE_TIME.now().getTime(); long now = SliceBeam.TRUE_TIME.now().getTime();
Prefs.setCloudLocalLastModified(now); Prefs.setCloudLocalLastModified(now);
if (!isSyncAvailable() || !Prefs.isCloudProfileSyncEnabled()) { if (!isSyncAvailable() || !Prefs.isCloudProfileSyncEnabled()) {
return; return;
@@ -1,4 +1,4 @@
package com.dark98.santoku.components; package ru.ytkab0bp.slicebeam.components;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
@@ -21,9 +21,9 @@ import androidx.appcompat.widget.AppCompatCheckedTextView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
public class BeamAlertDialogBuilder extends MaterialAlertDialogBuilder { public class BeamAlertDialogBuilder extends MaterialAlertDialogBuilder {
public BeamAlertDialogBuilder(@NonNull Context context) { public BeamAlertDialogBuilder(@NonNull Context context) {
@@ -1,4 +1,4 @@
package com.dark98.santoku.components; package ru.ytkab0bp.slicebeam.components;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
@@ -8,9 +8,9 @@ import android.widget.TextView;
import com.mrudultora.colorpicker.ColorPickerPopUp; import com.mrudultora.colorpicker.ColorPickerPopUp;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
public class BeamColorPickerPopUp extends ColorPickerPopUp { public class BeamColorPickerPopUp extends ColorPickerPopUp {
public BeamColorPickerPopUp(Context context) { public BeamColorPickerPopUp(Context context) {
@@ -0,0 +1,255 @@
package ru.ytkab0bp.slicebeam.components;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.GradientDrawable;
import android.net.Uri;
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.Scroller;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.graphics.ColorUtils;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
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.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import ru.ytkab0bp.eventbus.EventHandler;
import ru.ytkab0bp.slicebeam.BeamServerData;
import ru.ytkab0bp.slicebeam.R;
import ru.ytkab0bp.slicebeam.SliceBeam;
import ru.ytkab0bp.slicebeam.events.BeamServerDataUpdatedEvent;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.view.BeamButton;
import ru.ytkab0bp.slicebeam.view.BoostySubsView;
public class ChangeLogBottomSheet extends BottomSheetDialog {
private BoostySubsView subsView;
private ScrollView scrollView;
private ViewPager pager;
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);
TextView titleB = new TextView(context);
titleB.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20);
titleB.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
titleB.setText(R.string.ChangelogBoosty);
titleB.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
titleB.setGravity(Gravity.CENTER);
titleB.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
leftMargin = rightMargin = ViewUtils.dp(21);
}});
titleB.setAlpha(0f);
fl.addView(titleB);
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();
pager = new ViewPager(context) {{
try {
Field scroller = ViewPager.class.getDeclaredField("mScroller");
scroller.setAccessible(true);
Scroller mScroller = new Scroller(getContext(), ViewUtils.CUBIC_INTERPOLATOR::getInterpolation);
scroller.set(this, mScroller);
} catch (Exception ignored) {}
}};
pager.setAdapter(new PagerAdapter() {
@Override
public int getCount() {
return BeamServerData.isBoostyAvailable() ? 2 : 1;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
View v;
if (position == 0) {
v = scrollView;
} else {
LinearLayout ll = new LinearLayout(context);
ll.setOrientation(LinearLayout.VERTICAL);
TextView subtitle = new TextView(context);
subtitle.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
subtitle.setText(R.string.ChangelogBoostyDescription);
subtitle.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
subtitle.setGravity(Gravity.CENTER);
subtitle.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
subtitle.setPadding(ViewUtils.dp(12), 0, ViewUtils.dp(12), 0);
ll.addView(subtitle);
subsView = new BoostySubsView(context);
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));
TextView subscribeButton = new TextView(context);
subscribeButton.setText(R.string.IntroBoostySupport);
subscribeButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
subscribeButton.setTextColor(ThemesRepo.getColor(R.attr.boostyColorTop));
subscribeButton.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
subscribeButton.setGravity(Gravity.CENTER);
subscribeButton.setPadding(ViewUtils.dp(12), ViewUtils.dp(8), ViewUtils.dp(12), ViewUtils.dp(8));
subscribeButton.setOnClickListener(v2 -> context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://boosty.to/ytkab0bp"))));
ll.addView(subscribeButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
v = ll;
}
container.addView(v);
return v;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
});
BeamButton btn = new BeamButton(context);
pager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
if (position == pager.getAdapter().getCount() - 1) {
btn.setText(R.string.ChangelogOK);
} else {
btn.setText(R.string.ChangelogNext);
}
}
private int[] colors = new int[2];
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
float pr = position == 0 ? positionOffset : 1f;
colors[0] = ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.dialogBackground), ThemesRepo.getColor(R.attr.boostyColorTop), pr);
colors[1] = ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.dialogBackground), ThemesRepo.getColor(R.attr.boostyColorBottom), pr);
gd.setColors(colors);
titleA.setAlpha(1f - pr);
titleA.setTranslationX(-titleA.getWidth() * 0.25f * pr);
titleB.setAlpha(pr);
titleB.setTranslationX(titleB.getWidth() * 0.25f * (1f - pr));
btn.setColor(ColorUtils.blendARGB(ThemesRepo.getColor(android.R.attr.colorAccent), ThemesRepo.getColor(R.attr.boostyColorTop), pr));
}
});
ll.addView(pager, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) (dm.heightPixels * 0.45f)));
btn.setText(R.string.ChangelogNext);
btn.setOnClickListener(v -> {
if (pager.getCurrentItem() != pager.getAdapter().getCount() - 1) {
pager.setCurrentItem(pager.getCurrentItem() + 1);
} else {
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);
SliceBeam.EVENT_BUS.registerListener(this);
setOnDismissListener(dialog -> SliceBeam.EVENT_BUS.unregisterListener(this));
}
@EventHandler(runOnMainThread = true)
public void onDataUpdated(BeamServerDataUpdatedEvent e) {
if (SliceBeam.SERVER_DATA != null) {
List<String> list = new ArrayList<>(SliceBeam.SERVER_DATA.boostySubscribers);
Collections.shuffle(list);
subsView.setStrings(list);
}
pager.getAdapter().notifyDataSetChanged();
}
@Override
public void show() {
super.show();
getBehavior().setState(BottomSheetBehavior.STATE_EXPANDED);
}
}
@@ -0,0 +1,158 @@
package ru.ytkab0bp.slicebeam.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.Space;
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 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.events.NeedDismissSnackbarEvent;
import ru.ytkab0bp.slicebeam.recycler.PreferenceSwitchItem;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerAdapter;
import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.Prefs;
import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import ru.ytkab0bp.slicebeam.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);
TextView description = new TextView(context);
description.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
description.setText(context.getString(R.string.SettingsCloudManageLoggedInAs, CloudController.getUserInfo().displayName));
description.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
description.setGravity(Gravity.CENTER);
description.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
leftMargin = rightMargin = ViewUtils.dp(21);
topMargin = ViewUtils.dp(8);
}});
ll.addView(description);
int currentLevel = CloudController.getUserInfo().currentLevel;
CloudAPI.SubscriptionLevel lvl = null;
CloudAPI.UserFeatures features = CloudController.getUserFeatures();
for (CloudAPI.SubscriptionLevel level : features.levels) {
if (level.level != -1 && level.level <= currentLevel && (lvl == null || level.level > lvl.level)) {
lvl = level;
}
}
if (lvl != null) {
List<SimpleRecyclerItem> items = new ArrayList<>();
if (currentLevel >= features.syncRequiredLevel) {
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 {
SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CloudController.CLOUD_SYNC_TAG));
}
}));
}
if (!items.isEmpty()) {
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));
CloudAPI.SubscriptionLevel finalLvl = lvl;
manageButton.setOnClickListener(v -> v.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(finalLvl.manageUrl))));
ll.addView(manageButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(48)) {{
leftMargin = rightMargin = ViewUtils.dp(16);
topMargin = bottomMargin = ViewUtils.dp(6);
}});
} else {
ll.addView(new Space(context), new LinearLayout.LayoutParams(0, ViewUtils.dp(16)));
}
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 com.dark98.santoku.components; package ru.ytkab0bp.slicebeam.components;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
@@ -21,12 +21,12 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import ru.ytkab0bp.eventbus.EventHandler; import ru.ytkab0bp.eventbus.EventHandler;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.events.SlicingProgressEvent; import ru.ytkab0bp.slicebeam.events.SlicingProgressEvent;
import com.dark98.santoku.slic3r.Slic3rLocalization; import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
public class SliceProgressBottomSheet extends BottomSheetDialog { public class SliceProgressBottomSheet extends BottomSheetDialog {
private RecyclerView recyclerView; private RecyclerView recyclerView;
@@ -122,12 +122,12 @@ public class SliceProgressBottomSheet extends BottomSheetDialog {
@Override @Override
public void onAttachedToWindow() { public void onAttachedToWindow() {
super.onAttachedToWindow(); super.onAttachedToWindow();
Santoku.EVENT_BUS.registerListener(this); SliceBeam.EVENT_BUS.registerListener(this);
} }
@Override @Override
public void onDetachedFromWindow() { public void onDetachedFromWindow() {
super.onDetachedFromWindow(); super.onDetachedFromWindow();
Santoku.EVENT_BUS.unregisterListener(this); SliceBeam.EVENT_BUS.unregisterListener(this);
} }
} }
@@ -1,4 +1,4 @@
package com.dark98.santoku.components; package ru.ytkab0bp.slicebeam.components;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
@@ -18,10 +18,10 @@ import androidx.dynamicanimation.animation.FloatValueHolder;
import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce; import androidx.dynamicanimation.animation.SpringForce;
import com.dark98.santoku.fragment.BedFragment; import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.view.MirrorView; import ru.ytkab0bp.slicebeam.view.MirrorView;
public abstract class UnfoldMenu { public abstract class UnfoldMenu {
protected BedFragment fragment; protected BedFragment fragment;
@@ -1,4 +1,4 @@
package com.dark98.santoku.components; package ru.ytkab0bp.slicebeam.components;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
@@ -23,14 +23,14 @@ import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.util.Locale; import java.util.Locale;
import com.dark98.santoku.BuildConfig; import ru.ytkab0bp.slicebeam.BuildConfig;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.events.NeedDismissCalibrationsMenu; import ru.ytkab0bp.slicebeam.events.NeedDismissCalibrationsMenu;
import com.dark98.santoku.fragment.BedFragment; import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.view.DividerView; import ru.ytkab0bp.slicebeam.view.DividerView;
public class WebViewMenu extends UnfoldMenu { public class WebViewMenu extends UnfoldMenu {
private final Uri uri; private final Uri uri;
@@ -93,10 +93,10 @@ public class WebViewMenu extends UnfoldMenu {
return super.dispatchTouchEvent(ev); return super.dispatchTouchEvent(ev);
} }
}; };
webView.addJavascriptInterface(new Bridge(), "Santoku"); webView.addJavascriptInterface(new Bridge(), "SliceBeam");
WebSettings settings = webView.getSettings(); WebSettings settings = webView.getSettings();
settings.setUserAgentString(String.format(Locale.ROOT, "Santoku/%s-%d", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)); settings.setUserAgentString(String.format(Locale.ROOT, "SliceBeam/%s-%d", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
settings.setJavaScriptEnabled(true); settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true); settings.setDomStorageEnabled(true);
settings.setDatabaseEnabled(true); settings.setDatabaseEnabled(true);
@@ -131,7 +131,7 @@ public class WebViewMenu extends UnfoldMenu {
@JavascriptInterface @JavascriptInterface
public void beginDownload(String filename) { public void beginDownload(String filename) {
cacheFile = new File(Santoku.getModelCacheDir(), filename); cacheFile = new File(SliceBeam.getModelCacheDir(), filename);
try { try {
fileStream = new FileOutputStream(cacheFile); fileStream = new FileOutputStream(cacheFile);
} catch (Exception e) { } catch (Exception e) {
@@ -155,7 +155,7 @@ public class WebViewMenu extends UnfoldMenu {
ViewUtils.postOnMainThread(() -> { ViewUtils.postOnMainThread(() -> {
dismiss(true); dismiss(true);
Santoku.EVENT_BUS.fireEvent(new NeedDismissCalibrationsMenu()); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissCalibrationsMenu());
ViewUtils.postOnMainThread(() -> fragment.loadGCode(cacheFile), 200); ViewUtils.postOnMainThread(() -> fragment.loadGCode(cacheFile), 200);
}); });
} catch (Exception e) { } catch (Exception e) {
@@ -1,12 +1,12 @@
package com.dark98.santoku.components.bed_menu; package ru.ytkab0bp.slicebeam.components.bed_menu;
import android.content.Context; import android.content.Context;
import android.view.View; import android.view.View;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
import com.dark98.santoku.fragment.BedFragment; import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import com.dark98.santoku.slic3r.Bed3D; import ru.ytkab0bp.slicebeam.slic3r.Bed3D;
public abstract class BedMenu { public abstract class BedMenu {
private View view; private View view;
@@ -1,4 +1,4 @@
package com.dark98.santoku.components.bed_menu; package ru.ytkab0bp.slicebeam.components.bed_menu;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
@@ -31,13 +31,13 @@ import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.recycler.SimpleRecyclerItem; import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import com.dark98.santoku.theme.IThemeView; import ru.ytkab0bp.slicebeam.theme.IThemeView;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.RandomUtils; import ru.ytkab0bp.slicebeam.utils.RandomUtils;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolderView> { public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolderView> {
public final int titleRes; public final int titleRes;
@@ -169,7 +169,7 @@ public class BedMenuItem extends SimpleRecyclerItem<BedMenuItem.BedMenuItemHolde
canvas.save(); canvas.save();
if (sparkles == null) sparkles = new ArrayList<>(); if (sparkles == null) sparkles = new ArrayList<>();
if (sparkleDrawable == null) { if (sparkleDrawable == null) {
sparkleDrawable = ContextCompat.getDrawable(Santoku.INSTANCE, R.drawable.sparkle_28); sparkleDrawable = ContextCompat.getDrawable(SliceBeam.INSTANCE, R.drawable.sparkle_28);
sparkleDrawable.setColorFilter(new PorterDuffColorFilter(ThemesRepo.getColor(android.R.attr.colorAccent), PorterDuff.Mode.SRC_IN)); sparkleDrawable.setColorFilter(new PorterDuffColorFilter(ThemesRepo.getColor(android.R.attr.colorAccent), PorterDuff.Mode.SRC_IN));
} }
@@ -1,4 +1,4 @@
package com.dark98.santoku.components.bed_menu; package ru.ytkab0bp.slicebeam.components.bed_menu;
import android.content.Context; import android.content.Context;
import android.widget.Toast; import android.widget.Toast;
@@ -10,16 +10,16 @@ import androidx.dynamicanimation.animation.SpringForce;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.components.BeamAlertDialogBuilder; import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import com.dark98.santoku.recycler.SimpleRecyclerItem; import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import com.dark98.santoku.recycler.SpaceItem; import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
import com.dark98.santoku.render.Camera; import ru.ytkab0bp.slicebeam.render.Camera;
import com.dark98.santoku.slic3r.Bed3D; import ru.ytkab0bp.slicebeam.slic3r.Bed3D;
import com.dark98.santoku.utils.Prefs; import ru.ytkab0bp.slicebeam.utils.Prefs;
import com.dark98.santoku.utils.Vec3d; import ru.ytkab0bp.slicebeam.utils.Vec3d;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.view.GLView; import ru.ytkab0bp.slicebeam.view.GLView;
public class CameraMenu extends ListBedMenu { public class CameraMenu extends ListBedMenu {
private boolean checkInvalidBed() { private boolean checkInvalidBed() {
@@ -1,9 +1,10 @@
package com.dark98.santoku.components.bed_menu; package ru.ytkab0bp.slicebeam.components.bed_menu;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.provider.MediaStore;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.Gravity; import android.view.Gravity;
import android.view.View; import android.view.View;
@@ -14,6 +15,7 @@ import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.core.content.FileProvider;
import androidx.core.graphics.ColorUtils; import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@@ -31,31 +33,40 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import ru.ytkab0bp.eventbus.EventHandler; import ru.ytkab0bp.eventbus.EventHandler;
import com.dark98.santoku.MainActivity; import ru.ytkab0bp.slicebeam.BeamServerData;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.BuildConfig;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.MainActivity;
import com.dark98.santoku.components.BeamAlertDialogBuilder; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.components.UnfoldMenu; import ru.ytkab0bp.slicebeam.SetupActivity;
import com.dark98.santoku.components.WebViewMenu; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.config.ConfigObject; import ru.ytkab0bp.slicebeam.cloud.CloudController;
import com.dark98.santoku.events.NeedDismissCalibrationsMenu; import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import com.dark98.santoku.events.NeedDismissSnackbarEvent; import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
import com.dark98.santoku.events.NeedSnackbarEvent; import ru.ytkab0bp.slicebeam.components.WebViewMenu;
import com.dark98.santoku.events.ObjectsListChangedEvent; import ru.ytkab0bp.slicebeam.config.ConfigObject;
import com.dark98.santoku.events.SelectedObjectChangedEvent; import ru.ytkab0bp.slicebeam.events.CloudFeaturesUpdatedEvent;
import com.dark98.santoku.fragment.BedFragment; import ru.ytkab0bp.slicebeam.events.CloudModelsRemainingCountUpdatedEvent;
import com.dark98.santoku.recycler.PreferenceItem; import ru.ytkab0bp.slicebeam.events.NeedDismissAIGeneratorMenu;
import com.dark98.santoku.recycler.SimpleRecyclerAdapter; import ru.ytkab0bp.slicebeam.events.NeedDismissCalibrationsMenu;
import com.dark98.santoku.recycler.SimpleRecyclerItem; import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import com.dark98.santoku.recycler.SpaceItem; import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import com.dark98.santoku.slic3r.Bed3D; import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
import com.dark98.santoku.slic3r.Slic3rRuntimeError; import ru.ytkab0bp.slicebeam.events.SelectedObjectChangedEvent;
import com.dark98.santoku.theme.BeamTheme; import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.recycler.PreferenceItem;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerAdapter;
import com.dark98.santoku.view.DividerView; import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import com.dark98.santoku.view.FadeRecyclerView; import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
import com.dark98.santoku.view.SnackbarsLayout; 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;
public class FileMenu extends ListBedMenu { public class FileMenu extends ListBedMenu {
private final static List<String> K3D_SUPPORTED_LANGUAGES = Arrays.asList("en", "ru"); private final static List<String> K3D_SUPPORTED_LANGUAGES = Arrays.asList("en", "ru");
@@ -115,6 +126,29 @@ public class FileMenu extends ListBedMenu {
} }
}), }),
new SpaceItem(portrait ? ViewUtils.dp(3) : 0, portrait ? 0 : ViewUtils.dp(3)))); 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( list.addAll(Arrays.asList(
new BedMenuItem(R.string.MenuFileCalibrations, R.drawable.wrench_outline_28).setSingleLine(true).onClick(v -> { new BedMenuItem(R.string.MenuFileCalibrations, R.drawable.wrench_outline_28).setSingleLine(true).onClick(v -> {
if (!fragment.getGlView().getRenderer().getBed().isValid()) { if (!fragment.getGlView().getRenderer().getBed().isValid()) {
@@ -135,24 +169,24 @@ public class FileMenu extends ListBedMenu {
} }
}), }),
new BedMenuItem(R.string.MenuFileExportProfiles, R.drawable.folder_simple_arrow_right_outline_28).onClick(v -> { new BedMenuItem(R.string.MenuFileExportProfiles, R.drawable.folder_simple_arrow_right_outline_28).onClick(v -> {
CharSequence[] prints = new CharSequence[Santoku.CONFIG.printConfigs.size()]; CharSequence[] prints = new CharSequence[SliceBeam.CONFIG.printConfigs.size()];
boolean[] enabledPrints = new boolean[prints.length]; boolean[] enabledPrints = new boolean[prints.length];
for (int i = 0; i < prints.length; i++) { for (int i = 0; i < prints.length; i++) {
prints[i] = Santoku.CONFIG.printConfigs.get(i).getTitle(); prints[i] = SliceBeam.CONFIG.printConfigs.get(i).getTitle();
enabledPrints[i] = true; enabledPrints[i] = true;
} }
CharSequence[] filaments = new CharSequence[Santoku.CONFIG.filamentConfigs.size()]; CharSequence[] filaments = new CharSequence[SliceBeam.CONFIG.filamentConfigs.size()];
boolean[] enabledFilaments = new boolean[filaments.length]; boolean[] enabledFilaments = new boolean[filaments.length];
for (int i = 0; i < filaments.length; i++) { for (int i = 0; i < filaments.length; i++) {
filaments[i] = Santoku.CONFIG.filamentConfigs.get(i).getTitle(); filaments[i] = SliceBeam.CONFIG.filamentConfigs.get(i).getTitle();
enabledFilaments[i] = true; enabledFilaments[i] = true;
} }
CharSequence[] printers = new CharSequence[Santoku.CONFIG.printerConfigs.size()]; CharSequence[] printers = new CharSequence[SliceBeam.CONFIG.printerConfigs.size()];
boolean[] enabledPrinters = new boolean[printers.length]; boolean[] enabledPrinters = new boolean[printers.length];
for (int i = 0; i < printers.length; i++) { for (int i = 0; i < printers.length; i++) {
printers[i] = Santoku.CONFIG.printerConfigs.get(i).getTitle(); printers[i] = SliceBeam.CONFIG.printerConfigs.get(i).getTitle();
enabledPrinters[i] = true; enabledPrinters[i] = true;
} }
@@ -171,21 +205,21 @@ public class FileMenu extends ListBedMenu {
for (int i = 0; i < enabledPrints.length; i++) { for (int i = 0; i < enabledPrints.length; i++) {
if (enabledPrints[i]) { if (enabledPrints[i]) {
hasEnabled = true; hasEnabled = true;
MainActivity.EXPORTING_PRINTS.add(Santoku.CONFIG.printConfigs.get(i)); MainActivity.EXPORTING_PRINTS.add(SliceBeam.CONFIG.printConfigs.get(i));
} }
} }
MainActivity.EXPORTING_FILAMENTS = new ArrayList<>(); MainActivity.EXPORTING_FILAMENTS = new ArrayList<>();
for (int i = 0; i < enabledFilaments.length; i++) { for (int i = 0; i < enabledFilaments.length; i++) {
if (enabledFilaments[i]) { if (enabledFilaments[i]) {
hasEnabled = true; hasEnabled = true;
MainActivity.EXPORTING_FILAMENTS.add(Santoku.CONFIG.filamentConfigs.get(i)); MainActivity.EXPORTING_FILAMENTS.add(SliceBeam.CONFIG.filamentConfigs.get(i));
} }
} }
MainActivity.EXPORTING_PRINTERS = new ArrayList<>(); MainActivity.EXPORTING_PRINTERS = new ArrayList<>();
for (int i = 0; i < enabledPrinters.length; i++) { for (int i = 0; i < enabledPrinters.length; i++) {
if (enabledPrinters[i]) { if (enabledPrinters[i]) {
hasEnabled = true; hasEnabled = true;
MainActivity.EXPORTING_PRINTERS.add(Santoku.CONFIG.printerConfigs.get(i)); MainActivity.EXPORTING_PRINTERS.add(SliceBeam.CONFIG.printerConfigs.get(i));
} }
} }
if (!hasEnabled) { if (!hasEnabled) {
@@ -201,7 +235,7 @@ public class FileMenu extends ListBedMenu {
Activity act = (Activity) fragment.getContext(); Activity act = (Activity) fragment.getContext();
Intent i = new Intent(Intent.ACTION_CREATE_DOCUMENT); Intent i = new Intent(Intent.ACTION_CREATE_DOCUMENT);
i.setType("application/ini"); i.setType("application/ini");
i.putExtra(Intent.EXTRA_TITLE, "Santoku_config_bundle.ini"); i.putExtra(Intent.EXTRA_TITLE, "SliceBeam_config_bundle.ini");
act.startActivityForResult(i, MainActivity.REQUEST_CODE_EXPORT_PROFILES); act.startActivityForResult(i, MainActivity.REQUEST_CODE_EXPORT_PROFILES);
} }
}) })
@@ -217,7 +251,7 @@ public class FileMenu extends ListBedMenu {
Activity act = (Activity) fragment.getContext(); Activity act = (Activity) fragment.getContext();
Intent i = new Intent(Intent.ACTION_CREATE_DOCUMENT); Intent i = new Intent(Intent.ACTION_CREATE_DOCUMENT);
i.setType("application/3mf"); i.setType("application/3mf");
i.putExtra(Intent.EXTRA_TITLE, "Santoku_project.3mf"); i.putExtra(Intent.EXTRA_TITLE, "SliceBeam_project.3mf");
act.startActivityForResult(i, MainActivity.REQUEST_CODE_EXPORT_3MF); act.startActivityForResult(i, MainActivity.REQUEST_CODE_EXPORT_3MF);
} }
}) })
@@ -229,7 +263,10 @@ public class FileMenu extends ListBedMenu {
public void onObjectsChanged(ObjectsListChangedEvent e) { public void onObjectsChanged(ObjectsListChangedEvent e) {
((BedMenuItem) adapter.getItems().get(1)).setEnabled(hasSelection()); ((BedMenuItem) adapter.getItems().get(1)).setEnabled(hasSelection());
adapter.notifyItemChanged(1); adapter.notifyItemChanged(1);
updateModelItems();
int i = 8 - (BeamServerData.isBoostyAvailable() && CloudController.needShowAIGenerator() ? 0 : 1);
((BedMenuItem) adapter.getItems().get(i)).setEnabled(hasModel());
adapter.notifyItemChanged(i);
} }
@EventHandler(runOnMainThread = true) @EventHandler(runOnMainThread = true)
@@ -238,17 +275,147 @@ public class FileMenu extends ListBedMenu {
adapter.notifyItemChanged(1); adapter.notifyItemChanged(1);
} }
private void updateModelItems() { @EventHandler(runOnMainThread = true)
int idx = -1; public void onFeaturedUpdated(CloudFeaturesUpdatedEvent e) {
for (int j = adapter.getItems().size() - 1; j >= 0; j--) { adapter.setItems(onCreateItems(wasPortrait));
if (adapter.getItems().get(j) instanceof BedMenuItem) { }
idx = j;
break; 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);
}
@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);
} }
} }
if (idx != -1) { }),
((BedMenuItem) adapter.getItems().get(idx)).setEnabled(hasModel()); new PreferenceItem().setIcon(R.drawable.picture_outline_28).setTitle(ctx.getString(R.string.MenuFileAIGeneratorFromGallery)).setOnClickListener(v -> {
adapter.notifyItemChanged(idx); 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});
} }
} }
@@ -260,7 +427,7 @@ public class FileMenu extends ListBedMenu {
private String loadJSLoader(String key) { private String loadJSLoader(String key) {
try { try {
InputStream in = Santoku.INSTANCE.getAssets().open("js_loader/" + key + ".js"); InputStream in = SliceBeam.INSTANCE.getAssets().open("js_loader/" + key + ".js");
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[10240]; int c; byte[] buffer = new byte[10240]; int c;
while ((c = in.read(buffer)) != -1) { while ((c = in.read(buffer)) != -1) {
@@ -269,7 +436,7 @@ public class FileMenu extends ListBedMenu {
bos.close(); bos.close();
in.close(); in.close();
ConfigObject cfg = Santoku.buildCurrentConfigObject(); ConfigObject cfg = SliceBeam.buildCurrentConfigObject();
Bed3D bed = FileMenu.this.fragment.getGlView().getRenderer().getBed(); Bed3D bed = FileMenu.this.fragment.getGlView().getRenderer().getBed();
double bedX = bed.getVolumeMax().x - bed.getVolumeMin().x; double bedX = bed.getVolumeMax().x - bed.getVolumeMin().x;
double bedY = bed.getVolumeMax().y - bed.getVolumeMin().y; double bedY = bed.getVolumeMax().y - bed.getVolumeMin().y;
@@ -395,14 +562,14 @@ public class FileMenu extends ListBedMenu {
protected void onCreate() { protected void onCreate() {
super.onCreate(); super.onCreate();
Santoku.EVENT_BUS.registerListener(this); SliceBeam.EVENT_BUS.registerListener(this);
} }
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
Santoku.EVENT_BUS.unregisterListener(this); SliceBeam.EVENT_BUS.unregisterListener(this);
} }
} }
@@ -410,10 +577,10 @@ public class FileMenu extends ListBedMenu {
private void loadModel(String key) { private void loadModel(String key) {
BedFragment fragment = this.fragment; BedFragment fragment = this.fragment;
ViewUtils.postOnMainThread(() -> { ViewUtils.postOnMainThread(() -> {
File f = new File(Santoku.getModelCacheDir(), "calibration_" + key + ".stl"); File f = new File(SliceBeam.getModelCacheDir(), "calibration_" + key + ".stl");
new Thread(()->{ new Thread(()->{
try { try {
InputStream in = Santoku.INSTANCE.getAssets().open("models/" + key + ".stl"); InputStream in = SliceBeam.INSTANCE.getAssets().open("models/" + key + ".stl");
FileOutputStream fos = new FileOutputStream(f); FileOutputStream fos = new FileOutputStream(f);
byte[] buffer = new byte[10240]; int c; byte[] buffer = new byte[10240]; int c;
while ((c = in.read(buffer)) != -1) { while ((c = in.read(buffer)) != -1) {
@@ -428,9 +595,9 @@ public class FileMenu extends ListBedMenu {
fragment.loadGCode(f); fragment.loadGCode(f);
} else { } else {
fragment.loadModel(f); fragment.loadModel(f);
Santoku.EVENT_BUS.fireEvent(new ObjectsListChangedEvent()); SliceBeam.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
} }
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileOpenFileLoaded)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuFileOpenFileLoaded));
} catch (Slic3rRuntimeError e) { } catch (Slic3rRuntimeError e) {
f.delete(); f.delete();
@@ -451,7 +618,7 @@ public class FileMenu extends ListBedMenu {
} }
}).start(); }).start();
}, 200); }, 200);
Santoku.EVENT_BUS.fireEvent(new NeedDismissCalibrationsMenu()); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissCalibrationsMenu());
dismiss(true); dismiss(true);
} }
@@ -1,4 +1,4 @@
package com.dark98.santoku.components.bed_menu; package ru.ytkab0bp.slicebeam.components.bed_menu;
import android.content.Context; import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
@@ -11,11 +11,11 @@ import androidx.recyclerview.widget.RecyclerView;
import java.util.List; import java.util.List;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.fragment.BedFragment; import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import com.dark98.santoku.recycler.SimpleRecyclerAdapter; import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerAdapter;
import com.dark98.santoku.recycler.SimpleRecyclerItem; import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
public abstract class ListBedMenu extends BedMenu { public abstract class ListBedMenu extends BedMenu {
protected BedFragment fragment; protected BedFragment fragment;
@@ -69,13 +69,13 @@ public abstract class ListBedMenu extends BedMenu {
@Override @Override
public void onViewCreated(View v) { public void onViewCreated(View v) {
super.onViewCreated(v); super.onViewCreated(v);
Santoku.EVENT_BUS.registerListener(ListBedMenu.this); SliceBeam.EVENT_BUS.registerListener(ListBedMenu.this);
} }
@Override @Override
public void onViewDestroyed() { public void onViewDestroyed() {
super.onViewDestroyed(); super.onViewDestroyed();
Santoku.EVENT_BUS.unregisterListener(ListBedMenu.this); SliceBeam.EVENT_BUS.unregisterListener(ListBedMenu.this);
} }
protected abstract List<SimpleRecyclerItem> onCreateItems(boolean portrait); protected abstract List<SimpleRecyclerItem> onCreateItems(boolean portrait);
@@ -1,4 +1,4 @@
package com.dark98.santoku.components.bed_menu; package ru.ytkab0bp.slicebeam.components.bed_menu;
import android.content.Context; import android.content.Context;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
@@ -26,25 +26,25 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import ru.ytkab0bp.eventbus.EventHandler; import ru.ytkab0bp.eventbus.EventHandler;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.components.BeamAlertDialogBuilder; import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import com.dark98.santoku.components.UnfoldMenu; import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
import com.dark98.santoku.events.FlattenModeResetEvent; import ru.ytkab0bp.slicebeam.events.FlattenModeResetEvent;
import com.dark98.santoku.events.LongClickTranslationEvent; import ru.ytkab0bp.slicebeam.events.LongClickTranslationEvent;
import com.dark98.santoku.events.NeedSnackbarEvent; import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import com.dark98.santoku.events.ObjectsListChangedEvent; import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
import com.dark98.santoku.events.SelectedObjectChangedEvent; import ru.ytkab0bp.slicebeam.events.SelectedObjectChangedEvent;
import com.dark98.santoku.recycler.SimpleRecyclerItem; import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import com.dark98.santoku.recycler.SpaceItem; import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
import com.dark98.santoku.slic3r.Model; import ru.ytkab0bp.slicebeam.slic3r.Model;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.Vec3d; import ru.ytkab0bp.slicebeam.utils.Vec3d;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.view.BeamButton; import ru.ytkab0bp.slicebeam.view.BeamButton;
import com.dark98.santoku.view.DividerView; import ru.ytkab0bp.slicebeam.view.DividerView;
import com.dark98.santoku.view.PositionScrollView; import ru.ytkab0bp.slicebeam.view.PositionScrollView;
import com.dark98.santoku.view.TextColorImageSpan; import ru.ytkab0bp.slicebeam.view.TextColorImageSpan;
public class OrientationMenu extends ListBedMenu { public class OrientationMenu extends ListBedMenu {
private boolean hasSelection() { private boolean hasSelection() {
@@ -61,7 +61,7 @@ public class OrientationMenu extends ListBedMenu {
fragment.getGlView().requestRender(); fragment.getGlView().requestRender();
} }
}); });
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuOrientationArrangeFinished)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuOrientationArrangeFinished));
}).setEnabled(fragment.getGlView().getRenderer().getModel() != null), }).setEnabled(fragment.getGlView().getRenderer().getModel() != null),
new SpaceItem(portrait ? ViewUtils.dp(8) : 0, portrait ? 0 : ViewUtils.dp(8)), new SpaceItem(portrait ? ViewUtils.dp(8) : 0, portrait ? 0 : ViewUtils.dp(8)),
new BedMenuItem(R.string.MenuOrientationAutoOrient, R.drawable.menu_orientation_auto_28).setEnabled(hasSelection()).onClick(view -> { new BedMenuItem(R.string.MenuOrientationAutoOrient, R.drawable.menu_orientation_auto_28).setEnabled(hasSelection()).onClick(view -> {
@@ -76,7 +76,7 @@ public class OrientationMenu extends ListBedMenu {
fragment.getGlView().getRenderer().invalidateGlModel(i); fragment.getGlView().getRenderer().invalidateGlModel(i);
fragment.getGlView().requestRender(); fragment.getGlView().requestRender();
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuOrientationAutoOrientDone, Snackbar.LENGTH_SHORT)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(R.string.MenuOrientationAutoOrientDone, Snackbar.LENGTH_SHORT));
}), }),
new BedMenuItem(R.string.MenuOrientationFlatten, R.drawable.menu_orientation_flatten_28).setEnabled(hasSelection()).setCheckable((buttonView, isChecked) -> { new BedMenuItem(R.string.MenuOrientationFlatten, R.drawable.menu_orientation_flatten_28).setEnabled(hasSelection()).setCheckable((buttonView, isChecked) -> {
fragment.getGlView().getRenderer().setInFlattenMode(isChecked); fragment.getGlView().getRenderer().setInFlattenMode(isChecked);
@@ -200,10 +200,10 @@ public class OrientationMenu extends ListBedMenu {
} }
private CharSequence formatTrackTitle(int res, double value) { private CharSequence formatTrackTitle(int res, double value) {
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(Santoku.INSTANCE.getString(res, value)); SpannableStringBuilder sb = SpannableStringBuilder.valueOf(SliceBeam.INSTANCE.getString(res, value));
sb.append(" d"); sb.append(" d");
int size = ViewUtils.dp(14); int size = ViewUtils.dp(14);
Drawable dr = ContextCompat.getDrawable(Santoku.INSTANCE, R.drawable.edit_outline_28); Drawable dr = ContextCompat.getDrawable(SliceBeam.INSTANCE, R.drawable.edit_outline_28);
dr.setTint(ThemesRepo.getColor(android.R.attr.textColorSecondary)); dr.setTint(ThemesRepo.getColor(android.R.attr.textColorSecondary));
dr.setBounds(0, 0, size, size); dr.setBounds(0, 0, size, size);
sb.setSpan(new TextColorImageSpan(dr, 0), sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); sb.setSpan(new TextColorImageSpan(dr, 0), sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
@@ -355,7 +355,7 @@ public class OrientationMenu extends ListBedMenu {
protected void onCreate() { protected void onCreate() {
super.onCreate(); super.onCreate();
Santoku.EVENT_BUS.registerListener(this); SliceBeam.EVENT_BUS.registerListener(this);
setSelectionValues(); setSelectionValues();
} }
@@ -363,7 +363,7 @@ public class OrientationMenu extends ListBedMenu {
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
Santoku.EVENT_BUS.unregisterListener(this); SliceBeam.EVENT_BUS.unregisterListener(this);
stopScroll(); stopScroll();
} }
@@ -501,10 +501,10 @@ public class OrientationMenu extends ListBedMenu {
} }
private CharSequence formatTrackTitle(int res, double value) { private CharSequence formatTrackTitle(int res, double value) {
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(Santoku.INSTANCE.getString(res, value)); SpannableStringBuilder sb = SpannableStringBuilder.valueOf(SliceBeam.INSTANCE.getString(res, value));
sb.append(" d"); sb.append(" d");
int size = ViewUtils.dp(14); int size = ViewUtils.dp(14);
Drawable dr = ContextCompat.getDrawable(Santoku.INSTANCE, R.drawable.edit_outline_28); Drawable dr = ContextCompat.getDrawable(SliceBeam.INSTANCE, R.drawable.edit_outline_28);
dr.setTint(ThemesRepo.getColor(android.R.attr.textColorSecondary)); dr.setTint(ThemesRepo.getColor(android.R.attr.textColorSecondary));
dr.setBounds(0, 0, size, size); dr.setBounds(0, 0, size, size);
sb.setSpan(new TextColorImageSpan(dr, 0), sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); sb.setSpan(new TextColorImageSpan(dr, 0), sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
@@ -666,7 +666,7 @@ public class OrientationMenu extends ListBedMenu {
protected void onCreate() { protected void onCreate() {
super.onCreate(); super.onCreate();
Santoku.EVENT_BUS.registerListener(this); SliceBeam.EVENT_BUS.registerListener(this);
setSelectionValues(); setSelectionValues();
} }
@@ -674,7 +674,7 @@ public class OrientationMenu extends ListBedMenu {
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
Santoku.EVENT_BUS.unregisterListener(this); SliceBeam.EVENT_BUS.unregisterListener(this);
stopScroll(); stopScroll();
} }
@@ -1,6 +1,6 @@
package com.dark98.santoku.components.bed_menu; package ru.ytkab0bp.slicebeam.components.bed_menu;
import static com.dark98.santoku.utils.DebugUtils.assertTrue; import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
@@ -46,28 +46,27 @@ import java.util.UUID;
import cz.msebera.android.httpclient.Header; import cz.msebera.android.httpclient.Header;
import cz.msebera.android.httpclient.entity.ContentType; import cz.msebera.android.httpclient.entity.ContentType;
import cz.msebera.android.httpclient.message.BasicHeader; import cz.msebera.android.httpclient.message.BasicHeader;
import com.dark98.santoku.BuildConfig; import ru.ytkab0bp.slicebeam.BuildConfig;
import com.dark98.santoku.MainActivity; import ru.ytkab0bp.slicebeam.MainActivity;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.components.BeamAlertDialogBuilder; import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import com.dark98.santoku.components.UnfoldMenu; import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
import com.dark98.santoku.config.ConfigObject; import ru.ytkab0bp.slicebeam.config.ConfigObject;
import com.dark98.santoku.events.NeedDismissSnackbarEvent; import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import com.dark98.santoku.events.NeedSnackbarEvent; import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import com.dark98.santoku.fragment.BedFragment; import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import com.dark98.santoku.print_host.ElegooLinkClient; import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import com.dark98.santoku.recycler.SimpleRecyclerItem; import ru.ytkab0bp.slicebeam.slic3r.GCodeProcessorResult;
import com.dark98.santoku.slic3r.GCodeProcessorResult; import ru.ytkab0bp.slicebeam.slic3r.GCodeViewer;
import com.dark98.santoku.slic3r.GCodeViewer; import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
import com.dark98.santoku.slic3r.Slic3rLocalization; import ru.ytkab0bp.slicebeam.theme.IThemeView;
import com.dark98.santoku.theme.IThemeView; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.view.DividerView;
import com.dark98.santoku.view.DividerView; import ru.ytkab0bp.slicebeam.view.PositionScrollView;
import com.dark98.santoku.view.PositionScrollView; import ru.ytkab0bp.slicebeam.view.SegmentsView;
import com.dark98.santoku.view.SegmentsView; import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
import com.dark98.santoku.view.SnackbarsLayout;
public class SliceMenu extends ListBedMenu { public class SliceMenu extends ListBedMenu {
private AsyncHttpClient client = new AsyncHttpClient(); private AsyncHttpClient client = new AsyncHttpClient();
@@ -77,12 +76,12 @@ public class SliceMenu extends ListBedMenu {
client.setMaxRetriesAndTimeout(0, 10000); client.setMaxRetriesAndTimeout(0, 10000);
} }
private final static List<String> SUPPORTED_SEND = Arrays.asList("octoprint", "klipper", "moonraker", "elegoolink"); private final static List<String> SUPPORTED_SEND = Collections.singletonList("octoprint");
private int lastUid; private int lastUid;
@Override @Override
protected List<SimpleRecyclerItem> onCreateItems(boolean portrait) { protected List<SimpleRecyclerItem> onCreateItems(boolean portrait) {
lastUid = Santoku.CONFIG_UID; lastUid = SliceBeam.CONFIG_UID;
List<SimpleRecyclerItem> items = new ArrayList<>(Arrays.asList( List<SimpleRecyclerItem> items = new ArrayList<>(Arrays.asList(
new BedMenuItem(R.string.MenuSliceInfo, R.drawable.clock_circle_dashed_outline_24).onClick(v -> fragment.showUnfoldMenu(new InfoMenu(), v)), new BedMenuItem(R.string.MenuSliceInfo, R.drawable.clock_circle_dashed_outline_24).onClick(v -> fragment.showUnfoldMenu(new InfoMenu(), v)),
new BedMenuItem(R.string.MenuSliceLayers, R.drawable.square_stack_up_outline_28).onClick(v -> fragment.showUnfoldMenu(new LayersMenu(), v)), new BedMenuItem(R.string.MenuSliceLayers, R.drawable.square_stack_up_outline_28).onClick(v -> fragment.showUnfoldMenu(new LayersMenu(), v)),
@@ -114,7 +113,7 @@ public class SliceMenu extends ListBedMenu {
} }
}) })
)); ));
ConfigObject obj = Santoku.CONFIG.findPrinter(Santoku.CONFIG.presets.get("printer")); ConfigObject obj = SliceBeam.CONFIG.findPrinter(SliceBeam.CONFIG.presets.get("printer"));
assertTrue(obj != null); assertTrue(obj != null);
String type = obj.get("host_type"); String type = obj.get("host_type");
if (type == null) type = "octoprint"; if (type == null) type = "octoprint";
@@ -122,78 +121,22 @@ public class SliceMenu extends ListBedMenu {
String apiKey = obj.get("printhost_apikey"); String apiKey = obj.get("printhost_apikey");
if (SUPPORTED_SEND.contains(type) && !TextUtils.isEmpty(host)) { if (SUPPORTED_SEND.contains(type) && !TextUtils.isEmpty(host)) {
String finalType = type; String finalType = type;
ConfigObject finalObj = obj; 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.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)));
items.add(new BedMenuItem(R.string.MenuSliceSendToPrinterAndPrint, R.drawable.send_28).onClick(v -> upload(finalType, host, apiKey, true, finalObj)));
} }
return items; return items;
} }
private void upload(String type, String host, String apiKey, boolean print, ConfigObject config) { private void upload(String type, String host, String apiKey, boolean print) {
String name = fragment.getGlView().getRenderer().getGcodeResult().getRecommendedName(); String name = fragment.getGlView().getRenderer().getGcodeResult().getRecommendedName();
switch (type) { switch (type) {
case "klipper":
case "moonraker": {
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 + "/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));
// Moonraker responses may wrap data in "result"
JSONObject payload = obj;
if (obj.has("result") && obj.opt("result") instanceof JSONObject) {
payload = obj.optJSONObject("result");
}
// 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);
}
}
@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;
}
default: default:
case "octoprint": case "octoprint":
if (!host.startsWith("http://") && !host.startsWith("https://")) { if (!host.startsWith("http://")) {
host = "http://" + host; host = "http://" + host;
} }
String tag = UUID.randomUUID().toString(); String tag = UUID.randomUUID().toString();
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuSliceSendToPrinterLoading).tag(tag)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuSliceSendToPrinterLoading).tag(tag));
Header[] headers = TextUtils.isEmpty(apiKey) ? new Header[0] : new Header[] {new BasicHeader("X-Api-Key", apiKey)}; Header[] headers = TextUtils.isEmpty(apiKey) ? new Header[0] : new Header[] {new BasicHeader("X-Api-Key", apiKey)};
RequestParams params = new RequestParams(); RequestParams params = new RequestParams();
try { try {
@@ -204,18 +147,16 @@ public class SliceMenu extends ListBedMenu {
params.put("select", String.valueOf(print)); params.put("select", String.valueOf(print));
params.put("print", String.valueOf(print)); params.put("print", String.valueOf(print));
String url = host + "/api/files/local"; client.post(SliceBeam.INSTANCE, host + "/api/files/local", headers, params, null, new AsyncHttpResponseHandler() {
client.post(Santoku.INSTANCE, url, headers, params, null, new AsyncHttpResponseHandler() {
@Override @Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
try { try {
JSONObject obj = new JSONObject(new String(responseBody)); JSONObject obj = new JSONObject(new String(responseBody));
// OctoPrint: check for "action" or "files"
if (!obj.has("action") && !obj.has("files")) { if (!obj.has("action") && !obj.has("files")) {
throw new JSONException("Unexpected response: " + obj.toString()); throw new JSONException(obj.toString());
} }
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag)); SliceBeam.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)); SliceBeam.EVENT_BUS.fireEvent(new NeedSnackbarEvent(print ? SnackbarsLayout.Type.INFO : SnackbarsLayout.Type.DONE, print ? R.string.MenuSliceSendToPrinterPrintStarted : R.string.MenuSliceSendToPrinterOK));
} catch (JSONException e) { } catch (JSONException e) {
onFailure(statusCode, headers, responseBody, e); onFailure(statusCode, headers, responseBody, e);
} }
@@ -223,7 +164,7 @@ public class SliceMenu extends ListBedMenu {
@Override @Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) { public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag)); SliceBeam.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(tag));
ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(fragment.getContext()) ViewUtils.postOnMainThread(() -> new BeamAlertDialogBuilder(fragment.getContext())
.setTitle(R.string.MenuSliceSendToPrinterFailed) .setTitle(R.string.MenuSliceSendToPrinterFailed)
.setMessage(error.toString()) .setMessage(error.toString())
@@ -232,40 +173,6 @@ public class SliceMenu extends ListBedMenu {
} }
}); });
break; 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;
}
} }
} }
@@ -275,7 +182,7 @@ public class SliceMenu extends ListBedMenu {
v.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { v.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override @Override
public void onViewAttachedToWindow(@NonNull View v) { public void onViewAttachedToWindow(@NonNull View v) {
if (lastUid != Santoku.CONFIG_UID) { if (lastUid != SliceBeam.CONFIG_UID) {
adapter.setItems(onCreateItems(v.getWidth() < v.getHeight())); adapter.setItems(onCreateItems(v.getWidth() < v.getHeight()));
} }
} }
@@ -443,9 +350,9 @@ public class SliceMenu extends ListBedMenu {
private static String formatComplex(double weight, double length, float time) { private static String formatComplex(double weight, double length, float time) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (weight > 0) { if (weight > 0) {
sb.append(format.format(weight)).append(" ").append(Santoku.INSTANCE.getString(R.string.MenuSliceInfoWeight)).append(" | "); sb.append(format.format(weight)).append(" ").append(SliceBeam.INSTANCE.getString(R.string.MenuSliceInfoWeight)).append(" | ");
} }
sb.append(format.format(length)).append(" ").append(Santoku.INSTANCE.getString(R.string.MenuSliceInfoLength)).append(" | "); sb.append(format.format(length)).append(" ").append(SliceBeam.INSTANCE.getString(R.string.MenuSliceInfoLength)).append(" | ");
sb.append(formatTime(time)); sb.append(formatTime(time));
return sb.toString(); return sb.toString();
} }
@@ -458,17 +365,17 @@ public class SliceMenu extends ListBedMenu {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (hours > 0) { if (hours > 0) {
sb.append(hours).append(" ").append(Santoku.INSTANCE.getString(R.string.MenuSliceInfoHour)); sb.append(hours).append(" ").append(SliceBeam.INSTANCE.getString(R.string.MenuSliceInfoHour));
} }
if (minutes > 0) { if (minutes > 0) {
if (sb.length() > 0) sb.append(" "); if (sb.length() > 0) sb.append(" ");
sb.append(minutes).append(" ").append(Santoku.INSTANCE.getString(R.string.MenuSliceInfoMinute)); sb.append(minutes).append(" ").append(SliceBeam.INSTANCE.getString(R.string.MenuSliceInfoMinute));
} }
if (seconds > 0 || sb.length() == 0) { if (seconds > 0 || sb.length() == 0) {
if (sb.length() > 0) sb.append(" "); if (sb.length() > 0) sb.append(" ");
sb.append(seconds).append(" ").append(Santoku.INSTANCE.getString(R.string.MenuSliceInfoSecond)); sb.append(seconds).append(" ").append(SliceBeam.INSTANCE.getString(R.string.MenuSliceInfoSecond));
} }
return sb.toString(); return sb.toString();
@@ -1,4 +1,4 @@
package com.dark98.santoku.components.bed_menu; package ru.ytkab0bp.slicebeam.components.bed_menu;
import android.content.Context; import android.content.Context;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
@@ -16,7 +16,6 @@ import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.Space; import android.widget.Space;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
@@ -26,24 +25,23 @@ import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import ru.ytkab0bp.eventbus.EventHandler; import ru.ytkab0bp.eventbus.EventHandler;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.components.BeamAlertDialogBuilder; import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import com.dark98.santoku.components.UnfoldMenu; import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
import com.dark98.santoku.events.ObjectsListChangedEvent; import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
import com.dark98.santoku.events.SelectedObjectChangedEvent; import ru.ytkab0bp.slicebeam.events.SelectedObjectChangedEvent;
import com.dark98.santoku.recycler.PreferenceSwitchItem; import ru.ytkab0bp.slicebeam.recycler.PreferenceSwitchItem;
import com.dark98.santoku.recycler.SimpleRecyclerItem; import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import com.dark98.santoku.render.GLRenderer; import ru.ytkab0bp.slicebeam.slic3r.Model;
import com.dark98.santoku.slic3r.Model; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.utils.DoubleMatrix;
import com.dark98.santoku.utils.DoubleMatrix; import ru.ytkab0bp.slicebeam.utils.Prefs;
import com.dark98.santoku.utils.Prefs; import ru.ytkab0bp.slicebeam.utils.Vec3d;
import com.dark98.santoku.utils.Vec3d; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.view.DividerView;
import com.dark98.santoku.view.DividerView; import ru.ytkab0bp.slicebeam.view.PositionScrollView;
import com.dark98.santoku.view.PositionScrollView; import ru.ytkab0bp.slicebeam.view.TextColorImageSpan;
import com.dark98.santoku.view.TextColorImageSpan;
public class TransformMenu extends ListBedMenu { public class TransformMenu extends ListBedMenu {
private double[] tempMatrix = new double[16]; private double[] tempMatrix = new double[16];
@@ -57,77 +55,6 @@ public class TransformMenu extends ListBedMenu {
protected List<SimpleRecyclerItem> onCreateItems(boolean portrait) { protected List<SimpleRecyclerItem> onCreateItems(boolean portrait) {
return Arrays.asList( return Arrays.asList(
new BedMenuItem(R.string.MenuTransformScale, R.drawable.arrow_up_right_corner_outline_24).setEnabled(hasSelection()).onClick(v -> fragment.showUnfoldMenu(new ScaleMenu(), v)), new BedMenuItem(R.string.MenuTransformScale, R.drawable.arrow_up_right_corner_outline_24).setEnabled(hasSelection()).onClick(v -> fragment.showUnfoldMenu(new ScaleMenu(), v)),
new BedMenuItem(R.string.MenuTransformClone, R.drawable.square_stack_up_outline_28).setEnabled(hasSelection()).onClick(v -> {
Context ctx = fragment.getContext();
LinearLayout ll = new LinearLayout(ctx);
ll.setOrientation(LinearLayout.VERTICAL);
AtomicBoolean autoArrange = new AtomicBoolean(false);
EditText countInput = new EditText(ctx);
countInput.setInputType(InputType.TYPE_CLASS_NUMBER);
countInput.setHint(R.string.MenuTransformCloneCountHint);
countInput.setText("1");
countInput.setSelection(countInput.getText().length());
countInput.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
ll.addView(countInput, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
leftMargin = rightMargin = ViewUtils.dp(21);
}});
PreferenceSwitchItem.SwitchPreferenceHolderView holderView = new PreferenceSwitchItem.SwitchPreferenceHolderView(ctx);
holderView.title.setText(R.string.MenuTransformCloneAutoArrange);
holderView.title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
holderView.subtitle.setVisibility(View.GONE);
holderView.icon.setVisibility(View.GONE);
holderView.matSwitch.setChecked(autoArrange.get());
holderView.setOnClickListener(v1 -> {
autoArrange.set(!autoArrange.get());
holderView.matSwitch.setChecked(autoArrange.get());
});
ll.addView(holderView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
leftMargin = rightMargin = ViewUtils.dp(8);
}});
new BeamAlertDialogBuilder(ctx)
.setTitle(R.string.MenuTransformCloneCountTitle)
.setView(ll)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok, (dialog, which) -> {
int count = 1;
try {
count = Integer.parseInt(countInput.getText().toString());
} catch (NumberFormatException ignored) {
count = 1;
}
if (count <= 0) return;
int clones = count;
boolean arrange = autoArrange.get();
fragment.getGlView().queueEvent(() -> {
GLRenderer renderer = fragment.getGlView().getRenderer();
Model model = renderer.getModel();
int selected = renderer.getSelectedObject();
if (model == null || selected == -1) return;
for (int i = 0; i < clones; i++) {
model.addObject(model, selected);
}
if (arrange) {
renderer.getBed().arrange(model);
renderer.resetGlModels();
}
fragment.getGlView().requestRender();
Santoku.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
ViewUtils.postOnMainThread(() -> Toast.makeText(fragment.getContext(), fragment.getContext().getResources().getQuantityString(R.plurals.MenuTransformCloneSuccessCount, clones, clones), Toast.LENGTH_SHORT).show());
});
fragment.getGlView().requestRender();
})
.show();
ViewUtils.postOnMainThread(() -> {
countInput.requestFocus();
InputMethodManager imm = (InputMethodManager) ctx.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(countInput, 0);
}, 200);
}),
new BedMenuItem(R.string.MenuTransformMirror, R.drawable.menu_transform_cut_or_mirror_28).setEnabled(hasSelection()).onClick(v -> { new BedMenuItem(R.string.MenuTransformMirror, R.drawable.menu_transform_cut_or_mirror_28).setEnabled(hasSelection()).onClick(v -> {
Context ctx = fragment.getContext(); Context ctx = fragment.getContext();
new BeamAlertDialogBuilder(ctx) new BeamAlertDialogBuilder(ctx)
@@ -178,9 +105,6 @@ public class TransformMenu extends ListBedMenu {
((BedMenuItem) adapter.getItems().get(1)).setEnabled(hasSelection()); ((BedMenuItem) adapter.getItems().get(1)).setEnabled(hasSelection());
adapter.notifyItemChanged(1); adapter.notifyItemChanged(1);
((BedMenuItem) adapter.getItems().get(2)).setEnabled(hasSelection());
adapter.notifyItemChanged(2);
} }
@EventHandler(runOnMainThread = true) @EventHandler(runOnMainThread = true)
@@ -190,9 +114,6 @@ public class TransformMenu extends ListBedMenu {
((BedMenuItem) adapter.getItems().get(1)).setEnabled(hasSelection()); ((BedMenuItem) adapter.getItems().get(1)).setEnabled(hasSelection());
adapter.notifyItemChanged(1); adapter.notifyItemChanged(1);
((BedMenuItem) adapter.getItems().get(2)).setEnabled(hasSelection());
adapter.notifyItemChanged(2);
} }
public final class ScaleMenu extends UnfoldMenu { public final class ScaleMenu extends UnfoldMenu {
@@ -330,10 +251,10 @@ public class TransformMenu extends ListBedMenu {
} }
private CharSequence formatTrackTitle(int res, double value, double mm) { private CharSequence formatTrackTitle(int res, double value, double mm) {
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(Santoku.INSTANCE.getString(res, value, mm)); SpannableStringBuilder sb = SpannableStringBuilder.valueOf(SliceBeam.INSTANCE.getString(res, value, mm));
sb.append(" d"); sb.append(" d");
int size = ViewUtils.dp(14); int size = ViewUtils.dp(14);
Drawable dr = ContextCompat.getDrawable(Santoku.INSTANCE, R.drawable.edit_outline_28); Drawable dr = ContextCompat.getDrawable(SliceBeam.INSTANCE, R.drawable.edit_outline_28);
dr.setTint(ThemesRepo.getColor(android.R.attr.textColorSecondary)); dr.setTint(ThemesRepo.getColor(android.R.attr.textColorSecondary));
dr.setBounds(0, 0, size, size); dr.setBounds(0, 0, size, size);
sb.setSpan(new TextColorImageSpan(dr, 0), sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); sb.setSpan(new TextColorImageSpan(dr, 0), sb.length() - 1, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
@@ -588,7 +509,7 @@ public class TransformMenu extends ListBedMenu {
protected void onCreate() { protected void onCreate() {
super.onCreate(); super.onCreate();
Santoku.EVENT_BUS.registerListener(this); SliceBeam.EVENT_BUS.registerListener(this);
setSelectionValues(); setSelectionValues();
} }
@@ -596,7 +517,7 @@ public class TransformMenu extends ListBedMenu {
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
Santoku.EVENT_BUS.unregisterListener(this); SliceBeam.EVENT_BUS.unregisterListener(this);
stopScroll(); stopScroll();
} }
@@ -1,12 +1,12 @@
package com.dark98.santoku.config; package ru.ytkab0bp.slicebeam.config;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.dark98.santoku.BuildConfig; import ru.ytkab0bp.slicebeam.BuildConfig;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.fragment.ProfileListFragment; import ru.ytkab0bp.slicebeam.fragment.ProfileListFragment;
/** @noinspection CopyConstructorMissesField*/ /** @noinspection CopyConstructorMissesField*/
public class ConfigObject implements ProfileListFragment.ProfileListItem { public class ConfigObject implements ProfileListFragment.ProfileListItem {
@@ -70,35 +70,28 @@ public class ConfigObject implements ProfileListFragment.ProfileListItem {
public boolean isSelected() { public boolean isSelected() {
switch (profileListType) { switch (profileListType) {
case PROFILE_LIST_PRINT: case PROFILE_LIST_PRINT:
return getTitle().equals(Santoku.CONFIG.presets.get("print")); return getTitle().equals(SliceBeam.CONFIG.presets.get("print"));
case PROFILE_LIST_FILAMENT: case PROFILE_LIST_FILAMENT:
return getTitle().equals(Santoku.CONFIG.presets.get("filament")); return getTitle().equals(SliceBeam.CONFIG.presets.get("filament"));
case PROFILE_LIST_PRINTER: case PROFILE_LIST_PRINTER:
return getTitle().equals(Santoku.CONFIG.presets.get("printer")); return getTitle().equals(SliceBeam.CONFIG.presets.get("printer"));
} }
return false; return false;
} }
public String serialize() { public String serialize() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("# generated by Santoku ").append(BuildConfig.VERSION_NAME).append("\n\n"); sb.append("# generated by Slice Beam ").append(BuildConfig.VERSION_NAME).append("\n\n");
for (Map.Entry<String, String> en : values.entrySet()) { for (Map.Entry<String, String> en : values.entrySet()) {
String value = en.getValue(); sb.append(en.getKey()).append(" = ").append(en.getValue().replace("\n", "\\n")).append("\n");
if (value != null) {
value = value.replace("\r\n", "\n").replace("\r", "\n");
value = value.replace("\n", "\\n");
}
sb.append(en.getKey()).append(" = ").append(value).append("\n");
} }
return sb.toString(); return sb.toString();
} }
public static ConfigObject createCustomPrinterProfile() { public static ConfigObject createCustomPrinterProfile() {
ConfigObject custom = new ConfigObject(Santoku.INSTANCE.getString(R.string.IntroCustomProfileName)); ConfigObject custom = new ConfigObject(SliceBeam.INSTANCE.getString(R.string.IntroCustomProfileName));
custom.put("printer_technology", "FFF"); custom.put("printer_technology", "FFF");
custom.put("bed_shape", "0x0,200x0,200x200,0x200"); custom.put("bed_shape", "0x0,200x0,200x200,0x200");
custom.put("auto_arrange_bed_clearance", "5");
custom.put("auto_arrange_rotate", "1");
custom.put("binary_gcode", "0"); custom.put("binary_gcode", "0");
custom.put("gcode_flavor", "marlin"); custom.put("gcode_flavor", "marlin");
custom.put("max_print_height", "200"); custom.put("max_print_height", "200");
@@ -131,10 +124,6 @@ public class ConfigObject implements ProfileListFragment.ProfileListItem {
custom.put("machine_min_extruding_rate", "0"); custom.put("machine_min_extruding_rate", "0");
custom.put("machine_min_travel_rate", "0"); custom.put("machine_min_travel_rate", "0");
custom.put("elegoolink_timelapse", "0");
custom.put("elegoolink_bed_leveling", "0");
custom.put("elegoolink_bed_type", "pte");
custom.put("start_gcode", "G90 ; use absolute coordinates\\nM83 ; extruder relative mode\\nM104 S{is_nil(idle_temperature[0]) ? 150 : idle_temperature[0]} ; set temporary nozzle temp to prevent oozing during homing\\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\\nG4 S30 ; allow partial nozzle warmup\\nG28 ; home all axis\\nG1 Z50 F240\\nG1 X2.0 Y10 F3000\\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\\nG1 Z0.28 F240\\nG92 E0\\nG1 X2.0 Y140 E10 F1500 ; prime the nozzle\\nG1 X2.3 Y140 F5000\\nG92 E0\\nG1 X2.3 Y10 E10 F1200 ; prime the nozzle\\nG92 E0"); custom.put("start_gcode", "G90 ; use absolute coordinates\\nM83 ; extruder relative mode\\nM104 S{is_nil(idle_temperature[0]) ? 150 : idle_temperature[0]} ; set temporary nozzle temp to prevent oozing during homing\\nM140 S{first_layer_bed_temperature[0]} ; set final bed temp\\nG4 S30 ; allow partial nozzle warmup\\nG28 ; home all axis\\nG1 Z50 F240\\nG1 X2.0 Y10 F3000\\nM104 S{first_layer_temperature[0]} ; set final nozzle temp\\nM190 S{first_layer_bed_temperature[0]} ; wait for bed temp to stabilize\\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\\nG1 Z0.28 F240\\nG92 E0\\nG1 X2.0 Y140 E10 F1500 ; prime the nozzle\\nG1 X2.3 Y140 F5000\\nG92 E0\\nG1 X2.3 Y10 E10 F1200 ; prime the nozzle\\nG92 E0");
custom.put("end_gcode", "{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F600 ; Move print head up{endif}\\nG1 X5 Y{print_bed_max[1]*0.85} F{travel_speed*60} ; present print\\n{if max_layer_z < max_print_height-10}G1 Z{z_offset+min(max_layer_z+70, max_print_height-10)} F600 ; Move print head further up{endif}\\n{if max_layer_z < max_print_height*0.6}G1 Z{max_print_height*0.6} F600 ; Move print head further up{endif}\\nM140 S0 ; turn off heatbed\\nM104 S0 ; turn off temperature\\nM107 ; turn off fan\\nM84 X Y E ; disable motors"); custom.put("end_gcode", "{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+2, max_print_height)} F600 ; Move print head up{endif}\\nG1 X5 Y{print_bed_max[1]*0.85} F{travel_speed*60} ; present print\\n{if max_layer_z < max_print_height-10}G1 Z{z_offset+min(max_layer_z+70, max_print_height-10)} F600 ; Move print head further up{endif}\\n{if max_layer_z < max_print_height*0.6}G1 Z{max_print_height*0.6} F600 ; Move print head further up{endif}\\nM140 S0 ; turn off heatbed\\nM104 S0 ; turn off temperature\\nM107 ; turn off fan\\nM84 X Y E ; disable motors");
@@ -142,7 +131,7 @@ public class ConfigObject implements ProfileListFragment.ProfileListItem {
} }
public static ConfigObject createCustomFilamentProfile() { public static ConfigObject createCustomFilamentProfile() {
ConfigObject genericFilament = new ConfigObject(Santoku.INSTANCE.getString(R.string.IntroCustomProfileFilamentName)); ConfigObject genericFilament = new ConfigObject(SliceBeam.INSTANCE.getString(R.string.IntroCustomProfileFilamentName));
genericFilament.profileListType = ConfigObject.PROFILE_LIST_FILAMENT; genericFilament.profileListType = ConfigObject.PROFILE_LIST_FILAMENT;
genericFilament.put("first_layer_bed_temperature", "60"); genericFilament.put("first_layer_bed_temperature", "60");
genericFilament.put("bed_temperature", "60"); genericFilament.put("bed_temperature", "60");
@@ -0,0 +1,6 @@
package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event;
@Event
public class BeamServerDataUpdatedEvent {}
@@ -0,0 +1,6 @@
package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event;
@Event
public class CloudFeaturesUpdatedEvent {}
@@ -1,4 +1,4 @@
package com.dark98.santoku.events; package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event; import ru.ytkab0bp.eventbus.Event;
@@ -0,0 +1,6 @@
package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event;
@Event
public class CloudModelsRemainingCountUpdatedEvent {}
@@ -1,4 +1,4 @@
package com.dark98.santoku.events; package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event; import ru.ytkab0bp.eventbus.Event;
@@ -1,4 +1,4 @@
package com.dark98.santoku.events; package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event; import ru.ytkab0bp.eventbus.Event;
@@ -1,4 +1,4 @@
package com.dark98.santoku.events; package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event; import ru.ytkab0bp.eventbus.Event;
@@ -1,4 +1,4 @@
package com.dark98.santoku.events; package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event; import ru.ytkab0bp.eventbus.Event;
@@ -0,0 +1,6 @@
package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event;
@Event
public class NeedDismissAIGeneratorMenu {}
@@ -1,4 +1,4 @@
package com.dark98.santoku.events; package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event; import ru.ytkab0bp.eventbus.Event;
@@ -1,4 +1,4 @@
package com.dark98.santoku.events; package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event; import ru.ytkab0bp.eventbus.Event;
@@ -1,10 +1,10 @@
package com.dark98.santoku.events; package ru.ytkab0bp.slicebeam.events;
import android.view.View; import android.view.View;
import ru.ytkab0bp.eventbus.Event; import ru.ytkab0bp.eventbus.Event;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.view.SnackbarsLayout; import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
@Event @Event
public class NeedSnackbarEvent { public class NeedSnackbarEvent {
@@ -25,12 +25,12 @@ public class NeedSnackbarEvent {
} }
public NeedSnackbarEvent(int title, Object... args) { public NeedSnackbarEvent(int title, Object... args) {
this.title = Santoku.INSTANCE.getString(title, args); this.title = SliceBeam.INSTANCE.getString(title, args);
} }
public NeedSnackbarEvent(SnackbarsLayout.Type type, int title, Object... args) { public NeedSnackbarEvent(SnackbarsLayout.Type type, int title, Object... args) {
this.type = type; this.type = type;
this.title = Santoku.INSTANCE.getString(title, args); this.title = SliceBeam.INSTANCE.getString(title, args);
} }
public NeedSnackbarEvent tag(String tag) { public NeedSnackbarEvent tag(String tag) {
@@ -39,7 +39,7 @@ public class NeedSnackbarEvent {
} }
public NeedSnackbarEvent button(int title, View.OnClickListener click) { public NeedSnackbarEvent button(int title, View.OnClickListener click) {
this.buttonTitle = Santoku.INSTANCE.getString(title); this.buttonTitle = SliceBeam.INSTANCE.getString(title);
this.buttonClick = click; this.buttonClick = click;
return this; return this;
} }
@@ -1,4 +1,4 @@
package com.dark98.santoku.events; package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event; import ru.ytkab0bp.eventbus.Event;
@@ -1,4 +1,4 @@
package com.dark98.santoku.events; package ru.ytkab0bp.slicebeam.events;
import ru.ytkab0bp.eventbus.Event; import ru.ytkab0bp.eventbus.Event;
@@ -1,4 +1,4 @@
package com.dark98.santoku.events; package ru.ytkab0bp.slicebeam.events;
public class SlicingProgressEvent { public class SlicingProgressEvent {
public final int progress; public final int progress;
@@ -1,4 +1,4 @@
package com.dark98.santoku.fragment; package ru.ytkab0bp.slicebeam.fragment;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
@@ -32,37 +32,37 @@ import com.google.android.material.navigation.NavigationBarView;
import java.io.File; import java.io.File;
import ru.ytkab0bp.eventbus.EventHandler; import ru.ytkab0bp.eventbus.EventHandler;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.components.BeamAlertDialogBuilder; import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import com.dark98.santoku.components.SliceProgressBottomSheet; import ru.ytkab0bp.slicebeam.components.SliceProgressBottomSheet;
import com.dark98.santoku.components.UnfoldMenu; import ru.ytkab0bp.slicebeam.components.UnfoldMenu;
import com.dark98.santoku.components.bed_menu.BedMenu; import ru.ytkab0bp.slicebeam.components.bed_menu.BedMenu;
import com.dark98.santoku.components.bed_menu.CameraMenu; import ru.ytkab0bp.slicebeam.components.bed_menu.CameraMenu;
import com.dark98.santoku.components.bed_menu.FileMenu; import ru.ytkab0bp.slicebeam.components.bed_menu.FileMenu;
import com.dark98.santoku.components.bed_menu.OrientationMenu; import ru.ytkab0bp.slicebeam.components.bed_menu.OrientationMenu;
import com.dark98.santoku.components.bed_menu.SliceMenu; import ru.ytkab0bp.slicebeam.components.bed_menu.SliceMenu;
import com.dark98.santoku.components.bed_menu.TransformMenu; import ru.ytkab0bp.slicebeam.components.bed_menu.TransformMenu;
import com.dark98.santoku.config.ConfigObject; import ru.ytkab0bp.slicebeam.config.ConfigObject;
import com.dark98.santoku.events.FlattenModeResetEvent; import ru.ytkab0bp.slicebeam.events.FlattenModeResetEvent;
import com.dark98.santoku.events.NeedDismissSnackbarEvent; import ru.ytkab0bp.slicebeam.events.NeedDismissSnackbarEvent;
import com.dark98.santoku.events.NeedSnackbarEvent; import ru.ytkab0bp.slicebeam.events.NeedSnackbarEvent;
import com.dark98.santoku.events.SlicingProgressEvent; import ru.ytkab0bp.slicebeam.events.SlicingProgressEvent;
import com.dark98.santoku.navigation.Fragment; import ru.ytkab0bp.slicebeam.navigation.Fragment;
import com.dark98.santoku.slic3r.Bed3D; import ru.ytkab0bp.slicebeam.slic3r.Bed3D;
import com.dark98.santoku.slic3r.GCodeProcessorResult; import ru.ytkab0bp.slicebeam.slic3r.GCodeProcessorResult;
import com.dark98.santoku.slic3r.GCodeThumbnailer; import ru.ytkab0bp.slicebeam.slic3r.GCodeThumbnailer;
import com.dark98.santoku.slic3r.Model; import ru.ytkab0bp.slicebeam.slic3r.Model;
import com.dark98.santoku.slic3r.Slic3rRuntimeError; import ru.ytkab0bp.slicebeam.slic3r.Slic3rRuntimeError;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.Vec3d; import ru.ytkab0bp.slicebeam.utils.Vec3d;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.view.BedSwipeDownLayout; import ru.ytkab0bp.slicebeam.view.BedSwipeDownLayout;
import com.dark98.santoku.view.DividerView; import ru.ytkab0bp.slicebeam.view.DividerView;
import com.dark98.santoku.view.GLView; import ru.ytkab0bp.slicebeam.view.GLView;
import com.dark98.santoku.view.SnackbarsLayout; import ru.ytkab0bp.slicebeam.view.SnackbarsLayout;
import com.dark98.santoku.view.ThemeBottomNavigationView; import ru.ytkab0bp.slicebeam.view.ThemeBottomNavigationView;
import com.dark98.santoku.view.ThemeRailNavigationView; import ru.ytkab0bp.slicebeam.view.ThemeRailNavigationView;
public class BedFragment extends Fragment { public class BedFragment extends Fragment {
private final static boolean DEBUG_VIEWER = false; private final static boolean DEBUG_VIEWER = false;
@@ -129,14 +129,14 @@ public class BedFragment extends Fragment {
} }
public static File getTempGCodePath() { public static File getTempGCodePath() {
return tempExportingFile != null ? tempExportingFile : new File(Santoku.INSTANCE.getCacheDir(), "temp.gcode"); return tempExportingFile != null ? tempExportingFile : new File(SliceBeam.INSTANCE.getCacheDir(), "temp.gcode");
} }
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
Santoku.EVENT_BUS.registerListener(this); SliceBeam.EVENT_BUS.registerListener(this);
} }
@EventHandler(runOnMainThread = true) @EventHandler(runOnMainThread = true)
@@ -210,7 +210,7 @@ public class BedFragment extends Fragment {
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
Santoku.EVENT_BUS.unregisterListener(this); SliceBeam.EVENT_BUS.unregisterListener(this);
for (int i = 0; i < menuMap.size(); i++) { for (int i = 0; i < menuMap.size(); i++) {
menuMap.valueAt(i).onViewDestroyed(); menuMap.valueAt(i).onViewDestroyed();
@@ -232,7 +232,7 @@ public class BedFragment extends Fragment {
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
glView.onResume(); glView.onResume();
ConfigObject cfg = Santoku.CONFIG.findPrinter(Santoku.CONFIG.presets.get("printer")); ConfigObject cfg = SliceBeam.CONFIG.findPrinter(SliceBeam.CONFIG.presets.get("printer"));
boolean enable = cfg != null && cfg.get("host_type") != null && !TextUtils.isEmpty(cfg.get("print_host")) && panelWebView != null; boolean enable = cfg != null && cfg.get("host_type") != null && !TextUtils.isEmpty(cfg.get("print_host")) && panelWebView != null;
swipeDownLayout.setEnableTop(enable); swipeDownLayout.setEnableTop(enable);
if (enable) { if (enable) {
@@ -388,7 +388,7 @@ public class BedFragment extends Fragment {
.show(); .show();
} else { } else {
tempExportingFile = null; tempExportingFile = null;
File cfg = Santoku.getCurrentConfigFile(); File cfg = SliceBeam.getCurrentConfigFile();
File gcode = getTempGCodePath(); File gcode = getTempGCodePath();
if (!DEBUG_VIEWER) { if (!DEBUG_VIEWER) {
@@ -399,12 +399,12 @@ public class BedFragment extends Fragment {
Process.setThreadPriority(-20); Process.setThreadPriority(-20);
try { try {
Santoku.genCurrentConfig(); SliceBeam.genCurrentConfig();
} catch (Exception e) { } catch (Exception e) {
Log.e("BedFragment", "Failed to write config", e); Log.e("BedFragment", "Failed to write config", e);
ViewUtils.postOnMainThread(()->{ ViewUtils.postOnMainThread(()->{
Santoku.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, "")); SliceBeam.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, ""));
new BeamAlertDialogBuilder(ctx) new BeamAlertDialogBuilder(ctx)
.setTitle(R.string.SliceFailed) .setTitle(R.string.SliceFailed)
.setMessage(e.getMessage()) .setMessage(e.getMessage())
@@ -414,12 +414,12 @@ public class BedFragment extends Fragment {
} }
if (!DEBUG_VIEWER) { if (!DEBUG_VIEWER) {
gCodeResult = glView.getRenderer().getModel().slice(cfg.getAbsolutePath(), gcode.getAbsolutePath(), (progress, text) -> Santoku.EVENT_BUS.fireEvent(new SlicingProgressEvent(progress, text))); gCodeResult = glView.getRenderer().getModel().slice(cfg.getAbsolutePath(), gcode.getAbsolutePath(), (progress, text) -> SliceBeam.EVENT_BUS.fireEvent(new SlicingProgressEvent(progress, text)));
GCodeThumbnailer.addThumbnailsToGcode(gcode, Santoku.buildCurrentConfigObject(), glView); GCodeThumbnailer.addThumbnailsToGcode(gcode, SliceBeam.buildCurrentConfigObject(), glView);
Santoku.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, "")); SliceBeam.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, ""));
} else { } else {
gCodeResult = new GCodeProcessorResult(gcode); gCodeResult = new GCodeProcessorResult(gcode);
GCodeThumbnailer.addThumbnailsToGcode(gcode, Santoku.buildCurrentConfigObject(), glView); GCodeThumbnailer.addThumbnailsToGcode(gcode, SliceBeam.buildCurrentConfigObject(), glView);
} }
ViewUtils.postOnMainThread(()-> { ViewUtils.postOnMainThread(()-> {
glView.queueEvent(()->{ glView.queueEvent(()->{
@@ -438,7 +438,7 @@ public class BedFragment extends Fragment {
} catch (Exception e) { } catch (Exception e) {
Log.e("BedFragment", "Slice failed", e); Log.e("BedFragment", "Slice failed", e);
ViewUtils.postOnMainThread(()->{ ViewUtils.postOnMainThread(()->{
Santoku.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, "")); SliceBeam.EVENT_BUS.fireEvent(new SlicingProgressEvent(100, ""));
new BeamAlertDialogBuilder(ctx) new BeamAlertDialogBuilder(ctx)
.setTitle(R.string.SliceFailed) .setTitle(R.string.SliceFailed)
.setMessage(e.getMessage()) .setMessage(e.getMessage())
@@ -491,7 +491,7 @@ public class BedFragment extends Fragment {
private void selectMenu(Context ctx, boolean portrait, int slot) { private void selectMenu(Context ctx, boolean portrait, int slot) {
if (glView.getRenderer().resetFlattenMode()) { if (glView.getRenderer().resetFlattenMode()) {
glView.requestRender(); glView.requestRender();
Santoku.EVENT_BUS.fireEvent(new FlattenModeResetEvent()); SliceBeam.EVENT_BUS.fireEvent(new FlattenModeResetEvent());
} }
isAnimatingMenu = true; isAnimatingMenu = true;
@@ -1,18 +1,18 @@
package com.dark98.santoku.fragment; package ru.ytkab0bp.slicebeam.fragment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.config.ConfigObject; import ru.ytkab0bp.slicebeam.config.ConfigObject;
import com.dark98.santoku.recycler.SpaceItem; import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
import com.dark98.santoku.slic3r.PrintConfigDef; import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
import com.dark98.santoku.slic3r.Slic3rLocalization; import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
import com.dark98.santoku.slic3r.Slic3rUtils; import ru.ytkab0bp.slicebeam.slic3r.Slic3rUtils;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
public class FilamentConfigFragment extends ProfileListFragment { public class FilamentConfigFragment extends ProfileListFragment {
private List<ProfileListItem> compatItems; private List<ProfileListItem> compatItems;
@@ -29,18 +29,18 @@ public class FilamentConfigFragment extends ProfileListFragment {
@Override @Override
protected List<ProfileListItem> getItems(boolean filter) { protected List<ProfileListItem> getItems(boolean filter) {
List<ConfigObject> list = Santoku.CONFIG.filamentConfigs; List<ConfigObject> list = SliceBeam.CONFIG.filamentConfigs;
if (filter) { if (filter) {
String printer = Santoku.CONFIG.presets.get("printer"); String printer = SliceBeam.CONFIG.presets.get("printer");
String print = Santoku.CONFIG.presets.get("print"); String print = SliceBeam.CONFIG.presets.get("print");
if (Objects.equals(lastPrinter, printer) && Objects.equals(lastPrint, print) && compatItems != null && lastUid == Santoku.CONFIG_UID) { if (Objects.equals(lastPrinter, printer) && Objects.equals(lastPrint, print) && compatItems != null && lastUid == SliceBeam.CONFIG_UID) {
return compatItems; return compatItems;
} }
List<ConfigObject> nList = new ArrayList<>(list.size()); List<ConfigObject> nList = new ArrayList<>(list.size());
Slic3rUtils.ConfigChecker checker = new Slic3rUtils.ConfigChecker(Santoku.CONFIG.findPrinter(printer).serialize()); Slic3rUtils.ConfigChecker checker = new Slic3rUtils.ConfigChecker(SliceBeam.CONFIG.findPrinter(printer).serialize());
if (Santoku.CONFIG.findPrint(print) != null) { if (SliceBeam.CONFIG.findPrint(print) != null) {
Slic3rUtils.ConfigChecker printChecker = new Slic3rUtils.ConfigChecker(Santoku.CONFIG.findPrint(print).serialize()); Slic3rUtils.ConfigChecker printChecker = new Slic3rUtils.ConfigChecker(SliceBeam.CONFIG.findPrint(print).serialize());
for (ConfigObject obj : list) { for (ConfigObject obj : list) {
if (checker.checkCompatibility(obj.get("compatible_printers_condition")) && printChecker.checkCompatibility(obj.get("compatible_prints_condition"))) { if (checker.checkCompatibility(obj.get("compatible_printers_condition")) && printChecker.checkCompatibility(obj.get("compatible_prints_condition"))) {
nList.add(obj); nList.add(obj);
@@ -51,7 +51,7 @@ public class FilamentConfigFragment extends ProfileListFragment {
checker.release(); checker.release();
lastPrinter = printer; lastPrinter = printer;
lastPrint = print; lastPrint = print;
lastUid = Santoku.CONFIG_UID; lastUid = SliceBeam.CONFIG_UID;
return compatItems = (List) nList; return compatItems = (List) nList;
} }
return (List) list; return (List) list;
@@ -218,14 +218,14 @@ public class FilamentConfigFragment extends ProfileListFragment {
@Override @Override
protected void cloneCurrentProfile() { protected void cloneCurrentProfile() {
ConfigObject obj = new ConfigObject(Santoku.INSTANCE.getString(R.string.SettingsProfileCopy, currentConfig.getTitle())); ConfigObject obj = new ConfigObject(SliceBeam.INSTANCE.getString(R.string.SettingsProfileCopy, currentConfig.getTitle()));
obj.values.putAll(currentConfig.values); obj.values.putAll(currentConfig.values);
currentConfig = new ConfigObject(obj); currentConfig = new ConfigObject(obj);
Santoku.CONFIG.filamentConfigs.add(obj); SliceBeam.CONFIG.filamentConfigs.add(obj);
Santoku.CONFIG.presets.put("filament", obj.getTitle()); SliceBeam.CONFIG.presets.put("filament", obj.getTitle());
Santoku.saveConfig(); SliceBeam.saveConfig();
Santoku.getCurrentConfigFile().delete(); SliceBeam.getCurrentConfigFile().delete();
currentConfig = new ConfigObject(obj); currentConfig = new ConfigObject(obj);
dropdownView.setTitle(getCurrentConfig().getTitle()); dropdownView.setTitle(getCurrentConfig().getTitle());
@@ -235,7 +235,7 @@ public class FilamentConfigFragment extends ProfileListFragment {
@Override @Override
protected void deleteCurrentProfile() { protected void deleteCurrentProfile() {
compatItems = null; compatItems = null;
Santoku.CONFIG.filamentConfigs.remove(Santoku.CONFIG.findFilament(currentConfig.getTitle())); SliceBeam.CONFIG.filamentConfigs.remove(SliceBeam.CONFIG.findFilament(currentConfig.getTitle()));
selectItem(getItems(true).get(0)); selectItem(getItems(true).get(0));
dropdownView.setTitle(getCurrentConfig().getTitle()); dropdownView.setTitle(getCurrentConfig().getTitle());
@@ -244,25 +244,21 @@ public class FilamentConfigFragment extends ProfileListFragment {
@Override @Override
protected void onApplyConfig(String title) { protected void onApplyConfig(String title) {
compatItems = null; compatItems = null;
ConfigObject obj = Santoku.CONFIG.findFilament(currentConfig.getTitle()); ConfigObject obj = SliceBeam.CONFIG.findFilament(currentConfig.getTitle());
obj.setTitle(title); obj.setTitle(title);
obj.values.putAll(currentConfig.values); obj.values.putAll(currentConfig.values);
currentConfig.setTitle(title); currentConfig.setTitle(title);
Santoku.CONFIG.presets.put("filament", title); SliceBeam.CONFIG.presets.put("filament", title);
Santoku.saveConfig(); SliceBeam.saveConfig();
Santoku.getCurrentConfigFile().delete(); SliceBeam.getCurrentConfigFile().delete();
dropdownView.setTitle(title); dropdownView.setTitle(title);
} }
@Override @Override
protected void onResetConfig() { protected void onResetConfig() {
ConfigObject base = Santoku.CONFIG.findFilament(Santoku.CONFIG.presets.get("filament")); currentConfig = new ConfigObject(SliceBeam.CONFIG.findFilament(SliceBeam.CONFIG.presets.get("filament")));
if (base == null && Santoku.CONFIG.filamentConfigs != null && !Santoku.CONFIG.filamentConfigs.isEmpty()) {
base = Santoku.CONFIG.filamentConfigs.get(0);
}
currentConfig = base != null ? new ConfigObject(base) : ConfigObject.createCustomFilamentProfile();
} }
@Override @Override
@@ -278,7 +274,7 @@ public class FilamentConfigFragment extends ProfileListFragment {
@Override @Override
protected void selectItem(ProfileListItem item) { protected void selectItem(ProfileListItem item) {
currentConfig = new ConfigObject((ConfigObject) item); currentConfig = new ConfigObject((ConfigObject) item);
Santoku.CONFIG.presets.put("filament", item.getTitle()); SliceBeam.CONFIG.presets.put("filament", item.getTitle());
Santoku.saveConfig(); SliceBeam.saveConfig();
} }
} }
@@ -1,17 +1,17 @@
package com.dark98.santoku.fragment; package ru.ytkab0bp.slicebeam.fragment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.config.ConfigObject; import ru.ytkab0bp.slicebeam.config.ConfigObject;
import com.dark98.santoku.recycler.SpaceItem; import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
import com.dark98.santoku.slic3r.PrintConfigDef; import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
import com.dark98.santoku.slic3r.Slic3rUtils; import ru.ytkab0bp.slicebeam.slic3r.Slic3rUtils;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
public class PrintConfigFragment extends ProfileListFragment { public class PrintConfigFragment extends ProfileListFragment {
private List<ProfileListItem> compatItems; private List<ProfileListItem> compatItems;
@@ -28,15 +28,15 @@ public class PrintConfigFragment extends ProfileListFragment {
@Override @Override
protected List<ProfileListItem> getItems(boolean filter) { protected List<ProfileListItem> getItems(boolean filter) {
List<ConfigObject> list = Santoku.CONFIG.printConfigs; List<ConfigObject> list = SliceBeam.CONFIG.printConfigs;
if (filter) { if (filter) {
String printer = Santoku.CONFIG.presets.get("printer"); String printer = SliceBeam.CONFIG.presets.get("printer");
if (Objects.equals(lastPrinter, printer) && compatItems != null && lastUid == Santoku.CONFIG_UID) { if (Objects.equals(lastPrinter, printer) && compatItems != null && lastUid == SliceBeam.CONFIG_UID) {
return compatItems; return compatItems;
} }
List<ConfigObject> nList = new ArrayList<>(list.size()); List<ConfigObject> nList = new ArrayList<>(list.size());
Slic3rUtils.ConfigChecker checker = new Slic3rUtils.ConfigChecker(Santoku.CONFIG.findPrinter(printer).serialize()); Slic3rUtils.ConfigChecker checker = new Slic3rUtils.ConfigChecker(SliceBeam.CONFIG.findPrinter(printer).serialize());
for (ConfigObject obj : list) { for (ConfigObject obj : list) {
if (checker.checkCompatibility(obj.get("compatible_printers_condition"))) { if (checker.checkCompatibility(obj.get("compatible_printers_condition"))) {
nList.add(obj); nList.add(obj);
@@ -44,7 +44,7 @@ public class PrintConfigFragment extends ProfileListFragment {
} }
checker.release(); checker.release();
lastPrinter = printer; lastPrinter = printer;
lastUid = Santoku.CONFIG_UID; lastUid = SliceBeam.CONFIG_UID;
return compatItems = (List) nList; return compatItems = (List) nList;
} }
return (List) list; return (List) list;
@@ -354,14 +354,14 @@ public class PrintConfigFragment extends ProfileListFragment {
@Override @Override
protected void cloneCurrentProfile() { protected void cloneCurrentProfile() {
ConfigObject obj = new ConfigObject(Santoku.INSTANCE.getString(R.string.SettingsProfileCopy, currentConfig.getTitle())); ConfigObject obj = new ConfigObject(SliceBeam.INSTANCE.getString(R.string.SettingsProfileCopy, currentConfig.getTitle()));
obj.values.putAll(currentConfig.values); obj.values.putAll(currentConfig.values);
currentConfig = new ConfigObject(obj); currentConfig = new ConfigObject(obj);
Santoku.CONFIG.printConfigs.add(obj); SliceBeam.CONFIG.printConfigs.add(obj);
Santoku.CONFIG.presets.put("print", obj.getTitle()); SliceBeam.CONFIG.presets.put("print", obj.getTitle());
Santoku.saveConfig(); SliceBeam.saveConfig();
Santoku.getCurrentConfigFile().delete(); SliceBeam.getCurrentConfigFile().delete();
currentConfig = new ConfigObject(obj); currentConfig = new ConfigObject(obj);
dropdownView.setTitle(getCurrentConfig().getTitle()); dropdownView.setTitle(getCurrentConfig().getTitle());
@@ -371,7 +371,7 @@ public class PrintConfigFragment extends ProfileListFragment {
@Override @Override
protected void deleteCurrentProfile() { protected void deleteCurrentProfile() {
compatItems = null; compatItems = null;
Santoku.CONFIG.printConfigs.remove(Santoku.CONFIG.findPrint(currentConfig.getTitle())); SliceBeam.CONFIG.printConfigs.remove(SliceBeam.CONFIG.findPrint(currentConfig.getTitle()));
selectItem(getItems(true).get(0)); selectItem(getItems(true).get(0));
dropdownView.setTitle(getCurrentConfig().getTitle()); dropdownView.setTitle(getCurrentConfig().getTitle());
@@ -380,28 +380,28 @@ public class PrintConfigFragment extends ProfileListFragment {
@Override @Override
protected void onApplyConfig(String title) { protected void onApplyConfig(String title) {
compatItems = null; compatItems = null;
ConfigObject obj = Santoku.CONFIG.findPrint(currentConfig.getTitle()); ConfigObject obj = SliceBeam.CONFIG.findPrint(currentConfig.getTitle());
obj.setTitle(title); obj.setTitle(title);
obj.values.putAll(currentConfig.values); obj.values.putAll(currentConfig.values);
currentConfig.setTitle(title); currentConfig.setTitle(title);
Santoku.CONFIG.presets.put("print", title); SliceBeam.CONFIG.presets.put("print", title);
Santoku.saveConfig(); SliceBeam.saveConfig();
Santoku.getCurrentConfigFile().delete(); SliceBeam.getCurrentConfigFile().delete();
dropdownView.setTitle(title); dropdownView.setTitle(title);
} }
@Override @Override
protected void onResetConfig() { protected void onResetConfig() {
ConfigObject print = Santoku.CONFIG.findPrint(Santoku.CONFIG.presets.get("print")); ConfigObject print = SliceBeam.CONFIG.findPrint(SliceBeam.CONFIG.presets.get("print"));
if (print != null) { if (print != null) {
currentConfig = new ConfigObject(print); currentConfig = new ConfigObject(print);
} else { } else {
currentConfig = new ConfigObject(Santoku.INSTANCE.getString(R.string.IntroCustomProfileName)); currentConfig = new ConfigObject(SliceBeam.INSTANCE.getString(R.string.IntroCustomProfileName));
Santoku.CONFIG.printConfigs.add(new ConfigObject(currentConfig)); SliceBeam.CONFIG.printConfigs.add(new ConfigObject(currentConfig));
Santoku.saveConfig(); SliceBeam.saveConfig();
Santoku.getCurrentConfigFile().delete(); SliceBeam.getCurrentConfigFile().delete();
} }
} }
@@ -418,7 +418,7 @@ public class PrintConfigFragment extends ProfileListFragment {
@Override @Override
protected void selectItem(ProfileListItem item) { protected void selectItem(ProfileListItem item) {
currentConfig = new ConfigObject((ConfigObject) item); currentConfig = new ConfigObject((ConfigObject) item);
Santoku.CONFIG.presets.put("print", item.getTitle()); SliceBeam.CONFIG.presets.put("print", item.getTitle());
Santoku.saveConfig(); SliceBeam.saveConfig();
} }
} }
@@ -1,17 +1,16 @@
package com.dark98.santoku.fragment; package ru.ytkab0bp.slicebeam.fragment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.config.ConfigObject; import ru.ytkab0bp.slicebeam.config.ConfigObject;
import com.dark98.santoku.recycler.SpaceItem; import ru.ytkab0bp.slicebeam.recycler.SpaceItem;
import com.dark98.santoku.slic3r.PrintConfigDef; import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
import com.dark98.santoku.slic3r.ConfigOptionDef; import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
import com.dark98.santoku.slic3r.Slic3rLocalization; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.utils.ViewUtils;
public class PrinterConfigFragment extends ProfileListFragment { public class PrinterConfigFragment extends ProfileListFragment {
private ConfigObject currentConfig; private ConfigObject currentConfig;
@@ -24,7 +23,7 @@ public class PrinterConfigFragment extends ProfileListFragment {
@Override @Override
protected List<ProfileListItem> getItems(boolean filter) { protected List<ProfileListItem> getItems(boolean filter) {
return (List) Santoku.CONFIG.printerConfigs; return (List) SliceBeam.CONFIG.printerConfigs;
} }
@Override @Override
@@ -165,11 +164,6 @@ public class PrinterConfigFragment extends ProfileListFragment {
)); ));
} }
list.addAll(Arrays.asList( list.addAll(Arrays.asList(
new OptionElement(R.drawable.settings_outline_28, "Advanced"),
new OptionElement(new SubHeader("Auto arrange")),
new OptionElement(def.options.get("auto_arrange_bed_clearance")),
new OptionElement(def.options.get("auto_arrange_rotate")),
new OptionElement(R.drawable.note_pen_outline_96, "Notes"), new OptionElement(R.drawable.note_pen_outline_96, "Notes"),
new OptionElement(new SubHeader("Notes")), new OptionElement(new SubHeader("Notes")),
new OptionElement(def.options.get("printer_notes")), new OptionElement(def.options.get("printer_notes")),
@@ -181,56 +175,19 @@ public class PrinterConfigFragment extends ProfileListFragment {
new OptionElement(def.options.get("printhost_apikey")) new OptionElement(def.options.get("printhost_apikey"))
)); ));
String hostType = null;
if (diffObject != null && diffObject.has("host_type")) {
hostType = diffObject.get("host_type");
}
if (hostType == null) {
hostType = currentConfig.get("host_type");
}
if ("elegoolink".equalsIgnoreCase(hostType)) {
list.addAll(Arrays.asList(
new OptionElement(new SubHeader("ElegooLink")),
new OptionElement(def.options.get("elegoolink_timelapse")),
new OptionElement(def.options.get("elegoolink_bed_leveling")),
new OptionElement(def.options.get("elegoolink_bed_type"))
));
}
if ("moonraker".equalsIgnoreCase(hostType) || "klipper".equalsIgnoreCase(hostType)) {
int insertIndex = list.size();
for (int i = 0; i < list.size(); i++) {
OptionElement el = list.get(i);
if (el != null && el.simpleItem instanceof SubHeader) {
String title = ((SubHeader) el.simpleItem).title;
if ("Advanced".equals(title)) {
insertIndex = i;
break;
}
}
}
list.addAll(insertIndex, Arrays.asList(
new OptionElement(new SubHeader("Adaptive bed mesh")),
new OptionElement(def.options.get("bed_mesh_probe_distance")),
new OptionElement(def.options.get("bed_mesh_limit_min")),
new OptionElement(def.options.get("bed_mesh_limit_max")),
new OptionElement(def.options.get("adaptive_bed_mesh_margin")),
new OptionElement(new SpaceItem(0, ViewUtils.dp(4)))
));
}
return list; return list;
} }
@Override @Override
protected void cloneCurrentProfile() { protected void cloneCurrentProfile() {
ConfigObject obj = new ConfigObject(Santoku.INSTANCE.getString(R.string.SettingsProfileCopy, currentConfig.getTitle())); ConfigObject obj = new ConfigObject(SliceBeam.INSTANCE.getString(R.string.SettingsProfileCopy, currentConfig.getTitle()));
obj.values.putAll(currentConfig.values); obj.values.putAll(currentConfig.values);
currentConfig = new ConfigObject(obj); currentConfig = new ConfigObject(obj);
Santoku.CONFIG.printerConfigs.add(obj); SliceBeam.CONFIG.printerConfigs.add(obj);
Santoku.CONFIG.presets.put("printer", obj.getTitle()); SliceBeam.CONFIG.presets.put("printer", obj.getTitle());
Santoku.saveConfig(); SliceBeam.saveConfig();
Santoku.getCurrentConfigFile().delete(); SliceBeam.getCurrentConfigFile().delete();
currentConfig = new ConfigObject(obj); currentConfig = new ConfigObject(obj);
dropdownView.setTitle(getCurrentConfig().getTitle()); dropdownView.setTitle(getCurrentConfig().getTitle());
@@ -238,28 +195,28 @@ public class PrinterConfigFragment extends ProfileListFragment {
@Override @Override
protected void deleteCurrentProfile() { protected void deleteCurrentProfile() {
Santoku.CONFIG.printerConfigs.remove(Santoku.CONFIG.findPrinter(currentConfig.getTitle())); SliceBeam.CONFIG.printerConfigs.remove(SliceBeam.CONFIG.findPrinter(currentConfig.getTitle()));
selectItem(getItems(true).get(0)); selectItem(getItems(true).get(0));
dropdownView.setTitle(getCurrentConfig().getTitle()); dropdownView.setTitle(getCurrentConfig().getTitle());
} }
@Override @Override
protected void onApplyConfig(String title) { protected void onApplyConfig(String title) {
ConfigObject obj = Santoku.CONFIG.findPrinter(currentConfig.getTitle()); ConfigObject obj = SliceBeam.CONFIG.findPrinter(currentConfig.getTitle());
obj.setTitle(title); obj.setTitle(title);
obj.values.putAll(currentConfig.values); obj.values.putAll(currentConfig.values);
currentConfig.setTitle(title); currentConfig.setTitle(title);
Santoku.CONFIG.presets.put("printer", title); SliceBeam.CONFIG.presets.put("printer", title);
Santoku.saveConfig(); SliceBeam.saveConfig();
Santoku.getCurrentConfigFile().delete(); SliceBeam.getCurrentConfigFile().delete();
dropdownView.setTitle(title); dropdownView.setTitle(title);
} }
@Override @Override
protected void onResetConfig() { protected void onResetConfig() {
currentConfig = new ConfigObject(Santoku.CONFIG.findPrinter(Santoku.CONFIG.presets.get("printer"))); currentConfig = new ConfigObject(SliceBeam.CONFIG.findPrinter(SliceBeam.CONFIG.presets.get("printer")));
} }
@Override @Override
@@ -275,23 +232,9 @@ public class PrinterConfigFragment extends ProfileListFragment {
@Override @Override
protected void selectItem(ProfileListItem item) { protected void selectItem(ProfileListItem item) {
currentConfig = new ConfigObject((ConfigObject) item); currentConfig = new ConfigObject((ConfigObject) item);
Santoku.CONFIG.presets.put("printer", item.getTitle()); SliceBeam.CONFIG.presets.put("printer", item.getTitle());
// TODO: Reset print/filament profiles, maybe physical profiles? // TODO: Reset print/filament profiles, maybe physical profiles?
Santoku.saveConfig(); SliceBeam.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 com.dark98.santoku.fragment; package ru.ytkab0bp.slicebeam.fragment;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
@@ -49,30 +49,30 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.cloud.CloudAPI; import ru.ytkab0bp.slicebeam.cloud.CloudAPI;
import com.dark98.santoku.cloud.CloudController; import ru.ytkab0bp.slicebeam.cloud.CloudController;
import com.dark98.santoku.components.BeamAlertDialogBuilder; import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import com.dark98.santoku.components.BeamColorPickerPopUp; import ru.ytkab0bp.slicebeam.components.BeamColorPickerPopUp;
import com.dark98.santoku.config.ConfigObject; import ru.ytkab0bp.slicebeam.config.ConfigObject;
import com.dark98.santoku.navigation.Fragment; import ru.ytkab0bp.slicebeam.navigation.Fragment;
import com.dark98.santoku.recycler.CubicBezierItemAnimator; import ru.ytkab0bp.slicebeam.recycler.CubicBezierItemAnimator;
import com.dark98.santoku.recycler.PreferenceItem; import ru.ytkab0bp.slicebeam.recycler.PreferenceItem;
import com.dark98.santoku.recycler.PreferenceSwitchItem; import ru.ytkab0bp.slicebeam.recycler.PreferenceSwitchItem;
import com.dark98.santoku.recycler.SimpleRecyclerItem; import ru.ytkab0bp.slicebeam.recycler.SimpleRecyclerItem;
import com.dark98.santoku.slic3r.ConfigOptionDef; import ru.ytkab0bp.slicebeam.slic3r.ConfigOptionDef;
import com.dark98.santoku.slic3r.PrintConfigDef; import ru.ytkab0bp.slicebeam.slic3r.PrintConfigDef;
import com.dark98.santoku.slic3r.Slic3rConfigWrapper; import ru.ytkab0bp.slicebeam.slic3r.Slic3rConfigWrapper;
import com.dark98.santoku.slic3r.Slic3rLocalization; import ru.ytkab0bp.slicebeam.slic3r.Slic3rLocalization;
import com.dark98.santoku.theme.IThemeView; import ru.ytkab0bp.slicebeam.theme.IThemeView;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.Prefs; import ru.ytkab0bp.slicebeam.utils.Prefs;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.view.BeamButton; import ru.ytkab0bp.slicebeam.view.BeamButton;
import com.dark98.santoku.view.DividerView; import ru.ytkab0bp.slicebeam.view.DividerView;
import com.dark98.santoku.view.FadeRecyclerView; import ru.ytkab0bp.slicebeam.view.FadeRecyclerView;
import com.dark98.santoku.view.ProfileDropdownView; import ru.ytkab0bp.slicebeam.view.ProfileDropdownView;
public abstract class ProfileListFragment extends Fragment { public abstract class ProfileListFragment extends Fragment {
public final static int SPECIAL_TYPE_CLOUD_HEADER = 0; public final static int SPECIAL_TYPE_CLOUD_HEADER = 0;
@@ -507,19 +507,18 @@ public abstract class ProfileListFragment extends Fragment {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
Santoku.EVENT_BUS.registerListener(this); SliceBeam.EVENT_BUS.registerListener(this);
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
Santoku.EVENT_BUS.unregisterListener(this); SliceBeam.EVENT_BUS.unregisterListener(this);
unfolded.clear(); unfolded.clear();
} }
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
protected void setConfigItems(List<OptionElement> items) { protected void setConfigItems(List<OptionElement> items) {
categoryElements.clear();
List<OptionWrapper> list = new ArrayList<>(); List<OptionWrapper> list = new ArrayList<>();
int j = 0; int j = 0;
for (int i = 0; i < items.size(); i++) { for (int i = 0; i < items.size(); i++) {
@@ -540,21 +539,7 @@ public abstract class ProfileListFragment extends Fragment {
categoryElements.get(j - 1).add(w); categoryElements.get(j - 1).add(w);
} }
} }
List<OptionWrapper> expanded = new ArrayList<>(); currentList = list;
int categoryIndex = 0;
for (OptionWrapper w : list) {
expanded.add(w);
if (w.categoryIndex == categoryIndex && unfolded.get(categoryIndex)) {
List<OptionWrapper> extra = categoryElements.get(categoryIndex);
if (extra != null) {
expanded.addAll(extra);
}
}
if (w.categoryIndex == categoryIndex) {
categoryIndex++;
}
}
currentList = expanded;
recyclerView.getAdapter().notifyDataSetChanged(); recyclerView.getAdapter().notifyDataSetChanged();
} }
@@ -691,8 +676,8 @@ public abstract class ProfileListFragment extends Fragment {
String[] labels; String[] labels;
String[] values; String[] values;
if (Objects.equals("host_type", def.key)) { if (Objects.equals("host_type", def.key)) {
labels = new String[]{"OctoPrint", "Klipper (Moonraker)", "ElegooLink"}; labels = new String[]{"OctoPrint"};
values = new String[]{"octoprint", "moonraker", "elegoolink"}; values = new String[]{"octoprint"};
} else { } else {
labels = new String[def.enumLabels.length]; labels = new String[def.enumLabels.length];
values = def.enumValues; values = def.enumValues;
@@ -1,4 +1,4 @@
package com.dark98.santoku.fragment; package ru.ytkab0bp.slicebeam.fragment;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
@@ -20,18 +20,19 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import ru.ytkab0bp.eventbus.EventHandler; import ru.ytkab0bp.eventbus.EventHandler;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.BeamServerData;
import com.dark98.santoku.SetupActivity; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SetupActivity;
import com.dark98.santoku.components.BeamAlertDialogBuilder; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.components.BeamColorPickerPopUp; import ru.ytkab0bp.slicebeam.components.BeamAlertDialogBuilder;
import com.dark98.santoku.config.ConfigObject; import ru.ytkab0bp.slicebeam.components.BeamColorPickerPopUp;
import com.dark98.santoku.events.CloudUserInfoUpdatedEvent; import ru.ytkab0bp.slicebeam.config.ConfigObject;
import com.dark98.santoku.cloud.CloudController; import ru.ytkab0bp.slicebeam.events.BeamServerDataUpdatedEvent;
import com.dark98.santoku.recycler.PreferenceItem; import ru.ytkab0bp.slicebeam.events.CloudUserInfoUpdatedEvent;
import com.dark98.santoku.theme.BeamTheme; import ru.ytkab0bp.slicebeam.recycler.PreferenceItem;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.BeamTheme;
import com.dark98.santoku.utils.Prefs; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import ru.ytkab0bp.slicebeam.utils.Prefs;
public class SettingsFragment extends ProfileListFragment { public class SettingsFragment extends ProfileListFragment {
@@ -45,7 +46,7 @@ public class SettingsFragment extends ProfileListFragment {
@Override @Override
protected List<OptionElement> getConfigItems() { protected List<OptionElement> getConfigItems() {
return Arrays.asList( return Arrays.asList(
CloudController.hasAccountFeatures() ? new OptionElement(SPECIAL_TYPE_CLOUD_HEADER).setOnClick(() -> { BeamServerData.isCloudAvailable() ? new OptionElement(SPECIAL_TYPE_CLOUD_HEADER).setOnClick(() -> {
Activity act = (Activity) getContext(); Activity act = (Activity) getContext();
act.startActivity(new Intent(act, SetupActivity.class).putExtra(SetupActivity.EXTRA_CLOUD_PROFILE, true)); act.startActivity(new Intent(act, SetupActivity.class).putExtra(SetupActivity.EXTRA_CLOUD_PROFILE, true));
}) : null, }) : null,
@@ -111,7 +112,7 @@ public class SettingsFragment extends ProfileListFragment {
BeamTheme.LIGHT.colors.put(android.R.attr.colorAccent, Prefs.getAccentColor()); BeamTheme.LIGHT.colors.put(android.R.attr.colorAccent, Prefs.getAccentColor());
BeamTheme.DARK.colors.put(android.R.attr.colorAccent, Prefs.getAccentColor()); BeamTheme.DARK.colors.put(android.R.attr.colorAccent, Prefs.getAccentColor());
ThemesRepo.invalidate((Activity) getContext()); ThemesRepo.invalidate((Activity) getContext());
recyclerView.getAdapter().notifyItemChanged(2 - (CloudController.hasAccountFeatures() ? 0 : 1)); recyclerView.getAdapter().notifyItemChanged(2 - (BeamServerData.isCloudAvailable() ? 0 : 1));
} }
}) })
.setNegativeButtonText(getContext().getString(R.string.SettingsInterfaceColorReset)) .setNegativeButtonText(getContext().getString(R.string.SettingsInterfaceColorReset))
@@ -134,7 +135,7 @@ public class SettingsFragment extends ProfileListFragment {
Prefs.setRenderScale(variants[which]); Prefs.setRenderScale(variants[which]);
dialog.dismiss(); dialog.dismiss();
// I'm too lazy to calculate real position for now // I'm too lazy to calculate real position for now
recyclerView.getAdapter().notifyItemChanged(4 - (CloudController.hasAccountFeatures() ? 0 : 1)); recyclerView.getAdapter().notifyItemChanged(4 - (BeamServerData.isCloudAvailable() ? 0 : 1));
}) })
.show(); .show();
})), })),
@@ -142,6 +143,18 @@ public class SettingsFragment extends ProfileListFragment {
Activity act = (Activity) getContext(); Activity act = (Activity) getContext();
act.startActivity(new Intent(act, SetupActivity.class).putExtra(SetupActivity.EXTRA_ABOUT, true)); act.startActivity(new Intent(act, SetupActivity.class).putExtra(SetupActivity.EXTRA_ABOUT, true));
}), }),
new OptionElement(R.drawable.telegram, getContext().getString(R.string.SettingsTelegram)).setColor(R.attr.telegramColor, false).setOnClick(() -> {
Activity act = (Activity) getContext();
act.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://t.me/ytkab0bp_channel")));
}),
BeamServerData.isBoostyAvailable() ? new OptionElement(R.drawable.boosty, getContext().getString(R.string.SettingsBoosty)).setColor(R.attr.boostyColorTop, true).setOnClick(() -> {
Activity act = (Activity) getContext();
act.startActivity(new Intent(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(() -> { new OptionElement(R.drawable.refresh_outline_28, getContext().getString(R.string.SettingsResetToDefault)).setColor(R.attr.textColorNegative, false).setOnClick(() -> {
Context ctx = getContext(); Context ctx = getContext();
if (ctx instanceof Activity) { if (ctx instanceof Activity) {
@@ -150,8 +163,8 @@ public class SettingsFragment extends ProfileListFragment {
.setTitle(R.string.SettingsResetToDefaultTitle) .setTitle(R.string.SettingsResetToDefaultTitle)
.setMessage(R.string.SettingsResetToDefaultDescription) .setMessage(R.string.SettingsResetToDefaultDescription)
.setPositiveButton(android.R.string.ok, (dialog, which) -> { .setPositiveButton(android.R.string.ok, (dialog, which) -> {
Santoku.getConfigFile().delete(); SliceBeam.getConfigFile().delete();
Santoku.CONFIG = null; SliceBeam.CONFIG = null;
Prefs.getPrefs().edit().clear().apply(); Prefs.getPrefs().edit().clear().apply();
Prefs.setLastCommit(); Prefs.setLastCommit();
act.startActivity(new Intent(act, SetupActivity.class)); act.startActivity(new Intent(act, SetupActivity.class));
@@ -164,9 +177,14 @@ public class SettingsFragment extends ProfileListFragment {
); );
} }
@EventHandler(runOnMainThread = true)
public void onDataUpdated(BeamServerDataUpdatedEvent e) {
setConfigItems(getConfigItems());
}
@EventHandler(runOnMainThread = true) @EventHandler(runOnMainThread = true)
public void onUserInfoUpdated(CloudUserInfoUpdatedEvent e) { public void onUserInfoUpdated(CloudUserInfoUpdatedEvent e) {
if (CloudController.hasAccountFeatures()) { if (BeamServerData.isCloudAvailable()) {
recyclerView.getAdapter().notifyItemChanged(0); recyclerView.getAdapter().notifyItemChanged(0);
} }
} }
@@ -1,14 +1,14 @@
package com.dark98.santoku.navigation; package ru.ytkab0bp.slicebeam.navigation;
import androidx.annotation.DrawableRes; import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.fragment.BedFragment; import ru.ytkab0bp.slicebeam.fragment.BedFragment;
import com.dark98.santoku.fragment.FilamentConfigFragment; import ru.ytkab0bp.slicebeam.fragment.FilamentConfigFragment;
import com.dark98.santoku.fragment.PrintConfigFragment; import ru.ytkab0bp.slicebeam.fragment.PrintConfigFragment;
import com.dark98.santoku.fragment.PrinterConfigFragment; import ru.ytkab0bp.slicebeam.fragment.PrinterConfigFragment;
import com.dark98.santoku.fragment.SettingsFragment; import ru.ytkab0bp.slicebeam.fragment.SettingsFragment;
public abstract class DelegateSlotImpl extends NavigationDelegate { public abstract class DelegateSlotImpl extends NavigationDelegate {
public int getSlotCount() { public int getSlotCount() {
@@ -1,11 +1,11 @@
package com.dark98.santoku.navigation; package ru.ytkab0bp.slicebeam.navigation;
import android.content.Context; import android.content.Context;
import android.view.View; import android.view.View;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
public abstract class Fragment { public abstract class Fragment {
private View mView; private View mView;
@@ -1,4 +1,4 @@
package com.dark98.santoku.navigation; package ru.ytkab0bp.slicebeam.navigation;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
@@ -23,12 +23,12 @@ import androidx.core.content.ContextCompat;
import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.navigation.NavigationBarView; import com.google.android.material.navigation.NavigationBarView;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.view.TextColorImageSpan; import ru.ytkab0bp.slicebeam.view.TextColorImageSpan;
import com.dark98.santoku.view.ThemeBottomNavigationView; import ru.ytkab0bp.slicebeam.view.ThemeBottomNavigationView;
import com.dark98.santoku.view.ThemeRailNavigationView; import ru.ytkab0bp.slicebeam.view.ThemeRailNavigationView;
public class MobileNavigationDelegate extends DelegateSlotImpl { public class MobileNavigationDelegate extends DelegateSlotImpl {
private boolean portrait; private boolean portrait;
@@ -1,6 +1,6 @@
package com.dark98.santoku.navigation; package ru.ytkab0bp.slicebeam.navigation;
import static com.dark98.santoku.utils.DebugUtils.assertTrue; import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
import android.content.Context; import android.content.Context;
import android.util.SparseArray; import android.util.SparseArray;
@@ -14,7 +14,7 @@ import androidx.dynamicanimation.animation.SpringForce;
import java.util.Stack; import java.util.Stack;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
public abstract class NavigationDelegate { public abstract class NavigationDelegate {
protected Context context; protected Context context;
@@ -1,4 +1,4 @@
package com.dark98.santoku.recycler; package ru.ytkab0bp.slicebeam.recycler;
import android.content.Context; import android.content.Context;
import android.util.TypedValue; import android.util.TypedValue;
@@ -8,8 +8,8 @@ import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
public class BigHeaderItem extends SimpleRecyclerItem<TextView> { public class BigHeaderItem extends SimpleRecyclerItem<TextView> {
public String title; public String title;
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.dark98.santoku.recycler; package ru.ytkab0bp.slicebeam.recycler;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
@@ -29,7 +29,7 @@ import androidx.recyclerview.widget.SimpleItemAnimator;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
/** /**
* This implementation of {@link RecyclerView.ItemAnimator} provides basic * This implementation of {@link RecyclerView.ItemAnimator} provides basic
@@ -1,12 +1,12 @@
package com.dark98.santoku.recycler; package ru.ytkab0bp.slicebeam.recycler;
import android.content.Context; import android.content.Context;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.view.DividerView; import ru.ytkab0bp.slicebeam.view.DividerView;
public class DividerItem extends SimpleRecyclerItem<DividerView> { public class DividerItem extends SimpleRecyclerItem<DividerView> {
@Override @Override
@@ -1,4 +1,4 @@
package com.dark98.santoku.recycler; package ru.ytkab0bp.slicebeam.recycler;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
@@ -20,11 +20,11 @@ import androidx.appcompat.widget.AppCompatImageView;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.theme.BeamTheme; import ru.ytkab0bp.slicebeam.theme.BeamTheme;
import com.dark98.santoku.theme.IThemeView; import ru.ytkab0bp.slicebeam.theme.IThemeView;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.PreferenceHolderView> { public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.PreferenceHolderView> {
private Drawable mIcon; private Drawable mIcon;
@@ -75,7 +75,7 @@ public class PreferenceItem extends SimpleRecyclerItem<PreferenceItem.Preference
} }
public PreferenceItem setIcon(int iconRes) { public PreferenceItem setIcon(int iconRes) {
mIcon = ContextCompat.getDrawable(Santoku.INSTANCE, iconRes); mIcon = ContextCompat.getDrawable(SliceBeam.INSTANCE, iconRes);
return this; return this;
} }
@@ -1,4 +1,4 @@
package com.dark98.santoku.recycler; package ru.ytkab0bp.slicebeam.recycler;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
@@ -15,12 +15,12 @@ import android.widget.TextView;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.theme.IThemeView; import ru.ytkab0bp.slicebeam.theme.IThemeView;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.Prefs; import ru.ytkab0bp.slicebeam.utils.Prefs;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.view.BeamSwitch; import ru.ytkab0bp.slicebeam.view.BeamSwitch;
public class PreferenceSwitchItem extends SimpleRecyclerItem<PreferenceSwitchItem.SwitchPreferenceHolderView> { public class PreferenceSwitchItem extends SimpleRecyclerItem<PreferenceSwitchItem.SwitchPreferenceHolderView> {
private Drawable mIcon; private Drawable mIcon;
@@ -64,7 +64,7 @@ public class PreferenceSwitchItem extends SimpleRecyclerItem<PreferenceSwitchIte
} }
public PreferenceSwitchItem setIcon(int iconRes) { public PreferenceSwitchItem setIcon(int iconRes) {
mIcon = ContextCompat.getDrawable(Santoku.INSTANCE, iconRes); mIcon = ContextCompat.getDrawable(SliceBeam.INSTANCE, iconRes);
return this; return this;
} }
@@ -1,4 +1,4 @@
package com.dark98.santoku.recycler; package ru.ytkab0bp.slicebeam.recycler;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -1,4 +1,4 @@
package com.dark98.santoku.recycler; package ru.ytkab0bp.slicebeam.recycler;
import android.content.Context; import android.content.Context;
import android.view.View; import android.view.View;
@@ -1,4 +1,4 @@
package com.dark98.santoku.recycler; package ru.ytkab0bp.slicebeam.recycler;
import android.content.Context; import android.content.Context;
import android.widget.Space; import android.widget.Space;
@@ -1,4 +1,4 @@
package com.dark98.santoku.recycler; package ru.ytkab0bp.slicebeam.recycler;
import android.content.Context; import android.content.Context;
import android.util.TypedValue; import android.util.TypedValue;
@@ -8,8 +8,8 @@ import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
public class TextHintRecyclerItem extends SimpleRecyclerItem<TextView> { public class TextHintRecyclerItem extends SimpleRecyclerItem<TextView> {
public String title; public String title;
@@ -1,9 +1,9 @@
package com.dark98.santoku.render; package ru.ytkab0bp.slicebeam.render;
import androidx.core.math.MathUtils; import androidx.core.math.MathUtils;
import com.dark98.santoku.utils.DoubleMatrix; import ru.ytkab0bp.slicebeam.utils.DoubleMatrix;
import com.dark98.santoku.utils.Vec3d; import ru.ytkab0bp.slicebeam.utils.Vec3d;
public class Camera { public class Camera {
private double[] viewMatrix = new double[16]; private double[] viewMatrix = new double[16];
@@ -1,13 +1,13 @@
package com.dark98.santoku.render; package ru.ytkab0bp.slicebeam.render;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.slic3r.GLModel; import ru.ytkab0bp.slicebeam.slic3r.GLModel;
import com.dark98.santoku.slic3r.GLShaderProgram; import ru.ytkab0bp.slicebeam.slic3r.GLShaderProgram;
import com.dark98.santoku.slic3r.GLShadersManager; import ru.ytkab0bp.slicebeam.slic3r.GLShadersManager;
import com.dark98.santoku.slic3r.Slic3rUtils; import ru.ytkab0bp.slicebeam.slic3r.Slic3rUtils;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.DoubleMatrix; import ru.ytkab0bp.slicebeam.utils.DoubleMatrix;
import com.dark98.santoku.utils.Vec3d; import ru.ytkab0bp.slicebeam.utils.Vec3d;
public class CoordAxes { public class CoordAxes {
public Vec3d origin = new Vec3d(0, 0, 0); public Vec3d origin = new Vec3d(0, 0, 0);
@@ -1,7 +1,7 @@
package com.dark98.santoku.render; package ru.ytkab0bp.slicebeam.render;
import static android.opengl.GLES30.*; import static android.opengl.GLES30.*;
import static com.dark98.santoku.utils.DebugUtils.assertTrue; import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Color; import android.graphics.Color;
@@ -18,24 +18,24 @@ import java.nio.IntBuffer;
import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL10;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.events.ObjectsListChangedEvent; import ru.ytkab0bp.slicebeam.events.ObjectsListChangedEvent;
import com.dark98.santoku.events.SelectedObjectChangedEvent; import ru.ytkab0bp.slicebeam.events.SelectedObjectChangedEvent;
import com.dark98.santoku.slic3r.Bed3D; import ru.ytkab0bp.slicebeam.slic3r.Bed3D;
import com.dark98.santoku.slic3r.GCodeProcessorResult; import ru.ytkab0bp.slicebeam.slic3r.GCodeProcessorResult;
import com.dark98.santoku.slic3r.GCodeViewer; import ru.ytkab0bp.slicebeam.slic3r.GCodeViewer;
import com.dark98.santoku.slic3r.GLModel; import ru.ytkab0bp.slicebeam.slic3r.GLModel;
import com.dark98.santoku.slic3r.GLShaderProgram; import ru.ytkab0bp.slicebeam.slic3r.GLShaderProgram;
import com.dark98.santoku.slic3r.GLShadersManager; import ru.ytkab0bp.slicebeam.slic3r.GLShadersManager;
import com.dark98.santoku.slic3r.Model; import ru.ytkab0bp.slicebeam.slic3r.Model;
import com.dark98.santoku.slic3r.Slic3rUtils; import ru.ytkab0bp.slicebeam.slic3r.Slic3rUtils;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.DoubleMatrix; import ru.ytkab0bp.slicebeam.utils.DoubleMatrix;
import com.dark98.santoku.utils.Prefs; import ru.ytkab0bp.slicebeam.utils.Prefs;
import com.dark98.santoku.utils.Vec3d; import ru.ytkab0bp.slicebeam.utils.Vec3d;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
import com.dark98.santoku.view.GLView; import ru.ytkab0bp.slicebeam.view.GLView;
public class GLRenderer implements GLSurfaceView.Renderer { public class GLRenderer implements GLSurfaceView.Renderer {
private final static float FOV = 60f; private final static float FOV = 60f;
@@ -425,7 +425,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
boolean bottom = Prefs.isOrthoProjectionEnabled() ? camera.getDirForward().z > 0 : camera.getDirToBed().z > 0; boolean bottom = Prefs.isOrthoProjectionEnabled() ? camera.getDirForward().z > 0 : camera.getDirToBed().z > 0;
if (lastConfigUid != Santoku.CONFIG_UID) { if (lastConfigUid != SliceBeam.CONFIG_UID) {
configureBed(); configureBed();
} }
if (bed.isValid() && bedVisible) { if (bed.isValid() && bedVisible) {
@@ -567,14 +567,14 @@ public class GLRenderer implements GLSurfaceView.Renderer {
selX = selY = selZ = 0; selX = selY = selZ = 0;
selRotX = selRotY = selRotZ = 0; selRotX = selRotY = selRotZ = 0;
selScaleX = selScaleY = selScaleZ = 1; selScaleX = selScaleY = selScaleZ = 1;
Santoku.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent()); SliceBeam.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent());
} }
if (model.getObjectsCount() == 0) { if (model.getObjectsCount() == 0) {
model.release(); model.release();
model = null; model = null;
} }
Santoku.EVENT_BUS.fireEvent(new ObjectsListChangedEvent()); SliceBeam.EVENT_BUS.fireEvent(new ObjectsListChangedEvent());
return true; return true;
} }
@@ -640,7 +640,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
flattenPlanes.clear(); flattenPlanes.clear();
selectedObject = -1; selectedObject = -1;
Santoku.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent()); SliceBeam.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent());
return true; return true;
} }
@@ -658,7 +658,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
selRotX = selRotY = selRotZ = 0; selRotX = selRotY = selRotZ = 0;
selScaleX = selScaleY = selScaleZ = 1; selScaleX = selScaleY = selScaleZ = 1;
} }
Santoku.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent()); SliceBeam.EVENT_BUS.fireEvent(new SelectedObjectChangedEvent());
} }
return render; return render;
} }
@@ -793,9 +793,9 @@ public class GLRenderer implements GLSurfaceView.Renderer {
private void configureBed() { private void configureBed() {
try { try {
lastConfigUid = Santoku.CONFIG_UID; lastConfigUid = SliceBeam.CONFIG_UID;
Santoku.genCurrentConfig(); SliceBeam.genCurrentConfig();
bed.configure(Santoku.getCurrentConfigFile()); bed.configure(SliceBeam.getCurrentConfigFile());
} catch (Exception e) { } catch (Exception e) {
Log.e("GLRenderer", "Failed to update config", e); Log.e("GLRenderer", "Failed to update config", e);
} }
@@ -1,17 +1,17 @@
package com.dark98.santoku.slic3r; package ru.ytkab0bp.slicebeam.slic3r;
import static android.opengl.GLES30.*; import static android.opengl.GLES30.*;
import static com.dark98.santoku.utils.DebugUtils.assertTrue; import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
import java.io.File; import java.io.File;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.render.CoordAxes; import ru.ytkab0bp.slicebeam.render.CoordAxes;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
import com.dark98.santoku.utils.DoubleMatrix; import ru.ytkab0bp.slicebeam.utils.DoubleMatrix;
import com.dark98.santoku.utils.Vec3d; import ru.ytkab0bp.slicebeam.utils.Vec3d;
import com.dark98.santoku.utils.ViewUtils; import ru.ytkab0bp.slicebeam.utils.ViewUtils;
public class Bed3D { public class Bed3D {
private final static float GROUND_Z = -0.02f; private final static float GROUND_Z = -0.02f;
@@ -88,6 +88,9 @@ public class Bed3D {
assertTrue(projectionMatrix.length == 16); assertTrue(projectionMatrix.length == 16);
DoubleMatrix.setIdentityM(modelMatrix, 0); DoubleMatrix.setIdentityM(modelMatrix, 0);
if (!likelyDelta) {
DoubleMatrix.translateM(modelMatrix, 0, -getVolumeMin().x * 2, -getVolumeMin().y * 2, -getVolumeMin().z);
}
DoubleMatrix.multiplyMM(outModelMatrix, 0, viewModelMatrix, 0, modelMatrix, 0); DoubleMatrix.multiplyMM(outModelMatrix, 0, viewModelMatrix, 0, modelMatrix, 0);
renderDefaultBed(shadersManager, bottom, outModelMatrix, projectionMatrix); renderDefaultBed(shadersManager, bottom, outModelMatrix, projectionMatrix);
axes.render(shadersManager, viewModelMatrix, projectionMatrix, 0.25f, invZoom); axes.render(shadersManager, viewModelMatrix, projectionMatrix, 0.25f, invZoom);
@@ -1,4 +1,4 @@
package com.dark98.santoku.slic3r; package ru.ytkab0bp.slicebeam.slic3r;
import android.text.TextUtils; import android.text.TextUtils;
@@ -1,4 +1,4 @@
package com.dark98.santoku.slic3r; package ru.ytkab0bp.slicebeam.slic3r;
import java.io.File; import java.io.File;
@@ -1,4 +1,4 @@
package com.dark98.santoku.slic3r; package ru.ytkab0bp.slicebeam.slic3r;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
@@ -23,8 +23,8 @@ import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import com.dark98.santoku.config.ConfigObject; import ru.ytkab0bp.slicebeam.config.ConfigObject;
import com.dark98.santoku.view.GLView; import ru.ytkab0bp.slicebeam.view.GLView;
public final class GCodeThumbnailer { public final class GCodeThumbnailer {
private static final String TAG = "GCodeThumbnailer"; private static final String TAG = "GCodeThumbnailer";
@@ -1,6 +1,6 @@
package com.dark98.santoku.slic3r; package ru.ytkab0bp.slicebeam.slic3r;
import static com.dark98.santoku.utils.DebugUtils.assertTrue; import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
import android.graphics.Color; import android.graphics.Color;
@@ -10,8 +10,8 @@ import androidx.core.util.Pair;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import com.dark98.santoku.R; import ru.ytkab0bp.slicebeam.R;
import com.dark98.santoku.theme.ThemesRepo; import ru.ytkab0bp.slicebeam.theme.ThemesRepo;
public class GCodeViewer { public class GCodeViewer {
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@@ -1,15 +1,15 @@
package com.dark98.santoku.slic3r; package ru.ytkab0bp.slicebeam.slic3r;
import static com.dark98.santoku.utils.DebugUtils.assertTrue; import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
import android.graphics.Color; import android.graphics.Color;
import java.util.ArrayList; import java.util.ArrayList;
import com.dark98.santoku.render.Camera; import ru.ytkab0bp.slicebeam.render.Camera;
import com.dark98.santoku.render.GLRenderer; import ru.ytkab0bp.slicebeam.render.GLRenderer;
import com.dark98.santoku.utils.Prefs; import ru.ytkab0bp.slicebeam.utils.Prefs;
import com.dark98.santoku.utils.Vec3d; import ru.ytkab0bp.slicebeam.utils.Vec3d;
public class GLModel { public class GLModel {
long pointer; long pointer;
@@ -1,6 +1,6 @@
package com.dark98.santoku.slic3r; package ru.ytkab0bp.slicebeam.slic3r;
import static com.dark98.santoku.utils.DebugUtils.assertTrue; import static ru.ytkab0bp.slicebeam.utils.DebugUtils.assertTrue;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.graphics.Color; import android.graphics.Color;
@@ -13,8 +13,8 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
import com.dark98.santoku.utils.IOUtils; import ru.ytkab0bp.slicebeam.utils.IOUtils;
public class GLShaderProgram { public class GLShaderProgram {
long pointer; long pointer;
@@ -39,7 +39,7 @@ public class GLShaderProgram {
}; };
public GLShaderProgram(String name) { public GLShaderProgram(String name) {
AssetManager assets = Santoku.INSTANCE.getAssets(); AssetManager assets = SliceBeam.INSTANCE.getAssets();
try { try {
pointer = Native.shader_init_from_texts(name, IOUtils.readString(assets.open("shaders/" + name + ".fs")), IOUtils.readString(assets.open("shaders/" + name + ".vs"))); pointer = Native.shader_init_from_texts(name, IOUtils.readString(assets.open("shaders/" + name + ".fs")), IOUtils.readString(assets.open("shaders/" + name + ".vs")));
} catch (IOException e) { } catch (IOException e) {
@@ -1,4 +1,4 @@
package com.dark98.santoku.slic3r; package ru.ytkab0bp.slicebeam.slic3r;
import android.opengl.GLES30; import android.opengl.GLES30;
@@ -1,11 +1,11 @@
package com.dark98.santoku.slic3r; package ru.ytkab0bp.slicebeam.slic3r;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import com.dark98.santoku.utils.Vec3d; import ru.ytkab0bp.slicebeam.utils.Vec3d;
public class Model { public class Model {
public final String key = UUID.randomUUID().toString(); public final String key = UUID.randomUUID().toString();
@@ -1,6 +1,6 @@
package com.dark98.santoku.slic3r; package ru.ytkab0bp.slicebeam.slic3r;
import com.dark98.santoku.Santoku; import ru.ytkab0bp.slicebeam.SliceBeam;
class Native { class Native {
static { static {
@@ -12,7 +12,7 @@ class Native {
System.loadLibrary("slic3r"); System.loadLibrary("slic3r");
set_svg_path_prefix(Santoku.INSTANCE.getCacheDir().getAbsolutePath()); set_svg_path_prefix(SliceBeam.INSTANCE.getCacheDir().getAbsolutePath());
} }
static native void get_print_config_def(PrintConfigDef def); static native void get_print_config_def(PrintConfigDef def);
@@ -1,4 +1,4 @@
package com.dark98.santoku.slic3r; package ru.ytkab0bp.slicebeam.slic3r;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@@ -1,4 +1,4 @@
package com.dark98.santoku.slic3r; package ru.ytkab0bp.slicebeam.slic3r;
import androidx.annotation.Keep; import androidx.annotation.Keep;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;

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