Initial commit
15
.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/caches
|
||||||
|
/.idea/libraries
|
||||||
|
/.idea/modules.xml
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/navEditor.xml
|
||||||
|
/.idea/assetWizardSettings.xml
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
|
local.properties
|
||||||
3
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
1
.idea/.name
generated
Normal file
@ -0,0 +1 @@
|
|||||||
|
Android(Studio
|
||||||
6
.idea/AndroidProjectSystem.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AndroidProjectSystem">
|
||||||
|
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/compiler.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<bytecodeTargetLevel target="21" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
18
.idea/deploymentTargetSelector.xml
generated
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="deploymentTargetSelector">
|
||||||
|
<selectionStates>
|
||||||
|
<SelectionState runConfigName="app">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
<DropdownSelection timestamp="2026-02-07T21:35:39.547765521Z">
|
||||||
|
<Target type="DEFAULT_BOOT">
|
||||||
|
<handle>
|
||||||
|
<DeviceId pluginId="PhysicalDevice" identifier="serial=M7GEJFYHBA554L7T" />
|
||||||
|
</handle>
|
||||||
|
</Target>
|
||||||
|
</DropdownSelection>
|
||||||
|
<DialogSelection />
|
||||||
|
</SelectionState>
|
||||||
|
</selectionStates>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
7
.idea/dictionaries/project.xml
generated
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<component name="ProjectDictionaryState">
|
||||||
|
<dictionary name="project">
|
||||||
|
<words>
|
||||||
|
<w>цель</w>
|
||||||
|
</words>
|
||||||
|
</dictionary>
|
||||||
|
</component>
|
||||||
19
.idea/gradle.xml
generated
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/app" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
10
.idea/migrations.xml
generated
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectMigrations">
|
||||||
|
<option name="MigrateToGradleLocalJavaHome">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
9
.idea/misc.xml
generated
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<project version="4">
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="Android" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
17
.idea/runConfigurations.xml
generated
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RunConfigurationProducerService">
|
||||||
|
<option name="ignoredProducers">
|
||||||
|
<set>
|
||||||
|
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
1
app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
44
app/build.gradle.kts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.application)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "pictures.tristagram.lina.bodytune"
|
||||||
|
compileSdk {
|
||||||
|
version = release(36)
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId = "pictures.tristagram.lina.bodytune"
|
||||||
|
minSdk = 24
|
||||||
|
targetSdk = 36
|
||||||
|
versionCode = 1
|
||||||
|
versionName = "1.0"
|
||||||
|
|
||||||
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = false
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.appcompat)
|
||||||
|
implementation(libs.material)
|
||||||
|
implementation(libs.activity)
|
||||||
|
implementation(libs.constraintlayout)
|
||||||
|
testImplementation(libs.junit)
|
||||||
|
androidTestImplementation(libs.ext.junit)
|
||||||
|
androidTestImplementation(libs.espresso.core)
|
||||||
|
}
|
||||||
21
app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package pictures.tristagram.bodytuneup;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
public void useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||||
|
assertEquals("pictures.tristagram.androidstudio", appContext.getPackageName());
|
||||||
|
}
|
||||||
|
}
|
||||||
35
app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="false"
|
||||||
|
android:fullBackupContent="false"
|
||||||
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.AndroidStudio">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/file_paths" />
|
||||||
|
</provider>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
14
app/src/main/assets/chart.min.js
vendored
Normal file
541
app/src/main/assets/index.html
Normal file
@ -0,0 +1,541 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||||
|
<title>WhatWeight 1.3.9</title>
|
||||||
|
|
||||||
|
<script src="chart.min.js"></script>
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
|
||||||
|
:root {
|
||||||
|
--bg: #f8fafc; --card: #ffffff; --text: #1e293b; --sub: #64748b;
|
||||||
|
--primary: #3b82f6; --accent: #A0F6B1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--bg: #14171a;
|
||||||
|
--card: #1f2329;
|
||||||
|
--text: #ffffff;
|
||||||
|
--sub: #8e99a3;
|
||||||
|
--primary: #A0F6B1; /* Твой цвет мятной иконки */
|
||||||
|
--success: #4ade80; /* Зеленый для тренда */
|
||||||
|
--danger: #f87171; /* Красный для тренда */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Контейнер для ВСЕЙ карточки графика */
|
||||||
|
.chart-container-fixed {
|
||||||
|
display: flex; /* Важно: выстраивает шкалу и график в ряд */
|
||||||
|
position: relative;
|
||||||
|
height: 240px;
|
||||||
|
background: var(--card);
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid rgba(128,128,128,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Фиксированная левая шкала */
|
||||||
|
.y-axis-fixed {
|
||||||
|
width: 50px;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--card);
|
||||||
|
z-index: 10;
|
||||||
|
flex-shrink: 0; /* Не дает шкале сжиматься */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Область прокрутки для графика */
|
||||||
|
.scroll-container {
|
||||||
|
flex-grow: 1; /* Занимает всё оставшееся место справа от шкалы */
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Обертка внутри прокрутки (её ширину мы меняем в JS) */
|
||||||
|
#wWrapper {
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Стили для второго графика (замеры) */
|
||||||
|
.chart-wrapper {
|
||||||
|
height: 200px;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
opacity: 0; /* Делаем страницу полностью прозрачной */
|
||||||
|
transition: opacity 0.3s ease-in; /* Плавное появление */
|
||||||
|
}
|
||||||
|
|
||||||
|
body.loaded {
|
||||||
|
opacity: 1; /* Показываем, когда добавим класс loaded */
|
||||||
|
}
|
||||||
|
body { background: var(--bg); color: var(--text); font-family: system-ui, sans-serif; margin: 0; padding: 12px; }
|
||||||
|
.card { background: var(--card); border-radius: 24px; padding: 18px; box-shadow: 0 8px 32px rgba(0,0,0,0.2); margin-bottom: 15px; }
|
||||||
|
|
||||||
|
h2 { font-size: 10px; text-transform: uppercase; color: var(--sub); margin: 0 0 15px 0; letter-spacing: 1.5px; display: flex; justify-content: space-between; align-items: center; }
|
||||||
|
|
||||||
|
input, select {
|
||||||
|
width: 100%; height: 48px; border-radius: 14px; border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
background: rgba(0,0,0,0.2); color: var(--text); font-size: 16px; font-weight: 600; text-align: center; outline: none; margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
input:focus { border-color: var(--primary); }
|
||||||
|
input.invalid { border-color: var(--danger); color: var(--danger); }
|
||||||
|
|
||||||
|
.goals { display: flex; gap: 8px; margin-bottom: 15px; }
|
||||||
|
.goal-btn { flex: 1; height: 40px; border-radius: 12px; border: 1px solid var(--primary); background: transparent; color: var(--sub); font-size: 9px; font-weight: 800; cursor: pointer; display: flex; align-items: center; justify-content: center; }
|
||||||
|
.goal-btn.active { background: var(--primary); color: #0d1117; }
|
||||||
|
|
||||||
|
.btn-main { width: 100%; height: 54px; border-radius: 16px; border: none; background: var(--primary); color: #0d1117; font-weight: 900; font-size: 16px; margin-top: 10px; cursor: pointer; }
|
||||||
|
|
||||||
|
.scroll-container { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
|
||||||
|
|
||||||
|
|
||||||
|
.hist-item { padding: 16px 0; border-bottom: 1px solid rgba(255, 255, 255, 0.05); display: flex; align-items: center; justify-content: space-between; }
|
||||||
|
.hist-val { font-size: 18px; font-weight: 900; color: var(--primary); }
|
||||||
|
.detail-tag { font-size: 10px; background: rgba(160, 246, 177, 0.05); color: var(--primary); padding: 4px 8px; border-radius: 8px; margin-right: 5px; border: 1px solid rgba(160, 246, 177, 0.1); }
|
||||||
|
|
||||||
|
.del-btn { background: rgba(248, 113, 113, 0.1); color: var(--danger); border: none; width: 44px; height: 44px; border-radius: 14px; font-size: 28px; display: flex; align-items: center; justify-content: center; cursor: pointer; }
|
||||||
|
|
||||||
|
summary { font-size: 11px; color: var(--primary); font-weight: 800; padding: 12px; text-align: center; border: 1px solid rgba(160, 246, 177, 0.2); border-radius: 14px; margin-bottom: 10px; cursor: pointer; }
|
||||||
|
|
||||||
|
.app-icon { width: 48px; height: 48px; background: color:var(--primary); border: 2px solid var(--primary); border-radius: 14px; display: flex; align-items: center; justify-content: center; margin-right: 12px; }
|
||||||
|
/* Красивые кнопки для экспорта/импорта */
|
||||||
|
.data-btn {
|
||||||
|
flex: 1;
|
||||||
|
padding: 12px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 16px;
|
||||||
|
background: var(--bg); /* Цвет фона приложения */
|
||||||
|
color: var(--text);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
border: 1px solid rgba(128,128,128,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-btn:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
background: var(--primary);
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header style="display: flex; align-items: center; margin: 10px 0 20px 5px;">
|
||||||
|
<div class="app-icon"> ️
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24">
|
||||||
|
<g transform="translate(5.04 5.04) scale(0.58)">
|
||||||
|
<path
|
||||||
|
fill="var(--primary)"
|
||||||
|
d="M24,4c0,0.55 -0.45,1 -1,1h-1v1c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V5h-1c-0.55,0 -1,-0.45 -1,-1c0,-0.55 0.45,-1 1,-1h1V2c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v1h1C23.55,3 24,3.45 24,4zM21.52,8.95C21.83,9.91 22,10.94 22,12c0,5.52 -4.48,10 -10,10S2,17.52 2,12C2,6.48 6.48,2 12,2c1.5,0 2.92,0.34 4.2,0.94C16.08,3.27 16,3.62 16,4c0,1.35 0.9,2.5 2.13,2.87C18.5,8.1 19.65,9 21,9C21.18,9 21.35,8.98 21.52,8.95zM7,9.5C7,10.33 7.67,11 8.5,11S10,10.33 10,9.5S9.33,8 8.5,8S7,8.67 7,9.5zM16.31,14H7.69c-0.38,0 -0.63,0.42 -0.44,0.75C8.2,16.39 9.97,17.5 12,17.5s3.8,-1.11 4.75,-2.75C16.94,14.42 16.7,14 16.31,14zM17,9.5C17,8.67 16.33,8 15.5,8S14,8.67 14,9.5s0.67,1.5 1.5,1.5S17,10.33 17,9.5z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<b style="font-size: 20px; flex-grow: 1;" ><span style="color:var(--primary)">[ </span><span data-i18n="title-app">What-Weight</span><span style="color:var(--primary)"> ]</span></b>
|
||||||
|
<div id="trendDisplay" style="font-size: 10px; font-weight: 900; color: var(--sub);">...</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2 data-i18n="card-input-title">Goal & Input</h2>
|
||||||
|
<div class="goals">
|
||||||
|
<div onclick="setGoal('keep')" id="g-keep" class="goal-btn" data-i18n="goal-keep">MAINTAIN</div>
|
||||||
|
<div onclick="setGoal('lose')" id="g-lose" class="goal-btn" data-i18n="goal-lose">LOSE</div>
|
||||||
|
<div onclick="setGoal('gain')" id="g-gain" class="goal-btn" data-i18n="goal-gain">GAIN</div>
|
||||||
|
</div>
|
||||||
|
<div style="display:grid; grid-template-columns: 1fr 1fr; gap: 10px;">
|
||||||
|
<input type="number" id="targetWeight" data-i18n="placeholder-target" placeholder="Target kg" step="0.1">
|
||||||
|
<input type="number" id="wVal" data-i18n="placeholder-weight" placeholder="Weight kg" step="0.1" inputmode="decimal" oninput="validate(this, 30, 250)">
|
||||||
|
</div>
|
||||||
|
<input type="date" id="wDate">
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary >📏 <span data-i18n="measurements-title">BODY MEASUREMENTS</span></summary>
|
||||||
|
<div style="display:grid; grid-template-columns: 1fr 1fr; gap: 10px;">
|
||||||
|
<input type="number" id="mWaist" data-i18n="placeholder-waist" placeholder="Waist" step="0.1" oninput="validate(this, 30, 180)">
|
||||||
|
<input type="number" id="mChest" data-i18n="placeholder-chest" placeholder="Chest" step="0.1" oninput="validate(this, 30, 180)">
|
||||||
|
<input type="number" id="mHips" data-i18n="placeholder-hips" placeholder="Hips" step="0.1" oninput="validate(this, 30, 180)">
|
||||||
|
<input type="number" id="mBicep" data-i18n="placeholder-bicep" placeholder="Bicep" step="0.1" oninput="validate(this, 10, 80)">
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
<button onclick="handleSaveClick()" class="btn-main" data-i18n="btn-save" id="saveBtn">SAVE</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2 data-i18n="chart-weight-title">Weight Dynamics</h2>
|
||||||
|
<div class="chart-container-fixed">
|
||||||
|
<canvas id="weightAxis" class="y-axis-fixed"></canvas>
|
||||||
|
<div id="wScroll" class="scroll-container">
|
||||||
|
<div id="wWrapper">
|
||||||
|
<canvas id="weightChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2><span data-i18n="chart-measures-title">Measurements</span>
|
||||||
|
<select id="measureType" style="height:26px; font-size:10px; width:auto; border-radius:8px; margin:0; border:none; background:transparent; color:var(--primary);" onchange="renderUI()">
|
||||||
|
<option value="waist" data-i18n="placeholder-waist">Waist</option>
|
||||||
|
<option value="chest" data-i18n="placeholder-chest">Chest</option>
|
||||||
|
<option value="hips" data-i18n="placeholder-hips">Hips</option>
|
||||||
|
<option value="bicep" data-i18n="placeholder-bicep">Bicep</option>
|
||||||
|
</select>
|
||||||
|
</h2>
|
||||||
|
<div class="chart-wrapper"><canvas id="measureChart"></canvas></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card" id="histBox" style="display:none">
|
||||||
|
<h2 data-i18n="history-title">History</h2>
|
||||||
|
<div id="log"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="card" style="margin-top: 20px;">
|
||||||
|
<h3 style="margin-top:0; margin-bottom: 15px; font-size: 16px; text-align: center;">Данные</h3>
|
||||||
|
<div style="display: flex; gap: 10px;">
|
||||||
|
<button onclick="exportToCSV()" class="data-btn">📤 Экспорт</button>
|
||||||
|
<button onclick="showImportModal()" class="data-btn">📥 Импорт</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="importModal" style="display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.8); z-index:10000; padding:20px;">
|
||||||
|
<div class="card" style="height:100%; display:flex; flex-direction:column;">
|
||||||
|
<h3 style="margin-top:0">Вставьте содержимое CSV</h3>
|
||||||
|
<textarea id="csvPasteArea" style="flex:1; width:100%; background:var(--bg); color:var(--text); border:1px solid var(--sub); border-radius:12px; padding:10px; font-family:monospace; font-size:12px;"></textarea>
|
||||||
|
<div style="display:flex; gap:10px; margin-top:15px;">
|
||||||
|
<button onclick="processPastedCSV()" class="data-btn" style="background:var(--primary); color:#000;">Загрузить</button>
|
||||||
|
<button onclick="document.getElementById('importModal').style.display='none'" class="data-btn">Отмена</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
let db = JSON.parse(localStorage.getItem('weight_tracker_data') || '{"entries":[], "config":{"goal":"keep", "target":0}}');
|
||||||
|
let wChart, mChart, wAxisChart; // Добавили wAxisChart
|
||||||
|
let confirmMode = false;
|
||||||
|
let translations = {}; // Будет заполнено через initLanguage
|
||||||
|
|
||||||
|
async function initLanguage() {
|
||||||
|
let lang = 'ru';
|
||||||
|
if (window.Android && window.Android.getLanguage) {
|
||||||
|
const fullLang = window.Android.getLanguage();
|
||||||
|
lang = fullLang.startsWith('ru') ? 'ru' : 'en';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (window.Android && window.Android.getTranslations) {
|
||||||
|
const jsonString = window.Android.getTranslations(lang);
|
||||||
|
translations = JSON.parse(jsonString);
|
||||||
|
document.querySelectorAll('[data-i18n]').forEach(el => {
|
||||||
|
const key = el.getAttribute('data-i18n');
|
||||||
|
if (translations[key]) {
|
||||||
|
if (el.tagName === 'INPUT') el.placeholder = translations[key];
|
||||||
|
else el.textContent = translations[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
renderUI();
|
||||||
|
}
|
||||||
|
} catch (e) { console.error("Language Error:", e); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveDB() { localStorage.setItem('weight_tracker_data', JSON.stringify(db)); }
|
||||||
|
|
||||||
|
function renderUI() {
|
||||||
|
if (!document.getElementById('wDate').value) document.getElementById('wDate').value = new Date().toISOString().split('T')[0];
|
||||||
|
document.querySelectorAll('.goal-btn').forEach(b => b.classList.remove('active'));
|
||||||
|
if(document.getElementById('g-' + db.config.goal)) document.getElementById('g-' + db.config.goal).classList.add('active');
|
||||||
|
document.getElementById('targetWeight').value = db.config.target || '';
|
||||||
|
|
||||||
|
const log = document.getElementById('log');
|
||||||
|
if (db.entries.length > 0) {
|
||||||
|
document.getElementById('histBox').style.display = 'block';
|
||||||
|
log.innerHTML = db.entries.slice().reverse().map(e => `
|
||||||
|
<div class="hist-item">
|
||||||
|
<div>
|
||||||
|
<div style="font-size:11px; font-weight:bold; color:var(--sub)">${e.date.split('-').reverse().join('.')}</div>
|
||||||
|
<div class="hist-val">${e.weight || '?'} кг</div>
|
||||||
|
<div style="margin-top:4px">
|
||||||
|
${['waist','chest','hips','bicep'].map(f => e[f] ? `<span class="detail-tag">${translations[f] || f}: ${e[f]}</span>` : '').join('')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="del-btn" onclick="manualDelete('${e.date}')">×</button>
|
||||||
|
</div>`).join('');
|
||||||
|
} else { document.getElementById('histBox').style.display = 'none'; }
|
||||||
|
updateTrendDisplay();
|
||||||
|
drawCharts();
|
||||||
|
document.body.classList.add('loaded');
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawCharts() {
|
||||||
|
const weights = db.entries
|
||||||
|
.filter(e => e.weight)
|
||||||
|
.sort((a, b) => new Date(a.date) - new Date(b.date));
|
||||||
|
|
||||||
|
if (weights.length === 0) return;
|
||||||
|
|
||||||
|
const scrollContainer = document.getElementById('wScroll');
|
||||||
|
const wWrapper = document.getElementById('wWrapper');
|
||||||
|
const axisCanvas = document.getElementById('weightAxis');
|
||||||
|
const chartCanvas = document.getElementById('weightChart');
|
||||||
|
|
||||||
|
const style = getComputedStyle(document.body);
|
||||||
|
const primaryColor = style.getPropertyValue('--primary').trim();
|
||||||
|
const textColor = style.getPropertyValue('--text').trim();
|
||||||
|
const subColor = style.getPropertyValue('--sub').trim();
|
||||||
|
|
||||||
|
// Цвета из твоей рабочей логики
|
||||||
|
const trendDown = '#4ade80'; // Зеленый (хорошо)
|
||||||
|
const trendUp = '#f87171'; // Красный (плохо)
|
||||||
|
const mintColor = primaryColor;
|
||||||
|
|
||||||
|
// --- ТВОЯ РАБОЧАЯ ЛОГИКА ЦВЕТОВ ---
|
||||||
|
const pointColors = weights.map((e, idx) => {
|
||||||
|
const target = db.config.target || 0;
|
||||||
|
|
||||||
|
// 1. Режим УДЕРЖАНИЕ (keep)
|
||||||
|
if (db.config.goal === 'keep' && target > 0) {
|
||||||
|
const diff = Math.abs(e.weight - target);
|
||||||
|
return diff > 0.4 ? trendUp : trendDown;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Для режимов Снижение и Набор сравниваем с предыдущим днем
|
||||||
|
if (idx === 0) return trendDown;
|
||||||
|
const dailyDiff = e.weight - weights[idx - 1].weight;
|
||||||
|
|
||||||
|
// 2. Режим СНИЖЕНИЕ (lose)
|
||||||
|
if (db.config.goal === 'lose') {
|
||||||
|
return dailyDiff <= 0 ? trendDown : trendUp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Режим НАБОР (gain)
|
||||||
|
if (db.config.goal === 'gain') {
|
||||||
|
return dailyDiff >= 0 ? trendDown : trendUp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mintColor;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Расчет границ шкалы (строго целые числа)
|
||||||
|
const targetVal = parseFloat(db.config.target) || 0;
|
||||||
|
const allValues = weights.map(e => e.weight);
|
||||||
|
if (targetVal > 0) allValues.push(targetVal);
|
||||||
|
const minW = Math.floor(Math.min(...allValues) - 1);
|
||||||
|
const maxW = Math.ceil(Math.max(...allValues) + 1);
|
||||||
|
|
||||||
|
const commonYAxis = {
|
||||||
|
min: minW, max: maxW,
|
||||||
|
grid: { display: false, drawBorder: false },
|
||||||
|
ticks: { color: subColor, font: { size: 11, weight: 'bold' }, stepSize: 1, precision: 0, padding: 5 }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (wAxisChart) wAxisChart.destroy();
|
||||||
|
if (wChart) wChart.destroy();
|
||||||
|
|
||||||
|
// Левая шкала (вертикальная)
|
||||||
|
axisCanvas.width = 40;
|
||||||
|
axisCanvas.height = 240;
|
||||||
|
wAxisChart = new Chart(axisCanvas.getContext('2d'), {
|
||||||
|
type: 'line',
|
||||||
|
data: { labels: weights.map(e => e.date), datasets: [{ data: weights.map(e => e.weight), showLine: false, pointRadius: 0 }] },
|
||||||
|
options: {
|
||||||
|
responsive: false, maintainAspectRatio: false,
|
||||||
|
layout: { padding: { top: 40, bottom: 0 } },
|
||||||
|
plugins: { legend: { display: false }, tooltip: { enabled: false } },
|
||||||
|
scales: {
|
||||||
|
y: { ...commonYAxis, display: true, position: 'left', afterFit: (axis) => { axis.width = 40; } },
|
||||||
|
x: { display: true, ticks: { display: false }, grid: { display: false }, afterFit: (axis) => { axis.height = 30; } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Основной график
|
||||||
|
const chartWidth = Math.max(weights.length * 60, scrollContainer.clientWidth);
|
||||||
|
wWrapper.style.width = chartWidth + 'px';
|
||||||
|
chartCanvas.height = 240;
|
||||||
|
|
||||||
|
wChart = new Chart(chartCanvas.getContext('2d'), {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: weights.map(e => e.date.split('-').reverse().join('.').substring(0, 5)),
|
||||||
|
datasets: [{
|
||||||
|
data: weights.map(e => e.weight),
|
||||||
|
borderColor: primaryColor,
|
||||||
|
borderWidth: 1,
|
||||||
|
tension: 0.3,
|
||||||
|
fill: true,
|
||||||
|
backgroundColor: primaryColor + '45',
|
||||||
|
pointRadius: 6,
|
||||||
|
pointBackgroundColor: pointColors, // Применяем рассчитанные цвета
|
||||||
|
pointBorderColor: primaryColor,
|
||||||
|
pointBorderWidth: 2
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true, maintainAspectRatio: false,
|
||||||
|
layout: { padding: { top: 40, bottom: 0, right: 20 } },
|
||||||
|
plugins: { legend: { display: false } },
|
||||||
|
scales: {
|
||||||
|
y: { ...commonYAxis, ticks: { display: false }, afterFit: (axis) => { axis.width = 0; } },
|
||||||
|
x: { grid: { display: false }, ticks: { color: subColor, font: { size: 10 }, maxRotation: 0 }, afterFit: (axis) => { axis.height = 30; } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: [{
|
||||||
|
afterDatasetsDraw: (chart) => {
|
||||||
|
const { ctx, chartArea: { right }, scales: { y } } = chart;
|
||||||
|
ctx.save();
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.font = 'bold 11px sans-serif';
|
||||||
|
ctx.fillStyle = textColor;
|
||||||
|
chart.getDatasetMeta(0).data.forEach((point, index) => {
|
||||||
|
const val = chart.data.datasets[0].data[index];
|
||||||
|
if (val) ctx.fillText(val, point.x, point.y - 12);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (targetVal > 0) {
|
||||||
|
const yPos = y.getPixelForValue(targetVal);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.setLineDash([5, 5]);
|
||||||
|
ctx.strokeStyle = primaryColor + '66';
|
||||||
|
ctx.lineWidth = 1.5;
|
||||||
|
ctx.moveTo(0, yPos); ctx.lineTo(right, yPos);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => { scrollContainer.scrollLeft = 99999; }, 50);
|
||||||
|
|
||||||
|
|
||||||
|
// Отрисовка замеров (если функция есть)
|
||||||
|
if (typeof renderMeasuresChart === 'function') renderMeasuresChart();
|
||||||
|
}
|
||||||
|
function renderMeasuresChart() {
|
||||||
|
const mType = document.getElementById('measureType').value;
|
||||||
|
const measures = db.entries.filter(e => e[mType]);
|
||||||
|
const mCtx = document.getElementById('measureChart').getContext('2d');
|
||||||
|
if (mChart) mChart.destroy();
|
||||||
|
if (measures.length > 0) {
|
||||||
|
mChart = new Chart(mCtx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: measures.map(e => e.date.split('-').reverse().join('.').substring(0,5)),
|
||||||
|
datasets: [{ data: measures.map(e => e[mType]), borderColor: '#A0F6B1', tension: 0.4, pointRadius: 4 }]
|
||||||
|
},
|
||||||
|
options: { responsive: true, maintainAspectRatio: false, animation: false, plugins: { legend: { display: false } } }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вспомогательные функции
|
||||||
|
function updateTrendDisplay() {
|
||||||
|
const entries = db.entries.filter(e => e.weight);
|
||||||
|
const el = document.getElementById('trendDisplay');
|
||||||
|
if (entries.length < 2) { el.innerText = translations['trend-analysis'] || "..."; return; }
|
||||||
|
const last = entries[entries.length - 1];
|
||||||
|
const avg = entries.slice(-8, -1).reduce((s, e) => s + e.weight, 0) / Math.max(1, entries.slice(-8, -1).length);
|
||||||
|
const diff = last.weight - avg;
|
||||||
|
if (Math.abs(diff) < 0.1) { el.innerText = translations['trend-stable']; el.style.color = "var(--sub)"; }
|
||||||
|
else if (diff > 0) { el.innerText = translations['trend-up']; el.style.color = "#f87171"; }
|
||||||
|
else { el.innerText = translations['trend-down']; el.style.color = "#4ade80"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
window.manualDelete = function(date) { db.entries = db.entries.filter(e => e.date !== date); saveDB(); renderUI(); };
|
||||||
|
|
||||||
|
function setGoal(g) { db.config.goal = g; saveDB(); renderUI(); }
|
||||||
|
|
||||||
|
function handleSaveClick() {
|
||||||
|
const date = document.getElementById('wDate').value;
|
||||||
|
if (!date) return;
|
||||||
|
db.config.target = parseFloat(document.getElementById('targetWeight').value) || 0;
|
||||||
|
let entry = db.entries.find(e => e.date === date) || { date };
|
||||||
|
if (document.getElementById('wVal').value) entry.weight = parseFloat(document.getElementById('wVal').value);
|
||||||
|
['waist', 'chest', 'hips', 'bicep'].forEach(f => {
|
||||||
|
let v = document.getElementById('m' + f.charAt(0).toUpperCase() + f.slice(1)).value;
|
||||||
|
if (v) entry[f] = parseFloat(v);
|
||||||
|
});
|
||||||
|
if (!db.entries.find(e => e.date === date)) db.entries.push(entry);
|
||||||
|
db.entries.sort((a, b) => new Date(a.date) - new Date(b.date));
|
||||||
|
saveDB(); renderUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportToCSV() {
|
||||||
|
if (!db.entries || db.entries.length === 0) return;
|
||||||
|
|
||||||
|
let csvContent = "date,weight,waist,chest,hips,bicep\n";
|
||||||
|
db.entries.forEach(e => {
|
||||||
|
csvContent += `${e.date},${e.weight || ''},${e.waist || ''},${e.chest || ''},${e.hips || ''},${e.bicep || ''}\n`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Проверяем, запущено ли в Android приложении
|
||||||
|
if (window.Android && window.Android.exportCSV) {
|
||||||
|
window.Android.exportCSV(csvContent, `weight_data_${new Date().toISOString().split('T')[0]}.csv`);
|
||||||
|
} else {
|
||||||
|
// Обычный браузерный способ (для тестов)
|
||||||
|
const blob = new Blob([csvContent], { type: 'text/csv' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = 'data.csv';
|
||||||
|
a.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ИМПОРТ
|
||||||
|
function showImportModal() {
|
||||||
|
document.getElementById('importModal').style.display = 'block';
|
||||||
|
document.getElementById('csvPasteArea').value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function processPastedCSV() {
|
||||||
|
const text = document.getElementById('csvPasteArea').value;
|
||||||
|
if (!text.trim()) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const lines = text.split(/\r?\n/);
|
||||||
|
const dataLines = lines.slice(1); // Пропускаем заголовок (date,weight...)
|
||||||
|
|
||||||
|
let count = 0;
|
||||||
|
dataLines.forEach(line => {
|
||||||
|
if (!line.trim()) return;
|
||||||
|
const [date, weight, waist, chest, hips, bicep] = line.split(',');
|
||||||
|
|
||||||
|
if (!date || date.length < 8) return; // Проверка на корректность даты
|
||||||
|
|
||||||
|
let entry = db.entries.find(item => item.date === date);
|
||||||
|
if (!entry) {
|
||||||
|
entry = { date };
|
||||||
|
db.entries.push(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weight && !isNaN(weight)) entry.weight = parseFloat(weight);
|
||||||
|
if (waist && !isNaN(waist)) entry.waist = parseFloat(waist);
|
||||||
|
if (chest && !isNaN(chest)) entry.chest = parseFloat(chest);
|
||||||
|
if (hips && !isNaN(hips)) entry.hips = parseFloat(hips);
|
||||||
|
if (bicep && !isNaN(bicep)) entry.bicep = parseFloat(bicep);
|
||||||
|
count++;
|
||||||
|
});
|
||||||
|
|
||||||
|
db.entries.sort((a, b) => new Date(a.date) - new Date(b.date));
|
||||||
|
saveDB();
|
||||||
|
renderUI();
|
||||||
|
|
||||||
|
document.getElementById('importModal').style.display = 'none';
|
||||||
|
alert("Успешно загружено записей: " + count);
|
||||||
|
} catch (err) {
|
||||||
|
alert("Ошибка формата: проверьте, что вы скопировали CSV целиком");
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = initLanguage;
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
29
app/src/main/assets/locales/en.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"title-app": "Body Tune",
|
||||||
|
"card-input-title": "Goal & Input",
|
||||||
|
"goal-keep": "MAINTAIN",
|
||||||
|
"goal-lose": "LOSE",
|
||||||
|
"goal-gain": "GAIN",
|
||||||
|
"placeholder-target": "Target kg",
|
||||||
|
"placeholder-weight": "Weight kg",
|
||||||
|
"measurements-title": "BODY MEASUREMENTS",
|
||||||
|
"placeholder-waist": "Waist",
|
||||||
|
"placeholder-chest": "Chest",
|
||||||
|
"placeholder-hips": "Hips",
|
||||||
|
"placeholder-bicep": "Bicep",
|
||||||
|
"btn-save": "SAVE",
|
||||||
|
"btn-save-confirm": "ARE YOU SURE?",
|
||||||
|
"btn-save-done": "DONE!",
|
||||||
|
"chart-weight-title": "Weight Dynamics",
|
||||||
|
"chart-measures-title": "Measurements",
|
||||||
|
"history-title": "History",
|
||||||
|
"btn-backup": "EXPORT / IMPORT",
|
||||||
|
"trend-stable": "STABLE",
|
||||||
|
"trend-up": "INCREASING ↑",
|
||||||
|
"trend-down": "DECREASING ↓",
|
||||||
|
"trend-analysis": "ANALYZING...",
|
||||||
|
"waist": "Waist",
|
||||||
|
"chest": "Chest",
|
||||||
|
"hips": "Hips",
|
||||||
|
"bicep": "Bicep"а
|
||||||
|
}
|
||||||
29
app/src/main/assets/locales/ru.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"title-app": "Тюнинг тела",
|
||||||
|
"card-input-title": "Цель и ввод",
|
||||||
|
"goal-keep": "ПОДДЕРЖКА",
|
||||||
|
"goal-lose": "СБРОС",
|
||||||
|
"goal-gain": "НАБОР",
|
||||||
|
"placeholder-target": "Цель кг",
|
||||||
|
"placeholder-weight": "Вес кг",
|
||||||
|
"measurements-title": "ЗАМЕРЫ ТЕЛА",
|
||||||
|
"placeholder-waist": "Талия",
|
||||||
|
"placeholder-chest": "Грудь",
|
||||||
|
"placeholder-hips": "Бедра",
|
||||||
|
"placeholder-bicep": "Бицепс",
|
||||||
|
"btn-save": "СОХРАНИТЬ",
|
||||||
|
"btn-save-confirm": "ТОЧНО ВЕРНО?",
|
||||||
|
"btn-save-done": "ГОТОВО!",
|
||||||
|
"chart-weight-title": "Динамика веса",
|
||||||
|
"chart-measures-title": "Замеры",
|
||||||
|
"history-title": "История",
|
||||||
|
"btn-backup": "ЭКСПОРТ / ИМПОРТ",
|
||||||
|
"trend-stable": "СТАБИЛЬНО",
|
||||||
|
"trend-up": "ПОВЫШЕНИЕ ↑",
|
||||||
|
"trend-down": "СНИЖЕНИЕ ↓",
|
||||||
|
"trend-analysis": "АНАЛИЗ...",
|
||||||
|
"waist": "Талия",
|
||||||
|
"chest": "Грудь",
|
||||||
|
"hips": "Бедра",
|
||||||
|
"bicep": "Бицепс"
|
||||||
|
}
|
||||||
BIN
app/src/main/ic_launcher-playstore.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
@ -0,0 +1,68 @@
|
|||||||
|
package pictures.tristagram.lina.bodytune;
|
||||||
|
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.webkit.ValueCallback;
|
||||||
|
import android.webkit.WebChromeClient;
|
||||||
|
import android.webkit.WebSettings;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
import android.webkit.WebViewClient;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
|
||||||
|
// WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
|
||||||
|
getWindow().setStatusBarColor(Color.TRANSPARENT);
|
||||||
|
WebView webView = new WebView(this);
|
||||||
|
// В Activity:
|
||||||
|
webView.addJavascriptInterface(new WebAppInterface(this), "Android");
|
||||||
|
setContentView(webView);
|
||||||
|
|
||||||
|
|
||||||
|
// 2. Сдвигаем контент вниз на высоту системной панели
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(webView, (v, windowInsets) -> {
|
||||||
|
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(0, insets.top, 0, 0);
|
||||||
|
return windowInsets;
|
||||||
|
});
|
||||||
|
|
||||||
|
WebSettings settings = webView.getSettings();
|
||||||
|
settings.setJavaScriptEnabled(true);
|
||||||
|
settings.setDomStorageEnabled(true); // Для сохранения веса в памяти
|
||||||
|
settings.setAllowFileAccess(true);
|
||||||
|
settings.setAllowContentAccess(true);
|
||||||
|
settings.setAllowFileAccessFromFileURLs(true);
|
||||||
|
settings.setAllowUniversalAccessFromFileURLs(true);
|
||||||
|
|
||||||
|
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
|
||||||
|
// Поддержка темной темы на уровне WebView
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
|
||||||
|
settings.setForceDark(WebSettings.FORCE_DARK_AUTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
webView.setWebViewClient(new WebViewClient());
|
||||||
|
|
||||||
|
webView.setWebChromeClient(new WebChromeClient() {
|
||||||
|
// Этот метод открывает системный выбор файла
|
||||||
|
@Override
|
||||||
|
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
|
||||||
|
// Здесь нужен код для открытия Intent.ACTION_GET_CONTENT
|
||||||
|
// Для простоты: импорт лучше сделать через текстовое поле (вставить текст из CSV)
|
||||||
|
// или использовать полноценный FileChooser.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
webView.loadUrl("file:///android_asset/index.html");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
package pictures.tristagram.lina.bodytune;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.webkit.JavascriptInterface;
|
||||||
|
|
||||||
|
import androidx.core.content.FileProvider;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
public class WebAppInterface {
|
||||||
|
private final Context mContext;
|
||||||
|
|
||||||
|
WebAppInterface(Context context) {
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
public void exportCSV(String data, String fileName) {
|
||||||
|
try {
|
||||||
|
// Создаем временный файл
|
||||||
|
File cachePath = new File(mContext.getCacheDir(), "exports");
|
||||||
|
cachePath.mkdirs();
|
||||||
|
File newFile = new File(cachePath, fileName);
|
||||||
|
FileOutputStream stream = new FileOutputStream(newFile);
|
||||||
|
stream.write(data.getBytes(StandardCharsets.UTF_8));
|
||||||
|
stream.close();
|
||||||
|
|
||||||
|
// Открываем диалог "Поделиться"
|
||||||
|
Uri contentUri = FileProvider.getUriForFile(mContext, mContext.getPackageName() + ".fileprovider", newFile);
|
||||||
|
if (contentUri != null) {
|
||||||
|
Intent shareIntent = new Intent();
|
||||||
|
shareIntent.setAction(Intent.ACTION_SEND);
|
||||||
|
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
shareIntent.setDataAndType(contentUri, "text/csv");
|
||||||
|
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
|
||||||
|
mContext.startActivity(Intent.createChooser(shareIntent, "Сохранить отчет"));
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
public String getLanguage() {
|
||||||
|
return java.util.Locale.getDefault().getLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
public String getTranslations(String lang) {
|
||||||
|
try {
|
||||||
|
InputStream is = mContext.getAssets().open("locales/" + lang + ".json");
|
||||||
|
byte[] buffer = new byte[is.available()];
|
||||||
|
is.read(buffer);
|
||||||
|
is.close();
|
||||||
|
return new String(buffer, StandardCharsets.UTF_8);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return "{}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
170
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:fillColor="#3DDC84"
|
||||||
|
android:pathData="M0,0h108v108h-108z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M9,0L9,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,0L19,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,0L29,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,0L39,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,0L49,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,0L59,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,0L69,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,0L79,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M89,0L89,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M99,0L99,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,9L108,9"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,19L108,19"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,29L108,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,39L108,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,49L108,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,59L108,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,69L108,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,79L108,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,89L108,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,99L108,99"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,29L89,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,39L89,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,49L89,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,59L89,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,69L89,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,79L89,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,19L29,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,19L39,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,19L49,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,19L59,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,19L69,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,19L79,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
</vector>
|
||||||
15
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="#A0F6B1">
|
||||||
|
<group android:scaleX="0.58"
|
||||||
|
android:scaleY="0.58"
|
||||||
|
android:translateX="5.04"
|
||||||
|
android:translateY="5.04">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M24,4c0,0.55 -0.45,1 -1,1h-1v1c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V5h-1c-0.55,0 -1,-0.45 -1,-1c0,-0.55 0.45,-1 1,-1h1V2c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v1h1C23.55,3 24,3.45 24,4zM21.52,8.95C21.83,9.91 22,10.94 22,12c0,5.52 -4.48,10 -10,10S2,17.52 2,12C2,6.48 6.48,2 12,2c1.5,0 2.92,0.34 4.2,0.94C16.08,3.27 16,3.62 16,4c0,1.35 0.9,2.5 2.13,2.87C18.5,8.1 19.65,9 21,9C21.18,9 21.35,8.98 21.52,8.95zM7,9.5C7,10.33 7.67,11 8.5,11S10,10.33 10,9.5S9.33,8 8.5,8S7,8.67 7,9.5zM16.31,14H7.69c-0.38,0 -0.63,0.42 -0.44,0.75C8.2,16.39 9.97,17.5 12,17.5s3.8,-1.11 4.75,-2.75C16.94,14.42 16.7,14 16.31,14zM17,9.5C17,8.67 16.33,8 15.5,8S14,8.67 14,9.5s0.67,1.5 1.5,1.5S17,10.33 17,9.5z"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
5
app/src/main/res/layout/activity_main.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/webview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 926 B |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
7
app/src/main/res/values-night/themes.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="Base.Theme.AndroidStudio" parent="Theme.Material3.DayNight.NoActionBar">
|
||||||
|
<!-- Customize your dark theme here. -->
|
||||||
|
<!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
4
app/src/main/res/values-ru-rRU/strings.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Тюнинг тела</string>
|
||||||
|
</resources>
|
||||||
5
app/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="black">#FF000000</color>
|
||||||
|
<color name="white">#FFFFFFFF</color>
|
||||||
|
</resources>
|
||||||
4
app/src/main/res/values/ic_launcher_background.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="ic_launcher_background">#2A3730</color>
|
||||||
|
</resources>
|
||||||
3
app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">Body Fit</string>
|
||||||
|
</resources>
|
||||||
14
app/src/main/res/values/themes.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="Base.Theme.AndroidStudio" parent="Theme.Material3.DayNight.NoActionBar">
|
||||||
|
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||||
|
|
||||||
|
<item name="android:windowLightStatusBar">true</item>
|
||||||
|
<item name="colorPrimary">@android:color/white</item>
|
||||||
|
|
||||||
|
<!-- Customize your light theme here. -->
|
||||||
|
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="Theme.AndroidStudio" parent="Base.Theme.AndroidStudio" />
|
||||||
|
</resources>
|
||||||
13
app/src/main/res/xml/backup_rules.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
Sample backup rules file; uncomment and customize as necessary.
|
||||||
|
See https://developer.android.com/guide/topics/data/autobackup
|
||||||
|
for details.
|
||||||
|
Note: This file is ignored for devices older than API 31
|
||||||
|
See https://developer.android.com/about/versions/12/backup-restore
|
||||||
|
-->
|
||||||
|
<full-backup-content>
|
||||||
|
<!--
|
||||||
|
<include domain="sharedpref" path="."/>
|
||||||
|
<exclude domain="sharedpref" path="device.xml"/>
|
||||||
|
-->
|
||||||
|
</full-backup-content>
|
||||||
19
app/src/main/res/xml/data_extraction_rules.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
Sample data extraction rules file; uncomment and customize as necessary.
|
||||||
|
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||||
|
for details.
|
||||||
|
-->
|
||||||
|
<data-extraction-rules>
|
||||||
|
<cloud-backup>
|
||||||
|
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||||
|
<include .../>
|
||||||
|
<exclude .../>
|
||||||
|
-->
|
||||||
|
</cloud-backup>
|
||||||
|
<!--
|
||||||
|
<device-transfer>
|
||||||
|
<include .../>
|
||||||
|
<exclude .../>
|
||||||
|
</device-transfer>
|
||||||
|
-->
|
||||||
|
</data-extraction-rules>
|
||||||
4
app/src/main/res/xml/file_paths.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<paths>
|
||||||
|
<cache-path name="my_cache" path="exports/" />
|
||||||
|
</paths>
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
package pictures.tristagram.bodytuneup;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
public class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
public void addition_isCorrect() {
|
||||||
|
assertEquals(4, 2 + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
4
build.gradle.kts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.application) apply false
|
||||||
|
}
|
||||||
21
gradle.properties
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Project-wide Gradle settings.
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Gradle settings configured through the IDE *will override*
|
||||||
|
# any settings specified in this file.
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||||
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
# This option should only be used with decoupled projects. For more details, visit
|
||||||
|
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
||||||
|
# org.gradle.parallel=true
|
||||||
|
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||||
|
# Android operating system, and which are packaged with your app's APK
|
||||||
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
|
android.useAndroidX=true
|
||||||
|
# Enables namespacing of each library's R class so that its R class includes only the
|
||||||
|
# resources declared in the library itself and none from the library's dependencies,
|
||||||
|
# thereby reducing the size of the R class for that library
|
||||||
|
android.nonTransitiveRClass=true
|
||||||
22
gradle/libs.versions.toml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
[versions]
|
||||||
|
agp = "9.0.0"
|
||||||
|
junit = "4.13.2"
|
||||||
|
junitVersion = "1.3.0"
|
||||||
|
espressoCore = "3.7.0"
|
||||||
|
appcompat = "1.7.1"
|
||||||
|
material = "1.13.0"
|
||||||
|
activity = "1.12.3"
|
||||||
|
constraintlayout = "2.2.1"
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||||
|
ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||||
|
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||||
|
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||||
|
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||||
|
activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
|
||||||
|
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
9
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#Sat Feb 07 20:11:06 CET 2026
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionSha256Sum=a17ddd85a26b6a7f5ddb71ff8b05fc5104c0202c6e64782429790c933686c806
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
251
gradlew
vendored
Executable file
@ -0,0 +1,251 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright © 2015 the original authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
app_path=$0
|
||||||
|
|
||||||
|
# Need this for daisy-chained symlinks.
|
||||||
|
while
|
||||||
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
|
[ -h "$app_path" ]
|
||||||
|
do
|
||||||
|
ls=$( ls -ld "$app_path" )
|
||||||
|
link=${ls#*' -> '}
|
||||||
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD=maximum
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
} >&2
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "$( uname )" in #(
|
||||||
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
|
Darwin* ) darwin=true ;; #(
|
||||||
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
|
NONSTOP* ) nonstop=true ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH="\\\"\\\""
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
|
else
|
||||||
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD=java
|
||||||
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
|
case $MAX_FD in #(
|
||||||
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
|
warn "Could not query maximum file descriptor limit"
|
||||||
|
esac
|
||||||
|
case $MAX_FD in #(
|
||||||
|
'' | soft) :;; #(
|
||||||
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC2039,SC3045
|
||||||
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
|
# * args from the command line
|
||||||
|
# * the main class name
|
||||||
|
# * -classpath
|
||||||
|
# * -D...appname settings
|
||||||
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command:
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
# and any embedded shellness will be escaped.
|
||||||
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
94
gradlew.bat
vendored
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%"=="" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
|
echo. 1>&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo. 1>&2
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
|
echo. 1>&2
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
24
settings.gradle.kts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
google {
|
||||||
|
content {
|
||||||
|
includeGroupByRegex("com\\.android.*")
|
||||||
|
includeGroupByRegex("com\\.google.*")
|
||||||
|
includeGroupByRegex("androidx.*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dependencyResolutionManagement {
|
||||||
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.name = "Android(Studio"
|
||||||
|
include(":app")
|
||||||
|
|
||||||