mirror of
https://github.com/Dark98/SliceBeam.git
synced 2026-07-03 00:38:53 +00:00
Cloud/Backend Overhaul
Reverse Engineered a Backend Removed Boosty Stuff Removed Socials Stuff Removed Subscription Stuff Probably Some More
This commit is contained in:
+5
-3
@@ -6,6 +6,8 @@ plugins {
|
||||
}
|
||||
|
||||
def commit = getGitCommitHash(file('.'))
|
||||
def prodCloudBaseUrl = "https://santoku.dark98.co.uk/v1/"
|
||||
def prodBeamBaseUrl = "https://santoku.dark98.co.uk"
|
||||
|
||||
android {
|
||||
namespace 'com.dark98.santoku'
|
||||
@@ -16,9 +18,11 @@ android {
|
||||
minSdk 21
|
||||
targetSdk 35
|
||||
versionCode 8
|
||||
versionName "0.3.0"
|
||||
versionName "0.0.1"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
buildConfigField "String", "CLOUD_BASE_URL_PROD", "\"" + prodCloudBaseUrl + "\""
|
||||
buildConfigField "String", "BEAM_BASE_URL_PROD", "\"" + prodBeamBaseUrl + "\""
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
@@ -39,7 +43,6 @@ android {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
buildConfigField "boolean", "IS_GOOGLE_PLAY", "false"
|
||||
buildConfigField "String", "COMMIT", "\"" + commit + "\""
|
||||
ndk {
|
||||
//noinspection ChromeOsAbiSupport
|
||||
@@ -47,7 +50,6 @@ android {
|
||||
}
|
||||
}
|
||||
debug {
|
||||
buildConfigField "boolean", "IS_GOOGLE_PLAY", "false"
|
||||
buildConfigField "String", "COMMIT", "\"" + commit + "\""
|
||||
ndk {
|
||||
debugSymbolLevel 'NONE'
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
|
||||
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.media.action.IMAGE_CAPTURE" />
|
||||
</intent>
|
||||
<!-- WebView fails sometime if not queried, idk why -->
|
||||
<package android:name="com.google.android.webview"/>
|
||||
</queries>
|
||||
@@ -46,6 +43,13 @@
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:scheme="santoku" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:scheme="file" />
|
||||
<data android:scheme="content" />
|
||||
<data android:mimeType="*/*" />
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
package com.dark98.santoku;
|
||||
|
||||
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 com.dark98.santoku.cloud.CloudController;
|
||||
import com.dark98.santoku.events.BeamServerDataUpdatedEvent;
|
||||
import com.dark98.santoku.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, "Santoku/%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 {
|
||||
Santoku.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);
|
||||
Santoku.EVENT_BUS.fireEvent(new BeamServerDataUpdatedEvent());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Santoku.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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,13 @@ package com.dark98.santoku;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.Process;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Base64;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
@@ -32,7 +26,6 @@ import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -48,14 +41,11 @@ import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import ru.ytkab0bp.sapil.APICallback;
|
||||
import com.dark98.santoku.cloud.CloudAPI;
|
||||
import com.dark98.santoku.cloud.CloudController;
|
||||
import com.dark98.santoku.components.BeamAlertDialogBuilder;
|
||||
import com.dark98.santoku.components.ChangeLogBottomSheet;
|
||||
import com.dark98.santoku.components.UnfoldMenu;
|
||||
import com.dark98.santoku.config.ConfigObject;
|
||||
import com.dark98.santoku.events.NeedDismissAIGeneratorMenu;
|
||||
import com.dark98.santoku.events.NeedDismissSnackbarEvent;
|
||||
import com.dark98.santoku.events.NeedSnackbarEvent;
|
||||
import com.dark98.santoku.events.ObjectsListChangedEvent;
|
||||
@@ -76,8 +66,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
// Activity result
|
||||
public final static int REQUEST_CODE_OPEN_FILE = 1, REQUEST_CODE_EXPORT_GCODE = 2,
|
||||
REQUEST_CODE_IMPORT_PROFILES = 3, REQUEST_CODE_EXPORT_PROFILES = 4,
|
||||
REQUEST_CODE_EXPORT_3MF = 5,
|
||||
REQUEST_CODE_AI_GENERATOR_TAKE_PHOTO = 6, REQUEST_CODE_AI_GENERATOR_CHOOSE_PHOTO = 7;
|
||||
REQUEST_CODE_EXPORT_3MF = 5;
|
||||
|
||||
private static MainActivity activeInstance;
|
||||
|
||||
@@ -85,9 +74,6 @@ public class MainActivity extends AppCompatActivity {
|
||||
public static List<ConfigObject> EXPORTING_FILAMENTS;
|
||||
public static List<ConfigObject> EXPORTING_PRINTERS;
|
||||
|
||||
public static boolean IS_GENERATING_AI_MODEL;
|
||||
|
||||
public static File aiTempFile;
|
||||
|
||||
private static SparseArray<NavigationDelegate> liveDelegate = new SparseArray<>();
|
||||
private static int lastId;
|
||||
@@ -154,8 +140,13 @@ public class MainActivity extends AppCompatActivity {
|
||||
setContentView(v);
|
||||
|
||||
if (getIntent() != null && getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_VIEW)) {
|
||||
loadFile(getIntent().getData());
|
||||
setIntent(null);
|
||||
Uri data = getIntent().getData();
|
||||
if (data != null && "santoku".equalsIgnoreCase(data.getScheme())) {
|
||||
setIntent(null);
|
||||
} else {
|
||||
loadFile(data);
|
||||
setIntent(null);
|
||||
}
|
||||
}
|
||||
|
||||
DisplayMetrics dm = getResources().getDisplayMetrics();
|
||||
@@ -196,7 +187,6 @@ public class MainActivity extends AppCompatActivity {
|
||||
|
||||
if (!Objects.equals(Prefs.getLastCommit(), BuildConfig.COMMIT) && Santoku.hasUpdateInfo) {
|
||||
Prefs.setLastCommit();
|
||||
BeamServerData.load();
|
||||
new ChangeLogBottomSheet(this).show();
|
||||
}
|
||||
}
|
||||
@@ -209,7 +199,11 @@ public class MainActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onNewIntent(@NonNull Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
loadFile(intent.getData());
|
||||
Uri data = intent.getData();
|
||||
if (data != null && "santoku".equalsIgnoreCase(data.getScheme())) {
|
||||
return;
|
||||
}
|
||||
loadFile(data);
|
||||
setIntent(null);
|
||||
}
|
||||
|
||||
@@ -327,23 +321,6 @@ public class MainActivity extends AppCompatActivity {
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
} else if (requestCode == REQUEST_CODE_AI_GENERATOR_TAKE_PHOTO) {
|
||||
Santoku.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) {
|
||||
Santoku.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -490,110 +467,6 @@ public class MainActivity extends AppCompatActivity {
|
||||
});
|
||||
}
|
||||
|
||||
private void generateAiModel(Bitmap bm) {
|
||||
IS_GENERATING_AI_MODEL = true;
|
||||
String uploadTag = UUID.randomUUID().toString();
|
||||
Santoku.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) {
|
||||
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(processTag));
|
||||
|
||||
String downloadTag = UUID.randomUUID().toString();
|
||||
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileAIGeneratorDownloading).tag(downloadTag));
|
||||
String fileName = "generated_" + UUID.randomUUID() + ".stl";
|
||||
|
||||
File f = new File(Santoku.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);
|
||||
}
|
||||
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(downloadTag));
|
||||
Santoku.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) {
|
||||
Santoku.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;
|
||||
}
|
||||
});
|
||||
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(uploadTag));
|
||||
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileAIGeneratorProcessing).tag(processTag));
|
||||
});
|
||||
}
|
||||
|
||||
private void loadIniForImport(InputStream in) {
|
||||
IOUtils.IO_POOL.submit(()->{
|
||||
try {
|
||||
|
||||
@@ -18,7 +18,6 @@ import java.util.Map;
|
||||
|
||||
import ru.ytkab0bp.eventbus.EventBus;
|
||||
import com.dark98.santoku.boot.AppBoot;
|
||||
import com.dark98.santoku.boot.BeamServerDataTask;
|
||||
import com.dark98.santoku.boot.CheckUpdateJsonTask;
|
||||
import com.dark98.santoku.boot.ClearModelCacheTask;
|
||||
import com.dark98.santoku.boot.CloudInitTask;
|
||||
@@ -41,7 +40,6 @@ public class Santoku extends Application {
|
||||
public static TrueTimeImpl TRUE_TIME;
|
||||
public static Slic3rConfigWrapper CONFIG;
|
||||
public static int CONFIG_UID = 0;
|
||||
public static BeamServerData SERVER_DATA;
|
||||
public static boolean hasUpdateInfo;
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
@@ -54,7 +52,6 @@ public class Santoku extends Application {
|
||||
new PrefsTask(),
|
||||
new VibrationUtilsTask(),
|
||||
new TrueTimeTask(),
|
||||
new BeamServerDataTask(),
|
||||
new PrintConfigWarmupTask(),
|
||||
new CheckUpdateJsonTask(),
|
||||
new ClearModelCacheTask(),
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.net.Uri;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
@@ -35,6 +36,8 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.EditText;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
@@ -60,6 +63,7 @@ import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
|
||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
|
||||
import com.loopj.android.http.AsyncHttpClient;
|
||||
import com.loopj.android.http.AsyncHttpResponseHandler;
|
||||
import ru.ytkab0bp.sapil.APICallback;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@@ -94,8 +98,6 @@ import com.dark98.santoku.cloud.CloudController;
|
||||
import com.dark98.santoku.components.BeamAlertDialogBuilder;
|
||||
import com.dark98.santoku.components.CloudManageBottomSheet;
|
||||
import com.dark98.santoku.config.ConfigObject;
|
||||
import com.dark98.santoku.events.BeamServerDataUpdatedEvent;
|
||||
import com.dark98.santoku.events.CloudFeaturesUpdatedEvent;
|
||||
import com.dark98.santoku.events.CloudLoginStateUpdatedEvent;
|
||||
import com.dark98.santoku.events.CloudSyncFinishedEvent;
|
||||
import com.dark98.santoku.recycler.BigHeaderItem;
|
||||
@@ -114,14 +116,12 @@ import com.dark98.santoku.theme.ThemesRepo;
|
||||
import com.dark98.santoku.utils.Prefs;
|
||||
import com.dark98.santoku.utils.ViewUtils;
|
||||
import com.dark98.santoku.view.BeamSwitch;
|
||||
import com.dark98.santoku.view.BoostySubsView;
|
||||
import com.dark98.santoku.view.FadeRecyclerView;
|
||||
import com.dark98.santoku.view.MiniColorView;
|
||||
import com.dark98.santoku.view.TextColorImageSpan;
|
||||
|
||||
public class SetupActivity extends AppCompatActivity {
|
||||
public final static String EXTRA_ABOUT = "about";
|
||||
public final static String EXTRA_BOOSTY_ONLY = "boosty_only";
|
||||
public final static String EXTRA_CLOUD_PROFILE = "cloud_profile";
|
||||
public final static String EXTRA_CLOUD_IMPORT_FROM_SETUP = "cloud_import_from_setup";
|
||||
|
||||
@@ -129,12 +129,11 @@ public class SetupActivity extends AppCompatActivity {
|
||||
|
||||
private final static List<String> REPOS_URLS = Arrays.asList(
|
||||
"https://preset-repo-api.prusa3d.com/v1/repos",
|
||||
"https://raw.githubusercontent.com/utkabobr/SliceBeam/refs/heads/master/.profiledumpsrepo/manifest.json"
|
||||
"https://raw.githubusercontent.com/Dark98/SliceBeam/refs/heads/master/.profiledumpsrepo/manifest.json"
|
||||
);
|
||||
|
||||
private final static int REPOS_INDEX = 1;
|
||||
private final static int PROFILES_INDEX = 2;
|
||||
private static int BOOSTY_INDEX = 3;
|
||||
|
||||
private final static int TYPE_PRINTER = 0, TYPE_PRINT_CONFIG = 1, TYPE_FILAMENT = 2;
|
||||
|
||||
@@ -148,7 +147,6 @@ public class SetupActivity extends AppCompatActivity {
|
||||
|
||||
private int titleY;
|
||||
private float backgroundProgress;
|
||||
private float boostyProgress;
|
||||
|
||||
private SpringAnimation fakeScroller;
|
||||
|
||||
@@ -166,7 +164,6 @@ public class SetupActivity extends AppCompatActivity {
|
||||
private Map<ProfilesRepo, List<Slic3rConfigWrapper>> profilesMap = new HashMap<>();
|
||||
private boolean isProfilesLoaded;
|
||||
private boolean about;
|
||||
private boolean boostyOnly;
|
||||
private boolean cloudProfile;
|
||||
private boolean cloudImport;
|
||||
|
||||
@@ -185,11 +182,10 @@ public class SetupActivity extends AppCompatActivity {
|
||||
Santoku.EVENT_BUS.registerListener(this);
|
||||
|
||||
about = getIntent().getBooleanExtra(EXTRA_ABOUT, false);
|
||||
boostyOnly = getIntent().getBooleanExtra(EXTRA_BOOSTY_ONLY, false);
|
||||
cloudProfile = getIntent().getBooleanExtra(EXTRA_CLOUD_PROFILE, false);
|
||||
cloudImport = getIntent().getBooleanExtra(EXTRA_CLOUD_IMPORT_FROM_SETUP, false);
|
||||
|
||||
if (!about && !boostyOnly && !cloudProfile) {
|
||||
if (!about && !cloudProfile) {
|
||||
new BeamAlertDialogBuilder(this)
|
||||
.setTitle(R.string.IntroEarlyAccess)
|
||||
.setMessage(R.string.IntroEarlyAccessMessage)
|
||||
@@ -197,7 +193,7 @@ public class SetupActivity extends AppCompatActivity {
|
||||
.show();
|
||||
}
|
||||
|
||||
if (boostyOnly || cloudProfile) {
|
||||
if (cloudProfile) {
|
||||
backgroundProgress = 1f;
|
||||
}
|
||||
|
||||
@@ -205,7 +201,7 @@ public class SetupActivity extends AppCompatActivity {
|
||||
adapter = new SimpleRecyclerAdapter() {
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return about || boostyOnly || cloudProfile ? 1 : limitRepoFragmentCount ? REPOS_INDEX + 1 : limitProfileFragmentCount ? PROFILES_INDEX + 1 : super.getItemCount();
|
||||
return about || cloudProfile ? 1 : limitRepoFragmentCount ? REPOS_INDEX + 1 : limitProfileFragmentCount ? PROFILES_INDEX + 1 : super.getItemCount();
|
||||
}
|
||||
};
|
||||
setItems();
|
||||
@@ -236,25 +232,12 @@ public class SetupActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
if (position == 0 && !boostyOnly && !cloudProfile) {
|
||||
if (position == 0 && !cloudProfile) {
|
||||
backgroundProgress = positionOffset;
|
||||
} else {
|
||||
backgroundProgress = 1f;
|
||||
}
|
||||
|
||||
if (boostyOnly) {
|
||||
boostyProgress = 1f;
|
||||
} else if (position == BOOSTY_INDEX) {
|
||||
boostyProgress = 1f - positionOffset;
|
||||
} else if (position == BOOSTY_INDEX - 1) {
|
||||
boostyProgress = positionOffset;
|
||||
} else {
|
||||
boostyProgress = 0f;
|
||||
}
|
||||
if (profilesItem != null && profilesItem.recyclerView != null) {
|
||||
profilesItem.recyclerView.setOverlayAlpha(1f - boostyProgress);
|
||||
}
|
||||
|
||||
if (position == REPOS_INDEX) {
|
||||
if (!isReposLoaded && !isLoading) {
|
||||
loadRepos(true);
|
||||
@@ -434,17 +417,13 @@ public class SetupActivity extends AppCompatActivity {
|
||||
shader.startUsing();
|
||||
int topColor = ThemesRepo.getColor(android.R.attr.colorAccent);
|
||||
int bottomColor = ThemesRepo.getColor(android.R.attr.windowBackground);
|
||||
if (boostyProgress != 0f) {
|
||||
topColor = ColorUtils.blendARGB(bottomColor, ThemesRepo.getColor(R.attr.boostyColorTop), boostyProgress);
|
||||
bottomColor = ColorUtils.blendARGB(bottomColor, ThemesRepo.getColor(R.attr.boostyColorBottom), boostyProgress);
|
||||
}
|
||||
if (cloudProfile) {
|
||||
bottomColor = ColorUtils.blendARGB(bottomColor, topColor, 0.5f);
|
||||
}
|
||||
|
||||
shader.setUniformColor("top_color", topColor);
|
||||
shader.setUniformColor("bottom_color", bottomColor);
|
||||
shader.setUniform("progress", backgroundProgress - (cloudProfile ? 1.4f : 0) - (boostyProgress != 0 ? 1.2f : 0));
|
||||
shader.setUniform("progress", backgroundProgress - (cloudProfile ? 1.4f : 0));
|
||||
shader.setUniform("time", time);
|
||||
backgroundModel.render();
|
||||
shader.stopUsing();
|
||||
@@ -489,7 +468,7 @@ public class SetupActivity extends AppCompatActivity {
|
||||
title.setPivotY(0);
|
||||
title.setScaleX(sc);
|
||||
title.setScaleY(sc);
|
||||
int color = ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.textColorOnAccent), ThemesRepo.getColor(android.R.attr.colorAccent), cloudProfile ? 0f : backgroundProgress - boostyProgress);
|
||||
int color = ColorUtils.blendARGB(ThemesRepo.getColor(R.attr.textColorOnAccent), ThemesRepo.getColor(android.R.attr.colorAccent), cloudProfile ? 0f : backgroundProgress);
|
||||
title.setTextColor(color);
|
||||
title.setTranslationY(ViewUtils.lerp(titleY, (ViewUtils.dp(52) - title.getHeight() * title.getScaleY()) / 2f, backgroundProgress));
|
||||
}
|
||||
@@ -501,23 +480,13 @@ public class SetupActivity extends AppCompatActivity {
|
||||
Santoku.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")
|
||||
@EventHandler(runOnMainThread = true)
|
||||
public void onCloudSyncFinished(CloudSyncFinishedEvent e) {
|
||||
if (cloudProfile && Prefs.getCloudAPIToken() != null && cloudImport) {
|
||||
finish();
|
||||
}
|
||||
if (!about && !boostyOnly && !cloudProfile) {
|
||||
if (!about && !cloudProfile) {
|
||||
if (Prefs.getCloudAPIToken() != null) {
|
||||
limitRepoFragmentCount = false;
|
||||
limitProfileFragmentCount = false;
|
||||
@@ -531,13 +500,8 @@ public class SetupActivity extends AppCompatActivity {
|
||||
public void onCloudAuthStateUpdated(CloudLoginStateUpdatedEvent e) {
|
||||
if (cloudProfile) {
|
||||
cloudItem.bindLoginButton(true);
|
||||
cloudItem.bindFeatures();
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(runOnMainThread = true)
|
||||
public void onCloudFeaturesUpdated(CloudFeaturesUpdatedEvent e) {
|
||||
if (!about && !boostyOnly && !cloudProfile) {
|
||||
if (!about && !cloudProfile && reposItem != null) {
|
||||
reposItem.onCloudInfoUpdated();
|
||||
}
|
||||
}
|
||||
@@ -545,8 +509,6 @@ public class SetupActivity extends AppCompatActivity {
|
||||
private void setItems() {
|
||||
if (cloudProfile){
|
||||
adapter.setItems(Collections.singletonList(cloudItem = new CloudProfileItem()));
|
||||
} else if (boostyOnly) {
|
||||
adapter.setItems(Collections.singletonList(new BoostyItem()));
|
||||
} else if (about) {
|
||||
adapter.setItems(Collections.singletonList(new AboutItem()));
|
||||
} else {
|
||||
@@ -555,13 +517,6 @@ public class SetupActivity extends AppCompatActivity {
|
||||
reposItem = new ReposItem(),
|
||||
profilesItem = new ProfilesItem()));
|
||||
|
||||
if (BeamServerData.isBoostyAvailable()) {
|
||||
BOOSTY_INDEX = items.size();
|
||||
items.add(new BoostyItem());
|
||||
} else {
|
||||
BOOSTY_INDEX = -1;
|
||||
}
|
||||
|
||||
items.add(new FinishItem());
|
||||
adapter.setItems(items);
|
||||
}
|
||||
@@ -685,7 +640,9 @@ public class SetupActivity extends AppCompatActivity {
|
||||
private FrameLayout buttonView;
|
||||
private TextView buttonText;
|
||||
private ProgressBar buttonProgress;
|
||||
private FadeRecyclerView recyclerView;
|
||||
private TextView titleView;
|
||||
private TextView signUpButton;
|
||||
private boolean signUpInProgress;
|
||||
|
||||
@Override
|
||||
public View onCreateView(Context ctx) {
|
||||
@@ -693,23 +650,16 @@ public class SetupActivity extends AppCompatActivity {
|
||||
ll.setOrientation(LinearLayout.VERTICAL);
|
||||
ll.setPadding(0, ViewUtils.dp(42), 0, 0);
|
||||
|
||||
TextView title = new TextView(ctx);
|
||||
title.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
|
||||
title.setText(R.string.SettingsCloudManageDescription);
|
||||
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||
title.setGravity(Gravity.CENTER);
|
||||
title.setPadding(ViewUtils.dp(12), 0, ViewUtils.dp(12), 0);
|
||||
ll.addView(title);
|
||||
titleView = new TextView(ctx);
|
||||
titleView.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
|
||||
titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||
titleView.setGravity(Gravity.CENTER);
|
||||
titleView.setPadding(ViewUtils.dp(12), 0, ViewUtils.dp(12), 0);
|
||||
ll.addView(titleView);
|
||||
bindHeader();
|
||||
|
||||
FrameLayout fl = new FrameLayout(ctx);
|
||||
recyclerView = new FadeRecyclerView(ctx);
|
||||
recyclerView.setBitmapMode();
|
||||
recyclerView.setAdapter(adapter = new SimpleRecyclerAdapter());
|
||||
recyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
|
||||
fl.addView(recyclerView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
|
||||
bindFeatures();
|
||||
|
||||
ll.addView(fl, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
|
||||
View spacer = new View(ctx);
|
||||
ll.addView(spacer, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
|
||||
|
||||
TextView tosButton = new TextView(ctx);
|
||||
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(ctx.getString(R.string.SettingsCloudManageTermsOfService)).append(" ");
|
||||
@@ -724,7 +674,7 @@ public class SetupActivity extends AppCompatActivity {
|
||||
tosButton.setGravity(Gravity.CENTER);
|
||||
tosButton.setPadding(ViewUtils.dp(12), ViewUtils.dp(8), ViewUtils.dp(12), ViewUtils.dp(8));
|
||||
tosButton.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
|
||||
tosButton.setOnClickListener(v -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://beam3d.ru/slicebeam_cloud_tos.html"))));
|
||||
tosButton.setOnClickListener(v -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(BuildConfig.BEAM_BASE_URL_PROD + "/tos"))));
|
||||
ll.addView(tosButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{
|
||||
leftMargin = rightMargin = ViewUtils.dp(16);
|
||||
bottomMargin = ViewUtils.dp(8);
|
||||
@@ -744,27 +694,30 @@ public class SetupActivity extends AppCompatActivity {
|
||||
buttonProgress.setIndeterminateTintList(ColorStateList.valueOf(ThemesRepo.getColor(R.attr.textColorOnAccent)));
|
||||
buttonView.addView(buttonProgress, new FrameLayout.LayoutParams(ViewUtils.dp(28), ViewUtils.dp(28), Gravity.CENTER));
|
||||
|
||||
bindLoginButton(false);
|
||||
|
||||
ll.addView(buttonView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{
|
||||
leftMargin = rightMargin = ViewUtils.dp(16);
|
||||
}});
|
||||
|
||||
signUpButton = new TextView(ctx);
|
||||
signUpButton.setText(R.string.SettingsCloudManageButtonSignUp);
|
||||
signUpButton.setTextColor(ThemesRepo.getColor(android.R.attr.colorAccent));
|
||||
signUpButton.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||
signUpButton.setGravity(Gravity.CENTER);
|
||||
signUpButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||
signUpButton.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
|
||||
signUpButton.setOnClickListener(v -> showSignUpDialog(v.getContext()));
|
||||
ll.addView(signUpButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{
|
||||
leftMargin = rightMargin = ViewUtils.dp(16);
|
||||
topMargin = ViewUtils.dp(8);
|
||||
bottomMargin = ViewUtils.dp(16);
|
||||
}});
|
||||
|
||||
bindLoginButton(false);
|
||||
|
||||
ll.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
return ll;
|
||||
}
|
||||
|
||||
private void bindFeatures() {
|
||||
List<SimpleRecyclerItem> items = new ArrayList<>();
|
||||
if (CloudController.getUserFeatures() != null) {
|
||||
for (CloudAPI.SubscriptionLevel lvl : CloudController.getUserFeatures().levels) {
|
||||
items.add(new CloudSubscriptionLevel(lvl));
|
||||
}
|
||||
}
|
||||
adapter.setItems(items);
|
||||
}
|
||||
|
||||
private void bindLoginButton(boolean animate) {
|
||||
boolean loggedIn = Prefs.getCloudAPIToken() != null;
|
||||
boolean loading = !loggedIn && CloudController.isLoggingIn();
|
||||
@@ -819,6 +772,7 @@ public class SetupActivity extends AppCompatActivity {
|
||||
buttonText.setVisibility(loading ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
buttonText.setText(loggedIn ? R.string.SettingsCloudManageButtonManage : R.string.SettingsCloudManageButtonLogIn);
|
||||
bindHeader();
|
||||
buttonView.setOnClickListener(v-> {
|
||||
if (loading) {
|
||||
new BeamAlertDialogBuilder(v.getContext())
|
||||
@@ -833,177 +787,145 @@ public class SetupActivity extends AppCompatActivity {
|
||||
CloudController.beginLogin();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private final static class CloudSubscriptionLevel extends SimpleRecyclerItem<CloudSubscriptionLevel.LevelHolderView> {
|
||||
private CloudAPI.SubscriptionLevel level;
|
||||
|
||||
private CloudSubscriptionLevel(CloudAPI.SubscriptionLevel level) {
|
||||
this.level = level;
|
||||
if (signUpButton != null) {
|
||||
boolean showSignUp = !loggedIn && !loading;
|
||||
signUpButton.setVisibility(showSignUp ? View.VISIBLE : View.GONE);
|
||||
signUpButton.setEnabled(showSignUp && !signUpInProgress);
|
||||
signUpButton.setAlpha(signUpButton.isEnabled() ? 1f : 0.6f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelHolderView onCreateView(Context ctx) {
|
||||
return new LevelHolderView(ctx);
|
||||
private void bindHeader() {
|
||||
if (titleView == null) return;
|
||||
CloudAPI.UserInfo info = CloudController.getUserInfo();
|
||||
if (Prefs.getCloudAPIToken() != null && info != null && info.displayName != null) {
|
||||
titleView.setText(titleView.getContext().getString(R.string.SettingsCloudManageLoggedInAs, info.displayName));
|
||||
} else {
|
||||
titleView.setText(R.string.SettingsCloudManageDescription);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindView(LevelHolderView view) {
|
||||
view.bind(this);
|
||||
}
|
||||
|
||||
public final static class LevelHolderView extends LinearLayout implements IThemeView {
|
||||
private ImageView icon;
|
||||
private TextView title;
|
||||
private TextView price;
|
||||
|
||||
private RecyclerView featuresLayout;
|
||||
private SimpleRecyclerAdapter featuresAdapter;
|
||||
|
||||
public LevelHolderView(@NonNull Context context) {
|
||||
super(context);
|
||||
|
||||
setOrientation(VERTICAL);
|
||||
setPadding(0, ViewUtils.dp(16), 0, ViewUtils.dp(8));
|
||||
|
||||
LinearLayout inner = new LinearLayout(context);
|
||||
inner.setOrientation(HORIZONTAL);
|
||||
inner.setGravity(Gravity.CENTER_VERTICAL);
|
||||
inner.setPadding(ViewUtils.dp(28), 0, ViewUtils.dp(28), 0);
|
||||
addView(inner, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||
bottomMargin = ViewUtils.dp(8);
|
||||
}});
|
||||
|
||||
icon = new ImageView(context);
|
||||
inner.addView(icon, new LayoutParams(ViewUtils.dp(26), ViewUtils.dp(26)));
|
||||
|
||||
title = new TextView(context);
|
||||
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||
title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||
inner.addView(title, new LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f) {{
|
||||
leftMargin = ViewUtils.dp(12);
|
||||
}});
|
||||
|
||||
price = new TextView(context);
|
||||
price.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||
price.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||
inner.addView(price);
|
||||
|
||||
featuresLayout = new RecyclerView(context) {
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean dispatchHoverEvent(MotionEvent event) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
featuresLayout.setLayoutManager(new LinearLayoutManager(context));
|
||||
featuresLayout.setAdapter(featuresAdapter = new SimpleRecyclerAdapter());
|
||||
addView(featuresLayout, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||
topMargin = ViewUtils.dp(3);
|
||||
leftMargin = rightMargin = ViewUtils.dp(16);
|
||||
bottomMargin = ViewUtils.dp(8);
|
||||
}});
|
||||
|
||||
setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||
leftMargin = rightMargin = ViewUtils.dp(12);
|
||||
topMargin = ViewUtils.dp(12);
|
||||
}});
|
||||
onApplyTheme();
|
||||
private void showSignUpDialog(Context ctx) {
|
||||
if (signUpInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
public void bind(CloudSubscriptionLevel item) {
|
||||
CloudAPI.SubscriptionLevel lvl = item.level;
|
||||
title.setText(lvl.title);
|
||||
price.setText(lvl.price);
|
||||
if (lvl.level <= 0) {
|
||||
icon.setImageResource(R.drawable.zero_ruble_outline_28);
|
||||
price.setText(R.string.SettingsCloudManageFree);
|
||||
} else if (lvl.level == 1) {
|
||||
icon.setImageResource(R.drawable.stars_outline_28);
|
||||
LinearLayout ll = new LinearLayout(ctx);
|
||||
ll.setOrientation(LinearLayout.VERTICAL);
|
||||
ll.setPadding(ViewUtils.dp(16), ViewUtils.dp(8), ViewUtils.dp(16), 0);
|
||||
|
||||
TextView emailLabel = new TextView(ctx);
|
||||
emailLabel.setText(R.string.SettingsCloudManageSignUpEmail);
|
||||
emailLabel.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
|
||||
emailLabel.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||
ll.addView(emailLabel, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
EditText emailInput = new EditText(ctx);
|
||||
emailInput.setHint(R.string.SettingsCloudManageSignUpEmail);
|
||||
emailInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
|
||||
emailInput.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||
emailInput.setHintTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||
ll.addView(emailInput, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
TextView displayNameLabel = new TextView(ctx);
|
||||
displayNameLabel.setText(R.string.SettingsCloudManageSignUpDisplayName);
|
||||
displayNameLabel.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
|
||||
displayNameLabel.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||
ll.addView(displayNameLabel, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||
topMargin = ViewUtils.dp(12);
|
||||
}});
|
||||
|
||||
EditText displayNameInput = new EditText(ctx);
|
||||
displayNameInput.setHint(R.string.SettingsCloudManageSignUpDisplayName);
|
||||
displayNameInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PERSON_NAME);
|
||||
displayNameInput.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||
displayNameInput.setHintTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||
ll.addView(displayNameInput, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||
topMargin = ViewUtils.dp(8);
|
||||
}});
|
||||
|
||||
TextView passwordLabel = new TextView(ctx);
|
||||
passwordLabel.setText(R.string.SettingsCloudManageSignUpPassword);
|
||||
passwordLabel.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
|
||||
passwordLabel.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||
ll.addView(passwordLabel, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||
topMargin = ViewUtils.dp(12);
|
||||
}});
|
||||
|
||||
EditText passwordInput = new EditText(ctx);
|
||||
passwordInput.setHint(R.string.SettingsCloudManageSignUpPassword);
|
||||
passwordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
passwordInput.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||
passwordInput.setHintTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||
ll.addView(passwordInput, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||
topMargin = ViewUtils.dp(8);
|
||||
}});
|
||||
|
||||
CheckBox showPassword = new CheckBox(ctx);
|
||||
showPassword.setText(R.string.SettingsCloudManageSignUpShowPassword);
|
||||
showPassword.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||
showPassword.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
int selection = passwordInput.getSelectionEnd();
|
||||
if (isChecked) {
|
||||
passwordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
|
||||
} else {
|
||||
icon.setImageResource(R.drawable.cloud_plus_outline_28);
|
||||
passwordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
}
|
||||
passwordInput.setSelection(Math.max(selection, 0));
|
||||
});
|
||||
ll.addView(showPassword, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {{
|
||||
topMargin = ViewUtils.dp(4);
|
||||
}});
|
||||
|
||||
List<SimpleRecyclerItem> items = new ArrayList<>();
|
||||
CloudAPI.UserFeatures features = CloudController.getUserFeatures();
|
||||
CloudAPI.UserInfo info = CloudController.getUserInfo();
|
||||
Context ctx = getContext();
|
||||
if (!BuildConfig.IS_GOOGLE_PLAY && features.earlyAccessLevel != -1 && lvl.level >= features.earlyAccessLevel) {
|
||||
items.add(new PreferenceItem()
|
||||
.setForceDark(true)
|
||||
.setPaddings(ViewUtils.dp(8))
|
||||
.setIcon(R.drawable.clock_circle_dashed_outline_24)
|
||||
.setTitle(ctx.getString(R.string.SettingsCloudManageFeatureEarlyAccess))
|
||||
.setSubtitle(ctx.getString(R.string.SettingsCloudManageFeatureEarlyAccessDescription)));
|
||||
}
|
||||
if (features.syncRequiredLevel != -1 && lvl.level >= features.syncRequiredLevel) {
|
||||
items.add(new PreferenceItem()
|
||||
.setForceDark(true)
|
||||
.setPaddings(ViewUtils.dp(8))
|
||||
.setIcon(R.drawable.sync_outline_28)
|
||||
.setTitle(ctx.getString(R.string.SettingsCloudManageFeatureCloudSync))
|
||||
.setSubtitle(ctx.getString(R.string.SettingsCloudManageFeatureCloudSyncDescription)));
|
||||
}
|
||||
if (features.aiGeneratorRequiredLevel != -1 && lvl.level >= features.aiGeneratorRequiredLevel) {
|
||||
items.add(new PreferenceItem()
|
||||
.setForceDark(true)
|
||||
.setPaddings(ViewUtils.dp(8))
|
||||
.setIcon(R.drawable.brain_outline_28)
|
||||
.setTitle(ctx.getString(R.string.SettingsCloudManageFeatureAIGenerator))
|
||||
.setSubtitle(ctx.getString(R.string.SettingsCloudManageFeatureAIGeneratorDescription, features.aiGeneratorModelsPerMonth)));
|
||||
}
|
||||
if (lvl.level > 0) {
|
||||
items.add(new PreferenceItem()
|
||||
.setForceDark(true)
|
||||
.setPaddings(ViewUtils.dp(8))
|
||||
.setIcon(R.drawable.box_heart_outline_28)
|
||||
.setTitle(ctx.getString(R.string.SettingsCloudManageFeatureFreeForAll))
|
||||
.setSubtitle(ctx.getString(R.string.SettingsCloudManageFeatureFreeForAllDescription)));
|
||||
}
|
||||
featuresAdapter.setItems(items);
|
||||
featuresLayout.setVisibility(items.isEmpty() ? View.GONE : View.VISIBLE);
|
||||
TextView dialogTitle = new TextView(ctx);
|
||||
dialogTitle.setText(R.string.SettingsCloudManageButtonSignUp);
|
||||
dialogTitle.setTextColor(ThemesRepo.getColor(android.R.attr.textColorPrimary));
|
||||
dialogTitle.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 28);
|
||||
dialogTitle.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||
dialogTitle.setGravity(Gravity.CENTER);
|
||||
dialogTitle.setPadding(0, ViewUtils.dp(4), 0, ViewUtils.dp(8));
|
||||
ll.addView(dialogTitle, 0, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
boolean subscribed = lvl.level > 0 && info != null && lvl.level == info.currentLevel;
|
||||
boolean allowSubscribe = lvl.level > 0 && (info == null || lvl.level > info.currentLevel);
|
||||
if (subscribed) {
|
||||
price.setText(R.string.SettingsCloudManageSubscribed);
|
||||
}
|
||||
price.setVisibility(allowSubscribe || subscribed ? View.VISIBLE : View.GONE);
|
||||
setOnClickListener(v -> {
|
||||
if (subscribed) {
|
||||
v.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(lvl.manageUrl)));
|
||||
} else {
|
||||
new BeamAlertDialogBuilder(getContext())
|
||||
.setTitle(lvl.title)
|
||||
.setMessage(R.string.SettingsCloudManageLevelRedirectMessage)
|
||||
.setPositiveButton(android.R.string.ok, (dialog, which) -> v.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(lvl.subscribeOrUpgradeUrl))))
|
||||
.setNegativeButton(R.string.SettingsCloudManageLevelRedirectAlreadySubscribed, (dialog, which) -> v.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(features.alreadySubscribedInfoUrl))))
|
||||
.show();
|
||||
}
|
||||
});
|
||||
setClickable(allowSubscribe || subscribed);
|
||||
onApplyTheme();
|
||||
}
|
||||
new BeamAlertDialogBuilder(ctx)
|
||||
.setView(ll)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.SettingsCloudManageButtonSignUp, (dialog, which) -> {
|
||||
String email = emailInput.getText().toString().trim();
|
||||
String displayName = displayNameInput.getText().toString().trim();
|
||||
String password = passwordInput.getText().toString();
|
||||
if (TextUtils.isEmpty(email) || TextUtils.isEmpty(displayName) || TextUtils.isEmpty(password)) {
|
||||
Toast.makeText(ctx, R.string.SettingsCloudManageSignUpMissingFields, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
signUpInProgress = true;
|
||||
bindLoginButton(true);
|
||||
CloudAPI.INSTANCE.signup(email, password, displayName, new APICallback<CloudAPI.AuthToken>() {
|
||||
@Override
|
||||
public void onResponse(CloudAPI.AuthToken response) {
|
||||
Prefs.setCloudAPIToken(response.bearer);
|
||||
signUpInProgress = false;
|
||||
CloudController.init();
|
||||
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplyTheme() {
|
||||
int accent = ThemesRepo.getColor(android.R.attr.colorAccent);
|
||||
if (ColorUtils.calculateLuminance(accent) >= 0.6f) {
|
||||
accent = ColorUtils.blendARGB(accent, Color.BLACK, 0.075f);
|
||||
}
|
||||
boolean tooLight = ColorUtils.calculateLuminance(accent) >= 0.6f;
|
||||
title.setTextColor(0xffffffff);
|
||||
price.setTextColor(0xffffffff);
|
||||
icon.setImageTintList(ColorStateList.valueOf(0xffffffff));
|
||||
featuresLayout.setBackground(ViewUtils.createRipple(0, tooLight ? 0x33ffffff : 0x21ffffff, 24));
|
||||
setBackground(ViewUtils.createRipple(0x21000000, ColorUtils.blendARGB(0xffffffff, accent, tooLight ? 0.9f : 0.75f), 32));
|
||||
}
|
||||
@Override
|
||||
public void onException(Exception e) {
|
||||
signUpInProgress = false;
|
||||
ViewUtils.postOnMainThread(() -> {
|
||||
new BeamAlertDialogBuilder(ctx)
|
||||
.setTitle(R.string.SettingsCloudManageSignUpFailed)
|
||||
.setMessage(e.toString())
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show();
|
||||
bindLoginButton(true);
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class AboutItem extends SimpleRecyclerItem<View> {
|
||||
@@ -1235,9 +1157,9 @@ public class SetupActivity extends AppCompatActivity {
|
||||
progressBar.setIndeterminateTintList(ColorStateList.valueOf(ThemesRepo.getColor(android.R.attr.colorAccent)));
|
||||
cloudImportView.setTextColor(ThemesRepo.getColor(android.R.attr.colorAccent));
|
||||
cloudImportView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
|
||||
cloudImportView.setVisibility(BeamServerData.isCloudAvailable() ? View.VISIBLE : View.GONE);
|
||||
cloudImportView.setVisibility(Prefs.getCloudAPIToken() != null ? View.VISIBLE : View.GONE);
|
||||
cloudOrView.setTextColor(ThemesRepo.getColor(android.R.attr.textColorSecondary));
|
||||
cloudOrView.setVisibility(BeamServerData.isCloudAvailable() ? View.VISIBLE : View.GONE);
|
||||
cloudOrView.setVisibility(Prefs.getCloudAPIToken() != null ? View.VISIBLE : View.GONE);
|
||||
customProfileView.setTextColor(ThemesRepo.getColor(android.R.attr.colorAccent));
|
||||
customProfileView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
|
||||
buttonView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), ThemesRepo.getColor(android.R.attr.colorAccent), 16));
|
||||
@@ -1264,8 +1186,8 @@ public class SetupActivity extends AppCompatActivity {
|
||||
|
||||
public void onCloudInfoUpdated() {
|
||||
if (cloudImportView != null) {
|
||||
cloudImportView.setVisibility(BeamServerData.isCloudAvailable() ? View.VISIBLE : View.GONE);
|
||||
cloudOrView.setVisibility(BeamServerData.isCloudAvailable() ? View.VISIBLE : View.GONE);
|
||||
cloudImportView.setVisibility(Prefs.getCloudAPIToken() != null ? View.VISIBLE : View.GONE);
|
||||
cloudOrView.setVisibility(Prefs.getCloudAPIToken() != null ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1425,80 +1347,6 @@ public class SetupActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private final class BoostyItem extends SimpleRecyclerItem<View> {
|
||||
|
||||
@Override
|
||||
public View onCreateView(Context ctx) {
|
||||
LinearLayout ll = new LinearLayout(ctx);
|
||||
ll.setOrientation(LinearLayout.VERTICAL);
|
||||
ll.setPadding(0, ViewUtils.dp(42), 0, 0);
|
||||
|
||||
TextView title = new TextView(ctx);
|
||||
title.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
|
||||
title.setText(R.string.IntroBoostyTitle);
|
||||
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||
title.setGravity(Gravity.CENTER);
|
||||
title.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||
title.setPadding(ViewUtils.dp(12), 0, ViewUtils.dp(12), 0);
|
||||
ll.addView(title);
|
||||
|
||||
BoostySubsView subsView = new BoostySubsView(ctx);
|
||||
if (Santoku.SERVER_DATA != null) {
|
||||
List<String> list = new ArrayList<>(Santoku.SERVER_DATA.boostySubscribers);
|
||||
Collections.shuffle(list);
|
||||
subsView.setStrings(list);
|
||||
}
|
||||
ll.addView(subsView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f) {{
|
||||
bottomMargin = ViewUtils.dp(64);
|
||||
}});
|
||||
|
||||
TextView subscribeButton = new TextView(ctx);
|
||||
SpannableStringBuilder sb = SpannableStringBuilder.valueOf(ctx.getString(R.string.IntroBoostySupport)).append(" ");
|
||||
Drawable dr = ContextCompat.getDrawable(ctx, R.drawable.external_link_outline_24);
|
||||
int size = ViewUtils.dp(16);
|
||||
dr.setBounds(0, 0, size, size);
|
||||
sb.append("d", new TextColorImageSpan(dr, ViewUtils.dp(2f)), SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
subscribeButton.setText(sb);
|
||||
subscribeButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
|
||||
subscribeButton.setTextColor(Color.WHITE);
|
||||
subscribeButton.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||
subscribeButton.setGravity(Gravity.CENTER);
|
||||
subscribeButton.setPadding(ViewUtils.dp(12), ViewUtils.dp(8), ViewUtils.dp(12), ViewUtils.dp(8));
|
||||
subscribeButton.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), 16));
|
||||
subscribeButton.setOnClickListener(v -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://boosty.to/ytkab0bp"))));
|
||||
ll.addView(subscribeButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{
|
||||
leftMargin = rightMargin = ViewUtils.dp(16);
|
||||
bottomMargin = ViewUtils.dp(8);
|
||||
}});
|
||||
|
||||
TextView buttonView = new TextView(ctx);
|
||||
if (boostyOnly) {
|
||||
buttonView.setText(android.R.string.ok);
|
||||
} else {
|
||||
buttonView.setText(R.string.IntroNext);
|
||||
}
|
||||
buttonView.setTextColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
|
||||
buttonView.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||
buttonView.setGravity(Gravity.CENTER);
|
||||
buttonView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||
buttonView.setBackground(ViewUtils.createRipple(ThemesRepo.getColor(android.R.attr.colorControlHighlight), ThemesRepo.getColor(R.attr.boostyColorTop), 16));
|
||||
buttonView.setOnClickListener(v-> {
|
||||
if (boostyOnly) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
scrollToNext();
|
||||
});
|
||||
ll.addView(buttonView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.dp(52)) {{
|
||||
leftMargin = rightMargin = ViewUtils.dp(16);
|
||||
bottomMargin = ViewUtils.dp(16);
|
||||
}});
|
||||
|
||||
ll.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
return ll;
|
||||
}
|
||||
}
|
||||
|
||||
private final class FinishItem extends SimpleRecyclerItem<View> {
|
||||
private TextView buttonView;
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package com.dark98.santoku.boot;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.dark98.santoku.BeamServerData;
|
||||
import com.dark98.santoku.Santoku;
|
||||
import com.dark98.santoku.utils.Prefs;
|
||||
import com.dark98.santoku.utils.ViewUtils;
|
||||
|
||||
public class BeamServerDataTask extends BootTask {
|
||||
public BeamServerDataTask() {
|
||||
super(() -> {
|
||||
try {
|
||||
Santoku.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();
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,7 @@ package com.dark98.santoku.cloud;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import ru.ytkab0bp.sapil.APICallback;
|
||||
@@ -27,7 +22,7 @@ public interface CloudAPI extends APIRunner {
|
||||
|
||||
@Override
|
||||
public String getBaseURL() {
|
||||
return "https://api.beam3d.ru/v1/";
|
||||
return BuildConfig.CLOUD_BASE_URL_PROD;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,10 +67,16 @@ public interface CloudAPI extends APIRunner {
|
||||
void userGetInfo(APICallback<UserInfo> callback);
|
||||
|
||||
/**
|
||||
* Gets user features
|
||||
* Creates a new account (email/password)
|
||||
*/
|
||||
@Method("user/getFeatures")
|
||||
void userGetFeatures(APICallback<UserFeatures> callback);
|
||||
@Method(requestType = RequestType.POST, value = "signup")
|
||||
void signup(@Arg("email") String email, @Arg("password") String password, @Arg("displayName") String displayName, APICallback<AuthToken> callback);
|
||||
|
||||
/**
|
||||
* Login with email/password
|
||||
*/
|
||||
@Method(requestType = RequestType.POST, value = "login")
|
||||
void login(@Arg("email") String email, @Arg("password") String password, APICallback<AuthToken> callback);
|
||||
|
||||
/**
|
||||
* Fetches sync state
|
||||
@@ -103,24 +104,6 @@ public interface CloudAPI extends APIRunner {
|
||||
@Method("sync/get")
|
||||
void syncGet(APICallback<String> callback);
|
||||
|
||||
/**
|
||||
* Generates 3D model from image
|
||||
* <p>
|
||||
* @param image Base64 encoded image
|
||||
* <p>
|
||||
* Requires authorization
|
||||
*/
|
||||
@Method(requestType = RequestType.POST, value = "models/generate")
|
||||
void modelsGenerate(@Arg("") String image, @Header("Content-Type") String type, APICallback<InputStream> callback);
|
||||
|
||||
/**
|
||||
* Gets remaining model generations count
|
||||
* <p>
|
||||
* Requires authorization
|
||||
*/
|
||||
@Method("models/getRemainingCount")
|
||||
void modelsGetRemainingCount(APICallback<ModelsRemainingCount> callback);
|
||||
|
||||
/**
|
||||
* Destroys token
|
||||
* <p>
|
||||
@@ -158,66 +141,13 @@ public interface CloudAPI extends APIRunner {
|
||||
public String bearer;
|
||||
}
|
||||
|
||||
final class UserFeatures {
|
||||
final class AuthToken {
|
||||
/**
|
||||
* Which level is required for early access
|
||||
* Bearer token
|
||||
*/
|
||||
public int earlyAccessLevel;
|
||||
|
||||
/**
|
||||
* Which level is required for data sync
|
||||
*/
|
||||
public int syncRequiredLevel;
|
||||
|
||||
/**
|
||||
* Which level is required for AI model generator
|
||||
*/
|
||||
public int aiGeneratorRequiredLevel;
|
||||
|
||||
/**
|
||||
* Models per month max
|
||||
*/
|
||||
public int aiGeneratorModelsPerMonth;
|
||||
|
||||
/**
|
||||
* Url at which user should be redirected for info about how to restore a subscription
|
||||
*/
|
||||
public String alreadySubscribedInfoUrl;
|
||||
|
||||
/**
|
||||
* List of subscription levels
|
||||
*/
|
||||
public List<SubscriptionLevel> levels = new ArrayList<>();
|
||||
public String bearer;
|
||||
}
|
||||
|
||||
final class SubscriptionLevel {
|
||||
/**
|
||||
* Int representation
|
||||
*/
|
||||
public int level;
|
||||
|
||||
/**
|
||||
* Title of this level
|
||||
*/
|
||||
public String title;
|
||||
|
||||
/**
|
||||
* Price of this level
|
||||
*/
|
||||
public String price;
|
||||
|
||||
/**
|
||||
* Url at which user should be redirected for purchase
|
||||
*/
|
||||
public String subscribeOrUpgradeUrl;
|
||||
|
||||
/**
|
||||
* Url at which user should be redirected for managing the subscription
|
||||
*/
|
||||
public String manageUrl;
|
||||
}
|
||||
|
||||
|
||||
final class UserInfo {
|
||||
/**
|
||||
* User's id
|
||||
@@ -235,10 +165,6 @@ public interface CloudAPI extends APIRunner {
|
||||
@Nullable
|
||||
public String avatarUrl;
|
||||
|
||||
/**
|
||||
* Current subscription level
|
||||
*/
|
||||
public int currentLevel;
|
||||
}
|
||||
|
||||
|
||||
@@ -259,15 +185,4 @@ public interface CloudAPI extends APIRunner {
|
||||
public long maxSize;
|
||||
}
|
||||
|
||||
final class ModelsRemainingCount {
|
||||
/**
|
||||
* Used generations
|
||||
*/
|
||||
public int used;
|
||||
|
||||
/**
|
||||
* Max available generations
|
||||
*/
|
||||
public int max;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,7 @@ import ru.ytkab0bp.sapil.APIRequestHandle;
|
||||
import com.dark98.santoku.R;
|
||||
import com.dark98.santoku.Santoku;
|
||||
import com.dark98.santoku.components.BeamAlertDialogBuilder;
|
||||
import com.dark98.santoku.events.CloudFeaturesUpdatedEvent;
|
||||
import com.dark98.santoku.events.CloudLoginStateUpdatedEvent;
|
||||
import com.dark98.santoku.events.CloudModelsRemainingCountUpdatedEvent;
|
||||
import com.dark98.santoku.events.CloudSyncFinishedEvent;
|
||||
import com.dark98.santoku.events.CloudUserInfoUpdatedEvent;
|
||||
import com.dark98.santoku.events.NeedDismissSnackbarEvent;
|
||||
@@ -35,19 +33,13 @@ import com.dark98.santoku.utils.ViewUtils;
|
||||
import com.dark98.santoku.view.SnackbarsLayout;
|
||||
|
||||
public class CloudController {
|
||||
public final static String USER_INFO_AI_GEN_TAG = "ai_gen_user_info";
|
||||
public final static String CLOUD_SYNC_TAG = "cloud_sync";
|
||||
|
||||
private final static String TAG = "cloud";
|
||||
private final static long MIN_SYNC_DELTA = 5 * 60 * 1000L; // Once in 5 minutes
|
||||
private final static long MIN_SYNC_FEATURES_DELTA = 12 * 60 * 60 * 1000L; // Once in 12 hours
|
||||
|
||||
private static boolean isSyncInProgress;
|
||||
private static CloudAPI.UserInfo userInfo;
|
||||
private static CloudAPI.UserFeatures userFeatures;
|
||||
|
||||
private static int modelsUsed;
|
||||
private static int modelsMaxGenerations;
|
||||
private static boolean isLoggingIn;
|
||||
private static APIRequestHandle beginLoginHandle;
|
||||
private static String loginSessionId;
|
||||
@@ -86,31 +78,21 @@ public class CloudController {
|
||||
private static Gson gson = new Gson();
|
||||
|
||||
public static void initCached() {
|
||||
if (Prefs.getCloudCachedUserFeatures() != null) {
|
||||
userFeatures = gson.fromJson(Prefs.getCloudCachedUserFeatures(), CloudAPI.UserFeatures.class);
|
||||
}
|
||||
if (Prefs.getCloudAPIToken() != null) {
|
||||
if (Prefs.getCloudCachedUserInfo() != null) {
|
||||
userInfo = gson.fromJson(Prefs.getCloudCachedUserInfo(), CloudAPI.UserInfo.class);
|
||||
modelsUsed = Prefs.getCloudCachedUsedModels();
|
||||
modelsMaxGenerations = Prefs.getCloudCachedMaxModels();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
long now = Santoku.TRUE_TIME.now().getTime();
|
||||
boolean needSyncInfo = userFeatures == null || now - Prefs.getCloudLastFeaturesSync() > MIN_SYNC_FEATURES_DELTA;
|
||||
if (needSyncInfo) {
|
||||
checkUserFeatures();
|
||||
}
|
||||
|
||||
if (Prefs.getCloudAPIToken() != null) {
|
||||
if (needSyncInfo || userInfo == null) {
|
||||
long now = Santoku.TRUE_TIME.now().getTime();
|
||||
if (userInfo == null) {
|
||||
loadUserInfo();
|
||||
}
|
||||
|
||||
if (!needSyncInfo && userInfo != null && isSyncAvailable() && Prefs.isCloudProfileSyncEnabled()) {
|
||||
if (userInfo != null && isSyncAvailable() && Prefs.isCloudProfileSyncEnabled()) {
|
||||
if (now - Prefs.getCloudLastSync() > MIN_SYNC_DELTA) {
|
||||
syncData();
|
||||
}
|
||||
@@ -137,7 +119,6 @@ public class CloudController {
|
||||
} else {
|
||||
Prefs.setCloudCachedUserInfo(gson.toJson(userInfo));
|
||||
|
||||
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(USER_INFO_AI_GEN_TAG));
|
||||
Santoku.EVENT_BUS.fireEvent(new CloudUserInfoUpdatedEvent());
|
||||
|
||||
if (isLoggingIn) {
|
||||
@@ -148,9 +129,7 @@ public class CloudController {
|
||||
if (isSyncAvailable() && Prefs.isCloudProfileSyncEnabled()) {
|
||||
syncData();
|
||||
}
|
||||
checkGeneratorRemaining();
|
||||
}
|
||||
Prefs.setCloudLastFeaturesSync(Santoku.TRUE_TIME.now().getTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -205,77 +184,23 @@ public class CloudController {
|
||||
}
|
||||
|
||||
public static void logout() {
|
||||
CloudAPI.INSTANCE.logout(response -> {});
|
||||
Prefs.setCloudAPIToken(null);
|
||||
userInfo = null;
|
||||
Santoku.EVENT_BUS.fireEvent(new CloudLoginStateUpdatedEvent());
|
||||
Santoku.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);
|
||||
Santoku.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(Santoku.TRUE_TIME.now().getTime());
|
||||
}
|
||||
Santoku.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() {
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
public static CloudAPI.UserFeatures getUserFeatures() {
|
||||
return userFeatures;
|
||||
}
|
||||
|
||||
public static boolean hasAccountFeatures() {
|
||||
return userFeatures != null && userFeatures.levels != null && !userFeatures.levels.isEmpty();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isSyncAvailable() {
|
||||
return Prefs.getCloudAPIToken() != null && userInfo != null && userFeatures != null && userInfo.currentLevel >= userFeatures.syncRequiredLevel;
|
||||
}
|
||||
|
||||
public static boolean needShowAIGenerator() {
|
||||
return userFeatures != null && userFeatures.aiGeneratorRequiredLevel >= 0;
|
||||
}
|
||||
|
||||
public static int getGeneratedModels() {
|
||||
return modelsUsed;
|
||||
}
|
||||
|
||||
public static int getMaxGeneratedModels() {
|
||||
return modelsMaxGenerations;
|
||||
return Prefs.getCloudAPIToken() != null && userInfo != null;
|
||||
}
|
||||
|
||||
private static void downloadData(long lastModified) {
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package com.dark98.santoku.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;
|
||||
@@ -13,13 +11,9 @@ 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;
|
||||
@@ -28,26 +22,17 @@ 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 com.dark98.santoku.BeamServerData;
|
||||
import com.dark98.santoku.R;
|
||||
import com.dark98.santoku.Santoku;
|
||||
import com.dark98.santoku.events.BeamServerDataUpdatedEvent;
|
||||
import com.dark98.santoku.theme.ThemesRepo;
|
||||
import com.dark98.santoku.utils.ViewUtils;
|
||||
import com.dark98.santoku.view.BeamButton;
|
||||
import com.dark98.santoku.view.BoostySubsView;
|
||||
|
||||
public class ChangeLogBottomSheet extends BottomSheetDialog {
|
||||
private BoostySubsView subsView;
|
||||
private ScrollView scrollView;
|
||||
private ViewPager pager;
|
||||
|
||||
public ChangeLogBottomSheet(@NonNull Context context) {
|
||||
super(context);
|
||||
@@ -77,18 +62,6 @@ public class ChangeLogBottomSheet extends BottomSheetDialog {
|
||||
}});
|
||||
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);
|
||||
@@ -120,112 +93,11 @@ public class ChangeLogBottomSheet extends BottomSheetDialog {
|
||||
scrollView.addView(text);
|
||||
|
||||
DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||
ll.addView(scrollView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) (dm.heightPixels * 0.45f)));
|
||||
|
||||
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 (Santoku.SERVER_DATA != null) {
|
||||
List<String> list = new ArrayList<>(Santoku.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();
|
||||
}
|
||||
});
|
||||
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);
|
||||
}});
|
||||
@@ -233,18 +105,6 @@ public class ChangeLogBottomSheet extends BottomSheetDialog {
|
||||
ll.setFitsSystemWindows(true);
|
||||
setContentView(ll);
|
||||
|
||||
Santoku.EVENT_BUS.registerListener(this);
|
||||
setOnDismissListener(dialog -> Santoku.EVENT_BUS.unregisterListener(this));
|
||||
}
|
||||
|
||||
@EventHandler(runOnMainThread = true)
|
||||
public void onDataUpdated(BeamServerDataUpdatedEvent e) {
|
||||
if (Santoku.SERVER_DATA != null) {
|
||||
List<String> list = new ArrayList<>(Santoku.SERVER_DATA.boostySubscribers);
|
||||
Collections.shuffle(list);
|
||||
subsView.setStrings(list);
|
||||
}
|
||||
pager.getAdapter().notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -10,7 +10,6 @@ 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;
|
||||
@@ -26,7 +25,7 @@ import java.util.List;
|
||||
|
||||
import com.dark98.santoku.R;
|
||||
import com.dark98.santoku.Santoku;
|
||||
import com.dark98.santoku.cloud.CloudAPI;
|
||||
import com.dark98.santoku.BuildConfig;
|
||||
import com.dark98.santoku.cloud.CloudController;
|
||||
import com.dark98.santoku.events.NeedDismissSnackbarEvent;
|
||||
import com.dark98.santoku.recycler.PreferenceSwitchItem;
|
||||
@@ -65,77 +64,56 @@ public class CloudManageBottomSheet extends BottomSheetDialog {
|
||||
}});
|
||||
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);
|
||||
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);
|
||||
}});
|
||||
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;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
Santoku.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)));
|
||||
}
|
||||
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);
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
@@ -15,7 +14,6 @@ import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
@@ -33,20 +31,13 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import ru.ytkab0bp.eventbus.EventHandler;
|
||||
import com.dark98.santoku.BeamServerData;
|
||||
import com.dark98.santoku.BuildConfig;
|
||||
import com.dark98.santoku.MainActivity;
|
||||
import com.dark98.santoku.R;
|
||||
import com.dark98.santoku.SetupActivity;
|
||||
import com.dark98.santoku.Santoku;
|
||||
import com.dark98.santoku.cloud.CloudController;
|
||||
import com.dark98.santoku.components.BeamAlertDialogBuilder;
|
||||
import com.dark98.santoku.components.UnfoldMenu;
|
||||
import com.dark98.santoku.components.WebViewMenu;
|
||||
import com.dark98.santoku.config.ConfigObject;
|
||||
import com.dark98.santoku.events.CloudFeaturesUpdatedEvent;
|
||||
import com.dark98.santoku.events.CloudModelsRemainingCountUpdatedEvent;
|
||||
import com.dark98.santoku.events.NeedDismissAIGeneratorMenu;
|
||||
import com.dark98.santoku.events.NeedDismissCalibrationsMenu;
|
||||
import com.dark98.santoku.events.NeedDismissSnackbarEvent;
|
||||
import com.dark98.santoku.events.NeedSnackbarEvent;
|
||||
@@ -61,11 +52,9 @@ import com.dark98.santoku.slic3r.Bed3D;
|
||||
import com.dark98.santoku.slic3r.Slic3rRuntimeError;
|
||||
import com.dark98.santoku.theme.BeamTheme;
|
||||
import com.dark98.santoku.theme.ThemesRepo;
|
||||
import com.dark98.santoku.utils.Prefs;
|
||||
import com.dark98.santoku.utils.ViewUtils;
|
||||
import com.dark98.santoku.view.DividerView;
|
||||
import com.dark98.santoku.view.FadeRecyclerView;
|
||||
import com.dark98.santoku.view.SegmentsView;
|
||||
import com.dark98.santoku.view.SnackbarsLayout;
|
||||
|
||||
public class FileMenu extends ListBedMenu {
|
||||
@@ -126,29 +115,6 @@ public class FileMenu extends ListBedMenu {
|
||||
}
|
||||
}),
|
||||
new SpaceItem(portrait ? ViewUtils.dp(3) : 0, portrait ? 0 : ViewUtils.dp(3))));
|
||||
if (BeamServerData.isBoostyAvailable() && CloudController.needShowAIGenerator()) {
|
||||
list.add(new BedMenuItem(R.string.MenuFileAIGenerator, R.drawable.picture_stack_outline_28).setShiny(true).onClick(view -> {
|
||||
if (Prefs.getCloudAPIToken() == null || CloudController.getUserInfo() != null && CloudController.getMaxGeneratedModels() == 0) {
|
||||
Context ctx = view.getContext();
|
||||
ctx.startActivity(new Intent(ctx, SetupActivity.class).putExtra(SetupActivity.EXTRA_CLOUD_PROFILE, true));
|
||||
return;
|
||||
}
|
||||
if (CloudController.getUserInfo() == null) {
|
||||
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.LOADING, R.string.MenuFileAIGeneratorPleaseWaitSetup).tag(CloudController.USER_INFO_AI_GEN_TAG));
|
||||
ViewUtils.postOnMainThread(() -> {
|
||||
if (CloudController.getUserInfo() == null) {
|
||||
Santoku.EVENT_BUS.fireEvent(new NeedDismissSnackbarEvent(CloudController.USER_INFO_AI_GEN_TAG));
|
||||
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.MenuFileAIGeneratorErrorNotLoadedUserAccount));
|
||||
} else {
|
||||
fragment.showUnfoldMenu(new AIGeneratorMenu(), view);
|
||||
}
|
||||
}, 2500);
|
||||
return;
|
||||
}
|
||||
|
||||
fragment.showUnfoldMenu(new AIGeneratorMenu(), view);
|
||||
}));
|
||||
}
|
||||
list.addAll(Arrays.asList(
|
||||
new BedMenuItem(R.string.MenuFileCalibrations, R.drawable.wrench_outline_28).setSingleLine(true).onClick(v -> {
|
||||
if (!fragment.getGlView().getRenderer().getBed().isValid()) {
|
||||
@@ -263,10 +229,7 @@ public class FileMenu extends ListBedMenu {
|
||||
public void onObjectsChanged(ObjectsListChangedEvent e) {
|
||||
((BedMenuItem) adapter.getItems().get(1)).setEnabled(hasSelection());
|
||||
adapter.notifyItemChanged(1);
|
||||
|
||||
int i = 8 - (BeamServerData.isBoostyAvailable() && CloudController.needShowAIGenerator() ? 0 : 1);
|
||||
((BedMenuItem) adapter.getItems().get(i)).setEnabled(hasModel());
|
||||
adapter.notifyItemChanged(i);
|
||||
updateModelItems();
|
||||
}
|
||||
|
||||
@EventHandler(runOnMainThread = true)
|
||||
@@ -275,147 +238,17 @@ public class FileMenu extends ListBedMenu {
|
||||
adapter.notifyItemChanged(1);
|
||||
}
|
||||
|
||||
@EventHandler(runOnMainThread = true)
|
||||
public void onFeaturedUpdated(CloudFeaturesUpdatedEvent e) {
|
||||
adapter.setItems(onCreateItems(wasPortrait));
|
||||
}
|
||||
|
||||
public final static class AIGeneratorMenu extends UnfoldMenu {
|
||||
private TextView remainingView;
|
||||
private SegmentsView segmentsView;
|
||||
|
||||
@Override
|
||||
public int getRequestedSize(FrameLayout into, boolean portrait) {
|
||||
return (int) (portrait ? ViewUtils.dp(52) + ViewUtils.dp(60) * 2 + ViewUtils.dp(28) + ViewUtils.dp(18) + ViewUtils.dp(2) : into.getWidth() * 0.6f);
|
||||
private void updateModelItems() {
|
||||
int idx = -1;
|
||||
for (int j = adapter.getItems().size() - 1; j >= 0; j--) {
|
||||
if (adapter.getItems().get(j) instanceof BedMenuItem) {
|
||||
idx = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View onCreateView(Context ctx, boolean portrait) {
|
||||
LinearLayout ll = new LinearLayout(ctx);
|
||||
ll.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
RecyclerView rv = new FadeRecyclerView(ctx);
|
||||
rv.setOverScrollMode(View.OVER_SCROLL_NEVER);
|
||||
SimpleRecyclerAdapter adapter = new SimpleRecyclerAdapter();
|
||||
adapter.setItems(Arrays.asList(
|
||||
new PreferenceItem().setIcon(R.drawable.camera_outline_28).setTitle(ctx.getString(R.string.MenuFileAIGeneratorFromCamera)).setOnClickListener(v -> {
|
||||
if (CloudController.getGeneratedModels() >= CloudController.getMaxGeneratedModels()) {
|
||||
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.MenuFileAIGeneratorNoGenerationsLeft));
|
||||
return;
|
||||
}
|
||||
if (MainActivity.IS_GENERATING_AI_MODEL) {
|
||||
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.WARNING, R.string.MenuFileAIGeneratorAlreadyGenerating));
|
||||
return;
|
||||
}
|
||||
if (ctx instanceof MainActivity) {
|
||||
try {
|
||||
MainActivity.aiTempFile = File.createTempFile("ai_capture", ".jpg");
|
||||
Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
i.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(ctx, BuildConfig.APPLICATION_ID + ".provider", MainActivity.aiTempFile));
|
||||
i.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
((MainActivity) ctx).startActivityForResult(i, MainActivity.REQUEST_CODE_AI_GENERATOR_TAKE_PHOTO);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}),
|
||||
new PreferenceItem().setIcon(R.drawable.picture_outline_28).setTitle(ctx.getString(R.string.MenuFileAIGeneratorFromGallery)).setOnClickListener(v -> {
|
||||
if (CloudController.getGeneratedModels() >= CloudController.getMaxGeneratedModels()) {
|
||||
Santoku.EVENT_BUS.fireEvent(new NeedSnackbarEvent(SnackbarsLayout.Type.ERROR, R.string.MenuFileAIGeneratorNoGenerationsLeft));
|
||||
return;
|
||||
}
|
||||
if (MainActivity.IS_GENERATING_AI_MODEL) {
|
||||
Santoku.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();
|
||||
|
||||
Santoku.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();
|
||||
|
||||
Santoku.EVENT_BUS.unregisterListener(this);
|
||||
}
|
||||
|
||||
private void updateRemaining() {
|
||||
int rev = CloudController.getMaxGeneratedModels() - CloudController.getGeneratedModels();
|
||||
remainingView.setText(Santoku.INSTANCE.getString(R.string.MenuFileAIGeneratorRemaining, rev, CloudController.getMaxGeneratedModels()));
|
||||
segmentsView.setValues(new float[]{0, rev / (float) CloudController.getMaxGeneratedModels(), 1});
|
||||
if (idx != -1) {
|
||||
((BedMenuItem) adapter.getItems().get(idx)).setEnabled(hasModel());
|
||||
adapter.notifyItemChanged(idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.dark98.santoku.events;
|
||||
|
||||
import ru.ytkab0bp.eventbus.Event;
|
||||
|
||||
@Event
|
||||
public class BeamServerDataUpdatedEvent {}
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.dark98.santoku.events;
|
||||
|
||||
import ru.ytkab0bp.eventbus.Event;
|
||||
|
||||
@Event
|
||||
public class CloudFeaturesUpdatedEvent {}
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.dark98.santoku.events;
|
||||
|
||||
import ru.ytkab0bp.eventbus.Event;
|
||||
|
||||
@Event
|
||||
public class CloudModelsRemainingCountUpdatedEvent {}
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.dark98.santoku.events;
|
||||
|
||||
import ru.ytkab0bp.eventbus.Event;
|
||||
|
||||
@Event
|
||||
public class NeedDismissAIGeneratorMenu {}
|
||||
@@ -20,15 +20,14 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import ru.ytkab0bp.eventbus.EventHandler;
|
||||
import com.dark98.santoku.BeamServerData;
|
||||
import com.dark98.santoku.R;
|
||||
import com.dark98.santoku.SetupActivity;
|
||||
import com.dark98.santoku.Santoku;
|
||||
import com.dark98.santoku.components.BeamAlertDialogBuilder;
|
||||
import com.dark98.santoku.components.BeamColorPickerPopUp;
|
||||
import com.dark98.santoku.config.ConfigObject;
|
||||
import com.dark98.santoku.events.BeamServerDataUpdatedEvent;
|
||||
import com.dark98.santoku.events.CloudUserInfoUpdatedEvent;
|
||||
import com.dark98.santoku.cloud.CloudController;
|
||||
import com.dark98.santoku.recycler.PreferenceItem;
|
||||
import com.dark98.santoku.theme.BeamTheme;
|
||||
import com.dark98.santoku.theme.ThemesRepo;
|
||||
@@ -46,7 +45,7 @@ public class SettingsFragment extends ProfileListFragment {
|
||||
@Override
|
||||
protected List<OptionElement> getConfigItems() {
|
||||
return Arrays.asList(
|
||||
BeamServerData.isCloudAvailable() ? new OptionElement(SPECIAL_TYPE_CLOUD_HEADER).setOnClick(() -> {
|
||||
CloudController.hasAccountFeatures() ? new OptionElement(SPECIAL_TYPE_CLOUD_HEADER).setOnClick(() -> {
|
||||
Activity act = (Activity) getContext();
|
||||
act.startActivity(new Intent(act, SetupActivity.class).putExtra(SetupActivity.EXTRA_CLOUD_PROFILE, true));
|
||||
}) : null,
|
||||
@@ -112,7 +111,7 @@ public class SettingsFragment extends ProfileListFragment {
|
||||
BeamTheme.LIGHT.colors.put(android.R.attr.colorAccent, Prefs.getAccentColor());
|
||||
BeamTheme.DARK.colors.put(android.R.attr.colorAccent, Prefs.getAccentColor());
|
||||
ThemesRepo.invalidate((Activity) getContext());
|
||||
recyclerView.getAdapter().notifyItemChanged(2 - (BeamServerData.isCloudAvailable() ? 0 : 1));
|
||||
recyclerView.getAdapter().notifyItemChanged(2 - (CloudController.hasAccountFeatures() ? 0 : 1));
|
||||
}
|
||||
})
|
||||
.setNegativeButtonText(getContext().getString(R.string.SettingsInterfaceColorReset))
|
||||
@@ -135,7 +134,7 @@ public class SettingsFragment extends ProfileListFragment {
|
||||
Prefs.setRenderScale(variants[which]);
|
||||
dialog.dismiss();
|
||||
// I'm too lazy to calculate real position for now
|
||||
recyclerView.getAdapter().notifyItemChanged(4 - (BeamServerData.isCloudAvailable() ? 0 : 1));
|
||||
recyclerView.getAdapter().notifyItemChanged(4 - (CloudController.hasAccountFeatures() ? 0 : 1));
|
||||
})
|
||||
.show();
|
||||
})),
|
||||
@@ -143,18 +142,6 @@ public class SettingsFragment extends ProfileListFragment {
|
||||
Activity act = (Activity) getContext();
|
||||
act.startActivity(new Intent(act, SetupActivity.class).putExtra(SetupActivity.EXTRA_ABOUT, true));
|
||||
}),
|
||||
new OptionElement(R.drawable.telegram, getContext().getString(R.string.SettingsTelegram)).setColor(R.attr.telegramColor, false).setOnClick(() -> {
|
||||
Activity act = (Activity) getContext();
|
||||
act.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://t.me/ytkab0bp_channel")));
|
||||
}),
|
||||
BeamServerData.isBoostyAvailable() ? new OptionElement(R.drawable.boosty, getContext().getString(R.string.SettingsBoosty)).setColor(R.attr.boostyColorTop, true).setOnClick(() -> {
|
||||
Activity act = (Activity) getContext();
|
||||
act.startActivity(new Intent(act, SetupActivity.class).putExtra(SetupActivity.EXTRA_BOOSTY_ONLY, true));
|
||||
}) : null,
|
||||
new OptionElement(R.drawable.k3d_logo_new_14, getContext().getString(R.string.SettingsK3D)).setColor(R.attr.k3dColor, true).setOnClick(() -> {
|
||||
Activity act = (Activity) getContext();
|
||||
act.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://t.me/K_3_D")));
|
||||
}),
|
||||
new OptionElement(R.drawable.refresh_outline_28, getContext().getString(R.string.SettingsResetToDefault)).setColor(R.attr.textColorNegative, false).setOnClick(() -> {
|
||||
Context ctx = getContext();
|
||||
if (ctx instanceof Activity) {
|
||||
@@ -177,14 +164,9 @@ public class SettingsFragment extends ProfileListFragment {
|
||||
);
|
||||
}
|
||||
|
||||
@EventHandler(runOnMainThread = true)
|
||||
public void onDataUpdated(BeamServerDataUpdatedEvent e) {
|
||||
setConfigItems(getConfigItems());
|
||||
}
|
||||
|
||||
@EventHandler(runOnMainThread = true)
|
||||
public void onUserInfoUpdated(CloudUserInfoUpdatedEvent e) {
|
||||
if (BeamServerData.isCloudAvailable()) {
|
||||
if (CloudController.hasAccountFeatures()) {
|
||||
recyclerView.getAdapter().notifyItemChanged(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,6 @@ public class BeamTheme {
|
||||
colors.put(R.attr.dividerContrastColor, 0xffcccccc);
|
||||
colors.put(R.attr.dialogBackground, 0xffffffff);
|
||||
colors.put(R.attr.switchThumbUncheckedColor, 0xffeef2f3);
|
||||
colors.put(R.attr.boostyColorTop, 0xfff06e2a);
|
||||
colors.put(R.attr.boostyColorBottom, 0xff884725);
|
||||
colors.put(R.attr.telegramColor, 0xff27a7e7);
|
||||
colors.put(R.attr.k3dColor, 0xff039045);
|
||||
colors.put(R.attr.modelHoverColor, 0xffffffff);
|
||||
colors.put(R.attr.textColorNegative, 0xffff464a);
|
||||
|
||||
@@ -73,7 +69,6 @@ public class BeamTheme {
|
||||
colors.put(R.attr.bedContourlinesColor, 0x40ffffff);
|
||||
colors.put(R.attr.backgroundColorTop, 0xff292929);
|
||||
colors.put(R.attr.backgroundColorBottom, 0xff181818);
|
||||
colors.put(R.attr.boostyColorBottom, 0xff884725);
|
||||
|
||||
colors.put(R.attr.xTrackColor, 0xffee0000);
|
||||
colors.put(R.attr.yTrackColor, 0xff00ee00);
|
||||
|
||||
@@ -47,31 +47,6 @@ public class Prefs {
|
||||
mPrefs.edit().putBoolean("scale_linked", v).apply();
|
||||
}
|
||||
|
||||
public static long getLastCheckedInfo() {
|
||||
return mPrefs.getLong("last_checked_info", 0);
|
||||
}
|
||||
|
||||
public static void setLastCheckedInfo() {
|
||||
mPrefs.edit().putLong("last_checked_info", System.currentTimeMillis()).apply();
|
||||
}
|
||||
|
||||
// Only used for displaying Boosty info, nothing more
|
||||
public static boolean isRussianIP() {
|
||||
return mPrefs.getBoolean("russian_ip", false);
|
||||
}
|
||||
|
||||
public static void setRussianIP(boolean v) {
|
||||
mPrefs.edit().putBoolean("russian_ip", v).apply();
|
||||
}
|
||||
|
||||
public static void setBeamServerData(String data) {
|
||||
mPrefs.edit().putString("beam_server_data", data).apply();
|
||||
}
|
||||
|
||||
public static String getBeamServerData() {
|
||||
return mPrefs.getString("beam_server_data", "{}");
|
||||
}
|
||||
|
||||
public static int getCameraControlMode() {
|
||||
return mPrefs.getInt("camera_control_mode", mPrefs.getBoolean("rotation_enabled", true) ? CAMERA_CONTROL_MODE_ROTATE_MOVE : CAMERA_CONTROL_MODE_MOVE_ONLY);
|
||||
}
|
||||
@@ -161,40 +136,6 @@ public class Prefs {
|
||||
e.apply();
|
||||
}
|
||||
|
||||
public static int getCloudCachedUsedModels() {
|
||||
return mPrefs.getInt("cloud_cached_models_used", 0);
|
||||
}
|
||||
|
||||
public static int getCloudCachedMaxModels() {
|
||||
return mPrefs.getInt("cloud_cached_models_max", 50);
|
||||
}
|
||||
|
||||
public static void setCloudCachedUsedMaxModels(int used, int max) {
|
||||
mPrefs.edit().putInt("cloud_cached_models_used", used).putInt("cloud_cached_models_max", max).apply();
|
||||
}
|
||||
|
||||
public static String getCloudCachedUserFeatures() {
|
||||
return mPrefs.getString("cloud_cached_user_features", null);
|
||||
}
|
||||
|
||||
public static void setCloudCachedUserFeatures(String features) {
|
||||
SharedPreferences.Editor e = mPrefs.edit();
|
||||
if (features == null) {
|
||||
e.remove("cloud_cached_user_features");
|
||||
} else {
|
||||
e.putString("cloud_cached_user_features", features);
|
||||
}
|
||||
e.apply();
|
||||
}
|
||||
|
||||
public static long getCloudLastFeaturesSync() {
|
||||
return mPrefs.getLong("cloud_last_features_sync", 0);
|
||||
}
|
||||
|
||||
public static void setCloudLastFeaturesSync(long ls) {
|
||||
mPrefs.edit().putLong("cloud_last_features_sync", ls).apply();
|
||||
}
|
||||
|
||||
public static long getCloudLastSync() {
|
||||
return mPrefs.getLong("cloud_last_sync", 0);
|
||||
}
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
package com.dark98.santoku.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.text.TextPaint;
|
||||
import android.text.TextUtils;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.core.math.MathUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.dark98.santoku.R;
|
||||
import com.dark98.santoku.theme.ThemesRepo;
|
||||
import com.dark98.santoku.utils.ViewUtils;
|
||||
|
||||
public class BoostySubsView extends View {
|
||||
private TextPaint paint = new TextPaint();
|
||||
|
||||
private List<String> strings = new ArrayList<>();
|
||||
private SparseArray<CharSequence> ellipsizedStrings = new SparseArray<>();
|
||||
private int index;
|
||||
private float progress;
|
||||
private long lastUpdated;
|
||||
private int firstHeight;
|
||||
|
||||
private Rect rect = new Rect();
|
||||
|
||||
public BoostySubsView(Context context) {
|
||||
super(context);
|
||||
|
||||
paint.setTextSize(ViewUtils.dp(20));
|
||||
paint.setTypeface(ViewUtils.getTypeface(ViewUtils.ROBOTO_MEDIUM));
|
||||
updateColors();
|
||||
}
|
||||
|
||||
public void setStrings(List<String> strings) {
|
||||
this.strings = strings;
|
||||
ellipsizedStrings.clear();
|
||||
index = 0;
|
||||
progress = 0;
|
||||
if (!strings.isEmpty()) {
|
||||
String str = strings.get(index);
|
||||
paint.getTextBounds(str, 0, str.length(), rect);
|
||||
firstHeight = rect.height();
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
long dt = Math.min(16, System.currentTimeMillis() - lastUpdated);
|
||||
lastUpdated = System.currentTimeMillis();
|
||||
if (!strings.isEmpty()) {
|
||||
float tY = (ViewUtils.dp(24) + firstHeight) * progress;
|
||||
canvas.save();
|
||||
canvas.translate(0, -tY);
|
||||
float halfHeight = getHeight() / 2f;
|
||||
int y = 0;
|
||||
|
||||
int i = index;
|
||||
while (y <= getHeight() + tY) {
|
||||
int j = i;
|
||||
while (j < 0) j += strings.size();
|
||||
while (j >= strings.size()) j -= strings.size();
|
||||
|
||||
CharSequence str = ellipsizedStrings.get(j);
|
||||
if (str == null) {
|
||||
ellipsizedStrings.set(j, str = TextUtils.ellipsize(strings.get(j), paint, getWidth() - getPaddingLeft() - getPaddingRight(), TextUtils.TruncateAt.END));
|
||||
}
|
||||
|
||||
paint.getTextBounds(str.toString(), 0, str.length(), rect);
|
||||
float highlight = (1f - Math.abs((y - tY - firstHeight / 2f - halfHeight) / halfHeight));
|
||||
highlight = MathUtils.clamp(highlight, 0, 1);
|
||||
paint.setAlpha((int) (0xFF * highlight));
|
||||
|
||||
float x = (getWidth() - rect.width()) / 2f;
|
||||
canvas.drawText(str, 0, str.length(), x, y, paint);
|
||||
|
||||
y += rect.height() + ViewUtils.dp(24);
|
||||
i++;
|
||||
}
|
||||
|
||||
canvas.restore();
|
||||
|
||||
progress += dt / 2000f;
|
||||
if (progress > 1) {
|
||||
progress -= 1f;
|
||||
index++;
|
||||
index %= strings.size();
|
||||
|
||||
String str = strings.get(index);
|
||||
paint.getTextBounds(str, 0, str.length(), rect);
|
||||
firstHeight = rect.height();
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateColors() {
|
||||
paint.setColor(ThemesRepo.getColor(R.attr.textColorOnAccent));
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="M8.3,27.6l6.8,-23.9h10.4l-2.1,7.4c0,0 0,0.1 -0.1,0.1l-5.5,19.6h5.2c-2.2,5.5 -3.8,9.8 -5.1,12.9C8.4,43.6 5.7,36.6 8,28.4L8.3,27.6zM17.9,43.7l12.5,-18.4h-5.3l4.6,-11.8c7.9,0.8 11.7,7.2 9.5,14.9c-2.4,8.3 -11.9,15.3 -21.1,15.3H17.9z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="27.365"
|
||||
android:startY="8.888"
|
||||
android:endX="18.461"
|
||||
android:endY="55.436"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFEF7829"/>
|
||||
<item android:offset="0.28" android:color="#FFF0692A"/>
|
||||
<item android:offset="0.63" android:color="#FFF15E2C"/>
|
||||
<item android:offset="1" android:color="#FFF15A2C"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</vector>
|
||||
@@ -1,10 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="28dp"
|
||||
android:height="28dp"
|
||||
android:viewportWidth="28"
|
||||
android:viewportHeight="28">
|
||||
<path
|
||||
android:pathData="M12.073,2C12.824,2 13.464,2.233 14,2.593C14.536,2.233 15.176,2 15.927,2C17.932,2 19.58,3.618 19.58,5.642C19.58,6.04 19.539,6.493 19.377,6.977C19.215,7.465 18.964,7.884 18.662,8.262C18.115,8.946 17.279,9.63 16.238,10.435L15.523,10.988C14.626,11.681 13.374,11.681 12.477,10.988L11.762,10.435C10.72,9.63 9.885,8.946 9.338,8.262C9.036,7.884 8.785,7.465 8.623,6.977C8.461,6.493 8.42,6.04 8.42,5.642C8.42,3.618 10.068,2 12.073,2ZM12.073,4C11.16,4 10.42,4.735 10.42,5.642C10.42,6.671 10.845,7.199 12.985,8.853L13.7,9.405C13.877,9.541 14.123,9.541 14.3,9.405L15.015,8.853C17.155,7.199 17.58,6.671 17.58,5.642C17.58,4.735 16.84,4 15.927,4C15.375,4 14.873,4.312 14.401,4.99L14.268,5.182C14.165,5.329 13.963,5.366 13.815,5.264C13.783,5.242 13.755,5.214 13.732,5.182L13.599,4.99C13.127,4.312 12.625,4 12.073,4ZM6.885,10.383C7.397,10.175 7.642,9.591 7.434,9.079C7.225,8.568 6.642,8.323 6.13,8.531L4.481,9.203L4.361,9.252C3.811,9.476 3.312,9.678 2.929,10.03C2.594,10.337 2.337,10.719 2.179,11.145C1.998,11.633 1.999,12.171 2,12.765L2,12.895V18.527L2,18.648C1.999,19.185 1.998,19.686 2.163,20.144C2.307,20.543 2.542,20.904 2.849,21.198C3.201,21.534 3.659,21.736 4.151,21.953L4.151,21.953L4.261,22.002L12.083,25.466L12.172,25.505C12.642,25.713 13.03,25.886 13.446,25.956C13.813,26.017 14.187,26.017 14.553,25.956C14.969,25.886 15.358,25.713 15.828,25.505L15.828,25.505L15.916,25.466L23.739,22.002L23.849,21.953C24.341,21.736 24.799,21.534 25.151,21.198C25.458,20.904 25.693,20.543 25.837,20.144C26.002,19.686 26.001,19.185 26,18.648V18.648L26,18.527V12.896L26,12.766C26.001,12.172 26.002,11.634 25.821,11.146C25.663,10.719 25.405,10.337 25.07,10.03C24.687,9.678 24.188,9.476 23.637,9.253L23.516,9.204L21.869,8.533C21.358,8.325 20.774,8.571 20.566,9.083C20.358,9.594 20.604,10.178 21.115,10.386L22.718,11.038L15.108,14.414C14.504,14.682 14.358,14.739 14.222,14.762C14.075,14.787 13.925,14.787 13.778,14.762C13.642,14.739 13.496,14.682 12.891,14.414L9.156,12.757L5.281,11.037L6.885,10.383ZM15.92,16.242L24,12.657C24,12.73 24,12.809 24,12.896V18.527C24,19.263 23.986,19.381 23.955,19.465C23.916,19.574 23.852,19.673 23.768,19.753C23.704,19.814 23.602,19.875 22.929,20.173L15.106,23.637L15,23.684V16.621C15.263,16.535 15.53,16.416 15.831,16.282L15.92,16.242ZM12.169,16.282C12.469,16.415 12.737,16.534 13,16.621V23.684L12.893,23.637L5.071,20.173C4.398,19.875 4.296,19.814 4.232,19.753C4.148,19.673 4.084,19.574 4.045,19.465C4.014,19.381 4,19.263 4,18.527V12.895C4,12.808 4,12.729 4.001,12.658L8.344,14.585L12.08,16.242L12.169,16.282L12.169,16.282Z"
|
||||
android:fillColor="#000"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -1,10 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="28dp"
|
||||
android:height="28dp"
|
||||
android:viewportWidth="28"
|
||||
android:viewportHeight="28">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M3.5,12.6C10,9.8 14.3,7.9 16.5,7c6.2,-2.6 7.5,-3 8.3,-3c0.2,0 0.6,0 0.9,0.3c0.2,0.2 0.3,0.4 0.3,0.6C26,5 26,5.4 26,5.7c-0.3,3.5 -1.8,12.1 -2.5,16c-0.3,1.7 -0.9,2.2 -1.5,2.3c-1.3,0.1 -2.3,-0.9 -3.5,-1.7c-2,-1.3 -3.1,-2.1 -5,-3.3c-2.2,-1.5 -0.8,-2.2 0.5,-3.6c0.3,-0.3 6,-5.5 6.1,-6c0,-0.1 0,-0.3 -0.1,-0.4c-0.1,-0.1 -0.3,-0.1 -0.5,0c-0.2,0 -3.3,2.1 -9.4,6.2c-0.9,0.6 -1.7,0.9 -2.4,0.9c-0.8,0 -2.3,-0.5 -3.5,-0.8c-1.4,-0.5 -2.3,-0.7 -2.2,-1.5C2.1,13.4 2.5,13 3.5,12.6z"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
@@ -16,19 +16,6 @@
|
||||
<string name="MenuFileOpenFileBigObject">Файл содержит более 500к треугольников. Нарезка может быть медленной.</string>
|
||||
<string name="MenuFileOpenFileLoading">Загрузка файла…</string>
|
||||
<string name="MenuFileDelete">Убрать модель</string>
|
||||
<string name="MenuFileAIGenerator">Модель\nпо фото</string>
|
||||
<string name="MenuFileAIGeneratorPleaseWaitSetup">Пожалуйста, подождите…</string>
|
||||
<string name="MenuFileAIGeneratorErrorNotLoadedUserAccount">Ошибка: данные о пользователе пока не загружены.</string>
|
||||
<string name="MenuFileAIGeneratorFromCamera">Сделать фото</string>
|
||||
<string name="MenuFileAIGeneratorFromGallery">Выбрать из галереи</string>
|
||||
<string name="MenuFileAIGeneratorRemaining">Осталось: %d / %d генераций</string>
|
||||
<string name="MenuFileAIGeneratorUploading">Загрузка изображения…</string>
|
||||
<string name="MenuFileAIGeneratorProcessing">Обработка изображения…</string>
|
||||
<string name="MenuFileAIGeneratorDownloading">Скачивание модели…</string>
|
||||
<string name="MenuFileAIGeneratorError">Не удалось сгенерировать модель</string>
|
||||
<string name="MenuFileAIGeneratorSavedAs">Модель сохранена как %s.</string>
|
||||
<string name="MenuFileAIGeneratorNoGenerationsLeft">Не осталось генераций.</string>
|
||||
<string name="MenuFileAIGeneratorAlreadyGenerating">Уже генерируется другая модель.</string>
|
||||
<string name="MenuFileCalibrations">Калибров.</string>
|
||||
<string name="MenuFileCalibrationsLA">K3D Linear Advance</string>
|
||||
<string name="MenuFileCalibrationsLADescription">Калибровка Linear/Pressure Advance</string>
|
||||
@@ -149,8 +136,6 @@
|
||||
<string name="IntroNoReposDescription">Вам необходимо настроить репозитории или использовать пользовательский профиль.</string>
|
||||
<string name="IntroNoProfiles">Профили не настроены</string>
|
||||
<string name="IntroNoProfilesDescription">Вы должны выбрать хотя бы один профиль.</string>
|
||||
<string name="IntroBoostyTitle">Поддерживается этими замечательными людьми на Boosty:</string>
|
||||
<string name="IntroBoostySupport">Подписаться на Boosty</string>
|
||||
<string name="IntroNext">Далее</string>
|
||||
<string name="IntroConfigured">Похоже всё настроено.\nПросто нажмите завершить!</string>
|
||||
<string name="IntroFinish">Завершить</string>
|
||||
@@ -163,9 +148,6 @@
|
||||
<string name="SettingsResetProfileDescription">Это действие не может быть отменено.</string>
|
||||
<string name="SettingsSave">Сохранить</string>
|
||||
<string name="SettingsSaveTitle">Сохранить профиль?</string>
|
||||
<string name="SettingsBoosty">Boosty разработчика</string>
|
||||
<string name="SettingsTelegram">Telegram разработчика</string>
|
||||
<string name="SettingsK3D">Чат K3D</string>
|
||||
<string name="SettingsInterface">Интерфейс</string>
|
||||
<string name="SettingsInterfaceTheme">Тема</string>
|
||||
<string name="SettingsInterfaceThemeSystem">Системная</string>
|
||||
@@ -176,7 +158,7 @@
|
||||
<string name="SettingsInterfaceResolutionScale">Масштаб разрешения 3D</string>
|
||||
<string name="SettingsInterfaceResolutionScaleDescription">Может увеличить производительность путём снижения разрешения</string>
|
||||
<string name="SettingsAbout">О приложении</string>
|
||||
<string name="SettingsAboutVersion">v%s\nОсновано на PrusaSlicer\n\nCreated by YTKAB0BP</string>
|
||||
<string name="SettingsAboutVersion">v%s\nОсновано на PrusaSlicer & SliceBeam\n\nCreated by Dark98</string>
|
||||
<string name="SettingsProfileCopy">%s - Копия</string>
|
||||
<string name="SettingsCloneProfile">Клон. текущий</string>
|
||||
<string name="SettingsDeleteProfile">Удалить текущий</string>
|
||||
@@ -184,20 +166,10 @@
|
||||
<string name="SettingsCloudLoading">Загрузка…</string>
|
||||
<string name="SettingsCloudTapToManage">Нажмите для управления</string>
|
||||
<string name="SettingsCloudTapToShowMore">Нажмите чтобы узнать больше</string>
|
||||
<string name="SettingsCloudManageTitle">Аккаунт Beam 3D</string>
|
||||
<string name="SettingsCloudManageTitle">Аккаунт Santoku</string>
|
||||
<string name="SettingsCloudManageDescription">Даёт следующие преимущества:</string>
|
||||
<string name="SettingsCloudManageFeatureEarlyAccess">Ранний доступ</string>
|
||||
<string name="SettingsCloudManageFeatureEarlyAccessDescription">Вы можете скачать ранние сборки из чата Telegram для подписчиков</string>
|
||||
<string name="SettingsCloudManageFeatureCloudSync">Облачная синхронизация профилей</string>
|
||||
<string name="SettingsCloudManageFeatureCloudSyncDescription">Храните свои профили в облаке Beam</string>
|
||||
<string name="SettingsCloudManageFeatureAIGenerator">ИИ генератор моделей</string>
|
||||
<string name="SettingsCloudManageFeatureAIGeneratorDescription">%1$d моделей по фото в месяц</string>
|
||||
<string name="SettingsCloudManageFeatureFreeForAll">Slice Beam может оставаться бесплатным для всех</string>
|
||||
<string name="SettingsCloudManageFeatureFreeForAllDescription">Ваш ник будет написан в списке поддержавших.\nСпасибо за вашу поддержку!</string>
|
||||
<string name="SettingsCloudManageLevelRedirectMessage">При подписке на данный уровень вы соглашаетесь с условиями обслуживания.</string>
|
||||
<string name="SettingsCloudManageLevelRedirectAlreadySubscribed">Уже подписаны?</string>
|
||||
<string name="SettingsCloudManageFree">Бесплатно</string>
|
||||
<string name="SettingsCloudManageSubscribed">Вы подписаны</string>
|
||||
<string name="SettingsCloudManageWillBeLater">Будет позже</string>
|
||||
<string name="SettingsCloudManageTermsOfService">Условия обслуживания</string>
|
||||
<string name="SettingsCloudManageButtonManage">Настройки аккаунта</string>
|
||||
@@ -206,9 +178,8 @@
|
||||
<string name="SettingsCloudManageButtonLogInCancel">Отменить авторизацию?</string>
|
||||
<string name="SettingsCloudManageButtonLogOut">Выйти</string>
|
||||
<string name="SettingsCloudManageLoggedInAs">Вошли как «%1$s»</string>
|
||||
<string name="SettingsCloudManageSubscription">Управление подпиской</string>
|
||||
<string name="SettingsCloudManageSubscription">Управление аккаунтом</string>
|
||||
<string name="Changelog">Список изменений</string>
|
||||
<string name="ChangelogBoostyDescription">Выход данного обновления поддержали:</string>
|
||||
<string name="ChangelogNext">Далее</string>
|
||||
<string name="ChangelogOK">ОК</string>
|
||||
<string name="OrcaConversionPleaseWait">Конвертация профилей, пожалуйста, подождите…</string>
|
||||
@@ -229,4 +200,4 @@
|
||||
<string name="CloudSyncConflictChooseLocal">Оставить локальные профили</string>
|
||||
<string name="Yes">Да</string>
|
||||
<string name="No">Нет</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -25,10 +25,6 @@
|
||||
<attr name="gcodeViewerWipeTower" format="reference|color"/>
|
||||
<attr name="gcodeViewerCustom" format="reference|color"/>
|
||||
<attr name="switchThumbUncheckedColor" format="reference|color"/>
|
||||
<attr name="boostyColorTop" format="reference|color"/>
|
||||
<attr name="boostyColorBottom" format="reference|color"/>
|
||||
<attr name="telegramColor" format="reference|color"/>
|
||||
<attr name="k3dColor" format="reference|color"/>
|
||||
<attr name="modelHoverColor" format="reference|color"/>
|
||||
<attr name="xTrackColor" format="reference|color"/>
|
||||
<attr name="yTrackColor" format="reference|color"/>
|
||||
|
||||
@@ -17,19 +17,6 @@
|
||||
<string name="MenuFileOpenFileBigObject">File has more than 500k triangles. Processing could be slow.</string>
|
||||
<string name="MenuFileOpenFileLoading">Loading file…</string>
|
||||
<string name="MenuFileDelete">Remove model</string>
|
||||
<string name="MenuFileAIGenerator">Model\nfrom photo</string>
|
||||
<string name="MenuFileAIGeneratorPleaseWaitSetup">Please wait…</string>
|
||||
<string name="MenuFileAIGeneratorErrorNotLoadedUserAccount">Error: user info not fetched yet.</string>
|
||||
<string name="MenuFileAIGeneratorFromCamera">Take a photo</string>
|
||||
<string name="MenuFileAIGeneratorFromGallery">Choose from gallery</string>
|
||||
<string name="MenuFileAIGeneratorRemaining">Remaining: %d / %d generations</string>
|
||||
<string name="MenuFileAIGeneratorUploading">Uploading image…</string>
|
||||
<string name="MenuFileAIGeneratorProcessing">Processing image…</string>
|
||||
<string name="MenuFileAIGeneratorDownloading">Downloading model…</string>
|
||||
<string name="MenuFileAIGeneratorError">Failed to generate model</string>
|
||||
<string name="MenuFileAIGeneratorSavedAs">Saved model as %s.</string>
|
||||
<string name="MenuFileAIGeneratorNoGenerationsLeft">No generations left.</string>
|
||||
<string name="MenuFileAIGeneratorAlreadyGenerating">Already generating another model.</string>
|
||||
<string name="MenuFileCalibrations">Calibrat.</string>
|
||||
<string name="MenuFileCalibrationsLA">K3D Linear Advance</string>
|
||||
<string name="MenuFileCalibrationsLADescription">Linear/Pressure Advance Calibration</string>
|
||||
@@ -149,8 +136,6 @@
|
||||
<string name="IntroNoReposDescription">You must configure repositories or use a custom profile.</string>
|
||||
<string name="IntroNoProfiles">No profiles configured</string>
|
||||
<string name="IntroNoProfilesDescription">You must select at least one profile.</string>
|
||||
<string name="IntroBoostyTitle">Is supported by these awesome people on Boosty:</string>
|
||||
<string name="IntroBoostySupport">Subscribe at Boosty</string>
|
||||
<string name="IntroNext">Next</string>
|
||||
<string name="IntroConfigured">All seems to be configured.\nJust tap finish!</string>
|
||||
<string name="IntroFinish">Finish</string>
|
||||
@@ -163,9 +148,6 @@
|
||||
<string name="SettingsResetProfileDescription">This action can not be undone.</string>
|
||||
<string name="SettingsSave">Save</string>
|
||||
<string name="SettingsSaveTitle">Save profile?</string>
|
||||
<string name="SettingsBoosty">Developer\'s Boosty</string>
|
||||
<string name="SettingsTelegram">Developer\'s Telegram</string>
|
||||
<string name="SettingsK3D">K3D Chat</string>
|
||||
<string name="SettingsInterface">Interface</string>
|
||||
<string name="SettingsInterfaceTheme">Theme</string>
|
||||
<string name="SettingsInterfaceThemeSystem">System</string>
|
||||
@@ -176,7 +158,7 @@
|
||||
<string name="SettingsInterfaceResolutionScale">3D Resolution scale</string>
|
||||
<string name="SettingsInterfaceResolutionScaleDescription">May improve performance by lowering resolution</string>
|
||||
<string name="SettingsAbout">About app</string>
|
||||
<string name="SettingsAboutVersion">v%s\nBased on PrusaSlicer\n\nCreated by YTKAB0BP</string>
|
||||
<string name="SettingsAboutVersion">v%s\nBased on PrusaSlicer & SliceBeam\n\nCreated by Dark98</string>
|
||||
<string name="SettingsProfileCopy">%s - Copy</string>
|
||||
<string name="SettingsCloneProfile">Clone current</string>
|
||||
<string name="SettingsDeleteProfile">Delete current</string>
|
||||
@@ -184,20 +166,10 @@
|
||||
<string name="SettingsCloudLoading">Loading…</string>
|
||||
<string name="SettingsCloudTapToManage">Tap to manage</string>
|
||||
<string name="SettingsCloudTapToShowMore">Tap to learn more</string>
|
||||
<string name="SettingsCloudManageTitle">Beam 3D Account</string>
|
||||
<string name="SettingsCloudManageDescription">Provides the following benefits:</string>
|
||||
<string name="SettingsCloudManageFeatureEarlyAccess">Early access</string>
|
||||
<string name="SettingsCloudManageFeatureEarlyAccessDescription">You can download early builds from Telegram chat for subscribers</string>
|
||||
<string name="SettingsCloudManageTitle">Santoku Account</string>
|
||||
<string name="SettingsCloudManageDescription">Cloud account</string>
|
||||
<string name="SettingsCloudManageFeatureCloudSync">Cloud profiles sync</string>
|
||||
<string name="SettingsCloudManageFeatureCloudSyncDescription">Store your profiles in Beam Cloud</string>
|
||||
<string name="SettingsCloudManageFeatureAIGenerator">AI model generator</string>
|
||||
<string name="SettingsCloudManageFeatureAIGeneratorDescription">%1$d models from photo per month</string>
|
||||
<string name="SettingsCloudManageFeatureFreeForAll">Slice Beam can remain free for all</string>
|
||||
<string name="SettingsCloudManageFeatureFreeForAllDescription">Your nickname will be written in the list of supporters.\nThanks for your support!</string>
|
||||
<string name="SettingsCloudManageLevelRedirectMessage">By subscribing to this level you accept terms of service.</string>
|
||||
<string name="SettingsCloudManageLevelRedirectAlreadySubscribed">Already subscribed?</string>
|
||||
<string name="SettingsCloudManageFree">Free</string>
|
||||
<string name="SettingsCloudManageSubscribed">You are subscribed</string>
|
||||
<string name="SettingsCloudManageWillBeLater">Will be later</string>
|
||||
<string name="SettingsCloudManageTermsOfService">Terms of service</string>
|
||||
<string name="SettingsCloudManageButtonManage">Account settings</string>
|
||||
@@ -206,10 +178,15 @@
|
||||
<string name="SettingsCloudManageButtonLogInCancel">Cancel login?</string>
|
||||
<string name="SettingsCloudManageButtonLogOut">Log out</string>
|
||||
<string name="SettingsCloudManageLoggedInAs">Logged in as «%1$s»</string>
|
||||
<string name="SettingsCloudManageSubscription">Manage subscription</string>
|
||||
<string name="SettingsCloudManageSubscription">Manage Account</string>
|
||||
<string name="SettingsCloudManageButtonSignUp">Sign up</string>
|
||||
<string name="SettingsCloudManageSignUpEmail">Email</string>
|
||||
<string name="SettingsCloudManageSignUpDisplayName">Display name</string>
|
||||
<string name="SettingsCloudManageSignUpPassword">Password</string>
|
||||
<string name="SettingsCloudManageSignUpMissingFields">Please fill in all fields</string>
|
||||
<string name="SettingsCloudManageSignUpFailed">Sign up failed</string>
|
||||
<string name="SettingsCloudManageSignUpShowPassword">Show password</string>
|
||||
<string name="Changelog">Changelog</string>
|
||||
<string name="ChangelogBoosty" translatable="false">Boosty</string>
|
||||
<string name="ChangelogBoostyDescription">The release of this update was supported by:</string>
|
||||
<string name="ChangelogNext">Next</string>
|
||||
<string name="ChangelogOK">OK</string>
|
||||
<string name="OrcaConversionPleaseWait">Converting profiles, please wait…</string>
|
||||
@@ -230,4 +207,4 @@
|
||||
<string name="CloudSyncConflictChooseLocal">Keep local profiles</string>
|
||||
<string name="Yes">Yes</string>
|
||||
<string name="No">No</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
+132
-1
@@ -19,4 +19,135 @@ allprojects {
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
}
|
||||
|
||||
def loadLocalProperties() {
|
||||
def props = new Properties()
|
||||
def propsFile = rootProject.file("local.properties")
|
||||
if (propsFile.exists()) {
|
||||
propsFile.withInputStream { props.load(it) }
|
||||
}
|
||||
return props
|
||||
}
|
||||
|
||||
def resolveSdkDir() {
|
||||
def props = loadLocalProperties()
|
||||
return props.getProperty("sdk.dir") ?: System.getenv("ANDROID_SDK_ROOT") ?: System.getenv("ANDROID_HOME")
|
||||
}
|
||||
|
||||
def isWindows = System.getProperty("os.name").toLowerCase().contains("windows")
|
||||
def npmCmd = isWindows ? "npm.cmd" : "npm"
|
||||
|
||||
def findWindowsNpm() {
|
||||
def candidates = []
|
||||
def pathEnv = System.getenv("PATH") ?: ""
|
||||
pathEnv.split(";").each { p ->
|
||||
if (!p) return
|
||||
def cmd = new File(p, "npm.cmd")
|
||||
if (cmd.exists()) candidates << cmd
|
||||
def exe = new File(p, "npm.exe")
|
||||
if (exe.exists()) candidates << exe
|
||||
}
|
||||
def nvmHome = System.getenv("NVM_HOME")
|
||||
def nvmSymlink = System.getenv("NVM_SYMLINK")
|
||||
if (nvmSymlink) {
|
||||
def cmd = new File(nvmSymlink, "npm.cmd")
|
||||
if (cmd.exists()) candidates << cmd
|
||||
}
|
||||
if (nvmHome) {
|
||||
def current = new File(nvmHome, "current")
|
||||
if (current.exists()) {
|
||||
def cmd = new File(current, "npm.cmd")
|
||||
if (cmd.exists()) candidates << cmd
|
||||
}
|
||||
def versions = new File(nvmHome)
|
||||
if (versions.exists()) {
|
||||
versions.listFiles()?.findAll { it.isDirectory() }?.each { v ->
|
||||
def cmd = new File(v, "npm.cmd")
|
||||
if (cmd.exists()) candidates << cmd
|
||||
}
|
||||
}
|
||||
}
|
||||
def appData = System.getenv("APPDATA")
|
||||
if (appData) {
|
||||
def cmd = new File(appData, "npm\\npm.cmd")
|
||||
if (cmd.exists()) candidates << cmd
|
||||
}
|
||||
def programFiles = System.getenv("ProgramFiles")
|
||||
if (programFiles) {
|
||||
def cmd = new File(programFiles, "nodejs\\npm.cmd")
|
||||
if (cmd.exists()) candidates << cmd
|
||||
}
|
||||
def localAppData = System.getenv("LOCALAPPDATA")
|
||||
if (localAppData) {
|
||||
def nvmLocal = new File(localAppData, "nvm")
|
||||
if (nvmLocal.exists()) {
|
||||
nvmLocal.listFiles()?.findAll { it.isDirectory() }?.each { v ->
|
||||
def cmd = new File(v, "npm.cmd")
|
||||
if (cmd.exists()) candidates << cmd
|
||||
}
|
||||
}
|
||||
}
|
||||
return candidates.isEmpty() ? null : candidates.first().absolutePath
|
||||
}
|
||||
|
||||
tasks.register("startBackend", Exec) {
|
||||
workingDir "${rootDir}/backend"
|
||||
if (isWindows) {
|
||||
doFirst {
|
||||
def npmPath = (project.findProperty("npmPath") ?: System.getenv("NPM_PATH"))?.toString()
|
||||
if (!npmPath) {
|
||||
npmPath = findWindowsNpm()
|
||||
}
|
||||
if (!npmPath) {
|
||||
def out = new ByteArrayOutputStream()
|
||||
exec {
|
||||
commandLine "cmd", "/c", "where.exe", "npm"
|
||||
standardOutput = out
|
||||
errorOutput = out
|
||||
ignoreExitValue = true
|
||||
}
|
||||
def line = out.toString().readLines().find { it.toLowerCase().endsWith("npm.cmd") || it.toLowerCase().endsWith("npm.exe") }
|
||||
if (line) {
|
||||
npmPath = line.trim()
|
||||
}
|
||||
}
|
||||
if (!npmPath) {
|
||||
throw new GradleException("npm not found. Set -PnpmPath=... or ensure npm is visible to cmd.exe. If using nvm, try your NVM_HOME version's npm.cmd.")
|
||||
}
|
||||
def nodeModules = new File(workingDir, "node_modules")
|
||||
if (!nodeModules.exists()) {
|
||||
exec {
|
||||
workingDir "${rootDir}/backend"
|
||||
commandLine "cmd", "/c", npmPath, "install"
|
||||
}
|
||||
}
|
||||
commandLine "cmd", "/c", "start", "\"\"", npmPath, "start"
|
||||
}
|
||||
} else {
|
||||
doFirst {
|
||||
def nodeModules = new File(workingDir, "node_modules")
|
||||
if (!nodeModules.exists()) {
|
||||
exec {
|
||||
workingDir "${rootDir}/backend"
|
||||
commandLine "sh", "-c", "${npmCmd} install"
|
||||
}
|
||||
}
|
||||
commandLine "sh", "-c", "${npmCmd} start &"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("runBackendDebug") {
|
||||
dependsOn(":app:installDebug", "startBackend")
|
||||
doLast {
|
||||
def sdkDir = resolveSdkDir()
|
||||
if (!sdkDir) {
|
||||
throw new GradleException("Missing sdk.dir in local.properties and ANDROID_SDK_ROOT/ANDROID_HOME is not set")
|
||||
}
|
||||
def adb = "${sdkDir}/platform-tools/adb" + (isWindows ? ".exe" : "")
|
||||
exec {
|
||||
commandLine adb, "shell", "am", "start", "-n", "com.dark98.santoku/.MainActivity"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user