From 077397749c89463316895f2d296697c2e48b100b Mon Sep 17 00:00:00 2001 From: Avior Date: Wed, 22 Dec 2021 01:44:34 +0100 Subject: [PATCH] Stated Adding Water Intake --- .gitignore | 7 +- .idea/deploymentTargetDropDown.xml | 17 + .idea/inspectionProfiles/Project_Default.xml | 6 + .idea/misc.xml | 5 +- .../java/com/dzeio/openhealth/MainActivity.kt | 6 - .../com/dzeio/openhealth/data/AppDatabase.kt | 15 +- .../com/dzeio/openhealth/data/water/Water.kt | 29 ++ .../dzeio/openhealth/data/water/WaterDao.kt | 26 ++ .../openhealth/data/water/WaterRepository.kt | 32 ++ .../com/dzeio/openhealth/di/DatabaseModule.kt | 6 + .../{connectors => extensions}/DataType.kt | 2 +- .../Connector.kt => extensions/Extension.kt} | 8 +- .../{connectors => extensions}/GoogleFit.kt | 357 +++++++++--------- .../GoogleFit.kt.old | 4 +- .../samsunghealth/SamsungHealth.kt | 214 +++++------ .../samsunghealth/StepCountReporter.kt | 2 +- .../ui/{main => }/home/HomeFragment.kt | 164 +++++--- .../ui/{main => }/home/HomeViewModel.kt | 17 +- .../ui/{main => }/imports/ImportFragment.kt | 14 +- .../ui/{main => }/imports/ImportViewModel.kt | 3 +- .../openhealth/ui/water/WaterHomeFragment.kt | 2 + .../ui/{dialogs => weight}/AddWeightDialog.kt | 133 ++++--- .../{dialogs => weight}/EditWeightDialog.kt | 325 ++++++++-------- .../ListWeightFragment.kt | 105 +++--- .../com/dzeio/openhealth/utils/BitmapUtils.kt | 49 +++ .../com/dzeio/openhealth/utils/DrawUtils.kt | 30 ++ app/src/main/res/drawable/ellipse.png | Bin 0 -> 9231 bytes .../main/res/drawable/vector_elipse_empty.xml | 12 + app/src/main/res/layout/fragment_home.xml | 195 ++++++++-- app/src/main/res/layout/fragment_import.xml | 2 +- .../main/res/layout/fragment_list_weight.xml | 2 +- .../main/res/layout/fragment_water_home.xml | 6 + .../main/res/navigation/mobile_navigation.xml | 8 +- 33 files changed, 1109 insertions(+), 694 deletions(-) create mode 100644 .idea/deploymentTargetDropDown.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 app/src/main/java/com/dzeio/openhealth/data/water/Water.kt create mode 100644 app/src/main/java/com/dzeio/openhealth/data/water/WaterDao.kt create mode 100644 app/src/main/java/com/dzeio/openhealth/data/water/WaterRepository.kt rename app/src/main/java/com/dzeio/openhealth/{connectors => extensions}/DataType.kt (83%) rename app/src/main/java/com/dzeio/openhealth/{connectors/Connector.kt => extensions/Extension.kt} (77%) rename app/src/main/java/com/dzeio/openhealth/{connectors => extensions}/GoogleFit.kt (92%) rename app/src/main/java/com/dzeio/openhealth/{connectors => extensions}/GoogleFit.kt.old (99%) rename app/src/main/java/com/dzeio/openhealth/{connectors => extensions}/samsunghealth/SamsungHealth.kt (93%) rename app/src/main/java/com/dzeio/openhealth/{connectors => extensions}/samsunghealth/StepCountReporter.kt (98%) rename app/src/main/java/com/dzeio/openhealth/ui/{main => }/home/HomeFragment.kt (58%) rename app/src/main/java/com/dzeio/openhealth/ui/{main => }/home/HomeViewModel.kt (53%) rename app/src/main/java/com/dzeio/openhealth/ui/{main => }/imports/ImportFragment.kt (94%) rename app/src/main/java/com/dzeio/openhealth/ui/{main => }/imports/ImportViewModel.kt (92%) create mode 100644 app/src/main/java/com/dzeio/openhealth/ui/water/WaterHomeFragment.kt rename app/src/main/java/com/dzeio/openhealth/ui/{dialogs => weight}/AddWeightDialog.kt (91%) rename app/src/main/java/com/dzeio/openhealth/ui/{dialogs => weight}/EditWeightDialog.kt (82%) rename app/src/main/java/com/dzeio/openhealth/ui/{main/list_weight => weight}/ListWeightFragment.kt (68%) create mode 100644 app/src/main/java/com/dzeio/openhealth/utils/BitmapUtils.kt create mode 100644 app/src/main/java/com/dzeio/openhealth/utils/DrawUtils.kt create mode 100644 app/src/main/res/drawable/ellipse.png create mode 100644 app/src/main/res/drawable/vector_elipse_empty.xml create mode 100644 app/src/main/res/layout/fragment_water_home.xml diff --git a/.gitignore b/.gitignore index aa724b7..10cfdbf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,7 @@ *.iml .gradle /local.properties -/.idea/caches -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml -/.idea/navEditor.xml -/.idea/assetWizardSettings.xml +/.idea .DS_Store /build /captures diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml new file mode 100644 index 0000000..1afdc7c --- /dev/null +++ b/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..ea37264 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 7318686..dbc3352 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -7,6 +7,8 @@ + + @@ -15,9 +17,10 @@ - + + diff --git a/app/src/main/java/com/dzeio/openhealth/MainActivity.kt b/app/src/main/java/com/dzeio/openhealth/MainActivity.kt index a79d649..20da0ef 100644 --- a/app/src/main/java/com/dzeio/openhealth/MainActivity.kt +++ b/app/src/main/java/com/dzeio/openhealth/MainActivity.kt @@ -5,10 +5,7 @@ import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.Menu -import androidx.appcompat.app.AppCompatActivity -import androidx.drawerlayout.widget.DrawerLayout import androidx.navigation.NavController -import androidx.navigation.findNavController import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.navigateUp @@ -16,9 +13,6 @@ import androidx.navigation.ui.setupActionBarWithNavController import androidx.navigation.ui.setupWithNavController import com.dzeio.openhealth.core.BaseActivity import com.dzeio.openhealth.databinding.ActivityMainBinding -import com.dzeio.openhealth.ui.main.home.HomeFragmentDirections -import com.google.android.material.navigation.NavigationView -import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint diff --git a/app/src/main/java/com/dzeio/openhealth/data/AppDatabase.kt b/app/src/main/java/com/dzeio/openhealth/data/AppDatabase.kt index f5dbd79..0cc4959 100644 --- a/app/src/main/java/com/dzeio/openhealth/data/AppDatabase.kt +++ b/app/src/main/java/com/dzeio/openhealth/data/AppDatabase.kt @@ -4,18 +4,27 @@ import android.content.Context import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase +import com.dzeio.openhealth.data.weight.Water +import com.dzeio.openhealth.data.weight.WaterDao import com.dzeio.openhealth.data.weight.WeightDao import com.dzeio.openhealth.data.weight.Weight -@Database(entities = [Weight::class], version = 1, exportSchema = false) +@Database( + entities = [ + Weight::class, + Water::class + ], version = 1, exportSchema = false +) abstract class AppDatabase : RoomDatabase() { - abstract fun weightDao() : WeightDao + abstract fun weightDao(): WeightDao + abstract fun waterDao(): WaterDao companion object { private const val DATABASE_NAME = "open_health" // For Singleton instantiation - @Volatile private var instance: AppDatabase? = null + @Volatile + private var instance: AppDatabase? = null fun getInstance(context: Context): AppDatabase { return instance ?: synchronized(this) { diff --git a/app/src/main/java/com/dzeio/openhealth/data/water/Water.kt b/app/src/main/java/com/dzeio/openhealth/data/water/Water.kt new file mode 100644 index 0000000..29e4943 --- /dev/null +++ b/app/src/main/java/com/dzeio/openhealth/data/water/Water.kt @@ -0,0 +1,29 @@ +package com.dzeio.openhealth.data.weight + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.sql.Date +import java.text.DateFormat.getDateInstance +import java.util.* + +@Entity() +data class Water( + @PrimaryKey(autoGenerate = true) var id: Long = 0, + var value: Int = 0, + @ColumnInfo(index = true) + var timestamp: Long = System.currentTimeMillis(), + var source: String = "OpenHealth" +) { + init { + val cal = Calendar.getInstance() + cal.set(Calendar.HOUR_OF_DAY, 0) + cal.set(Calendar.MINUTE, 0) + cal.set(Calendar.SECOND, 0) + cal.set(Calendar.MILLISECOND, 0) + + timestamp = cal.timeInMillis + } + + fun formatTimestamp(): String = getDateInstance().format(Date(timestamp)) +} \ No newline at end of file diff --git a/app/src/main/java/com/dzeio/openhealth/data/water/WaterDao.kt b/app/src/main/java/com/dzeio/openhealth/data/water/WaterDao.kt new file mode 100644 index 0000000..7b32715 --- /dev/null +++ b/app/src/main/java/com/dzeio/openhealth/data/water/WaterDao.kt @@ -0,0 +1,26 @@ +package com.dzeio.openhealth.data.weight + +import androidx.room.* +import com.dzeio.openhealth.core.BaseDao +import dagger.Provides +import kotlinx.coroutines.flow.Flow + +@Dao +interface WaterDao : BaseDao { + + @Query("SELECT * FROM Water") + fun getAll(): Flow> + + @Query("SELECT * FROM Water where id = :weightId") + fun getOne(weightId: Long): Flow + + + @Query("Select count(*) from Water") + fun getCount(): Flow + + @Query("Select * FROM Water WHERE id=(SELECT max(id) FROM Water)") + fun last(): Flow + + @Query("DELETE FROM Water where source = :source") + suspend fun deleteFromSource(source: String) +} \ No newline at end of file diff --git a/app/src/main/java/com/dzeio/openhealth/data/water/WaterRepository.kt b/app/src/main/java/com/dzeio/openhealth/data/water/WaterRepository.kt new file mode 100644 index 0000000..2a63e97 --- /dev/null +++ b/app/src/main/java/com/dzeio/openhealth/data/water/WaterRepository.kt @@ -0,0 +1,32 @@ +package com.dzeio.openhealth.data.weight + +import android.util.Log +import kotlinx.coroutines.flow.* +import java.util.* +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class WaterRepository @Inject constructor( + private val waterDao: WaterDao +) { + fun getWaters() = waterDao.getAll() + + fun lastWater() = waterDao.last() + + fun getWater(id: Long) = waterDao.getOne(id) + + suspend fun addWater(value: Water) = waterDao.insert(value) + suspend fun deleteWater(value: Water) = waterDao.delete(value) + suspend fun deleteFromSource(value: String) = waterDao.deleteFromSource(value) + + fun todayWater() = lastWater().filter { + val cal = Calendar.getInstance() + cal.set(Calendar.HOUR_OF_DAY, 0) + cal.set(Calendar.MINUTE, 0) + cal.set(Calendar.SECOND, 0) + cal.set(Calendar.MILLISECOND, 0) + Log.d("WaterRepository", "${it?.timestamp} ${cal.timeInMillis}") + return@filter it?.timestamp == cal.timeInMillis + } +} \ No newline at end of file diff --git a/app/src/main/java/com/dzeio/openhealth/di/DatabaseModule.kt b/app/src/main/java/com/dzeio/openhealth/di/DatabaseModule.kt index cf320b2..4c2fba8 100644 --- a/app/src/main/java/com/dzeio/openhealth/di/DatabaseModule.kt +++ b/app/src/main/java/com/dzeio/openhealth/di/DatabaseModule.kt @@ -2,6 +2,7 @@ package com.dzeio.openhealth.di import android.content.Context import com.dzeio.openhealth.data.AppDatabase +import com.dzeio.openhealth.data.weight.WaterDao import com.dzeio.openhealth.data.weight.WeightDao import dagger.Module import dagger.Provides @@ -24,4 +25,9 @@ class DatabaseModule { fun provideWeightDao(appDatabase: AppDatabase): WeightDao { return appDatabase.weightDao() } + + @Provides + fun provideWaterDao(appDatabase: AppDatabase): WaterDao { + return appDatabase.waterDao() + } } \ No newline at end of file diff --git a/app/src/main/java/com/dzeio/openhealth/connectors/DataType.kt b/app/src/main/java/com/dzeio/openhealth/extensions/DataType.kt similarity index 83% rename from app/src/main/java/com/dzeio/openhealth/connectors/DataType.kt rename to app/src/main/java/com/dzeio/openhealth/extensions/DataType.kt index b94ee19..e7777d6 100644 --- a/app/src/main/java/com/dzeio/openhealth/connectors/DataType.kt +++ b/app/src/main/java/com/dzeio/openhealth/extensions/DataType.kt @@ -1,4 +1,4 @@ -package com.dzeio.openhealth.connectors +package com.dzeio.openhealth.extensions enum class DataType { WEIGHT diff --git a/app/src/main/java/com/dzeio/openhealth/connectors/Connector.kt b/app/src/main/java/com/dzeio/openhealth/extensions/Extension.kt similarity index 77% rename from app/src/main/java/com/dzeio/openhealth/connectors/Connector.kt rename to app/src/main/java/com/dzeio/openhealth/extensions/Extension.kt index 2453d81..a42b04a 100644 --- a/app/src/main/java/com/dzeio/openhealth/connectors/Connector.kt +++ b/app/src/main/java/com/dzeio/openhealth/extensions/Extension.kt @@ -1,10 +1,10 @@ -package com.dzeio.openhealth.connectors +package com.dzeio.openhealth.extensions import android.app.Activity import android.content.Intent import com.dzeio.openhealth.data.weight.Weight -abstract class Connector { +abstract class Extension { enum class Data { WEIGHT, @@ -13,7 +13,7 @@ abstract class Connector { abstract val sourceID: String - open fun init(activity: Activity) {} + open fun init(activity: Activity): Array = arrayOf() /** * Same as Activity/Fragment onRequestPermissionResult @@ -27,5 +27,7 @@ abstract class Connector { */ open fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {} + open fun import(data: Data, cb: (item: T, end: Boolean) -> Unit) {} + open fun importWeight(callback: (weight: Weight, end: Boolean) -> Unit) {} } \ No newline at end of file diff --git a/app/src/main/java/com/dzeio/openhealth/connectors/GoogleFit.kt b/app/src/main/java/com/dzeio/openhealth/extensions/GoogleFit.kt similarity index 92% rename from app/src/main/java/com/dzeio/openhealth/connectors/GoogleFit.kt rename to app/src/main/java/com/dzeio/openhealth/extensions/GoogleFit.kt index c5e36e3..1cbf5e4 100644 --- a/app/src/main/java/com/dzeio/openhealth/connectors/GoogleFit.kt +++ b/app/src/main/java/com/dzeio/openhealth/extensions/GoogleFit.kt @@ -1,173 +1,186 @@ -package com.dzeio.openhealth.connectors - -import android.Manifest -import android.app.Activity -import android.content.Intent -import android.content.pm.PackageManager -import android.os.Build -import android.util.Log -import androidx.core.app.ActivityCompat -import com.dzeio.openhealth.data.weight.Weight -import com.google.android.gms.auth.api.signin.GoogleSignIn -import com.google.android.gms.fitness.Fitness -import com.google.android.gms.fitness.FitnessOptions -import com.google.android.gms.fitness.data.DataPoint -import com.google.android.gms.fitness.data.DataSet -import com.google.android.gms.fitness.data.DataType -import com.google.android.gms.fitness.request.DataReadRequest -import java.text.DateFormat -import java.util.* -import java.util.concurrent.TimeUnit - -class GoogleFit( - private val activity: Activity, -) : Connector() { - companion object { - const val TAG = "GoogleFitConnector" - } - - override val sourceID: String = "GoogleFit" - - private val fitnessOptions = FitnessOptions.builder() - .addDataType(DataType.TYPE_WEIGHT) - .addDataType(DataType.TYPE_STEP_COUNT_CUMULATIVE) - .build() - - private fun checkPermissionsAndRun(data: Data) { - if (permissionApproved()) { - signIn(data) - } else { - Log.d(TAG, "Asking for permission") - // Ask for permission - ActivityCompat.requestPermissions( - activity, - arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), - data.ordinal - ) - } - } - - private fun permissionApproved(): Boolean { - val approved = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission( - activity, - Manifest.permission.ACCESS_FINE_LOCATION) - } else { - true - } - return approved - } - - private fun signIn(data: Data) { - if (oAuthPermissionsApproved()) { - startImport(data) - } else { - Log.d("GoogleFitImporter", "Signing In") - GoogleSignIn.requestPermissions( - activity, - data.ordinal, - getGoogleAccount(), fitnessOptions) - } - } - - private fun oAuthPermissionsApproved() = GoogleSignIn.hasPermissions(getGoogleAccount(), fitnessOptions) - - private fun getGoogleAccount() = GoogleSignIn.getAccountForExtension(activity, fitnessOptions) - - private val timeRange by lazy { - val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")) - calendar.time = Date() - val endTime = calendar.timeInMillis - - // Set year to 2013 to be sure to get data from when Google Fit Started to today - calendar.set(Calendar.YEAR, 2013) - val startTime = calendar.timeInMillis - arrayOf(startTime, endTime) - } - - - private fun startImport(data: Data) { - Log.d("GoogleFitImporter", "Importing for ${data.name}") - - val dateFormat = DateFormat.getDateInstance() - Log.i(TAG, "Range Start: ${dateFormat.format(timeRange[0])}") - Log.i(TAG, "Range End: ${dateFormat.format(timeRange[1])}") - - var type = DataType.TYPE_WEIGHT - var timeUnit = TimeUnit.MILLISECONDS - - when (data) { - Data.STEPS -> { - type = DataType.TYPE_STEP_COUNT_CUMULATIVE - } - else -> {} - } - - runRequest(DataReadRequest.Builder() - .read(type) - .setTimeRange(timeRange[0], timeRange[1], timeUnit) - .build(), data) - - } - - private fun runRequest(request: DataReadRequest, data: Data) { - Fitness.getHistoryClient(activity, GoogleSignIn.getAccountForExtension(activity, fitnessOptions)) - .readData(request) - .addOnSuccessListener { response -> - Log.d(TAG, "Received response! ${response.dataSets.size} ${response.buckets.size} ${response.status}") - for (dataSet in response.dataSets) { - Log.i(TAG, "Data returned for Data type: ${dataSet.dataType.name} ${dataSet.dataPoints.size}") - dataSet.dataPoints.forEachIndexed { index, dp -> - val isLast = (index + 1) == dataSet.dataPoints.size - - // Global - Log.i(TAG,"Importing Data point:") - Log.i(TAG,"\tType: ${dp.dataType.name}") - Log.i(TAG,"\tStart: ${dp.getStartTimeString()}") - Log.i(TAG,"\tEnd: ${dp.getEndTimeString()}") - - // Field Specifics - for (field in dp.dataType.fields) { - Log.i(TAG,"\tField: ${field.name} Value: ${dp.getValue(field)}") - when (data) { - Data.WEIGHT -> { - val weight = Weight() - weight.timestamp = dp.getStartTime(TimeUnit.MILLISECONDS) - weight.weight = dp.getValue(field).asFloat() - weightCallback(weight, isLast) - } - else -> {} - } - } - } - } - } - .addOnFailureListener { e -> - Log.e(TAG,"There was an error reading data from Google Fit", e) - } - } - - private fun DataPoint.getStartTimeString(): String = Date(this.getStartTime(TimeUnit.SECONDS) * 1000L).toLocaleString() - - private fun DataPoint.getEndTimeString(): String = Date(this.getEndTime(TimeUnit.SECONDS) * 1000L).toLocaleString() - - override fun onRequestPermissionResult( - requestCode: Int, - permission: Array, - grantResult: IntArray - ) { - signIn(Data.values()[requestCode]) - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - signIn(Data.values()[requestCode]) - } - - private lateinit var weightCallback: (weight: Weight, end: Boolean) -> Unit - - override fun importWeight(callback: (weight: Weight, end: Boolean) -> Unit) { - this.weightCallback = callback - checkPermissionsAndRun(Data.WEIGHT) - } - +package com.dzeio.openhealth.extensions + +import android.Manifest +import android.app.Activity +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import android.util.Log +import androidx.core.app.ActivityCompat +import com.dzeio.openhealth.data.weight.Weight +import com.google.android.gms.auth.api.signin.GoogleSignIn +import com.google.android.gms.fitness.Fitness +import com.google.android.gms.fitness.FitnessOptions +import com.google.android.gms.fitness.data.DataPoint +import com.google.android.gms.fitness.data.DataType +import com.google.android.gms.fitness.request.DataReadRequest +import java.text.DateFormat +import java.util.* +import java.util.concurrent.TimeUnit + +class GoogleFit( + private val activity: Activity, +) : Extension() { + companion object { + const val TAG = "GoogleFitConnector" + } + + override val sourceID: String = "GoogleFit" + + private val fitnessOptions = FitnessOptions.builder() + .addDataType(DataType.TYPE_WEIGHT) + .addDataType(DataType.TYPE_STEP_COUNT_CUMULATIVE) + .build() + + private fun checkPermissionsAndRun(data: Data) { + if (permissionApproved()) { + signIn(data) + } else { + Log.d(TAG, "Asking for permission") + // Ask for permission + ActivityCompat.requestPermissions( + activity, + arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), + data.ordinal + ) + } + } + + private fun permissionApproved(): Boolean { + val approved = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission( + activity, + Manifest.permission.ACCESS_FINE_LOCATION) + } else { + true + } + return approved + } + + private fun signIn(data: Data) { + if (oAuthPermissionsApproved()) { + startImport(data) + } else { + Log.d("GoogleFitImporter", "Signing In") + GoogleSignIn.requestPermissions( + activity, + data.ordinal, + getGoogleAccount(), fitnessOptions) + } + } + + private fun oAuthPermissionsApproved() = GoogleSignIn.hasPermissions(getGoogleAccount(), fitnessOptions) + + private fun getGoogleAccount() = GoogleSignIn.getAccountForExtension(activity, fitnessOptions) + + private val timeRange by lazy { + val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")) + calendar.time = Date() + val endTime = calendar.timeInMillis + + // Set year to 2013 to be sure to get data from when Google Fit Started to today + calendar.set(Calendar.YEAR, 2013) + val startTime = calendar.timeInMillis + arrayOf(startTime, endTime) + } + + + private fun startImport(data: Data) { + Log.d("GoogleFitImporter", "Importing for ${data.name}") + + val dateFormat = DateFormat.getDateInstance() + Log.i(TAG, "Range Start: ${dateFormat.format(timeRange[0])}") + Log.i(TAG, "Range End: ${dateFormat.format(timeRange[1])}") + + var type = DataType.TYPE_WEIGHT + var timeUnit = TimeUnit.MILLISECONDS + + when (data) { + Data.STEPS -> { + type = DataType.TYPE_STEP_COUNT_CUMULATIVE + } + else -> {} + } + + runRequest(DataReadRequest.Builder() + .read(type) + .setTimeRange(timeRange[0], timeRange[1], timeUnit) + .build(), data) + + } + + private fun runRequest(request: DataReadRequest, data: Data) { + Fitness.getHistoryClient(activity, GoogleSignIn.getAccountForExtension(activity, fitnessOptions)) + .readData(request) + .addOnSuccessListener { response -> + Log.d(TAG, "Received response! ${response.dataSets.size} ${response.buckets.size} ${response.status}") + for (dataSet in response.dataSets) { + Log.i(TAG, "Data returned for Data type: ${dataSet.dataType.name} ${dataSet.dataPoints.size}") + dataSet.dataPoints.forEachIndexed { index, dp -> + val isLast = (index + 1) == dataSet.dataPoints.size + + // Global + Log.i(TAG,"Importing Data point:") + Log.i(TAG,"\tType: ${dp.dataType.name}") + Log.i(TAG,"\tStart: ${dp.getStartTimeString()}") + Log.i(TAG,"\tEnd: ${dp.getEndTimeString()}") + + // Field Specifics + for (field in dp.dataType.fields) { + Log.i(TAG,"\tField: ${field.name} Value: ${dp.getValue(field)}") + when (data) { + Data.WEIGHT -> { + val weight = Weight() + weight.timestamp = dp.getStartTime(TimeUnit.MILLISECONDS) + weight.weight = dp.getValue(field).asFloat() + weightCallback(weight, isLast) + } + else -> {} + } + } + } + } + } + .addOnFailureListener { e -> + Log.e(TAG,"There was an error reading data from Google Fit", e) + } + } + + private fun DataPoint.getStartTimeString(): String = Date(this.getStartTime(TimeUnit.SECONDS) * 1000L).toLocaleString() + + private fun DataPoint.getEndTimeString(): String = Date(this.getEndTime(TimeUnit.SECONDS) * 1000L).toLocaleString() + + override fun onRequestPermissionResult( + requestCode: Int, + permission: Array, + grantResult: IntArray + ) { + signIn(Data.values()[requestCode]) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + signIn(Data.values()[requestCode]) + } + + private lateinit var weightCallback: (weight: Weight, end: Boolean) -> Unit + + override fun importWeight(callback: (weight: Weight, end: Boolean) -> Unit) { + this.weightCallback = callback + checkPermissionsAndRun(Data.WEIGHT) + } + + private lateinit var callback : (item: Any, end: Boolean) -> Unit + + override fun import(data: Data, cb: (item: T, end: Boolean) -> Unit) { + callback = cb as (item: Any, end: Boolean) -> Unit + when (data) { + Data.WEIGHT -> { + checkPermissionsAndRun(data) + } + else -> { + Log.d(TAG, "PRRRRRRRRRRRRR") + } + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/dzeio/openhealth/connectors/GoogleFit.kt.old b/app/src/main/java/com/dzeio/openhealth/extensions/GoogleFit.kt.old similarity index 99% rename from app/src/main/java/com/dzeio/openhealth/connectors/GoogleFit.kt.old rename to app/src/main/java/com/dzeio/openhealth/extensions/GoogleFit.kt.old index 2d5e17c..54254cb 100644 --- a/app/src/main/java/com/dzeio/openhealth/connectors/GoogleFit.kt.old +++ b/app/src/main/java/com/dzeio/openhealth/extensions/GoogleFit.kt.old @@ -1,4 +1,4 @@ -package com.dzeio.openhealth.connectors +package com.dzeio.openhealth.extensions import android.Manifest import android.app.Activity @@ -7,7 +7,6 @@ import android.os.Build import android.util.Log import androidx.annotation.RequiresApi import androidx.core.app.ActivityCompat -import com.dzeio.openhealth.data.AppDatabase import com.dzeio.openhealth.data.weight.Weight import com.google.android.gms.auth.api.signin.GoogleSignIn import com.google.android.gms.fitness.Fitness @@ -22,7 +21,6 @@ import com.google.android.gms.fitness.request.OnDataPointListener import com.google.android.gms.fitness.request.SensorRequest import java.text.DateFormat import java.text.SimpleDateFormat -import java.time.* import java.util.* import java.util.concurrent.TimeUnit diff --git a/app/src/main/java/com/dzeio/openhealth/connectors/samsunghealth/SamsungHealth.kt b/app/src/main/java/com/dzeio/openhealth/extensions/samsunghealth/SamsungHealth.kt similarity index 93% rename from app/src/main/java/com/dzeio/openhealth/connectors/samsunghealth/SamsungHealth.kt rename to app/src/main/java/com/dzeio/openhealth/extensions/samsunghealth/SamsungHealth.kt index 6838dbe..14edd14 100644 --- a/app/src/main/java/com/dzeio/openhealth/connectors/samsunghealth/SamsungHealth.kt +++ b/app/src/main/java/com/dzeio/openhealth/extensions/samsunghealth/SamsungHealth.kt @@ -1,108 +1,108 @@ -package com.dzeio.openhealth.connectors.samsunghealth - -import android.app.Activity -import android.content.Intent -import android.os.Handler -import android.os.Looper -import android.util.Log -import com.dzeio.openhealth.connectors.Connector -import com.dzeio.openhealth.data.weight.Weight -import com.samsung.android.sdk.healthdata.* -import com.samsung.android.sdk.healthdata.HealthConstants.StepCount -import com.samsung.android.sdk.healthdata.HealthDataStore.ConnectionListener -import com.samsung.android.sdk.healthdata.HealthPermissionManager.* - - -/** - * Does not FUCKING work - */ -class SamsungHealth( - private val context: Activity -) : Connector() { - - companion object { - const val TAG = "SamsungHealthConnector" - } - - private val listener = object : ConnectionListener { - override fun onConnected() { - Log.d(TAG, "Connected!") - if (isPermissionAcquired()) { - reporter.start() - } else { - requestPermission() - } - } - override fun onConnectionFailed(p0: HealthConnectionErrorResult?) { - Log.d(TAG, "Health data service is not available.") - } - - override fun onDisconnected() { - Log.d(TAG, "Health data service is disconnected.") - } - - } - - private val store : HealthDataStore = HealthDataStore(context, listener) - - private fun isPermissionAcquired(): Boolean { - val permKey = PermissionKey(StepCount.HEALTH_DATA_TYPE, PermissionType.READ) - val pmsManager = HealthPermissionManager(store) - try { - // Check whether the permissions that this application needs are acquired - val resultMap = pmsManager.isPermissionAcquired(setOf(permKey)) - return !resultMap.containsValue(java.lang.Boolean.FALSE) - } catch (e: java.lang.Exception) { - Log.e(TAG, "Permission request fails.", e) - } - return false - } - - private fun requestPermission() { - val permKey = PermissionKey(StepCount.HEALTH_DATA_TYPE, PermissionType.READ) - val pmsManager = HealthPermissionManager(store) - try { - // Show user permission UI for allowing user to change options - pmsManager.requestPermissions(setOf(permKey), context) - .setResultListener { result: PermissionResult -> - Log.d(TAG, "Permission callback is received.") - val resultMap = - result.resultMap - if (resultMap.containsValue(java.lang.Boolean.FALSE)) { - Log.d(TAG, "No Data???") - } else { - // Get the current step count and display it - reporter.start() - } - } - } catch (e: Exception) { - Log.e(TAG, "Permission setting fails.", e) - } - } - - private val stepCountObserver = object : StepCountReporter.StepCountObserver { - override fun onChanged(count: Int) { - Log.d(TAG, "Step reported : $count") - } - } - - private val reporter = StepCountReporter(store, stepCountObserver, Handler(Looper.getMainLooper())) - - /** - * Connector - */ - - override val sourceID: String = "SamsungHealth" - - override fun onRequestPermissionResult( - requestCode: Int, - permission: Array, - grantResult: IntArray - ) {} - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {} - - override fun importWeight(callback: (weight: Weight, end: Boolean) -> Unit) { - store.connectService() - } +package com.dzeio.openhealth.extensions.samsunghealth + +import android.app.Activity +import android.content.Intent +import android.os.Handler +import android.os.Looper +import android.util.Log +import com.dzeio.openhealth.extensions.Extension +import com.dzeio.openhealth.data.weight.Weight +import com.samsung.android.sdk.healthdata.* +import com.samsung.android.sdk.healthdata.HealthConstants.StepCount +import com.samsung.android.sdk.healthdata.HealthDataStore.ConnectionListener +import com.samsung.android.sdk.healthdata.HealthPermissionManager.* + + +/** + * Does not FUCKING work + */ +class SamsungHealth( + private val context: Activity +) : Extension() { + + companion object { + const val TAG = "SamsungHealthConnector" + } + + private val listener = object : ConnectionListener { + override fun onConnected() { + Log.d(TAG, "Connected!") + if (isPermissionAcquired()) { + reporter.start() + } else { + requestPermission() + } + } + override fun onConnectionFailed(p0: HealthConnectionErrorResult?) { + Log.d(TAG, "Health data service is not available.") + } + + override fun onDisconnected() { + Log.d(TAG, "Health data service is disconnected.") + } + + } + + private val store : HealthDataStore = HealthDataStore(context, listener) + + private fun isPermissionAcquired(): Boolean { + val permKey = PermissionKey(StepCount.HEALTH_DATA_TYPE, PermissionType.READ) + val pmsManager = HealthPermissionManager(store) + try { + // Check whether the permissions that this application needs are acquired + val resultMap = pmsManager.isPermissionAcquired(setOf(permKey)) + return !resultMap.containsValue(java.lang.Boolean.FALSE) + } catch (e: java.lang.Exception) { + Log.e(TAG, "Permission request fails.", e) + } + return false + } + + private fun requestPermission() { + val permKey = PermissionKey(StepCount.HEALTH_DATA_TYPE, PermissionType.READ) + val pmsManager = HealthPermissionManager(store) + try { + // Show user permission UI for allowing user to change options + pmsManager.requestPermissions(setOf(permKey), context) + .setResultListener { result: PermissionResult -> + Log.d(TAG, "Permission callback is received.") + val resultMap = + result.resultMap + if (resultMap.containsValue(java.lang.Boolean.FALSE)) { + Log.d(TAG, "No Data???") + } else { + // Get the current step count and display it + reporter.start() + } + } + } catch (e: Exception) { + Log.e(TAG, "Permission setting fails.", e) + } + } + + private val stepCountObserver = object : StepCountReporter.StepCountObserver { + override fun onChanged(count: Int) { + Log.d(TAG, "Step reported : $count") + } + } + + private val reporter = StepCountReporter(store, stepCountObserver, Handler(Looper.getMainLooper())) + + /** + * Connector + */ + + override val sourceID: String = "SamsungHealth" + + override fun onRequestPermissionResult( + requestCode: Int, + permission: Array, + grantResult: IntArray + ) {} + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {} + + override fun importWeight(callback: (weight: Weight, end: Boolean) -> Unit) { + store.connectService() + } } \ No newline at end of file diff --git a/app/src/main/java/com/dzeio/openhealth/connectors/samsunghealth/StepCountReporter.kt b/app/src/main/java/com/dzeio/openhealth/extensions/samsunghealth/StepCountReporter.kt similarity index 98% rename from app/src/main/java/com/dzeio/openhealth/connectors/samsunghealth/StepCountReporter.kt rename to app/src/main/java/com/dzeio/openhealth/extensions/samsunghealth/StepCountReporter.kt index 4545915..83a1731 100644 --- a/app/src/main/java/com/dzeio/openhealth/connectors/samsunghealth/StepCountReporter.kt +++ b/app/src/main/java/com/dzeio/openhealth/extensions/samsunghealth/StepCountReporter.kt @@ -1,4 +1,4 @@ -package com.dzeio.openhealth.connectors.samsunghealth +package com.dzeio.openhealth.extensions.samsunghealth import android.os.Handler import android.util.Log diff --git a/app/src/main/java/com/dzeio/openhealth/ui/main/home/HomeFragment.kt b/app/src/main/java/com/dzeio/openhealth/ui/home/HomeFragment.kt similarity index 58% rename from app/src/main/java/com/dzeio/openhealth/ui/main/home/HomeFragment.kt rename to app/src/main/java/com/dzeio/openhealth/ui/home/HomeFragment.kt index 42e8629..5e51a91 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/main/home/HomeFragment.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/home/HomeFragment.kt @@ -1,32 +1,42 @@ -package com.dzeio.openhealth.ui.main.home +package com.dzeio.openhealth.ui.home +import android.annotation.SuppressLint import android.app.Activity.RESULT_OK import android.content.Intent import android.content.pm.PackageManager +import android.graphics.BitmapFactory +import android.graphics.Canvas +import android.graphics.drawable.BitmapDrawable import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.fragment.app.commit +import android.widget.LinearLayout +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.ContextCompat import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import com.dzeio.openhealth.R import com.dzeio.openhealth.core.BaseFragment +import com.dzeio.openhealth.data.weight.Water import com.dzeio.openhealth.databinding.FragmentHomeBinding import com.dzeio.openhealth.data.weight.Weight -import com.dzeio.openhealth.ui.dialogs.AddWeightDialog -import com.dzeio.openhealth.ui.main.list_weight.ListWeightFragment +import com.dzeio.openhealth.ui.weight.AddWeightDialog +import com.dzeio.openhealth.utils.BitmapUtils +import com.dzeio.openhealth.utils.DrawUtils import com.github.mikephil.charting.components.AxisBase import com.github.mikephil.charting.components.Description import com.github.mikephil.charting.components.XAxis -import com.github.mikephil.charting.components.YAxis import com.github.mikephil.charting.data.Entry import com.github.mikephil.charting.data.LineData import com.github.mikephil.charting.data.LineDataSet import com.github.mikephil.charting.formatter.ValueFormatter +import com.google.android.material.color.MaterialColors import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch +import java.io.IOException import java.text.SimpleDateFormat import java.util.* import kotlin.collections.ArrayList @@ -50,6 +60,54 @@ class HomeFragment : BaseFragment(HomeViewMo AddWeightDialog().show(requireActivity().supportFragmentManager, null) } + binding.fragmentHomeWaterAdd.setOnClickListener { + lifecycleScope.launchWhenStarted { + + Log.d(TAG, "Collecting latest $this") + if (viewModel.fetchTodayWater().count() == 0) { + Log.d(TAG, "No value, Adding...") + val w = Water() + w.value = 200 + viewModel.updateWater(w) + return@launchWhenStarted + } + try { + viewModel.fetchTodayWater().count() + val item = viewModel.fetchTodayWater().lastOrNull() + Log.d(TAG, "Collected latest $item") + if (item == null) { + + val w = Water() + w.value = 200 + viewModel.updateWater(w) + } else { + item.value += 200 + viewModel.updateWater(item) + } + } catch (e: IOException) { + Log.e(TAG, "EXCEPTION", e) + } + + } + } + + binding.fragmentHomeWaterRemove.setOnClickListener { + lifecycleScope.launch { + + val item = viewModel.fetchTodayWater().first() + Log.d(TAG, "Collected latest $it") + if (item != null) { + item.value -= 200 + if (item.value == 0) { + viewModel.deleteWater(item) + } else { + viewModel.updateWater(item) + } + } + + } + } + binding.listWeight.setOnClickListener { Log.d("T", "Trying to move") @@ -106,7 +164,10 @@ class HomeFragment : BaseFragment(HomeViewMo binding.weightGraph.axisRight.setDrawGridLines(false) binding.weightGraph.xAxis.valueFormatter = object : ValueFormatter() { override fun getAxisLabel(value: Float, axis: AxisBase?): String { - return SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date(value.toLong())) + return SimpleDateFormat( + "yyyy-MM-dd", + Locale.getDefault() + ).format(Date(value.toLong())) //return super.getAxisLabel(value, axis) } } @@ -121,6 +182,7 @@ class HomeFragment : BaseFragment(HomeViewMo // ) } + @SuppressLint("PrivateResource") override fun onStart() { super.onStart() @@ -132,68 +194,48 @@ class HomeFragment : BaseFragment(HomeViewMo updateGraph(it) } } - } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - Log.d(TAG, "Activity Result!") - when (resultCode) { - RESULT_OK -> { - // fit.performActionForRequestCode(ActionRequestCode.FIND_DATA_SOURCES) - } - else -> { - Log.e(TAG, "Error: $requestCode, $resultCode") + lifecycleScope.launchWhenStarted { + viewModel.fetchTodayWater().collect { + Log.d(TAG, "Pouet? $it") + if (it != null) { + updateWater(it.value) + } else { + updateWater(0) + } + } } + + updateWater(0) + } - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, - grantResults: IntArray) { - when { - grantResults.isEmpty() -> { - // If user interaction was interrupted, the permission request - // is cancelled and you receive empty arrays. - Log.i(TAG, "User interaction was cancelled.") - } + private fun updateWater(water: Int) { + binding.fragmentHomeWaterCurrent.text = water.toString() - grantResults[0] == PackageManager.PERMISSION_GRANTED -> { - Log.d(TAG, "Granted") - // Permission was granted. -// val fitActionRequestCode = ActionRequestCode.values()[requestCode] -// fitActionRequestCode.let { -// // fit.signIn(ActionRequestCode.FIND_DATA_SOURCES) -// } - } - else -> { - // Permission denied. + val graph = BitmapUtils.convertToMutable( + requireContext(), + BitmapFactory.decodeResource(resources, R.drawable.ellipse) + ) - // In this Activity we've chosen to notify the user that they - // have rejected a core permission for the app since it makes the Activity useless. - // We're communicating this message in a Snackbar since this is a sample app, but - // core permissions would typically be best requested during a welcome-screen flow. + graph?.let { btmp -> + val canvas = Canvas(btmp) + DrawUtils.drawArc( + canvas, + 100 * water / 1200, + MaterialColors.getColor( + requireView(), + com.google.android.material.R.attr.colorPrimary + ) + ) + canvas.save() - // Additionally, it is important to remember that a permission might have been - // rejected without asking the user for permission (device policy or "Never ask - // again" prompts). Therefore, a user interface affordance is typically implemented - // when permissions are denied. Otherwise, your app could appear unresponsive to - // touches or interactions which have required permissions. - Log.e(TAG, "Error") -// Snackbar.make( -// findViewById(R.id.main_activity_view), -// R.string.permission_denied_explanation, -// Snackbar.LENGTH_INDEFINITE) -// .setAction(R.string.settings) { -// // Build intent that displays the App settings screen. -// val intent = Intent() -// intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS -// val uri = Uri.fromParts("package", -// BuildConfig.APPLICATION_ID, null) -// intent.data = uri -// intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK -// startActivity(intent) -// } -// .show() - } +// val params = binding.background.layoutParams +// params.height = binding.background.measuredWidth +// binding.background.layoutParams = params + + binding.background.setImageBitmap(graph) } } } \ No newline at end of file diff --git a/app/src/main/java/com/dzeio/openhealth/ui/main/home/HomeViewModel.kt b/app/src/main/java/com/dzeio/openhealth/ui/home/HomeViewModel.kt similarity index 53% rename from app/src/main/java/com/dzeio/openhealth/ui/main/home/HomeViewModel.kt rename to app/src/main/java/com/dzeio/openhealth/ui/home/HomeViewModel.kt index db1a7ce..d320a2b 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/main/home/HomeViewModel.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/home/HomeViewModel.kt @@ -1,14 +1,20 @@ -package com.dzeio.openhealth.ui.main.home +package com.dzeio.openhealth.ui.home import com.dzeio.openhealth.core.BaseViewModel +import com.dzeio.openhealth.data.weight.Water +import com.dzeio.openhealth.data.weight.WaterRepository import com.dzeio.openhealth.data.weight.Weight import com.dzeio.openhealth.data.weight.WeightRepository import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.flow import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject internal constructor( - private val weightRepository: WeightRepository + private val weightRepository: WeightRepository, + private val waterRepository: WaterRepository ) : BaseViewModel() { fun fetchWeights() = weightRepository.getWeights() @@ -20,4 +26,11 @@ class HomeViewModel @Inject internal constructor( suspend fun deleteWeight(weight: Weight) = weightRepository.deleteWeight(weight) suspend fun addWeight(weight: Weight) = weightRepository.addWeight(weight) + + fun fetchTodayWater() = waterRepository.todayWater() + + suspend fun updateWater(water: Water) = waterRepository.addWater(water) + suspend fun deleteWater(water: Water) = waterRepository.deleteWater(water) + + } \ No newline at end of file diff --git a/app/src/main/java/com/dzeio/openhealth/ui/main/imports/ImportFragment.kt b/app/src/main/java/com/dzeio/openhealth/ui/imports/ImportFragment.kt similarity index 94% rename from app/src/main/java/com/dzeio/openhealth/ui/main/imports/ImportFragment.kt rename to app/src/main/java/com/dzeio/openhealth/ui/imports/ImportFragment.kt index ba28b6c..7b4c8bd 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/main/imports/ImportFragment.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/imports/ImportFragment.kt @@ -1,4 +1,4 @@ -package com.dzeio.openhealth.ui.main.imports +package com.dzeio.openhealth.ui.imports import android.app.ProgressDialog import android.content.Intent @@ -11,10 +11,10 @@ import android.view.View import android.view.ViewGroup import androidx.annotation.RequiresApi import androidx.lifecycle.lifecycleScope -import com.dzeio.openhealth.connectors.Connector -import com.dzeio.openhealth.connectors.GoogleFit +import com.dzeio.openhealth.extensions.Extension +import com.dzeio.openhealth.extensions.GoogleFit //import com.dzeio.openhealth.connectors.GoogleFit -import com.dzeio.openhealth.connectors.samsunghealth.SamsungHealth +import com.dzeio.openhealth.extensions.samsunghealth.SamsungHealth import com.dzeio.openhealth.core.BaseFragment import com.dzeio.openhealth.databinding.FragmentImportBinding import dagger.hilt.android.AndroidEntryPoint @@ -30,7 +30,7 @@ class ImportFragment : BaseFragment(Impo private lateinit var progressDialog: ProgressDialog - private lateinit var fit: Connector + private lateinit var fit: Extension override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -43,9 +43,7 @@ class ImportFragment : BaseFragment(Impo } binding.importGoogleFit.setOnClickListener { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - importFromGoogleFit() - } + importFromGoogleFit() } binding.importSamsungHealth.setOnClickListener { importFromSamsungHealth() diff --git a/app/src/main/java/com/dzeio/openhealth/ui/main/imports/ImportViewModel.kt b/app/src/main/java/com/dzeio/openhealth/ui/imports/ImportViewModel.kt similarity index 92% rename from app/src/main/java/com/dzeio/openhealth/ui/main/imports/ImportViewModel.kt rename to app/src/main/java/com/dzeio/openhealth/ui/imports/ImportViewModel.kt index ed3651c..86796f6 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/main/imports/ImportViewModel.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/imports/ImportViewModel.kt @@ -1,11 +1,10 @@ -package com.dzeio.openhealth.ui.main.imports +package com.dzeio.openhealth.ui.imports import androidx.lifecycle.MutableLiveData import com.dzeio.openhealth.core.BaseViewModel import com.dzeio.openhealth.data.weight.Weight import com.dzeio.openhealth.data.weight.WeightRepository import dagger.hilt.android.lifecycle.HiltViewModel -import java.security.CodeSource import javax.inject.Inject diff --git a/app/src/main/java/com/dzeio/openhealth/ui/water/WaterHomeFragment.kt b/app/src/main/java/com/dzeio/openhealth/ui/water/WaterHomeFragment.kt new file mode 100644 index 0000000..27a70b5 --- /dev/null +++ b/app/src/main/java/com/dzeio/openhealth/ui/water/WaterHomeFragment.kt @@ -0,0 +1,2 @@ +package com.dzeio.openhealth.ui.water + diff --git a/app/src/main/java/com/dzeio/openhealth/ui/dialogs/AddWeightDialog.kt b/app/src/main/java/com/dzeio/openhealth/ui/weight/AddWeightDialog.kt similarity index 91% rename from app/src/main/java/com/dzeio/openhealth/ui/dialogs/AddWeightDialog.kt rename to app/src/main/java/com/dzeio/openhealth/ui/weight/AddWeightDialog.kt index 8b30f87..a514195 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/dialogs/AddWeightDialog.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/weight/AddWeightDialog.kt @@ -1,68 +1,67 @@ -package com.dzeio.openhealth.ui.dialogs - -import android.app.AlertDialog -import android.view.LayoutInflater -import androidx.core.content.ContextCompat -import androidx.lifecycle.lifecycleScope -import com.dzeio.openhealth.R -import com.dzeio.openhealth.core.BaseDialog -import com.dzeio.openhealth.data.weight.Weight -import com.dzeio.openhealth.databinding.DialogAddWeightBinding -import com.dzeio.openhealth.ui.main.home.HomeViewModel -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collect - -@AndroidEntryPoint -class AddWeightDialog : BaseDialog(HomeViewModel::class.java) { - - override val bindingInflater: (LayoutInflater) -> DialogAddWeightBinding = DialogAddWeightBinding::inflate - - override fun onBuilderInit(builder: MaterialAlertDialogBuilder) { - super.onBuilderInit(builder) - - builder.apply { - setTitle("Add your weight (kg)") - setIcon(activity?.let { ContextCompat.getDrawable(it, R.drawable.ic_outline_timeline_24) }) - setPositiveButton("Validate") { dialog, _ -> - save() - } - setNegativeButton("Cancel") { dialog, _ -> - dialog.cancel() - } - - } - } - - override fun onCreated() { - super.onCreated() - - lifecycleScope.launchWhenStarted { - viewModel.lastWeight().collect { - if (it != null) { - binding.kg.value = it.weight.toInt() - binding.gram.value = ((it.weight - it.weight.toInt()) * 10 ).toInt() - } - } - } - - binding.kg.maxValue = 636 - binding.kg.minValue = 0 - - binding.gram.maxValue = 9 - binding.gram.minValue = 0 - } - - private fun save() { - val weight = Weight().apply { - weight = binding.kg.value + (binding.gram.value.toFloat() / 10) - source = "OpenHealth" - - } - lifecycleScope.launchWhenCreated { - viewModel.addWeight(weight) - } - //callback?.invoke() - dialog?.dismiss() - } +package com.dzeio.openhealth.ui.weight + +import android.view.LayoutInflater +import androidx.core.content.ContextCompat +import androidx.lifecycle.lifecycleScope +import com.dzeio.openhealth.R +import com.dzeio.openhealth.core.BaseDialog +import com.dzeio.openhealth.data.weight.Weight +import com.dzeio.openhealth.databinding.DialogAddWeightBinding +import com.dzeio.openhealth.ui.home.HomeViewModel +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.collect + +@AndroidEntryPoint +class AddWeightDialog : BaseDialog(HomeViewModel::class.java) { + + override val bindingInflater: (LayoutInflater) -> DialogAddWeightBinding = DialogAddWeightBinding::inflate + + override fun onBuilderInit(builder: MaterialAlertDialogBuilder) { + super.onBuilderInit(builder) + + builder.apply { + setTitle("Add your weight (kg)") + setIcon(activity?.let { ContextCompat.getDrawable(it, R.drawable.ic_outline_timeline_24) }) + setPositiveButton("Validate") { dialog, _ -> + save() + } + setNegativeButton("Cancel") { dialog, _ -> + dialog.cancel() + } + + } + } + + override fun onCreated() { + super.onCreated() + + lifecycleScope.launchWhenStarted { + viewModel.lastWeight().collect { + if (it != null) { + binding.kg.value = it.weight.toInt() + binding.gram.value = ((it.weight - it.weight.toInt()) * 10 ).toInt() + } + } + } + + binding.kg.maxValue = 636 + binding.kg.minValue = 0 + + binding.gram.maxValue = 9 + binding.gram.minValue = 0 + } + + private fun save() { + val weight = Weight().apply { + weight = binding.kg.value + (binding.gram.value.toFloat() / 10) + source = "OpenHealth" + + } + lifecycleScope.launchWhenCreated { + viewModel.addWeight(weight) + } + //callback?.invoke() + dialog?.dismiss() + } } \ No newline at end of file diff --git a/app/src/main/java/com/dzeio/openhealth/ui/dialogs/EditWeightDialog.kt b/app/src/main/java/com/dzeio/openhealth/ui/weight/EditWeightDialog.kt similarity index 82% rename from app/src/main/java/com/dzeio/openhealth/ui/dialogs/EditWeightDialog.kt rename to app/src/main/java/com/dzeio/openhealth/ui/weight/EditWeightDialog.kt index 6c5ad5e..c0a6c7b 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/dialogs/EditWeightDialog.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/weight/EditWeightDialog.kt @@ -1,162 +1,165 @@ -package com.dzeio.openhealth.ui.dialogs - -import android.app.AlertDialog -import android.app.DatePickerDialog -import android.app.Dialog -import android.content.res.Resources -import android.os.Build -import android.os.Bundle -import android.util.Log -import android.view.LayoutInflater -import android.view.MenuItem -import android.view.ViewGroup -import android.view.Window -import androidx.annotation.RequiresApi -import androidx.core.content.ContextCompat -import androidx.lifecycle.lifecycleScope -import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs -import com.dzeio.openhealth.R -import com.dzeio.openhealth.core.BaseFullscreenDialog -import com.dzeio.openhealth.data.weight.Weight -import com.dzeio.openhealth.databinding.DialogAddWeightBinding -import com.dzeio.openhealth.databinding.DialogEditWeightBinding -import com.dzeio.openhealth.ui.main.home.HomeViewModel -import com.google.android.material.datepicker.MaterialDatePicker -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.google.android.material.timepicker.MaterialTimePicker -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collect -import java.util.* - -@AndroidEntryPoint -class EditWeightDialog : BaseFullscreenDialog(HomeViewModel::class.java) { - - override val bindingInflater: (LayoutInflater) -> DialogEditWeightBinding = DialogEditWeightBinding::inflate - - override val isFullscreenLayout = true - - val args: EditWeightDialogArgs by navArgs() - - lateinit var weight: Weight - -// override fun onBuilderInit(builder: AlertDialog.Builder) { -// super.onBuilderInit(builder) -// -// builder.apply { -// setTitle("Add your weight (kg)") -// setIcon(activity?.let { ContextCompat.getDrawable(it, R.drawable.ic_outline_timeline_24) }) -// setPositiveButton("Validate") { dialog, _ -> -// save() -// } -// setNegativeButton("Cancel") { dialog, _ -> -// dialog.cancel() -// } -// -// } -// } - - override fun onDialogInit(dialog: Dialog) { - super.onDialogInit(dialog) - - dialog.requestWindowFeature(Window.FEATURE_NO_TITLE) - } - - override fun onCreated(savedInstanceState: Bundle?) { - super.onCreated(savedInstanceState) - - lifecycleScope.launchWhenStarted { - viewModel.fetchWeight(args.id).collect { - weight = it!! - binding.layoutDialogEditWeightKg.value = it.weight.toInt() - binding.layoutDialogEditWeightGram.value = ((it.weight - it.weight.toInt()) * 10 ).toInt() - - binding.layoutDialogEditWeightTimestamp.setText(it.formatTimestamp()) - } - } - - binding.layoutDialogEditWeightKg.maxValue = 636 - binding.layoutDialogEditWeightKg.minValue = 0 - - binding.layoutDialogEditWeightGram.maxValue = 9 - binding.layoutDialogEditWeightGram.minValue = 0 - - binding.layoutDialogEditWeightTimestamp.setOnClickListener { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - val date = Date(weight.timestamp) - val datePicker = MaterialDatePicker.Builder.datePicker() - .setTitleText("Select Date") - .setSelection(weight.timestamp) - .build() - - val fragManager = requireActivity().supportFragmentManager - - datePicker.addOnPositiveButtonClickListener { tsp -> - val timePicker = MaterialTimePicker.Builder() - .setHour(date.hours) - .setMinute(date.minutes) - .setTitleText("Pouet") - .build() - - timePicker.addOnPositiveButtonClickListener { - Log.d("T", "${timePicker.hour} ${timePicker.minute}") - val newDate = Date(tsp) - newDate.hours = timePicker.hour - newDate.minutes = timePicker.minute - weight.timestamp = newDate.time - binding.layoutDialogEditWeightTimestamp.setText(weight.formatTimestamp()) - Log.d("Res", newDate.time.toString()) - } - timePicker.show(fragManager, "dialog") - } - datePicker.show(fragManager, "dialog") - Log.d("Tag", "${date.year + 1900}, ${date.month}, ${date.day}") -// val dg = DatePickerDialog(requireActivity()) -// dg.setOnDateSetListener { _, year, month, day -> -// -// } -// dg.updateDate(date.year + 1900, date.month, date.day) -// dg.show() - } else { - TODO("VERSION.SDK_INT < N") - } - - - } - - } - - private fun save() { - lifecycleScope.launchWhenCreated { - weight.weight = binding.layoutDialogEditWeightKg.value + (binding.layoutDialogEditWeightGram.value.toFloat() / 10) - viewModel.addWeight(weight) - findNavController().popBackStack() - } - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.menu_fullscreen_dialog_save -> { - save() - true - } - R.id.menu_fullscreen_dialog_delete -> { - MaterialAlertDialogBuilder(requireContext()) - .setTitle("Delete Weight?") - .setMessage("Are you sure you want to delete this weight?") - .setPositiveButton("Yes") {_, _ -> - lifecycleScope.launchWhenStarted { - viewModel.deleteWeight(weight) - findNavController().popBackStack() - } - } - .setIcon(ContextCompat.getDrawable(requireContext(), R.drawable.ic_outline_delete_24)) - .setNegativeButton("No") { _, _ ->} - .show() - true - } - else -> super.onOptionsItemSelected(item) - } - - } +package com.dzeio.openhealth.ui.weight + +import android.app.Dialog +import android.os.Build +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.Window +import androidx.core.content.ContextCompat +import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs +import com.dzeio.openhealth.R +import com.dzeio.openhealth.core.BaseFullscreenDialog +import com.dzeio.openhealth.data.weight.Weight +import com.dzeio.openhealth.databinding.DialogEditWeightBinding +import com.dzeio.openhealth.ui.home.HomeViewModel +import com.google.android.material.datepicker.MaterialDatePicker +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.timepicker.MaterialTimePicker +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.collect +import java.util.* + +@AndroidEntryPoint +class EditWeightDialog : + BaseFullscreenDialog(HomeViewModel::class.java) { + + override val bindingInflater: (LayoutInflater) -> DialogEditWeightBinding = + DialogEditWeightBinding::inflate + + override val isFullscreenLayout = true + + val args: EditWeightDialogArgs by navArgs() + + lateinit var weight: Weight + +// override fun onBuilderInit(builder: AlertDialog.Builder) { +// super.onBuilderInit(builder) +// +// builder.apply { +// setTitle("Add your weight (kg)") +// setIcon(activity?.let { ContextCompat.getDrawable(it, R.drawable.ic_outline_timeline_24) }) +// setPositiveButton("Validate") { dialog, _ -> +// save() +// } +// setNegativeButton("Cancel") { dialog, _ -> +// dialog.cancel() +// } +// +// } +// } + + override fun onDialogInit(dialog: Dialog) { + super.onDialogInit(dialog) + + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE) + } + + override fun onCreated(savedInstanceState: Bundle?) { + super.onCreated(savedInstanceState) + + lifecycleScope.launchWhenStarted { + viewModel.fetchWeight(args.id).collect { + weight = it!! + binding.layoutDialogEditWeightKg.value = it.weight.toInt() + binding.layoutDialogEditWeightGram.value = + ((it.weight - it.weight.toInt()) * 10).toInt() + + binding.layoutDialogEditWeightTimestamp.setText(it.formatTimestamp()) + } + } + + binding.layoutDialogEditWeightKg.maxValue = 636 + binding.layoutDialogEditWeightKg.minValue = 0 + + binding.layoutDialogEditWeightGram.maxValue = 9 + binding.layoutDialogEditWeightGram.minValue = 0 + + binding.layoutDialogEditWeightTimestamp.setOnClickListener { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + val date = Date(weight.timestamp) + val datePicker = MaterialDatePicker.Builder.datePicker() + .setTitleText("Select Date") + .setSelection(weight.timestamp) + .build() + + val fragManager = requireActivity().supportFragmentManager + + datePicker.addOnPositiveButtonClickListener { tsp -> + val timePicker = MaterialTimePicker.Builder() + .setHour(date.hours) + .setMinute(date.minutes) + .setTitleText("Pouet") + .build() + + timePicker.addOnPositiveButtonClickListener { + Log.d("T", "${timePicker.hour} ${timePicker.minute}") + val newDate = Date(tsp) + newDate.hours = timePicker.hour + newDate.minutes = timePicker.minute + weight.timestamp = newDate.time + binding.layoutDialogEditWeightTimestamp.setText(weight.formatTimestamp()) + Log.d("Res", newDate.time.toString()) + } + timePicker.show(fragManager, "dialog") + } + datePicker.show(fragManager, "dialog") + Log.d("Tag", "${date.year + 1900}, ${date.month}, ${date.day}") +// val dg = DatePickerDialog(requireActivity()) +// dg.setOnDateSetListener { _, year, month, day -> +// +// } +// dg.updateDate(date.year + 1900, date.month, date.day) +// dg.show() + } else { + TODO("VERSION.SDK_INT < N") + } + + + } + + } + + private fun save() { + lifecycleScope.launchWhenCreated { + weight.weight = + binding.layoutDialogEditWeightKg.value + (binding.layoutDialogEditWeightGram.value.toFloat() / 10) + viewModel.addWeight(weight) + findNavController().popBackStack() + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.menu_fullscreen_dialog_save -> { + save() + true + } + R.id.menu_fullscreen_dialog_delete -> { + MaterialAlertDialogBuilder(requireContext()) + .setTitle("Delete Weight?") + .setMessage("Are you sure you want to delete this weight?") + .setPositiveButton("Yes") { _, _ -> + lifecycleScope.launchWhenStarted { + viewModel.deleteWeight(weight) + findNavController().popBackStack() + } + } + .setIcon( + ContextCompat.getDrawable( + requireContext(), + R.drawable.ic_outline_delete_24 + ) + ) + .setNegativeButton("No") { _, _ -> } + .show() + true + } + else -> super.onOptionsItemSelected(item) + } + + } } \ No newline at end of file diff --git a/app/src/main/java/com/dzeio/openhealth/ui/main/list_weight/ListWeightFragment.kt b/app/src/main/java/com/dzeio/openhealth/ui/weight/ListWeightFragment.kt similarity index 68% rename from app/src/main/java/com/dzeio/openhealth/ui/main/list_weight/ListWeightFragment.kt rename to app/src/main/java/com/dzeio/openhealth/ui/weight/ListWeightFragment.kt index 3f649d7..8e30517 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/main/list_weight/ListWeightFragment.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/weight/ListWeightFragment.kt @@ -1,54 +1,53 @@ -package com.dzeio.openhealth.ui.main.list_weight - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.os.bundleOf -import androidx.lifecycle.lifecycleScope -import androidx.navigation.fragment.findNavController -import androidx.recyclerview.widget.LinearLayoutManager -import com.dzeio.openhealth.adapters.WeightAdapter -import com.dzeio.openhealth.core.BaseFragment -import com.dzeio.openhealth.data.weight.Weight -import com.dzeio.openhealth.databinding.FragmentListWeightBinding -import com.dzeio.openhealth.ui.dialogs.EditWeightDialog -import com.dzeio.openhealth.ui.main.home.HomeViewModel -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.collectLatest -import java.util.* - -@AndroidEntryPoint -class ListWeightFragment : BaseFragment(HomeViewModel::class.java) { - - override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentListWeightBinding = FragmentListWeightBinding::inflate - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - val recycler = binding.list - - val manager = LinearLayoutManager(requireContext()) - recycler.layoutManager = manager - - val adapter = WeightAdapter() - adapter.onItemClick = { - findNavController().navigate(ListWeightFragmentDirections.actionNavListWeightToNavEditWeight(it.id)) - //EditWeightDialog().show(requireActivity().supportFragmentManager, "dialog") - } - recycler.adapter = adapter - - viewLifecycleOwner.lifecycleScope.launchWhenCreated { - viewModel.fetchWeights().collectLatest { - val itt = it.toMutableList() - itt.sortWith { o1, o2 -> if (o1.timestamp > o2.timestamp) -1 else 1 } - adapter.set(itt) - } - } - - - - - } +package com.dzeio.openhealth.ui.weight + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.LinearLayoutManager +import com.dzeio.openhealth.adapters.WeightAdapter +import com.dzeio.openhealth.core.BaseFragment +import com.dzeio.openhealth.databinding.FragmentListWeightBinding +import com.dzeio.openhealth.ui.home.HomeViewModel +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.collectLatest + +@AndroidEntryPoint +class ListWeightFragment : + BaseFragment(HomeViewModel::class.java) { + + override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentListWeightBinding = + FragmentListWeightBinding::inflate + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val recycler = binding.list + + val manager = LinearLayoutManager(requireContext()) + recycler.layoutManager = manager + + val adapter = WeightAdapter() + adapter.onItemClick = { + findNavController().navigate( + ListWeightFragmentDirections.actionNavListWeightToNavEditWeight( + it.id + ) + ) + //EditWeightDialog().show(requireActivity().supportFragmentManager, "dialog") + } + recycler.adapter = adapter + + viewLifecycleOwner.lifecycleScope.launchWhenCreated { + viewModel.fetchWeights().collectLatest { + val itt = it.toMutableList() + itt.sortWith { o1, o2 -> if (o1.timestamp > o2.timestamp) -1 else 1 } + adapter.set(itt) + } + } + + + } } \ No newline at end of file diff --git a/app/src/main/java/com/dzeio/openhealth/utils/BitmapUtils.kt b/app/src/main/java/com/dzeio/openhealth/utils/BitmapUtils.kt new file mode 100644 index 0000000..fc4cb76 --- /dev/null +++ b/app/src/main/java/com/dzeio/openhealth/utils/BitmapUtils.kt @@ -0,0 +1,49 @@ +package com.dzeio.openhealth.utils + +import android.content.Context +import android.graphics.Bitmap +import java.io.File +import java.io.RandomAccessFile +import java.nio.channels.FileChannel + +object BitmapUtils { + + /** + * Find source of function lol + */ + fun convertToMutable(context: Context, imgIn: Bitmap): Bitmap? { + val width = imgIn.width + val height = imgIn.height + val type = imgIn.config + var outputFile: File? = null + val outputDir = context.cacheDir + try { + outputFile = File.createTempFile( + System.currentTimeMillis().toString(), + null, + outputDir + ) + outputFile.deleteOnExit() + val randomAccessFile = RandomAccessFile(outputFile, "rw") + val channel = randomAccessFile.channel + val map = channel.map( + FileChannel.MapMode.READ_WRITE, + 0, + (imgIn.rowBytes * height).toLong() + ) + imgIn.copyPixelsToBuffer(map) + imgIn.recycle() + val result = Bitmap.createBitmap(width, height, type) + map.position(0) + result.copyPixelsFromBuffer(map) + channel.close() + randomAccessFile.close() + outputFile.delete() + return result + } catch (e: Exception) { + } finally { + outputFile?.delete() + } + return null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/dzeio/openhealth/utils/DrawUtils.kt b/app/src/main/java/com/dzeio/openhealth/utils/DrawUtils.kt new file mode 100644 index 0000000..99c207e --- /dev/null +++ b/app/src/main/java/com/dzeio/openhealth/utils/DrawUtils.kt @@ -0,0 +1,30 @@ +package com.dzeio.openhealth.utils + +import android.graphics.* + +object DrawUtils { + + /** + * Fuck Graphics + */ + fun drawArc(canvas: Canvas, percent: Int, pColor: Int) { + canvas.width + val spacing = 120f + val r1 = RectF( + spacing, + spacing, + canvas.width - spacing, + canvas.height * 2 - spacing * 3 + ) + val paint = Paint() + paint.apply { + strokeWidth = 200f + style = Paint.Style.STROKE + color = pColor + isAntiAlias = true + strokeCap = Paint.Cap.ROUND + } + canvas.drawArc(r1, 180f, 180 * percent / 100f, false, paint) + } + +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ellipse.png b/app/src/main/res/drawable/ellipse.png new file mode 100644 index 0000000000000000000000000000000000000000..e8f86951e2c00c15d68a1c96845abeb2d57f74da GIT binary patch literal 9231 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 B;j91v literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/vector_elipse_empty.xml b/app/src/main/res/drawable/vector_elipse_empty.xml new file mode 100644 index 0000000..01e2efd --- /dev/null +++ b/app/src/main/res/drawable/vector_elipse_empty.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index c466300..dad79c2 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -1,10 +1,157 @@ - + android:orientation="vertical" + tools:context=".ui.home.HomeFragment"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + android:layout_marginEnd="16dp"> - + android:orientation="vertical"> - + android:weightSum="2"> + android:layout_weight="1" /> + android:layout_weight="1" + android:gravity="end"> + + android:src="@drawable/ic_outline_hexagon_24" /> - + + android:minHeight="200dp" /> @@ -87,8 +220,8 @@ - + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_import.xml b/app/src/main/res/layout/fragment_import.xml index 65f44b6..f3587a8 100644 --- a/app/src/main/res/layout/fragment_import.xml +++ b/app/src/main/res/layout/fragment_import.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".ui.main.imports.ImportFragment"> + tools:context=".ui.imports.ImportFragment"> \ No newline at end of file + tools:context=".ui.weight.ListWeightFragment" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_water_home.xml b/app/src/main/res/layout/fragment_water_home.xml new file mode 100644 index 0000000..77d9ef6 --- /dev/null +++ b/app/src/main/res/layout/fragment_water_home.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 3ae8231..9b059fb 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -7,7 +7,7 @@