Compare commits
2 Commits
06ef90be69
...
c8d9b8a807
| Author | SHA1 | Date | |
|---|---|---|---|
| c8d9b8a807 | |||
|
|
b1efe8515a |
1
.idea/.name
generated
Normal file
@@ -0,0 +1 @@
|
|||||||
|
coloricam
|
||||||
15
.idea/deploymentTargetSelector.xml
generated
@@ -11,7 +11,20 @@
|
|||||||
</handle>
|
</handle>
|
||||||
</Target>
|
</Target>
|
||||||
</DropdownSelection>
|
</DropdownSelection>
|
||||||
<DialogSelection />
|
<DialogSelection>
|
||||||
|
<targets>
|
||||||
|
<Target type="DEFAULT_BOOT">
|
||||||
|
<handle>
|
||||||
|
<DeviceId pluginId="PhysicalDevice" identifier="serial=57191FDCG0001D" />
|
||||||
|
</handle>
|
||||||
|
</Target>
|
||||||
|
<Target type="DEFAULT_BOOT">
|
||||||
|
<handle>
|
||||||
|
<DeviceId pluginId="PhysicalDevice" identifier="serial=58051FDCG0038S" />
|
||||||
|
</handle>
|
||||||
|
</Target>
|
||||||
|
</targets>
|
||||||
|
</DialogSelection>
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
</selectionStates>
|
</selectionStates>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
13
.idea/deviceManager.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DeviceTable">
|
||||||
|
<option name="columnSorters">
|
||||||
|
<list>
|
||||||
|
<ColumnSorterState>
|
||||||
|
<option name="column" value="Name" />
|
||||||
|
<option name="order" value="ASCENDING" />
|
||||||
|
</ColumnSorterState>
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -3,14 +3,15 @@ plugins {
|
|||||||
alias(libs.plugins.kotlin.compose)
|
alias(libs.plugins.kotlin.compose)
|
||||||
alias(libs.plugins.ksp)
|
alias(libs.plugins.ksp)
|
||||||
alias(libs.plugins.secrets)
|
alias(libs.plugins.secrets)
|
||||||
|
alias(libs.plugins.google.services)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "com.example.coloricam"
|
namespace = "com.example.scanwich"
|
||||||
compileSdk = 35
|
compileSdk = 35
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.example.coloricam"
|
applicationId = "com.example.scanwich"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
@@ -19,9 +20,20 @@ android {
|
|||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
// On configure la release pour utiliser la même clé que le debug pour l'instant
|
||||||
|
getByName("debug") {
|
||||||
|
storeFile = file(System.getProperty("user.home") + "/.android/debug.keystore")
|
||||||
|
storePassword = "android"
|
||||||
|
keyAlias = "androiddebugkey"
|
||||||
|
keyPassword = "android"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = false
|
||||||
|
signingConfig = signingConfigs.getByName("debug")
|
||||||
proguardFiles(
|
proguardFiles(
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
"proguard-rules.pro"
|
"proguard-rules.pro"
|
||||||
@@ -73,6 +85,10 @@ dependencies {
|
|||||||
// Google Sign-In
|
// Google Sign-In
|
||||||
implementation(libs.play.services.auth)
|
implementation(libs.play.services.auth)
|
||||||
|
|
||||||
|
// Firebase
|
||||||
|
implementation(platform(libs.firebase.bom))
|
||||||
|
implementation(libs.firebase.analytics)
|
||||||
|
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"type": "APK",
|
"type": "APK",
|
||||||
"kind": "Directory"
|
"kind": "Directory"
|
||||||
},
|
},
|
||||||
"applicationId": "com.example.coloricam",
|
"applicationId": "com.example.scanwich",
|
||||||
"variantName": "release",
|
"variantName": "release",
|
||||||
"elements": [
|
"elements": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,64 +1 @@
|
|||||||
package com.example.coloricam
|
// Ce fichier est obsolète. Utilisez celui dans le package com.example.scanwich.
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.room.*
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
|
|
||||||
@Entity(tableName = "meals")
|
|
||||||
data class Meal(
|
|
||||||
@PrimaryKey(autoGenerate = true) val id: Int = 0,
|
|
||||||
val date: Long,
|
|
||||||
val name: String = "Repas",
|
|
||||||
val analysisText: String,
|
|
||||||
val totalCalories: Int,
|
|
||||||
val type: String = "Collation"
|
|
||||||
)
|
|
||||||
|
|
||||||
@Entity(tableName = "glycemia")
|
|
||||||
data class Glycemia(
|
|
||||||
@PrimaryKey(autoGenerate = true) val id: Int = 0,
|
|
||||||
val date: Long,
|
|
||||||
val value: Double,
|
|
||||||
val moment: String
|
|
||||||
)
|
|
||||||
|
|
||||||
@Entity(tableName = "sports")
|
|
||||||
data class SportActivity(
|
|
||||||
@PrimaryKey val id: Long,
|
|
||||||
val name: String,
|
|
||||||
val type: String,
|
|
||||||
val distance: Float,
|
|
||||||
val movingTime: Int,
|
|
||||||
val calories: Float?,
|
|
||||||
val date: Long // timestamp
|
|
||||||
)
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface AppDao {
|
|
||||||
@Insert suspend fun insertMeal(meal: Meal): Long
|
|
||||||
@Delete suspend fun deleteMeal(meal: Meal)
|
|
||||||
@Query("SELECT * FROM meals ORDER BY date DESC") fun getAllMeals(): Flow<List<Meal>>
|
|
||||||
|
|
||||||
@Insert suspend fun insertGlycemia(glycemia: Glycemia): Long
|
|
||||||
@Delete suspend fun deleteGlycemia(glycemia: Glycemia)
|
|
||||||
@Query("SELECT * FROM glycemia ORDER BY date DESC") fun getAllGlycemia(): Flow<List<Glycemia>>
|
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertSports(sports: List<SportActivity>)
|
|
||||||
@Query("SELECT * FROM sports ORDER BY date DESC") fun getAllSports(): Flow<List<SportActivity>>
|
|
||||||
@Query("SELECT * FROM sports WHERE date >= :startOfDay AND date < :endOfDay ORDER BY date DESC")
|
|
||||||
fun getSportsForDay(startOfDay: Long, endOfDay: Long): Flow<List<SportActivity>>
|
|
||||||
}
|
|
||||||
|
|
||||||
@Database(entities = [Meal::class, Glycemia::class, SportActivity::class], version = 5)
|
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
|
||||||
abstract fun appDao(): AppDao
|
|
||||||
companion object {
|
|
||||||
@Volatile private var INSTANCE: AppDatabase? = null
|
|
||||||
fun getDatabase(context: Context): AppDatabase =
|
|
||||||
INSTANCE ?: synchronized(this) {
|
|
||||||
Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_db")
|
|
||||||
.fallbackToDestructiveMigration()
|
|
||||||
.build().also { INSTANCE = it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,381 +1 @@
|
|||||||
package com.example.coloricam
|
// Ce fichier est obsolète. Utilisez celui dans le package com.example.scanwich.
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.util.Base64
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.activity.ComponentActivity
|
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
|
||||||
import androidx.activity.compose.setContent
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.browser.customtabs.CustomTabsIntent
|
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.*
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
|
|
||||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
|
||||||
import androidx.compose.material.icons.filled.*
|
|
||||||
import androidx.compose.material3.*
|
|
||||||
import androidx.compose.runtime.*
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.exifinterface.media.ExifInterface
|
|
||||||
import androidx.navigation.compose.NavHost
|
|
||||||
import androidx.navigation.compose.composable
|
|
||||||
import androidx.navigation.compose.rememberNavController
|
|
||||||
import com.example.coloricam.ui.theme.ColoricamTheme
|
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
|
|
||||||
import com.google.android.gms.common.api.ApiException
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.ResponseBody
|
|
||||||
import okhttp3.logging.HttpLoggingInterceptor
|
|
||||||
import retrofit2.Retrofit
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import retrofit2.converter.gson.GsonConverterFactory
|
|
||||||
import retrofit2.http.*
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import org.json.JSONObject
|
|
||||||
import org.json.JSONArray
|
|
||||||
|
|
||||||
// --- API MODELS ---
|
|
||||||
data class N8nMealRequest(
|
|
||||||
val imageBase64: String?,
|
|
||||||
val mealName: String?,
|
|
||||||
val prompt: String
|
|
||||||
)
|
|
||||||
|
|
||||||
interface N8nApi {
|
|
||||||
@POST("webhook/v1/gemini-proxy")
|
|
||||||
suspend fun analyzeMeal(
|
|
||||||
@Header("X-API-KEY") apiKey: String,
|
|
||||||
@Body request: N8nMealRequest
|
|
||||||
): ResponseBody
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- STRAVA API ---
|
|
||||||
data class StravaActivity(
|
|
||||||
val id: Long,
|
|
||||||
val name: String,
|
|
||||||
val type: String,
|
|
||||||
val distance: Float,
|
|
||||||
val moving_time: Int,
|
|
||||||
val elapsed_time: Int,
|
|
||||||
val calories: Float?,
|
|
||||||
val start_date: String,
|
|
||||||
val start_date_local: String
|
|
||||||
)
|
|
||||||
|
|
||||||
data class StravaTokenResponse(
|
|
||||||
val access_token: String,
|
|
||||||
val refresh_token: String,
|
|
||||||
val expires_at: Long
|
|
||||||
)
|
|
||||||
|
|
||||||
interface StravaApi {
|
|
||||||
@GET("athlete/activities")
|
|
||||||
suspend fun getActivities(
|
|
||||||
@Header("Authorization") token: String,
|
|
||||||
@Query("before") before: Long? = null,
|
|
||||||
@Query("after") after: Long? = null,
|
|
||||||
@Query("page") page: Int? = null,
|
|
||||||
@Query("per_page") perPage: Int? = 30
|
|
||||||
): List<StravaActivity>
|
|
||||||
|
|
||||||
@POST("oauth/token")
|
|
||||||
suspend fun exchangeToken(
|
|
||||||
@Query("client_id") clientId: String,
|
|
||||||
@Query("client_secret") clientSecret: String,
|
|
||||||
@Query("code") code: String,
|
|
||||||
@Query("grant_type") grantType: String = "authorization_code"
|
|
||||||
): StravaTokenResponse
|
|
||||||
|
|
||||||
@POST("oauth/token")
|
|
||||||
suspend fun refreshToken(
|
|
||||||
@Query("client_id") clientId: String,
|
|
||||||
@Query("client_secret") clientSecret: String,
|
|
||||||
@Query("refresh_token") refreshToken: String,
|
|
||||||
@Query("grant_type") grantType: String = "refresh_token"
|
|
||||||
): StravaTokenResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helpers
|
|
||||||
object ApiClient {
|
|
||||||
private val loggingInterceptor = HttpLoggingInterceptor().apply {
|
|
||||||
level = HttpLoggingInterceptor.Level.BODY
|
|
||||||
}
|
|
||||||
|
|
||||||
private val okHttpClient = OkHttpClient.Builder()
|
|
||||||
.addInterceptor(loggingInterceptor)
|
|
||||||
.connectTimeout(60, TimeUnit.SECONDS)
|
|
||||||
.readTimeout(60, TimeUnit.SECONDS)
|
|
||||||
.writeTimeout(60, TimeUnit.SECONDS)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
private val retrofitStrava = Retrofit.Builder()
|
|
||||||
.baseUrl("https://www.strava.com/api/v3/")
|
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
|
||||||
.build()
|
|
||||||
|
|
||||||
private val retrofitN8n = Retrofit.Builder()
|
|
||||||
.baseUrl("https://n8n.marquis1987.com/")
|
|
||||||
.client(okHttpClient)
|
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val stravaApi: StravaApi = retrofitStrava.create(StravaApi::class.java)
|
|
||||||
val n8nApi: N8nApi = retrofitN8n.create(N8nApi::class.java)
|
|
||||||
|
|
||||||
suspend fun getValidStravaToken(prefs: SharedPreferences): String? {
|
|
||||||
val stravaToken = prefs.getString("strava_token", null) ?: return null
|
|
||||||
val expiresAt = prefs.getLong("strava_expires_at", 0)
|
|
||||||
val refreshToken = prefs.getString("strava_refresh_token", null)
|
|
||||||
val clientId = prefs.getString("strava_client_id", "") ?: ""
|
|
||||||
val clientSecret = prefs.getString("strava_client_secret", "") ?: ""
|
|
||||||
|
|
||||||
val currentTime = System.currentTimeMillis() / 1000
|
|
||||||
if (currentTime >= expiresAt && refreshToken != null && clientId.isNotBlank()) {
|
|
||||||
try {
|
|
||||||
val refreshResponse = stravaApi.refreshToken(clientId, clientSecret, refreshToken)
|
|
||||||
prefs.edit()
|
|
||||||
.putString("strava_token", refreshResponse.access_token)
|
|
||||||
.putString("strava_refresh_token", refreshResponse.refresh_token)
|
|
||||||
.putLong("strava_expires_at", refreshResponse.expires_at)
|
|
||||||
.apply()
|
|
||||||
return refreshResponse.access_token
|
|
||||||
} catch (e: Exception) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return stravaToken
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseStravaDate(dateStr: String): Long {
|
|
||||||
return try {
|
|
||||||
val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)
|
|
||||||
inputFormat.timeZone = TimeZone.getTimeZone("UTC")
|
|
||||||
inputFormat.parse(dateStr)?.time ?: 0L
|
|
||||||
} catch (e: Exception) { 0L }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun estimateCaloriesFromDb(activity: SportActivity, weightKg: Double): Int {
|
|
||||||
if (activity.calories != null && activity.calories > 0) return activity.calories.toInt()
|
|
||||||
val met = when (activity.type.lowercase()) {
|
|
||||||
"run" -> 10.0
|
|
||||||
"ride" -> 8.0
|
|
||||||
"walk" -> 3.5
|
|
||||||
"hike" -> 6.0
|
|
||||||
"swim" -> 7.0
|
|
||||||
"weighttraining" -> 5.0
|
|
||||||
"workout" -> 4.5
|
|
||||||
else -> 5.0
|
|
||||||
}
|
|
||||||
val durationHours = activity.movingTime / 3600.0
|
|
||||||
return (met * weightKg * durationHours).toInt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- UI COMPONENTS ---
|
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
|
||||||
private lateinit var dao: AppDao
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
dao = AppDatabase.getDatabase(this).appDao()
|
|
||||||
handleStravaCallback(intent)
|
|
||||||
setContent {
|
|
||||||
ColoricamTheme {
|
|
||||||
AuthWrapper(dao)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent) {
|
|
||||||
super.onNewIntent(intent)
|
|
||||||
handleStravaCallback(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleStravaCallback(intent: Intent) {
|
|
||||||
val data: Uri? = intent.data
|
|
||||||
if (data != null && data.toString().startsWith("coloricam://localhost")) {
|
|
||||||
val code = data.getQueryParameter("code")
|
|
||||||
if (code != null) {
|
|
||||||
val prefs = getSharedPreferences("user_prefs", Context.MODE_PRIVATE)
|
|
||||||
prefs.edit().putString("strava_code", code).apply()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun AuthWrapper(dao: AppDao) {
|
|
||||||
val context = LocalContext.current
|
|
||||||
val gso = remember {
|
|
||||||
GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
|
|
||||||
.requestEmail()
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
val googleSignInClient = remember { GoogleSignIn.getClient(context, gso) }
|
|
||||||
var account by remember { mutableStateOf(GoogleSignIn.getLastSignedInAccount(context)) }
|
|
||||||
|
|
||||||
// Whitelist
|
|
||||||
val allowedEmails = listOf("marcandre.charest@gmail.com")
|
|
||||||
|
|
||||||
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
|
||||||
val task = GoogleSignIn.getSignedInAccountFromIntent(result.data)
|
|
||||||
try {
|
|
||||||
account = task.getResult(ApiException::class.java)
|
|
||||||
} catch (e: ApiException) {
|
|
||||||
Log.e("Auth", "signInResult:failed code=" + e.statusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val onLogout: () -> Unit = {
|
|
||||||
googleSignInClient.signOut().addOnCompleteListener {
|
|
||||||
account = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (account == null) {
|
|
||||||
LoginScreen { launcher.launch(googleSignInClient.signInIntent) }
|
|
||||||
} else {
|
|
||||||
if (allowedEmails.contains(account?.email)) {
|
|
||||||
MainApp(dao, onLogout)
|
|
||||||
} else {
|
|
||||||
AccessDeniedScreen(onLogout)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun LoginScreen(onLoginClick: () -> Unit) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.fillMaxSize().padding(16.dp),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.Center
|
|
||||||
) {
|
|
||||||
Text("Coloricam", style = MaterialTheme.typography.displayLarge, color = MaterialTheme.colorScheme.primary)
|
|
||||||
Spacer(Modifier.height(32.dp))
|
|
||||||
Button(onClick = onLoginClick, modifier = Modifier.fillMaxWidth().height(56.dp)) {
|
|
||||||
Icon(Icons.Default.AccountCircle, null)
|
|
||||||
Spacer(Modifier.width(8.dp))
|
|
||||||
Text("Se connecter avec Google")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun AccessDeniedScreen(onLogout: () -> Unit) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.fillMaxSize().padding(32.dp),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.Center
|
|
||||||
) {
|
|
||||||
Icon(Icons.Default.Warning, null, modifier = Modifier.size(80.dp), tint = MaterialTheme.colorScheme.error)
|
|
||||||
Spacer(Modifier.height(16.dp))
|
|
||||||
Text("Accès Refusé", style = MaterialTheme.typography.headlineMedium, color = MaterialTheme.colorScheme.error)
|
|
||||||
Spacer(Modifier.height(8.dp))
|
|
||||||
Text("Votre compte n'est pas autorisé à utiliser cette application.", style = MaterialTheme.typography.bodyLarge)
|
|
||||||
Spacer(Modifier.height(32.dp))
|
|
||||||
Button(onClick = onLogout) { Text("Changer de compte") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun MainApp(dao: AppDao, onLogout: () -> Unit) {
|
|
||||||
val context = LocalContext.current
|
|
||||||
val prefs = remember { context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE) }
|
|
||||||
var showSetup by remember { mutableStateOf(!prefs.contains("target_calories")) }
|
|
||||||
var isDiabetic by remember { mutableStateOf(prefs.getBoolean("is_diabetic", false)) }
|
|
||||||
|
|
||||||
if (showSetup) {
|
|
||||||
SetupScreen(prefs) {
|
|
||||||
showSetup = false
|
|
||||||
isDiabetic = prefs.getBoolean("is_diabetic", false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val navController = rememberNavController()
|
|
||||||
Scaffold(
|
|
||||||
bottomBar = {
|
|
||||||
NavigationBar {
|
|
||||||
NavigationBarItem(icon = { Icon(Icons.Default.Home, "Repas") }, label = { Text("Repas") }, selected = false, onClick = { navController.navigate("capture") })
|
|
||||||
NavigationBarItem(icon = { Icon(Icons.Default.Add, "Sport") }, label = { Text("Sport") }, selected = false, onClick = { navController.navigate("sport") })
|
|
||||||
NavigationBarItem(icon = { Icon(Icons.Default.Settings, "Paramètres") }, label = { Text("Paramètres") }, selected = false, onClick = { navController.navigate("settings") })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) { innerPadding ->
|
|
||||||
NavHost(navController, "capture", Modifier.padding(innerPadding)) {
|
|
||||||
composable("capture") { CaptureScreen(dao, prefs, isDiabetic) }
|
|
||||||
composable("sport") { SportScreen(dao, prefs) }
|
|
||||||
composable("settings") { SettingsScreen(prefs, onLogout) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun SetupScreen(prefs: SharedPreferences, onComplete: () -> Unit) {
|
|
||||||
var targetCalories by remember { mutableStateOf("2000") }
|
|
||||||
var targetCarbs by remember { mutableStateOf("250") }
|
|
||||||
var weightKg by remember { mutableStateOf("70") }
|
|
||||||
var isDiabetic by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.fillMaxSize().padding(24.dp).verticalScroll(rememberScrollState()),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.Center
|
|
||||||
) {
|
|
||||||
Text("Configuration initiale", style = MaterialTheme.typography.headlineLarge)
|
|
||||||
Spacer(Modifier.height(24.dp))
|
|
||||||
OutlinedTextField(value = targetCalories, onValueChange = { targetCalories = it }, label = { Text("Objectif Calories / jour") }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), modifier = Modifier.fillMaxWidth())
|
|
||||||
Spacer(Modifier.height(16.dp))
|
|
||||||
OutlinedTextField(value = targetCarbs, onValueChange = { targetCarbs = it }, label = { Text("Objectif Glucides / jour (g)") }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), modifier = Modifier.fillMaxWidth())
|
|
||||||
Spacer(Modifier.height(16.dp))
|
|
||||||
OutlinedTextField(value = weightKg, onValueChange = { weightKg = it }, label = { Text("Poids (kg)") }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), modifier = Modifier.fillMaxWidth())
|
|
||||||
Spacer(Modifier.height(24.dp))
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
|
|
||||||
Checkbox(checked = isDiabetic, onCheckedChange = { isDiabetic = it })
|
|
||||||
Text("Je suis diabétique (ajoute l'estimation d'insuline)")
|
|
||||||
}
|
|
||||||
Spacer(Modifier.height(32.dp))
|
|
||||||
Button(onClick = {
|
|
||||||
prefs.edit()
|
|
||||||
.putString("target_calories", targetCalories)
|
|
||||||
.putString("target_carbs", targetCarbs)
|
|
||||||
.putString("weight_kg", weightKg)
|
|
||||||
.putBoolean("is_diabetic", isDiabetic)
|
|
||||||
.apply()
|
|
||||||
onComplete()
|
|
||||||
}, modifier = Modifier.fillMaxWidth().height(56.dp)) {
|
|
||||||
Text("Commencer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.example.coloricam.ui.theme
|
package com.example.scanwich.ui.theme
|
||||||
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.example.coloricam.ui.theme
|
package com.example.scanwich.ui.theme
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
@@ -21,20 +21,10 @@ private val LightColorScheme = lightColorScheme(
|
|||||||
primary = Purple40,
|
primary = Purple40,
|
||||||
secondary = PurpleGrey40,
|
secondary = PurpleGrey40,
|
||||||
tertiary = Pink40
|
tertiary = Pink40
|
||||||
|
|
||||||
/* Other default colors to override
|
|
||||||
background = Color(0xFFFFFBFE),
|
|
||||||
surface = Color(0xFFFFFBFE),
|
|
||||||
onPrimary = Color.White,
|
|
||||||
onSecondary = Color.White,
|
|
||||||
onTertiary = Color.White,
|
|
||||||
onBackground = Color(0xFF1C1B1F),
|
|
||||||
onSurface = Color(0xFF1C1B1F),
|
|
||||||
*/
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ColoricamTheme(
|
fun ScanwichTheme(
|
||||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
// Dynamic color is available on Android 12+
|
// Dynamic color is available on Android 12+
|
||||||
dynamicColor: Boolean = true,
|
dynamicColor: Boolean = true,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.example.coloricam.ui.theme
|
package com.example.scanwich.ui.theme
|
||||||
|
|
||||||
import androidx.compose.material3.Typography
|
import androidx.compose.material3.Typography
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
@@ -15,20 +15,4 @@ val Typography = Typography(
|
|||||||
lineHeight = 24.sp,
|
lineHeight = 24.sp,
|
||||||
letterSpacing = 0.5.sp
|
letterSpacing = 0.5.sp
|
||||||
)
|
)
|
||||||
/* Other default text styles to override
|
|
||||||
titleLarge = TextStyle(
|
|
||||||
fontFamily = FontFamily.Default,
|
|
||||||
fontWeight = FontWeight.Normal,
|
|
||||||
fontSize = 22.sp,
|
|
||||||
lineHeight = 28.sp,
|
|
||||||
letterSpacing = 0.sp
|
|
||||||
),
|
|
||||||
labelSmall = TextStyle(
|
|
||||||
fontFamily = FontFamily.Default,
|
|
||||||
fontWeight = FontWeight.Medium,
|
|
||||||
fontSize = 11.sp,
|
|
||||||
lineHeight = 16.sp,
|
|
||||||
letterSpacing = 0.5.sp
|
|
||||||
)
|
|
||||||
*/
|
|
||||||
)
|
)
|
||||||
68
app/src/main/java/com/example/scanwich/Database.kt
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package com.example.scanwich
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.room.*
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
@Entity(tableName = "meals")
|
||||||
|
data class Meal(
|
||||||
|
@PrimaryKey(autoGenerate = true) val id: Int = 0,
|
||||||
|
val date: Long,
|
||||||
|
val name: String = "Repas",
|
||||||
|
val analysisText: String,
|
||||||
|
val totalCalories: Int,
|
||||||
|
val type: String = "Collation"
|
||||||
|
)
|
||||||
|
|
||||||
|
@Entity(tableName = "glycemia")
|
||||||
|
data class Glycemia(
|
||||||
|
@PrimaryKey(autoGenerate = true) val id: Int = 0,
|
||||||
|
val date: Long,
|
||||||
|
val value: Double,
|
||||||
|
val moment: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Entity(tableName = "sports")
|
||||||
|
data class SportActivity(
|
||||||
|
@PrimaryKey val id: Long,
|
||||||
|
val name: String,
|
||||||
|
val type: String,
|
||||||
|
val distance: Float,
|
||||||
|
val movingTime: Int,
|
||||||
|
val calories: Float?,
|
||||||
|
val date: Long // timestamp
|
||||||
|
)
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface AppDao {
|
||||||
|
@Insert suspend fun insertMeal(meal: Meal): Long
|
||||||
|
@Delete suspend fun deleteMeal(meal: Meal)
|
||||||
|
@Query("SELECT * FROM meals ORDER BY date DESC") fun getAllMeals(): Flow<List<Meal>>
|
||||||
|
@Query("SELECT * FROM meals WHERE date >= :startOfDay AND date < :endOfDay ORDER BY date DESC")
|
||||||
|
fun getMealsForDay(startOfDay: Long, endOfDay: Long): Flow<List<Meal>>
|
||||||
|
|
||||||
|
@Insert suspend fun insertGlycemia(glycemia: Glycemia): Long
|
||||||
|
@Delete suspend fun deleteGlycemia(glycemia: Glycemia)
|
||||||
|
@Query("SELECT * FROM glycemia ORDER BY date DESC") fun getAllGlycemia(): Flow<List<Glycemia>>
|
||||||
|
@Query("SELECT * FROM glycemia WHERE date >= :startOfDay AND date < :endOfDay ORDER BY date DESC")
|
||||||
|
fun getGlycemiaForDay(startOfDay: Long, endOfDay: Long): Flow<List<Glycemia>>
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertSports(sports: List<SportActivity>)
|
||||||
|
@Query("SELECT * FROM sports ORDER BY date DESC") fun getAllSports(): Flow<List<SportActivity>>
|
||||||
|
@Query("SELECT * FROM sports WHERE date >= :startOfDay AND date < :endOfDay ORDER BY date DESC")
|
||||||
|
fun getSportsForDay(startOfDay: Long, endOfDay: Long): Flow<List<SportActivity>>
|
||||||
|
}
|
||||||
|
|
||||||
|
@Database(entities = [Meal::class, Glycemia::class, SportActivity::class], version = 5)
|
||||||
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
abstract fun appDao(): AppDao
|
||||||
|
companion object {
|
||||||
|
@Volatile private var INSTANCE: AppDatabase? = null
|
||||||
|
fun getDatabase(context: Context): AppDatabase =
|
||||||
|
INSTANCE ?: synchronized(this) {
|
||||||
|
Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_db")
|
||||||
|
.fallbackToDestructiveMigration()
|
||||||
|
.build().also { INSTANCE = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1224
app/src/main/java/com/example/scanwich/MainActivity.kt
Normal file
@@ -1,170 +1,74 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
android:height="108dp"
|
||||||
|
android:width="108dp"
|
||||||
|
android:viewportHeight="108"
|
||||||
android:viewportWidth="108"
|
android:viewportWidth="108"
|
||||||
android:viewportHeight="108">
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<path
|
<path android:fillColor="#3DDC84"
|
||||||
android:fillColor="#3DDC84"
|
android:pathData="M0,0h108v108h-108z"/>
|
||||||
android:pathData="M0,0h108v108h-108z" />
|
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
|
||||||
<path
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:fillColor="#00000000"
|
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
|
||||||
android:pathData="M9,0L9,108"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:strokeWidth="0.8"
|
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
|
||||||
android:strokeColor="#33FFFFFF" />
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
<path
|
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
|
||||||
android:fillColor="#00000000"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:pathData="M19,0L19,108"
|
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
|
||||||
android:strokeWidth="0.8"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:strokeColor="#33FFFFFF" />
|
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
|
||||||
<path
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:fillColor="#00000000"
|
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
|
||||||
android:pathData="M29,0L29,108"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:strokeWidth="0.8"
|
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
|
||||||
android:strokeColor="#33FFFFFF" />
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
<path
|
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
|
||||||
android:fillColor="#00000000"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:pathData="M39,0L39,108"
|
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
|
||||||
android:strokeWidth="0.8"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:strokeColor="#33FFFFFF" />
|
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
|
||||||
<path
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:fillColor="#00000000"
|
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
|
||||||
android:pathData="M49,0L49,108"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:strokeWidth="0.8"
|
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
|
||||||
android:strokeColor="#33FFFFFF" />
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
<path
|
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
|
||||||
android:fillColor="#00000000"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:pathData="M59,0L59,108"
|
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
|
||||||
android:strokeWidth="0.8"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:strokeColor="#33FFFFFF" />
|
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
|
||||||
<path
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:fillColor="#00000000"
|
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
|
||||||
android:pathData="M69,0L69,108"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:strokeWidth="0.8"
|
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
|
||||||
android:strokeColor="#33FFFFFF" />
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
<path
|
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
|
||||||
android:fillColor="#00000000"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:pathData="M79,0L79,108"
|
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
|
||||||
android:strokeWidth="0.8"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:strokeColor="#33FFFFFF" />
|
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
|
||||||
<path
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:fillColor="#00000000"
|
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
|
||||||
android:pathData="M89,0L89,108"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:strokeWidth="0.8"
|
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
|
||||||
android:strokeColor="#33FFFFFF" />
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
<path
|
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
|
||||||
android:fillColor="#00000000"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:pathData="M99,0L99,108"
|
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
|
||||||
android:strokeWidth="0.8"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:strokeColor="#33FFFFFF" />
|
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
|
||||||
<path
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:fillColor="#00000000"
|
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
|
||||||
android:pathData="M0,9L108,9"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:strokeWidth="0.8"
|
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
|
||||||
android:strokeColor="#33FFFFFF" />
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
<path
|
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
|
||||||
android:fillColor="#00000000"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:pathData="M0,19L108,19"
|
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
|
||||||
android:strokeWidth="0.8"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:strokeColor="#33FFFFFF" />
|
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
|
||||||
<path
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
android:fillColor="#00000000"
|
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
|
||||||
android:pathData="M0,29L108,29"
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
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>
|
</vector>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 982 B After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 38 KiB |
@@ -1,3 +1,3 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">coloricam</string>
|
<string name="app_name">Scan-Wich</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -4,4 +4,5 @@ plugins {
|
|||||||
alias(libs.plugins.kotlin.compose) apply false
|
alias(libs.plugins.kotlin.compose) apply false
|
||||||
alias(libs.plugins.ksp) apply false
|
alias(libs.plugins.ksp) apply false
|
||||||
alias(libs.plugins.secrets) apply false
|
alias(libs.plugins.secrets) apply false
|
||||||
|
alias(libs.plugins.google.services) apply false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ browser = "1.8.0"
|
|||||||
exifinterface = "1.3.7"
|
exifinterface = "1.3.7"
|
||||||
secretsPlugin = "2.0.1"
|
secretsPlugin = "2.0.1"
|
||||||
playServicesAuth = "21.2.0"
|
playServicesAuth = "21.2.0"
|
||||||
|
googleServices = "4.4.2"
|
||||||
|
firebaseBom = "34.9.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
@@ -47,9 +49,12 @@ okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor",
|
|||||||
androidx-browser = { group = "androidx.browser", name = "browser", version.ref = "browser" }
|
androidx-browser = { group = "androidx.browser", name = "browser", version.ref = "browser" }
|
||||||
androidx-exifinterface = { group = "androidx.exifinterface", name = "exifinterface", version.ref = "exifinterface" }
|
androidx-exifinterface = { group = "androidx.exifinterface", name = "exifinterface", version.ref = "exifinterface" }
|
||||||
play-services-auth = { group = "com.google.android.gms", name = "play-services-auth", version.ref = "playServicesAuth" }
|
play-services-auth = { group = "com.google.android.gms", name = "play-services-auth", version.ref = "playServicesAuth" }
|
||||||
|
firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom" }
|
||||||
|
firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||||
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
||||||
secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secretsPlugin" }
|
secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secretsPlugin" }
|
||||||
|
google-services = { id = "com.google.gms.google-services", version.ref = "googleServices" }
|
||||||
|
|||||||