mirror of
https://github.com/dzeiocom/OpenHealth.git
synced 2025-04-22 10:52:13 +00:00
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
This commit is contained in:
parent
fe73d7504d
commit
4d901f8a51
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
* text=auto eol=lf
|
17
.idea/deploymentTargetDropDown.xml
generated
Normal file
17
.idea/deploymentTargetDropDown.xml
generated
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="deploymentTargetDropDown">
|
||||||
|
<runningDeviceTargetSelectedWithDropDown>
|
||||||
|
<Target>
|
||||||
|
<type value="RUNNING_DEVICE_TARGET" />
|
||||||
|
<deviceKey>
|
||||||
|
<Key>
|
||||||
|
<type value="SERIAL_NUMBER" />
|
||||||
|
<value value="14e181ff" />
|
||||||
|
</Key>
|
||||||
|
</deviceKey>
|
||||||
|
</Target>
|
||||||
|
</runningDeviceTargetSelectedWithDropDown>
|
||||||
|
<timeTargetWasSelectedWithDropDown value="2021-12-20T00:23:13.795732Z" />
|
||||||
|
</component>
|
||||||
|
</project>
|
15
.idea/misc.xml
generated
15
.idea/misc.xml
generated
@ -7,8 +7,21 @@
|
|||||||
<entry key="..\:/Git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_import.xml" value="0.1" />
|
<entry key="..\:/Git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_import.xml" value="0.1" />
|
||||||
<entry key="..\:/Git/Dzeio/OpenHealth/app/src/main/res/menu/activity_main_drawer.xml" value="0.10989583333333333" />
|
<entry key="..\:/Git/Dzeio/OpenHealth/app/src/main/res/menu/activity_main_drawer.xml" value="0.10989583333333333" />
|
||||||
<entry key="..\:/Git/Dzeio/OpenHealth/app/src/main/res/menu/main.xml" value="0.1875" />
|
<entry key="..\:/Git/Dzeio/OpenHealth/app/src/main/res/menu/main.xml" value="0.1875" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/activity_main.xml" value="0.5" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/app_bar_main.xml" value="0.5192107995846313" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/content_main.xml" value="0.5135869565217391" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/content_main.xml" value="0.5135869565217391" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_home.xml" value="0.5135869565217391" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/dialog_add_weight.xml" value="0.2604166666666667" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/dialog_edit_weight.xml" value="0.33" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/dialog_register_weight.xml" value="0.5" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_gallery.xml" value="0.3109375" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_home.xml" value="0.25" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_import.xml" value="0.55" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_list_weight.xml" value="0.33" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/layout_item_weight.xml" value="0.5" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/nav_header_main.xml" value="0.3109375" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/menu/activity_main_drawer.xml" value="0.39947916666666666" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/menu/fullscreen_dialog.xml" value="0.40185185185185185" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/menu/main.xml" value="0.39947916666666666" />
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
@ -2,6 +2,10 @@ plugins {
|
|||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
id 'org.jetbrains.kotlin.android'
|
id 'org.jetbrains.kotlin.android'
|
||||||
id 'kotlin-kapt'
|
id 'kotlin-kapt'
|
||||||
|
id 'dagger.hilt.android.plugin'
|
||||||
|
|
||||||
|
// Safe Navigation
|
||||||
|
id 'androidx.navigation.safeargs'
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@ -32,6 +36,8 @@ android {
|
|||||||
}
|
}
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding true
|
viewBinding true
|
||||||
|
dataBinding true
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +45,8 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'androidx.core:core-ktx:1.7.0'
|
implementation 'androidx.core:core-ktx:1.7.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.4.0'
|
implementation 'androidx.appcompat:appcompat:1.4.0'
|
||||||
implementation 'com.google.android.material:material:1.4.0'
|
implementation 'javax.inject:javax.inject:1'
|
||||||
|
implementation 'com.google.android.material:material:1.6.0-alpha01'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
|
||||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.0'
|
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.0'
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
|
||||||
@ -49,6 +56,13 @@ dependencies {
|
|||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||||
|
|
||||||
|
// Graph
|
||||||
|
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
|
||||||
|
|
||||||
|
// Hilt
|
||||||
|
implementation "com.google.dagger:hilt-android:2.38.1"
|
||||||
|
kapt "com.google.dagger:hilt-compiler:2.38.1"
|
||||||
|
|
||||||
// Google Fit
|
// Google Fit
|
||||||
implementation "com.google.android.gms:play-services-fitness:21.0.0"
|
implementation "com.google.android.gms:play-services-fitness:21.0.0"
|
||||||
implementation "com.google.android.gms:play-services-auth:20.0.0"
|
implementation "com.google.android.gms:play-services-auth:20.0.0"
|
||||||
@ -61,21 +75,8 @@ dependencies {
|
|||||||
|
|
||||||
implementation "androidx.room:room-runtime:$room_version"
|
implementation "androidx.room:room-runtime:$room_version"
|
||||||
kapt "androidx.room:room-compiler:$room_version"
|
kapt "androidx.room:room-compiler:$room_version"
|
||||||
|
implementation "androidx.room:room-ktx:$room_version"
|
||||||
// optional - RxJava2 support for Room
|
|
||||||
implementation "androidx.room:room-rxjava2:$room_version"
|
|
||||||
|
|
||||||
// optional - RxJava3 support for Room
|
|
||||||
implementation "androidx.room:room-rxjava3:$room_version"
|
|
||||||
|
|
||||||
// optional - Guava support for Room, including Optional and ListenableFuture
|
|
||||||
implementation "androidx.room:room-guava:$room_version"
|
|
||||||
|
|
||||||
// optional - Test helpers
|
|
||||||
testImplementation "androidx.room:room-testing:$room_version"
|
testImplementation "androidx.room:room-testing:$room_version"
|
||||||
|
|
||||||
// optional - Paging 3 Integration
|
|
||||||
implementation "androidx.room:room-paging:2.4.0-rc01"
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -14,6 +14,7 @@
|
|||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:name=".Application"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
@ -22,7 +23,11 @@
|
|||||||
<!-- Samsung Health-->
|
<!-- Samsung Health-->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.samsung.android.health.permission.read"
|
android:name="com.samsung.android.health.permission.read"
|
||||||
android:value="com.samsung.health.step_count;com.samsung.shealth.step_daily_trend" />
|
android:value="com.samsung.health.weight" />
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.android.gms.version"
|
||||||
|
android:value="@integer/google_play_services_version" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
7
app/src/main/java/com/dzeio/openhealth/Application.kt
Normal file
7
app/src/main/java/com/dzeio/openhealth/Application.kt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package com.dzeio.openhealth
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
|
|
||||||
|
@HiltAndroidApp
|
||||||
|
class Application : Application() {}
|
@ -3,64 +3,66 @@ package com.dzeio.openhealth
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.drawerlayout.widget.DrawerLayout
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
import androidx.navigation.ui.AppBarConfiguration
|
import androidx.navigation.ui.AppBarConfiguration
|
||||||
import androidx.navigation.ui.navigateUp
|
import androidx.navigation.ui.navigateUp
|
||||||
import androidx.navigation.ui.setupActionBarWithNavController
|
import androidx.navigation.ui.setupActionBarWithNavController
|
||||||
import androidx.navigation.ui.setupWithNavController
|
import androidx.navigation.ui.setupWithNavController
|
||||||
|
import com.dzeio.openhealth.core.BaseActivity
|
||||||
import com.dzeio.openhealth.databinding.ActivityMainBinding
|
import com.dzeio.openhealth.databinding.ActivityMainBinding
|
||||||
import com.dzeio.openhealth.db.AppDatabase
|
import com.dzeio.openhealth.ui.main.home.HomeFragmentDirections
|
||||||
import com.dzeio.openhealth.db.entities.Weight
|
|
||||||
import com.google.android.material.navigation.NavigationView
|
import com.google.android.material.navigation.NavigationView
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
@AndroidEntryPoint
|
||||||
|
class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||||
|
|
||||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||||
private lateinit var binding: ActivityMainBinding
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
private lateinit var navController: NavController
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
override val bindingInflater: (LayoutInflater) -> ActivityMainBinding = ActivityMainBinding::inflate
|
||||||
setContentView(binding.root)
|
|
||||||
|
|
||||||
setSupportActionBar(binding.appBarMain.toolbar)
|
override fun onCreated(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreated(savedInstanceState)
|
||||||
|
|
||||||
|
setSupportActionBar(binding.toolbar)
|
||||||
|
|
||||||
|
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
|
||||||
|
navController = navHostFragment.navController
|
||||||
|
binding.navView.setupWithNavController(navController)
|
||||||
|
|
||||||
binding.appBarMain.fab.setOnClickListener { view ->
|
|
||||||
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
|
|
||||||
.setAction("Action", null).show()
|
|
||||||
}
|
|
||||||
val drawerLayout: DrawerLayout = binding.drawerLayout
|
|
||||||
val navView: NavigationView = binding.navView
|
|
||||||
val navController = findNavController(R.id.nav_host_fragment_content_main)
|
|
||||||
// Passing each menu ID as a set of Ids because each
|
|
||||||
// menu should be considered as top level destinations.
|
|
||||||
appBarConfiguration = AppBarConfiguration(
|
appBarConfiguration = AppBarConfiguration(
|
||||||
setOf(
|
setOf(
|
||||||
R.id.nav_home, R.id.nav_gallery, R.id.nav_import
|
R.id.nav_home
|
||||||
), drawerLayout
|
), binding.drawerLayout
|
||||||
)
|
)
|
||||||
|
|
||||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||||
navView.setupWithNavController(navController)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
// Inflate the menu; this adds items to the action bar if it is present.
|
|
||||||
menuInflater.inflate(R.menu.main, menu)
|
menuInflater.inflate(R.menu.main, menu)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
override fun onSupportNavigateUp(): Boolean {
|
||||||
val navController = findNavController(R.id.nav_host_fragment_content_main)
|
|
||||||
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
|
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>,
|
override fun onRequestPermissionsResult(
|
||||||
grantResults: IntArray) {
|
requestCode: Int,
|
||||||
|
permissions: Array<String>,
|
||||||
|
grantResults: IntArray
|
||||||
|
) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
Log.d("MainActivity", "Result $requestCode")
|
Log.d("MainActivity", "Result $requestCode")
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package com.dzeio.openhealth.adapters
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import com.dzeio.openhealth.core.BaseAdapter
|
||||||
|
import com.dzeio.openhealth.core.BaseViewHolder
|
||||||
|
import com.dzeio.openhealth.data.weight.Weight
|
||||||
|
import com.dzeio.openhealth.databinding.LayoutItemWeightBinding
|
||||||
|
|
||||||
|
class WeightAdapter() : BaseAdapter<Weight, LayoutItemWeightBinding>() {
|
||||||
|
|
||||||
|
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> LayoutItemWeightBinding
|
||||||
|
get() = LayoutItemWeightBinding::inflate
|
||||||
|
|
||||||
|
var onItemClick: ((weight: Weight) -> Unit)? = null
|
||||||
|
|
||||||
|
override fun onBindData(
|
||||||
|
holder: BaseViewHolder<LayoutItemWeightBinding>,
|
||||||
|
item: Weight,
|
||||||
|
position: Int
|
||||||
|
) {
|
||||||
|
holder.binding.weight.text = "${item.weight}kg"
|
||||||
|
holder.binding.datetime.text = item.formatTimestamp()
|
||||||
|
holder.binding.edit.setOnClickListener {
|
||||||
|
onItemClick?.invoke(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,8 +7,8 @@ import android.os.Build
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import com.dzeio.openhealth.db.AppDatabase
|
import com.dzeio.openhealth.data.AppDatabase
|
||||||
import com.dzeio.openhealth.db.entities.Weight
|
import com.dzeio.openhealth.data.weight.Weight
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
||||||
import com.google.android.gms.fitness.Fitness
|
import com.google.android.gms.fitness.Fitness
|
||||||
import com.google.android.gms.fitness.FitnessOptions
|
import com.google.android.gms.fitness.FitnessOptions
|
||||||
@ -36,15 +36,29 @@ class GoogleFit(
|
|||||||
companion object {
|
companion object {
|
||||||
const val TAG = "GoogleFitConnector"
|
const val TAG = "GoogleFitConnector"
|
||||||
}
|
}
|
||||||
|
// private val fitnessOptions = FitnessOptions.builder()
|
||||||
|
// .addDataType(DataType.TYPE_ACTIVITY_SEGMENT, FitnessOptions.ACCESS_READ)
|
||||||
|
// .addDataType(DataType.TYPE_ACTIVITY_SEGMENT, FitnessOptions.ACCESS_WRITE)
|
||||||
|
//
|
||||||
|
// .addDataType(DataType.TYPE_HEART_RATE_BPM, FitnessOptions.ACCESS_READ)
|
||||||
|
// .addDataType(DataType.TYPE_HEART_RATE_BPM, FitnessOptions.ACCESS_WRITE)
|
||||||
|
//
|
||||||
|
// .addDataType(DataType.TYPE_HEIGHT, FitnessOptions.ACCESS_READ)
|
||||||
|
// .addDataType(DataType.TYPE_HEIGHT, FitnessOptions.ACCESS_WRITE)
|
||||||
|
//
|
||||||
|
// .addDataType(DataType.TYPE_WEIGHT, FitnessOptions.ACCESS_READ)
|
||||||
|
// .addDataType(DataType.TYPE_WEIGHT, FitnessOptions.ACCESS_WRITE)
|
||||||
|
// .build()
|
||||||
|
|
||||||
private val fitnessOptions = FitnessOptions.builder()
|
private val fitnessOptions = FitnessOptions.builder()
|
||||||
.addDataType(DataType.TYPE_STEP_COUNT_CUMULATIVE)
|
// .addDataType(DataType.TYPE_STEP_COUNT_CUMULATIVE)
|
||||||
.addDataType(DataType.TYPE_ACTIVITY_SEGMENT)
|
// .addDataType(DataType.TYPE_ACTIVITY_SEGMENT)
|
||||||
.addDataType(DataType.TYPE_SLEEP_SEGMENT)
|
// .addDataType(DataType.TYPE_SLEEP_SEGMENT)
|
||||||
.addDataType(DataType.TYPE_CALORIES_EXPENDED)
|
// .addDataType(DataType.TYPE_CALORIES_EXPENDED)
|
||||||
.addDataType(DataType.TYPE_BASAL_METABOLIC_RATE)
|
// .addDataType(DataType.TYPE_BASAL_METABOLIC_RATE)
|
||||||
.addDataType(DataType.TYPE_POWER_SAMPLE)
|
// .addDataType(DataType.TYPE_POWER_SAMPLE)
|
||||||
.addDataType(DataType.TYPE_HEART_RATE_BPM)
|
// .addDataType(DataType.TYPE_HEART_RATE_BPM)
|
||||||
.addDataType(DataType.TYPE_LOCATION_SAMPLE)
|
// .addDataType(DataType.TYPE_LOCATION_SAMPLE)
|
||||||
.addDataType(DataType.TYPE_WEIGHT)
|
.addDataType(DataType.TYPE_WEIGHT)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
@ -196,12 +210,16 @@ class GoogleFit(
|
|||||||
@RequiresApi(Build.VERSION_CODES.O)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
fun getHistory() {
|
fun getHistory() {
|
||||||
|
|
||||||
val endTime = LocalDateTime.now().atZone(ZoneId.systemDefault())
|
val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
|
||||||
val startTime = LocalDateTime.MIN.atZone(ZoneId.systemDefault())
|
val now = Date()
|
||||||
|
calendar.time = now
|
||||||
|
val endTime = calendar.timeInMillis
|
||||||
|
calendar.set(Calendar.YEAR, 2013) // Set year to 2013 to be sure to get data from when Google Fit Started to today
|
||||||
|
val startTime = calendar.timeInMillis
|
||||||
val readRequest = DataReadRequest.Builder()
|
val readRequest = DataReadRequest.Builder()
|
||||||
.aggregate(DataType.AGGREGATE_CALORIES_EXPENDED)
|
.aggregate(DataType.AGGREGATE_CALORIES_EXPENDED)
|
||||||
.bucketByActivityType(1, TimeUnit.SECONDS)
|
.bucketByActivityType(1, TimeUnit.SECONDS)
|
||||||
.setTimeRange(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
|
.setTimeRange(startTime, endTime, TimeUnit.SECONDS)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
Fitness.getHistoryClient(activity, GoogleSignIn.getAccountForExtension(activity, fitnessOptions))
|
Fitness.getHistoryClient(activity, GoogleSignIn.getAccountForExtension(activity, fitnessOptions))
|
||||||
@ -219,13 +237,6 @@ class GoogleFit(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @RequiresApi(Build.VERSION_CODES.O)
|
|
||||||
// fun importStepCount() {
|
|
||||||
//
|
|
||||||
// runRequest(queryFitnessData())
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
fun importWeight(callback : () -> Unit) {
|
fun importWeight(callback : () -> Unit) {
|
||||||
|
|
||||||
val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
|
val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
|
||||||
@ -295,7 +306,7 @@ class GoogleFit(
|
|||||||
weight.weight = dp.getValue(field).asFloat()
|
weight.weight = dp.getValue(field).asFloat()
|
||||||
Log.i(TAG,"\tField: ${field.name.toString()} Value: ${dp.getValue(field)}")
|
Log.i(TAG,"\tField: ${field.name.toString()} Value: ${dp.getValue(field)}")
|
||||||
}
|
}
|
||||||
AppDatabase.getInstance(activity).weightDao().insert(weight)
|
// AppDatabase.getInstance(activity).weightDao().insert(weight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,142 +2,133 @@ package com.dzeio.openhealth.connectors.samsunghealth
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
|
import android.content.DialogInterface
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.samsung.android.sdk.healthdata.*
|
import com.samsung.android.sdk.healthdata.*
|
||||||
|
import com.samsung.android.sdk.healthdata.HealthDataStore.ConnectionListener
|
||||||
|
import com.samsung.android.sdk.healthdata.HealthPermissionManager.*
|
||||||
|
import com.samsung.android.sdk.healthdata.HealthResultHolder.ResultListener
|
||||||
|
import java.lang.Boolean
|
||||||
|
import kotlin.Exception
|
||||||
|
|
||||||
|
|
||||||
class SamsungHealth(
|
class SamsungHealth(
|
||||||
private val activity: Activity
|
private val context: Activity
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private var currentStartTime: Long = 0
|
|
||||||
private var isFinishing = false
|
|
||||||
|
|
||||||
init {
|
|
||||||
currentStartTime = StepCountReader.TODAY_START_UTC_TIME
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "SamsungHealth"
|
const val TAG = "SamsungHealthConnector"
|
||||||
}
|
}
|
||||||
|
private val store: HealthDataStore get() = _store!!
|
||||||
|
private var _store: HealthDataStore? = null
|
||||||
|
private val keySet: HashSet<PermissionKey> = HashSet()
|
||||||
|
|
||||||
// private val binningListAdapter: StepBinningData by lazy { StepBinningData() }
|
private val permissionListener: ResultListener<PermissionResult> =
|
||||||
|
ResultListener<PermissionResult> { result ->
|
||||||
|
Log.d(TAG, "Permission callback is received.")
|
||||||
|
val resultMap: Map<PermissionKey, kotlin.Boolean> = result!!.resultMap
|
||||||
|
|
||||||
private val healthDataStore: HealthDataStore by lazy { HealthDataStore(activity, connectionListener) }
|
if (resultMap.containsValue(Boolean.FALSE)) {
|
||||||
private val stepCountReader: StepCountReader by lazy { StepCountReader(healthDataStore, stepCountObserver) }
|
// Requesting permission fails
|
||||||
|
Log.d(TAG, "Requesting permission fails")
|
||||||
|
|
||||||
private val stepCountObserver: StepCountObserver = object : StepCountObserver {
|
|
||||||
override fun onChanged(count: Int) {
|
|
||||||
Log.d(TAG, "$count")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBinningDataChanged(binningCountList: List<StepBinningData>) {
|
|
||||||
Log.d(TAG, "${binningCountList.size}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val connectionListener: HealthDataStore.ConnectionListener = object : HealthDataStore.ConnectionListener {
|
|
||||||
override fun onConnected() {
|
|
||||||
Log.d(TAG, "onConnected")
|
|
||||||
if (checkPermissionsAcquired()) {
|
|
||||||
stepCountReader.requestDailyStepCount(currentStartTime)
|
|
||||||
} else {
|
} else {
|
||||||
requestPermission()
|
// Get the current step count and display it
|
||||||
|
getStepCount()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val connectionListener: ConnectionListener = object : ConnectionListener {
|
||||||
|
override fun onConnected() {
|
||||||
|
Log.d(TAG, "Health data service is connected.")
|
||||||
|
val pmsManager = HealthPermissionManager(store)
|
||||||
|
try {
|
||||||
|
val resultMap = pmsManager.isPermissionAcquired(keySet)
|
||||||
|
|
||||||
|
if (resultMap.containsValue(Boolean.FALSE)) {
|
||||||
|
// Request the permission for reading step counts if it is not acquired
|
||||||
|
pmsManager.requestPermissions(keySet, context)
|
||||||
|
.setResultListener(permissionListener)
|
||||||
|
} else {
|
||||||
|
// Get the current step count and display it
|
||||||
|
getStepCount()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, e.javaClass.name + " - " + e.message)
|
||||||
|
Log.e(TAG, "Permission setting fails.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onConnectionFailed(error: HealthConnectionErrorResult) {
|
override fun onConnectionFailed(error: HealthConnectionErrorResult) {
|
||||||
Log.d(TAG, "onConnectionFailed")
|
Log.d(TAG, "Health data service is not available.")
|
||||||
showConnectionFailureDialog(error)
|
showConnectionFailureDialog(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDisconnected() {
|
override fun onDisconnected() {
|
||||||
Log.d(TAG, "onDisconnected")
|
Log.d(TAG, "Health data service is disconnected.")
|
||||||
if (!isFinishing) {
|
|
||||||
healthDataStore.connectService()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
//keySet = HashSet()
|
||||||
|
keySet.add(PermissionKey(HealthConstants.Weight.HEALTH_DATA_TYPE, PermissionType.READ))
|
||||||
|
|
||||||
|
_store = HealthDataStore(context, connectionListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
store.connectService()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getStepCount() {
|
||||||
|
val resolver = HealthDataResolver(store, null)
|
||||||
|
val request = HealthDataResolver.ReadRequest.Builder()
|
||||||
|
.setDataType(HealthConstants.Weight.HEALTH_DATA_TYPE)
|
||||||
|
.setLocalTimeRange(HealthConstants.Weight.START_TIME, HealthConstants.Weight.TIME_OFFSET, 0, System.currentTimeMillis())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
try {
|
||||||
|
val res = resolver.read(request).await()
|
||||||
|
|
||||||
|
res.use { res1 ->
|
||||||
|
val iterator = res1.iterator()
|
||||||
|
if (iterator.hasNext()) {
|
||||||
|
val data = iterator.next()
|
||||||
|
val value = data.getFloat(HealthConstants.Weight.WEIGHT)
|
||||||
|
val time = data.getLong(HealthConstants.Weight.CREATE_TIME)
|
||||||
|
Log.d(TAG, "Data received! $value $time")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e : Exception) {
|
||||||
|
Log.d(TAG, "Reading failed!")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showConnectionFailureDialog(error: HealthConnectionErrorResult) {
|
private fun showConnectionFailureDialog(error: HealthConnectionErrorResult) {
|
||||||
if (isFinishing) {
|
val alert: AlertDialog.Builder = AlertDialog.Builder(context)
|
||||||
return
|
var message = "Connection with Samsung Health is not available"
|
||||||
}
|
|
||||||
val alert = AlertDialog.Builder(activity)
|
|
||||||
if (error.hasResolution()) {
|
if (error.hasResolution()) {
|
||||||
when (error.errorCode) {
|
message = when (error.errorCode) {
|
||||||
HealthConnectionErrorResult.PLATFORM_NOT_INSTALLED -> alert.setMessage("R.string.msg_req_install")
|
HealthConnectionErrorResult.PLATFORM_NOT_INSTALLED -> "Please install Samsung Health"
|
||||||
HealthConnectionErrorResult.OLD_VERSION_PLATFORM -> alert.setMessage("R.string.msg_req_upgrade")
|
HealthConnectionErrorResult.OLD_VERSION_PLATFORM -> "Please upgrade Samsung Health"
|
||||||
HealthConnectionErrorResult.PLATFORM_DISABLED -> alert.setMessage("R.string.msg_req_enable")
|
HealthConnectionErrorResult.PLATFORM_DISABLED -> "Please enable Samsung Health"
|
||||||
HealthConnectionErrorResult.USER_AGREEMENT_NEEDED -> alert.setMessage("R.string.msg_req_agree")
|
HealthConnectionErrorResult.USER_AGREEMENT_NEEDED -> "Please agree with Samsung Health policy"
|
||||||
else -> alert.setMessage("R.string.msg_req_available")
|
else -> "Please make Samsung Health available"
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
alert.setMessage("R.string.msg_conn_not_available")
|
|
||||||
}
|
}
|
||||||
alert.setPositiveButton("R.string.ok") { _, _ ->
|
alert.setMessage(message)
|
||||||
|
alert.setPositiveButton("OK", DialogInterface.OnClickListener { dialog, id ->
|
||||||
if (error.hasResolution()) {
|
if (error.hasResolution()) {
|
||||||
error.resolve(activity)
|
error.resolve(context)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
if (error.hasResolution()) {
|
if (error.hasResolution()) {
|
||||||
alert.setNegativeButton("R.string.cancel", null)
|
alert.setNegativeButton("Cancel", null)
|
||||||
}
|
}
|
||||||
alert.show()
|
alert.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether the permissions that this application needs are acquired
|
|
||||||
private fun checkPermissionsAcquired(): Boolean {
|
|
||||||
val pmsManager = HealthPermissionManager(healthDataStore)
|
|
||||||
|
|
||||||
// Check whether the permissions that this application needs are acquired
|
fun destroy() {
|
||||||
return runCatching { pmsManager.isPermissionAcquired(permissionKeySet) }
|
store.disconnectService()
|
||||||
.onFailure { Log.e(TAG, "Permission request fails.", it) }
|
|
||||||
.map { it.values.all { it } }
|
|
||||||
.getOrDefault(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun requestPermission() {
|
|
||||||
val pmsManager = HealthPermissionManager(healthDataStore)
|
|
||||||
|
|
||||||
// Show user permission UI for allowing user to change options
|
|
||||||
runCatching { pmsManager.requestPermissions(permissionKeySet, activity) }
|
|
||||||
.onFailure { Log.e(TAG, "Permission setting fails.", it) }
|
|
||||||
.getOrNull()
|
|
||||||
?.setResultListener(mPermissionListener)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val permissionKeySet: Set<HealthPermissionManager.PermissionKey> =
|
|
||||||
setOf(
|
|
||||||
HealthPermissionManager.PermissionKey(HealthConstants.StepCount.HEALTH_DATA_TYPE, HealthPermissionManager.PermissionType.READ),
|
|
||||||
HealthPermissionManager.PermissionKey(
|
|
||||||
HealthConstants.StepDailyTrend.HEALTH_DATA_TYPE,
|
|
||||||
HealthPermissionManager.PermissionType.READ
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
private val mPermissionListener = HealthResultHolder.ResultListener<HealthPermissionManager.PermissionResult> { result ->
|
|
||||||
// Show a permission alarm and clear step count if permissions are not acquired
|
|
||||||
if (result.resultMap.values.any { !it }) {
|
|
||||||
// List is now empty
|
|
||||||
showPermissionAlarmDialog()
|
|
||||||
} else {
|
|
||||||
// Get the daily step count of a particular day and display it
|
|
||||||
stepCountReader.requestDailyStepCount(currentStartTime)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showPermissionAlarmDialog() {
|
|
||||||
if (isFinishing) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
AlertDialog.Builder(activity)
|
|
||||||
.setTitle("R.string.notice")
|
|
||||||
.setMessage("R.string.msg_perm_acquired")
|
|
||||||
.setPositiveButton("R.string.ok", null)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun importStepCount() {
|
|
||||||
healthDataStore.connectService()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,3 +0,0 @@
|
|||||||
package com.dzeio.openhealth.connectors.samsunghealth
|
|
||||||
|
|
||||||
data class StepBinningData(var time: String, val count: Int)
|
|
@ -1,8 +0,0 @@
|
|||||||
package com.dzeio.openhealth.connectors.samsunghealth
|
|
||||||
|
|
||||||
interface StepCountObserver {
|
|
||||||
|
|
||||||
fun onChanged(count: Int)
|
|
||||||
|
|
||||||
fun onBinningDataChanged(binningCountList: List<StepBinningData>)
|
|
||||||
}
|
|
@ -1,141 +0,0 @@
|
|||||||
package com.dzeio.openhealth.connectors.samsunghealth
|
|
||||||
|
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.util.Log
|
|
||||||
import com.samsung.android.sdk.healthdata.HealthConstants.StepCount
|
|
||||||
import com.samsung.android.sdk.healthdata.HealthConstants.StepDailyTrend
|
|
||||||
import com.samsung.android.sdk.healthdata.HealthDataResolver
|
|
||||||
import com.samsung.android.sdk.healthdata.HealthDataResolver.AggregateRequest
|
|
||||||
import com.samsung.android.sdk.healthdata.HealthDataResolver.AggregateRequest.AggregateFunction
|
|
||||||
import com.samsung.android.sdk.healthdata.HealthDataResolver.AggregateRequest.TimeGroupUnit
|
|
||||||
import com.samsung.android.sdk.healthdata.HealthDataResolver.Filter
|
|
||||||
import com.samsung.android.sdk.healthdata.HealthDataResolver.ReadRequest
|
|
||||||
import com.samsung.android.sdk.healthdata.HealthDataResolver.SortOrder
|
|
||||||
import com.samsung.android.sdk.healthdata.HealthDataStore
|
|
||||||
import com.samsung.android.sdk.healthdata.HealthDataUtil
|
|
||||||
import java.util.Calendar
|
|
||||||
import java.util.Locale
|
|
||||||
import java.util.TimeZone
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
class StepCountReader(
|
|
||||||
store: HealthDataStore,
|
|
||||||
private val observer: StepCountObserver
|
|
||||||
) {
|
|
||||||
private val healthDataResolver: HealthDataResolver = HealthDataResolver(store, Handler(Looper.getMainLooper()))
|
|
||||||
|
|
||||||
// Get the daily total step count of a specified day
|
|
||||||
fun requestDailyStepCount(startTime: Long) {
|
|
||||||
if (startTime >= TODAY_START_UTC_TIME) {
|
|
||||||
// Get today step count
|
|
||||||
readStepCount(startTime)
|
|
||||||
} else {
|
|
||||||
// Get historical step count
|
|
||||||
readStepDailyTrend(startTime)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readStepCount(startTime: Long) {
|
|
||||||
// Get sum of step counts by device
|
|
||||||
val request = AggregateRequest.Builder()
|
|
||||||
.setDataType(StepCount.HEALTH_DATA_TYPE)
|
|
||||||
.addFunction(AggregateFunction.SUM, StepCount.COUNT, ALIAS_TOTAL_COUNT)
|
|
||||||
.addGroup(StepCount.DEVICE_UUID, ALIAS_DEVICE_UUID)
|
|
||||||
.setLocalTimeRange(StepCount.START_TIME, StepCount.TIME_OFFSET, startTime, startTime + TIME_INTERVAL)
|
|
||||||
.setSort(ALIAS_TOTAL_COUNT, SortOrder.DESC)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
runCatching { healthDataResolver.aggregate(request) }
|
|
||||||
.onFailure { Log.e(TAG, "Getting step count fails.", it) }
|
|
||||||
.getOrNull()
|
|
||||||
?.setResultListener {
|
|
||||||
it.use {
|
|
||||||
it.firstOrNull()
|
|
||||||
.also { observer.onChanged(it?.getInt(ALIAS_TOTAL_COUNT) ?: 0) }
|
|
||||||
?.let { readStepCountBinning(startTime, it.getString(ALIAS_DEVICE_UUID)) }
|
|
||||||
?: observer.onBinningDataChanged(emptyList())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readStepDailyTrend(dayStartTime: Long) {
|
|
||||||
val request = ReadRequest.Builder()
|
|
||||||
.setDataType(StepDailyTrend.HEALTH_DATA_TYPE)
|
|
||||||
.setProperties(arrayOf(StepDailyTrend.COUNT, StepDailyTrend.BINNING_DATA))
|
|
||||||
.setFilter(Filter.and(
|
|
||||||
Filter.eq(StepDailyTrend.DAY_TIME, dayStartTime),
|
|
||||||
Filter.eq(StepDailyTrend.SOURCE_TYPE, StepDailyTrend.SOURCE_TYPE_ALL)))
|
|
||||||
.build()
|
|
||||||
|
|
||||||
runCatching { healthDataResolver.read(request) }
|
|
||||||
.onFailure { Log.e(TAG, "Getting daily step trend fails.", it) }
|
|
||||||
.getOrNull()
|
|
||||||
?.setResultListener {
|
|
||||||
it.use {
|
|
||||||
it.firstOrNull().also {
|
|
||||||
observer.onChanged(it?.getInt(StepDailyTrend.COUNT) ?: 0)
|
|
||||||
observer.onBinningDataChanged(
|
|
||||||
it?.getBlob(StepDailyTrend.BINNING_DATA)?.let { getBinningData(it) } ?: emptyList())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getBinningData(zip: ByteArray): List<StepBinningData> {
|
|
||||||
// decompress ZIP
|
|
||||||
val binningDataList = HealthDataUtil.getStructuredDataList(zip, StepBinningData::class.java)
|
|
||||||
return binningDataList.asSequence()
|
|
||||||
.withIndex()
|
|
||||||
.filter { it.value.count != 0 }
|
|
||||||
.onEach { it.value.time = String.format(Locale.US, "%02d:%02d", it.index / 6, it.index % 6 * 10) }
|
|
||||||
.map { it.value }
|
|
||||||
.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readStepCountBinning(startTime: Long, deviceUuid: String) {
|
|
||||||
|
|
||||||
// Get 10 minute binning data of a particular device
|
|
||||||
val request = AggregateRequest.Builder()
|
|
||||||
.setDataType(StepCount.HEALTH_DATA_TYPE)
|
|
||||||
.addFunction(AggregateFunction.SUM, StepCount.COUNT, ALIAS_TOTAL_COUNT)
|
|
||||||
.setTimeGroup(TimeGroupUnit.MINUTELY, 10, StepCount.START_TIME, StepCount.TIME_OFFSET, ALIAS_BINNING_TIME)
|
|
||||||
.setLocalTimeRange(StepCount.START_TIME, StepCount.TIME_OFFSET, startTime, startTime + TIME_INTERVAL)
|
|
||||||
.setFilter(Filter.eq(StepCount.DEVICE_UUID, deviceUuid))
|
|
||||||
.setSort(ALIAS_BINNING_TIME, SortOrder.ASC)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
runCatching { healthDataResolver.aggregate(request) }
|
|
||||||
.onFailure { Log.e(TAG, "Getting step binning data fails.", it) }
|
|
||||||
.getOrNull()
|
|
||||||
?.setResultListener {
|
|
||||||
it.use {
|
|
||||||
it.asSequence()
|
|
||||||
.map { it.getString(ALIAS_BINNING_TIME) to it.getInt(ALIAS_TOTAL_COUNT) }
|
|
||||||
.filter { it.first != null }
|
|
||||||
.map { StepBinningData(it.first.split(" ")[1], it.second) }
|
|
||||||
.toList()
|
|
||||||
}
|
|
||||||
.also { observer.onBinningDataChanged(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val TODAY_START_UTC_TIME = todayStartUtcTime
|
|
||||||
val TIME_INTERVAL = TimeUnit.DAYS.toMillis(1)
|
|
||||||
private const val ALIAS_TOTAL_COUNT = "count"
|
|
||||||
private const val ALIAS_DEVICE_UUID = "deviceuuid"
|
|
||||||
private const val ALIAS_BINNING_TIME = "binning_time"
|
|
||||||
const val TAG = "SamsungHealth.StepCountReader"
|
|
||||||
|
|
||||||
private val todayStartUtcTime: Long
|
|
||||||
get() {
|
|
||||||
val today = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
|
|
||||||
today[Calendar.HOUR_OF_DAY] = 0
|
|
||||||
today[Calendar.MINUTE] = 0
|
|
||||||
today[Calendar.SECOND] = 0
|
|
||||||
today[Calendar.MILLISECOND] = 0
|
|
||||||
return today.timeInMillis
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
37
app/src/main/java/com/dzeio/openhealth/core/BaseActivity.kt
Normal file
37
app/src/main/java/com/dzeio/openhealth/core/BaseActivity.kt
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package com.dzeio.openhealth.core
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.LayoutRes
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.databinding.DataBindingUtil
|
||||||
|
import androidx.databinding.ViewDataBinding
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
|
import com.dzeio.openhealth.databinding.ActivityMainBinding
|
||||||
|
|
||||||
|
abstract class BaseActivity<VB : ViewBinding>() : AppCompatActivity() {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to inflate the Fragment Bindings
|
||||||
|
*
|
||||||
|
* use like this: `ViewBinding::inflater`
|
||||||
|
*/
|
||||||
|
abstract val bindingInflater: (LayoutInflater) -> VB
|
||||||
|
|
||||||
|
protected lateinit var binding: VB
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
binding = bindingInflater(layoutInflater)
|
||||||
|
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
onCreated(savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected open fun onCreated(savedInstanceState: Bundle?) {}
|
||||||
|
}
|
46
app/src/main/java/com/dzeio/openhealth/core/BaseAdapter.kt
Normal file
46
app/src/main/java/com/dzeio/openhealth/core/BaseAdapter.kt
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package com.dzeio.openhealth.core
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
|
|
||||||
|
abstract class BaseAdapter<T, VB : ViewBinding> : RecyclerView.Adapter<BaseViewHolder<VB>>() {
|
||||||
|
private var items = mutableListOf<T>()
|
||||||
|
// private var lastPosition = -1
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun set(items: List<T>) {
|
||||||
|
this.items = items.toMutableList()
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun add(vararg items: T) {
|
||||||
|
val len = this.items.size
|
||||||
|
this.items.addAll(items)
|
||||||
|
notifyItemInserted(len)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to inflate the Adapter Bindings
|
||||||
|
*
|
||||||
|
* use like this: `ViewBinding::inflater`
|
||||||
|
*/
|
||||||
|
abstract val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> VB
|
||||||
|
|
||||||
|
abstract fun onBindData(holder: BaseViewHolder<VB>, item: T, position: Int)
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<VB> {
|
||||||
|
return BaseViewHolder<VB>(
|
||||||
|
bindingInflater(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: BaseViewHolder<VB>, position: Int) {
|
||||||
|
onBindData(holder, items[position], position)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = items.size
|
||||||
|
|
||||||
|
}
|
@ -1,16 +1,21 @@
|
|||||||
package com.dzeio.openhealth.core
|
package com.dzeio.openhealth.core
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.room.Delete
|
||||||
import androidx.room.*
|
import androidx.room.Insert
|
||||||
import com.dzeio.openhealth.db.entities.Weight
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Update
|
||||||
|
|
||||||
|
|
||||||
interface BaseDao<T> {
|
interface BaseDao<T> {
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
fun insert(vararg obj: T)
|
suspend fun insert(vararg obj: T): List<Long>
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
suspend fun insert(obj: T): Long
|
||||||
|
|
||||||
@Update
|
@Update
|
||||||
fun update(vararg obj: T)
|
suspend fun update(vararg obj: T)
|
||||||
|
|
||||||
@Delete
|
@Delete
|
||||||
fun delete(vararg obj: T)
|
suspend fun delete(vararg obj: T)
|
||||||
}
|
}
|
61
app/src/main/java/com/dzeio/openhealth/core/BaseDialog.kt
Normal file
61
app/src/main/java/com/dzeio/openhealth/core/BaseDialog.kt
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package com.dzeio.openhealth.core
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
|
||||||
|
abstract class BaseDialog<VM : BaseViewModel, VB : ViewBinding>(private val viewModelClass: Class<VM>) : DialogFragment() {
|
||||||
|
val viewModel by lazy {
|
||||||
|
ViewModelProvider(this)[viewModelClass]
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _binding: VB? = null
|
||||||
|
val binding get() = _binding!!
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup everything!
|
||||||
|
*/
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
return activity?.let { act ->
|
||||||
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
|
||||||
|
_binding = bindingInflater(act.layoutInflater)
|
||||||
|
|
||||||
|
builder.setView(binding.root)
|
||||||
|
|
||||||
|
onBuilderInit(builder)
|
||||||
|
|
||||||
|
val dialog = builder.create()
|
||||||
|
|
||||||
|
onDialogInit(dialog)
|
||||||
|
|
||||||
|
onCreated()
|
||||||
|
|
||||||
|
dialog
|
||||||
|
} ?: throw IllegalStateException("Activity cannot be null")
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun onBuilderInit(builder: MaterialAlertDialogBuilder): Unit {}
|
||||||
|
|
||||||
|
open fun onDialogInit(dialog: AlertDialog): Unit {}
|
||||||
|
|
||||||
|
open fun onCreated(): Unit {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to inflate the Fragment Bindings
|
||||||
|
*/
|
||||||
|
abstract val bindingInflater: (LayoutInflater) -> VB
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy binding
|
||||||
|
*/
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
48
app/src/main/java/com/dzeio/openhealth/core/BaseFragment.kt
Normal file
48
app/src/main/java/com/dzeio/openhealth/core/BaseFragment.kt
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package com.dzeio.openhealth.core
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
|
|
||||||
|
abstract class BaseFragment<VM : BaseViewModel, VB : ViewBinding>(private val viewModelClass: Class<VM>) : Fragment() {
|
||||||
|
val viewModel by lazy {
|
||||||
|
ViewModelProvider(this)[viewModelClass]
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _binding: VB? = null
|
||||||
|
val binding get() = _binding!!
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup everything!
|
||||||
|
*/
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
super.onCreateView(inflater, container, savedInstanceState)
|
||||||
|
|
||||||
|
_binding = bindingInflater(inflater, container, false)
|
||||||
|
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to inflate the Fragment Bindings
|
||||||
|
*
|
||||||
|
* use like this: `ViewBinding::inflater`
|
||||||
|
*/
|
||||||
|
abstract val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> VB
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy binding
|
||||||
|
*/
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package com.dzeio.openhealth.core
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.*
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
|
import com.dzeio.openhealth.R
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
|
||||||
|
abstract class BaseFullscreenDialog<VM : BaseViewModel, VB : ViewBinding>(private val viewModelClass: Class<VM>) : DialogFragment() {
|
||||||
|
|
||||||
|
val viewModel by lazy {
|
||||||
|
ViewModelProvider(this)[viewModelClass]
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _binding: VB? = null
|
||||||
|
val binding get() = _binding!!
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to inflate the Fragment Bindings
|
||||||
|
*/
|
||||||
|
abstract val bindingInflater: (LayoutInflater) -> VB
|
||||||
|
|
||||||
|
abstract val isFullscreenLayout: Boolean
|
||||||
|
|
||||||
|
open fun onCreated(savedInstanceState: Bundle?) {}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
|
||||||
|
_binding = bindingInflater(inflater)
|
||||||
|
|
||||||
|
setHasOptionsMenu(true)
|
||||||
|
|
||||||
|
onCreated(savedInstanceState)
|
||||||
|
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
return activity?.let { act ->
|
||||||
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
|
||||||
|
_binding = bindingInflater(act.layoutInflater)
|
||||||
|
|
||||||
|
builder.setView(binding.root)
|
||||||
|
|
||||||
|
val dialog = builder.create()
|
||||||
|
|
||||||
|
onDialogInit(dialog)
|
||||||
|
|
||||||
|
// onCreated()
|
||||||
|
|
||||||
|
dialog
|
||||||
|
} ?: throw IllegalStateException("Activity cannot be null")
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun onDialogInit(dialog: Dialog): Unit {}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater)
|
||||||
|
|
||||||
|
menu.clear()
|
||||||
|
requireActivity().menuInflater.inflate(R.menu.fullscreen_dialog, menu)
|
||||||
|
super.onCreateOptionsMenu(menu, inflater)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy binding
|
||||||
|
*/
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.dzeio.openhealth.core
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
|
|
||||||
|
class BaseViewHolder<VB : ViewBinding>(
|
||||||
|
val binding : VB
|
||||||
|
) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.dzeio.openhealth.core
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
|
||||||
|
abstract class BaseViewModel : ViewModel()
|
44
app/src/main/java/com/dzeio/openhealth/data/AppDatabase.kt
Normal file
44
app/src/main/java/com/dzeio/openhealth/data/AppDatabase.kt
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package com.dzeio.openhealth.data
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.room.Database
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
import com.dzeio.openhealth.data.weight.WeightDao
|
||||||
|
import com.dzeio.openhealth.data.weight.Weight
|
||||||
|
|
||||||
|
@Database(entities = [Weight::class], version = 1, exportSchema = false)
|
||||||
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
abstract fun weightDao() : WeightDao
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val DATABASE_NAME = "open_health"
|
||||||
|
|
||||||
|
// For Singleton instantiation
|
||||||
|
@Volatile private var instance: AppDatabase? = null
|
||||||
|
|
||||||
|
fun getInstance(context: Context): AppDatabase {
|
||||||
|
return instance ?: synchronized(this) {
|
||||||
|
instance ?: buildDatabase(context).also { instance = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and pre-populate the database. See this article for more details:
|
||||||
|
// https://medium.com/google-developers/7-pro-tips-for-room-fbadea4bfbd1#4785
|
||||||
|
private fun buildDatabase(context: Context): AppDatabase {
|
||||||
|
return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
|
||||||
|
// .addCallback(
|
||||||
|
// object : RoomDatabase.Callback() {
|
||||||
|
// override fun onCreate(db: SupportSQLiteDatabase) {
|
||||||
|
// super.onCreate(db)
|
||||||
|
// val request = OneTimeWorkRequestBuilder<SeedDatabaseWorker>()
|
||||||
|
// .setInputData(workDataOf(KEY_FILENAME to PLANT_DATA_FILENAME))
|
||||||
|
// .build()
|
||||||
|
// WorkManager.getInstance(context).enqueue(request)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
package com.dzeio.openhealth.db.entities
|
package com.dzeio.openhealth.data.weight
|
||||||
|
|
||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import java.sql.Timestamp
|
import java.sql.Date
|
||||||
|
import java.text.DateFormat.getDateInstance
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
data class Weight (
|
data class Weight (
|
||||||
@ -12,4 +13,6 @@ data class Weight (
|
|||||||
@ColumnInfo(index = true)
|
@ColumnInfo(index = true)
|
||||||
var timestamp: Long = System.currentTimeMillis(),
|
var timestamp: Long = System.currentTimeMillis(),
|
||||||
var source: String = ""
|
var source: String = ""
|
||||||
)
|
) {
|
||||||
|
fun formatTimestamp(): String = getDateInstance().format(Date(timestamp));
|
||||||
|
}
|
@ -1,24 +1,23 @@
|
|||||||
package com.dzeio.openhealth.db.dao
|
package com.dzeio.openhealth.data.weight
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import androidx.room.OnConflictStrategy.REPLACE
|
|
||||||
import com.dzeio.openhealth.core.BaseDao
|
import com.dzeio.openhealth.core.BaseDao
|
||||||
import com.dzeio.openhealth.db.entities.Weight
|
import dagger.Provides
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface WeightDao : BaseDao<Weight> {
|
interface WeightDao : BaseDao<Weight> {
|
||||||
|
|
||||||
@Query("SELECT * FROM Weight")
|
@Query("SELECT * FROM Weight")
|
||||||
fun getAll(): List<Weight>
|
fun getAll(): Flow<List<Weight>>
|
||||||
|
|
||||||
@Query("SELECT * FROM Weight where id = :weightId")
|
@Query("SELECT * FROM Weight where id = :weightId")
|
||||||
fun getOne(weightId: Long): Weight?
|
fun getOne(weightId: Long): Flow<Weight?>
|
||||||
|
|
||||||
|
|
||||||
@Query("Select count(*) from Weight")
|
@Query("Select count(*) from Weight")
|
||||||
fun getCount(): Int
|
fun getCount(): Flow<Int>
|
||||||
|
|
||||||
@Query("Select * FROM Weight WHERE id=(SELECT max(id) FROM Weight)")
|
@Query("Select * FROM Weight WHERE id=(SELECT max(id) FROM Weight)")
|
||||||
fun last(): Weight?
|
fun last(): Flow<Weight?>
|
||||||
}
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.dzeio.openhealth.data.weight
|
||||||
|
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class WeightRepository @Inject constructor(
|
||||||
|
private val weightDao: WeightDao
|
||||||
|
) {
|
||||||
|
fun getWeights() = weightDao.getAll()
|
||||||
|
|
||||||
|
fun lastWeight() = weightDao.last()
|
||||||
|
|
||||||
|
fun getWeight(id: Long) = weightDao.getOne(id)
|
||||||
|
|
||||||
|
suspend fun addWeight(weight: Weight) = weightDao.insert(weight)
|
||||||
|
suspend fun deleteWeight(weight: Weight) = weightDao.delete(weight)
|
||||||
|
}
|
@ -1,37 +0,0 @@
|
|||||||
package com.dzeio.openhealth.db
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.room.Database
|
|
||||||
import androidx.room.Room
|
|
||||||
import androidx.room.RoomDatabase
|
|
||||||
import com.dzeio.openhealth.db.dao.WeightDao
|
|
||||||
import com.dzeio.openhealth.db.entities.Weight
|
|
||||||
|
|
||||||
@Database(entities = [Weight::class], version = 1, exportSchema = false)
|
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
|
||||||
abstract fun weightDao() : WeightDao
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
// For Singleton instantiation
|
|
||||||
@Volatile
|
|
||||||
private var INSTANCE: AppDatabase? = null
|
|
||||||
private const val DATABASE_NAME = "open_health"
|
|
||||||
|
|
||||||
fun getInstance(context: Context): AppDatabase {
|
|
||||||
return INSTANCE ?: synchronized(this) {
|
|
||||||
var instance = INSTANCE
|
|
||||||
if (instance == null) {
|
|
||||||
instance = Room.databaseBuilder(
|
|
||||||
context.applicationContext,
|
|
||||||
AppDatabase::class.java,
|
|
||||||
DATABASE_NAME
|
|
||||||
)
|
|
||||||
.fallbackToDestructiveMigration()
|
|
||||||
.allowMainThreadQueries()
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
27
app/src/main/java/com/dzeio/openhealth/di/DatabaseModule.kt
Normal file
27
app/src/main/java/com/dzeio/openhealth/di/DatabaseModule.kt
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package com.dzeio.openhealth.di
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.dzeio.openhealth.data.AppDatabase
|
||||||
|
import com.dzeio.openhealth.data.weight.WeightDao
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
@Module
|
||||||
|
class DatabaseModule {
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun provideAppDatabase(@ApplicationContext context: Context): AppDatabase {
|
||||||
|
return AppDatabase.getInstance(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideWeightDao(appDatabase: AppDatabase): WeightDao {
|
||||||
|
return appDatabase.weightDao()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
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, DialogAddWeightBinding>(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()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,162 @@
|
|||||||
|
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, DialogEditWeightBinding>(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,42 +0,0 @@
|
|||||||
package com.dzeio.openhealth.ui.gallery
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import com.dzeio.openhealth.databinding.FragmentGalleryBinding
|
|
||||||
|
|
||||||
class GalleryFragment : Fragment() {
|
|
||||||
|
|
||||||
private var _binding: FragmentGalleryBinding? = null
|
|
||||||
|
|
||||||
// This property is only valid between onCreateView and
|
|
||||||
// onDestroyView.
|
|
||||||
private val binding get() = _binding!!
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
val galleryViewModel =
|
|
||||||
ViewModelProvider(this).get(GalleryViewModel::class.java)
|
|
||||||
|
|
||||||
_binding = FragmentGalleryBinding.inflate(inflater, container, false)
|
|
||||||
val root: View = binding.root
|
|
||||||
|
|
||||||
val textView: TextView = binding.textGallery
|
|
||||||
galleryViewModel.text.observe(viewLifecycleOwner) {
|
|
||||||
textView.text = it
|
|
||||||
}
|
|
||||||
return root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
|
||||||
super.onDestroyView()
|
|
||||||
_binding = null
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package com.dzeio.openhealth.ui.gallery
|
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
|
|
||||||
class GalleryViewModel : ViewModel() {
|
|
||||||
|
|
||||||
private val _text = MutableLiveData<String>().apply {
|
|
||||||
value = "This is gallery Fragment"
|
|
||||||
}
|
|
||||||
val text: LiveData<String> = _text
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package com.dzeio.openhealth.ui.home
|
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.SavedStateHandle
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
|
|
||||||
class HomeViewModel(
|
|
||||||
private val savedStateHandle: SavedStateHandle
|
|
||||||
) : ViewModel() {
|
|
||||||
val text = MutableLiveData<String>().apply {
|
|
||||||
value = "This is home Fragment"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
package com.dzeio.openhealth.ui.import
|
|
||||||
|
|
||||||
import android.app.ProgressDialog
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.ProgressBar
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import com.dzeio.openhealth.connectors.GoogleFit
|
|
||||||
import com.dzeio.openhealth.databinding.FragmentImportBinding
|
|
||||||
|
|
||||||
class ImportFragment : Fragment() {
|
|
||||||
|
|
||||||
private var _binding: FragmentImportBinding? = null
|
|
||||||
|
|
||||||
// This property is only valid between onCreateView and
|
|
||||||
// onDestroyView.
|
|
||||||
private val binding get() = _binding!!
|
|
||||||
private lateinit var viewModel: ImportViewModel
|
|
||||||
|
|
||||||
private lateinit var progressDialog: ProgressDialog
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
viewModel = ViewModelProvider(this).get(ImportViewModel::class.java)
|
|
||||||
|
|
||||||
_binding = FragmentImportBinding.inflate(inflater, container, false)
|
|
||||||
val root: View = binding.root
|
|
||||||
|
|
||||||
progressDialog = ProgressDialog(requireContext())
|
|
||||||
|
|
||||||
progressDialog.apply {
|
|
||||||
setProgressStyle(ProgressDialog.STYLE_HORIZONTAL)
|
|
||||||
setCancelable(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
binding.importGoogleFit.setOnClickListener {
|
|
||||||
importFromGoogleFit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
|
||||||
super.onDestroyView()
|
|
||||||
_binding = null
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
|
||||||
fun importFromGoogleFit() {
|
|
||||||
viewModel.importProgressTotal.postValue(-1)
|
|
||||||
val google = GoogleFit(requireActivity())
|
|
||||||
|
|
||||||
progressDialog.show()
|
|
||||||
google.importWeight {
|
|
||||||
progressDialog.dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun importFromSamsungHealth() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,199 @@
|
|||||||
|
package com.dzeio.openhealth.ui.main.home
|
||||||
|
|
||||||
|
import android.app.Activity.RESULT_OK
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
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 androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import com.dzeio.openhealth.R
|
||||||
|
import com.dzeio.openhealth.core.BaseFragment
|
||||||
|
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.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 dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewModel::class.java) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG = "HomeFragment"
|
||||||
|
}
|
||||||
|
|
||||||
|
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentHomeBinding
|
||||||
|
get() = FragmentHomeBinding::inflate
|
||||||
|
|
||||||
|
// private lateinit var fit: GoogleFit
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
binding.addWeight.setOnClickListener {
|
||||||
|
AddWeightDialog().show(requireActivity().supportFragmentManager, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.listWeight.setOnClickListener {
|
||||||
|
Log.d("T", "Trying to move")
|
||||||
|
|
||||||
|
findNavController().navigate(HomeFragmentDirections.actionNavHomeToNavListWeight())
|
||||||
|
|
||||||
|
// requireActivity().supportFragmentManager.commit {
|
||||||
|
// replace(R.id.nav_host_fragment_content_main, ListWeightFragment())
|
||||||
|
// addToBackStack(null)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup Graph
|
||||||
|
// binding.weightGraph.gridLabelRenderer.labelFormatter = DateAsXAxisLabelFormatter(requireContext())
|
||||||
|
// binding.weightGraph.gridLabelRenderer.numHorizontalLabels = 3 // only 4 because of the space
|
||||||
|
// binding.weightGraph.viewport.isXAxisBoundsManual = true
|
||||||
|
//// binding.weightGraph.gridLabelRenderer.setHumanRounding(false);
|
||||||
|
// binding.weightGraph.addSeries(serie)
|
||||||
|
}
|
||||||
|
|
||||||
|
// private val entries = LineGraphSeries<DataPoint>()
|
||||||
|
|
||||||
|
private fun updateGraph(list: List<Weight>) {
|
||||||
|
if (list.isNotEmpty()) {
|
||||||
|
val first = list[0].timestamp
|
||||||
|
val last = list[list.size - 1].timestamp
|
||||||
|
val weekFirst = last - 604800000
|
||||||
|
// binding.weightGraph.viewport.setMinX(if (first > weekFirst) first else weekFirst)
|
||||||
|
// binding.weightGraph.viewport.setMaxX(last);
|
||||||
|
|
||||||
|
|
||||||
|
val entries = ArrayList<Entry>()
|
||||||
|
for (item in list) {
|
||||||
|
entries.add(Entry(item.timestamp.toFloat(), item.weight))
|
||||||
|
}
|
||||||
|
|
||||||
|
val dataSet = LineDataSet(entries, "Label")
|
||||||
|
// binding.weightGraph.xAxis.axisMinimum = if (first > weekFirst) first.toFloat() else weekFirst.toFloat()
|
||||||
|
// binding.weightGraph.setExtraOffsets(
|
||||||
|
// if (first > weekFirst) first.toFloat() else weekFirst.toFloat(),
|
||||||
|
// 100f,
|
||||||
|
// last.toFloat(),
|
||||||
|
// 0f
|
||||||
|
// )
|
||||||
|
binding.weightGraph.isAutoScaleMinMaxEnabled = true
|
||||||
|
//binding.weightGraph.setvir
|
||||||
|
binding.weightGraph.isDragEnabled = true
|
||||||
|
binding.weightGraph.isScaleYEnabled = false
|
||||||
|
binding.weightGraph.description = Description().apply { isEnabled = false }
|
||||||
|
binding.weightGraph.isScaleXEnabled = true
|
||||||
|
binding.weightGraph.setPinchZoom(false)
|
||||||
|
binding.weightGraph.xAxis.setLabelCount(5, true)
|
||||||
|
binding.weightGraph.xAxis.position = XAxis.XAxisPosition.BOTTOM
|
||||||
|
binding.weightGraph.axisRight.isEnabled = false
|
||||||
|
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 super.getAxisLabel(value, axis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binding.weightGraph.data = LineData(dataSet)
|
||||||
|
binding.weightGraph.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// serie.resetData(list
|
||||||
|
// .map { DataPoint(Date(it.timestamp), it.weight.toDouble()) }
|
||||||
|
// .toTypedArray()
|
||||||
|
// )
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
|
||||||
|
lifecycleScope.launchWhenStarted {
|
||||||
|
viewModel.fetchWeights().collectLatest {
|
||||||
|
if (it.isEmpty()) {
|
||||||
|
return@collectLatest
|
||||||
|
}
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>,
|
||||||
|
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.")
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.dzeio.openhealth.ui.main.home
|
||||||
|
|
||||||
|
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 javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class HomeViewModel @Inject internal constructor(
|
||||||
|
private val weightRepository: WeightRepository
|
||||||
|
) : BaseViewModel() {
|
||||||
|
|
||||||
|
fun fetchWeights() = weightRepository.getWeights()
|
||||||
|
|
||||||
|
fun lastWeight() = weightRepository.lastWeight()
|
||||||
|
|
||||||
|
fun fetchWeight(id: Long) = weightRepository.getWeight(id)
|
||||||
|
|
||||||
|
suspend fun deleteWeight(weight: Weight) = weightRepository.deleteWeight(weight)
|
||||||
|
|
||||||
|
suspend fun addWeight(weight: Weight) = weightRepository.addWeight(weight)
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package com.dzeio.openhealth.ui.home
|
package com.dzeio.openhealth.ui.main.import
|
||||||
|
|
||||||
import android.app.Activity.RESULT_OK
|
import android.app.ProgressDialog
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
@ -9,67 +8,61 @@ import android.util.Log
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import com.dzeio.openhealth.connectors.ActionRequestCode
|
import com.dzeio.openhealth.connectors.ActionRequestCode
|
||||||
import com.dzeio.openhealth.connectors.GoogleFit
|
import com.dzeio.openhealth.connectors.GoogleFit
|
||||||
|
//import com.dzeio.openhealth.connectors.GoogleFit
|
||||||
import com.dzeio.openhealth.connectors.samsunghealth.SamsungHealth
|
import com.dzeio.openhealth.connectors.samsunghealth.SamsungHealth
|
||||||
import com.dzeio.openhealth.databinding.FragmentHomeBinding
|
import com.dzeio.openhealth.databinding.FragmentImportBinding
|
||||||
import com.dzeio.openhealth.db.AppDatabase
|
|
||||||
|
|
||||||
class HomeFragment : Fragment() {
|
class ImportFragment : Fragment() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "HomeFragment"
|
const val TAG = "ImportFragment"
|
||||||
}
|
}
|
||||||
|
|
||||||
private var _binding: FragmentHomeBinding? = null
|
private var _binding: FragmentImportBinding? = null
|
||||||
|
|
||||||
// This property is only valid between onCreateView and
|
// This property is only valid between onCreateView and
|
||||||
// onDestroyView.
|
// onDestroyView.
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
|
private lateinit var viewModel: ImportViewModel
|
||||||
|
|
||||||
|
private lateinit var progressDialog: ProgressDialog
|
||||||
|
|
||||||
private lateinit var fit: GoogleFit
|
private lateinit var fit: GoogleFit
|
||||||
|
|
||||||
private lateinit var viewModel: HomeViewModel
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
viewModel =
|
viewModel = ViewModelProvider(this).get(ImportViewModel::class.java)
|
||||||
ViewModelProvider(this).get(HomeViewModel::class.java)
|
|
||||||
|
|
||||||
_binding = FragmentHomeBinding.inflate(inflater, container, false)
|
_binding = FragmentImportBinding.inflate(inflater, container, false)
|
||||||
val root: View = binding.root
|
val root: View = binding.root
|
||||||
|
|
||||||
// binding.button.setOnClickListener {
|
progressDialog = ProgressDialog(requireContext())
|
||||||
// fit = GoogleFit(requireActivity())
|
|
||||||
// //fit.import()
|
|
||||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
// //fit.getHistory()
|
|
||||||
// fit.importWeight()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// //SamsungHealth(requireActivity()).importStepCount()
|
|
||||||
// }
|
|
||||||
viewModel.text.observe(viewLifecycleOwner) {
|
|
||||||
binding.textView2.text = it
|
|
||||||
}
|
|
||||||
|
|
||||||
|
progressDialog.apply {
|
||||||
|
setProgressStyle(ProgressDialog.STYLE_HORIZONTAL)
|
||||||
|
setCancelable(false)
|
||||||
|
}
|
||||||
|
|
||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
binding.importGoogleFit.setOnClickListener {
|
||||||
val weight = AppDatabase.getInstance(requireContext()).weightDao().last()
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
if (weight == null) {
|
importFromGoogleFit()
|
||||||
viewModel.text.postValue("No Weight Available")
|
}
|
||||||
} else {
|
}
|
||||||
viewModel.text.postValue("${weight.weight}kg\ndone at ${weight.timestamp}")
|
binding.importSamsungHealth.setOnClickListener {
|
||||||
|
importFromSamsungHealth()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,17 +71,22 @@ class HomeFragment : Fragment() {
|
|||||||
_binding = null
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
fun importFromGoogleFit() {
|
||||||
Log.d(TAG, "Activity Result!")
|
viewModel.importProgressTotal.postValue(-1)
|
||||||
when (resultCode) {
|
fit = GoogleFit(requireActivity())
|
||||||
RESULT_OK -> {
|
|
||||||
fit.performActionForRequestCode(ActionRequestCode.FIND_DATA_SOURCES)
|
progressDialog.show()
|
||||||
}
|
fit.import()
|
||||||
else -> {
|
fit.getHistory()
|
||||||
Log.e(TAG, "Error: $requestCode, $resultCode")
|
// google.importWeight {
|
||||||
}
|
// progressDialog.dismiss()
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun importFromSamsungHealth() {
|
||||||
|
SamsungHealth(requireActivity())
|
||||||
|
.test()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>,
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>,
|
@ -1,9 +1,8 @@
|
|||||||
package com.dzeio.openhealth.ui.import
|
package com.dzeio.openhealth.ui.main.import
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.dzeio.openhealth.connectors.GoogleFit
|
//import com.dzeio.openhealth.connectors.GoogleFit
|
||||||
|
|
||||||
class ImportViewModel : ViewModel() {
|
class ImportViewModel : ViewModel() {
|
||||||
|
|
@ -0,0 +1,49 @@
|
|||||||
|
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.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
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class ListWeightFragment : BaseFragment<HomeViewModel, FragmentListWeightBinding>(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().collect {
|
||||||
|
adapter.set(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
10
app/src/main/res/drawable/ic_baseline_add_24.xml
Normal file
10
app/src/main/res/drawable/ic_baseline_add_24.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||||
|
</vector>
|
10
app/src/main/res/drawable/ic_baseline_edit_24.xml
Normal file
10
app/src/main/res/drawable/ic_baseline_edit_24.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
|
||||||
|
</vector>
|
10
app/src/main/res/drawable/ic_outline_delete_24.xml
Normal file
10
app/src/main/res/drawable/ic_outline_delete_24.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M16,9v10H8V9h8m-1.5,-6h-5l-1,1H5v2h14V4h-3.5l-1,-1zM18,7H6v12c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7z"/>
|
||||||
|
</vector>
|
10
app/src/main/res/drawable/ic_outline_hexagon_24.xml
Normal file
10
app/src/main/res/drawable/ic_outline_hexagon_24.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M17.2,3H6.8l-5.2,9l5.2,9h10.4l5.2,-9L17.2,3zM16.05,19H7.95l-4.04,-7l4.04,-7h8.09l4.04,7L16.05,19z"/>
|
||||||
|
</vector>
|
10
app/src/main/res/drawable/ic_outline_timeline_24.xml
Normal file
10
app/src/main/res/drawable/ic_outline_timeline_24.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M23,8c0,1.1 -0.9,2 -2,2c-0.18,0 -0.35,-0.02 -0.51,-0.07l-3.56,3.55C16.98,13.64 17,13.82 17,14c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2c0,-0.18 0.02,-0.36 0.07,-0.52l-2.55,-2.55C10.36,10.98 10.18,11 10,11c-0.18,0 -0.36,-0.02 -0.52,-0.07l-4.55,4.56C4.98,15.65 5,15.82 5,16c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2s0.9,-2 2,-2c0.18,0 0.35,0.02 0.51,0.07l4.56,-4.55C8.02,9.36 8,9.18 8,9c0,-1.1 0.9,-2 2,-2s2,0.9 2,2c0,0.18 -0.02,0.36 -0.07,0.52l2.55,2.55C14.64,12.02 14.82,12 15,12c0.18,0 0.36,0.02 0.52,0.07l3.55,-3.56C19.02,8.35 19,8.18 19,8c0,-1.1 0.9,-2 2,-2S23,6.9 23,8z"/>
|
||||||
|
</vector>
|
@ -5,21 +5,55 @@
|
|||||||
android:id="@+id/drawer_layout"
|
android:id="@+id/drawer_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:fitsSystemWindows="true"
|
tools:openDrawer="start"
|
||||||
tools:openDrawer="start">
|
tools:context=".MainActivity">
|
||||||
|
|
||||||
<include
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:id="@+id/app_bar_main"
|
|
||||||
layout="@layout/app_bar_main"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent"
|
||||||
|
>
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
android:id="@+id/nav_host_fragment"
|
||||||
|
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
|
||||||
|
app:defaultNavHost="true"
|
||||||
|
app:navGraph="@navigation/mobile_navigation" />
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
<com.google.android.material.navigation.NavigationView
|
<com.google.android.material.navigation.NavigationView
|
||||||
android:id="@+id/nav_view"
|
android:id="@+id/nav_view"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="start"
|
android:layout_gravity="start"
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
app:headerLayout="@layout/nav_header_main"
|
app:headerLayout="@layout/nav_header_main"
|
||||||
app:menu="@menu/activity_main_drawer" />
|
app:menu="@menu/activity_main_drawer" />
|
||||||
</androidx.drawerlayout.widget.DrawerLayout>
|
</androidx.drawerlayout.widget.DrawerLayout>
|
@ -1,34 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".MainActivity">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:theme="@style/Theme.OpenHealth.AppBarOverlay">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?attr/actionBarSize"
|
|
||||||
android:background="?attr/colorPrimary"
|
|
||||||
app:popupTheme="@style/Theme.OpenHealth.PopupOverlay" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<include layout="@layout/content_main" />
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
android:id="@+id/fab"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="bottom|end"
|
|
||||||
android:layout_marginEnd="@dimen/fab_margin"
|
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
app:srcCompat="@android:drawable/ic_dialog_email" />
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -1,20 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
|
||||||
tools:showIn="@layout/app_bar_main">
|
|
||||||
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/nav_host_fragment_content_main"
|
|
||||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:defaultNavHost="true"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:navGraph="@navigation/mobile_navigation" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
22
app/src/main/res/layout/dialog_add_weight.xml
Normal file
22
app/src/main/res/layout/dialog_add_weight.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<android.widget.NumberPicker
|
||||||
|
android:id="@+id/kg"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<android.widget.NumberPicker
|
||||||
|
android:id="@+id/gram"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:progress="0" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
47
app/src/main/res/layout/dialog_edit_weight.xml
Normal file
47
app/src/main/res/layout/dialog_edit_weight.xml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layout_dialog_edit_weight_ll"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<android.widget.NumberPicker
|
||||||
|
android:id="@+id/layout_dialog_edit_weight_kg"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/layout_dialog_edit_weight_gram"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
tools:layout_editor_absoluteY="0dp" />
|
||||||
|
|
||||||
|
<android.widget.NumberPicker
|
||||||
|
android:id="@+id/layout_dialog_edit_weight_gram"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:progress="0"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
tools:layout_editor_absoluteY="0dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/layout_dialog_edit_weight_timestamp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/layout_dialog_edit_weight_ll" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,22 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".ui.gallery.GalleryFragment">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/text_gallery"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textSize="20sp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -4,14 +4,91 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".ui.home.HomeFragment">
|
tools:context=".ui.main.home.HomeFragment">
|
||||||
|
|
||||||
<TextView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/textView2"
|
style="?attr/materialCardViewFilledStyle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="TextView"
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/constraintLayout2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:gravity="fill_horizontal|center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:layout_editor_absoluteX="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/TextAppearance.Material3.TitleMedium"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Weight"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/add_weight"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:src="@drawable/ic_baseline_add_24" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/list_weight"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:src="@drawable/ic_outline_hexagon_24"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<com.github.mikephil.charting.charts.LineChart
|
||||||
|
android:id="@+id/weight_graph"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="200dp"
|
||||||
|
android:minHeight="200dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/constraintLayout2" />
|
||||||
|
|
||||||
|
<!-- <com.jjoe64.graphview.GraphView-->
|
||||||
|
<!-- android:id="@+id/weight_graph"-->
|
||||||
|
<!-- android:layout_width="match_parent"-->
|
||||||
|
<!-- android:layout_height="200dp"-->
|
||||||
|
<!-- android:minHeight="200dp"-->
|
||||||
|
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
|
||||||
|
<!-- app:layout_constraintEnd_toEndOf="parent"-->
|
||||||
|
<!-- app:layout_constraintStart_toStartOf="parent"-->
|
||||||
|
<!-- app:layout_constraintTop_toBottomOf="@+id/constraintLayout2" />-->
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -4,7 +4,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".ui.import.ImportFragment">
|
tools:context=".ui.main.import.ImportFragment">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_slideshow"
|
android:id="@+id/text_slideshow"
|
||||||
|
14
app/src/main/res/layout/fragment_list_weight.xml
Normal file
14
app/src/main/res/layout/fragment_list_weight.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
|
||||||
|
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:id="@+id/list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="16dp"
|
||||||
|
|
||||||
|
tools:listitem="@layout/layout_item_weight"
|
||||||
|
tools:context=".ui.main.list_weight.ListWeightFragment" />
|
59
app/src/main/res/layout/layout_item_weight.xml
Normal file
59
app/src/main/res/layout/layout_item_weight.xml
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
style="?attr/materialCardViewFilledStyle"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:id="@+id/edit"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
tools:layout_editor_absoluteX="16dp"
|
||||||
|
tools:layout_editor_absoluteY="0dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/weight"
|
||||||
|
style="@style/TextAppearance.Material3.TitleMedium"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="xkg"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/datetime"
|
||||||
|
style="@style/TextAppearance.Material3.BodyMedium"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Taken: yyyy-mm-dd"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/weight" />
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:src="@drawable/ic_baseline_edit_24"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
15
app/src/main/res/menu/fullscreen_dialog.xml
Normal file
15
app/src/main/res/menu/fullscreen_dialog.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_fullscreen_dialog_save"
|
||||||
|
android:title="Save"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_fullscreen_dialog_delete"
|
||||||
|
android:title="Delete"
|
||||||
|
android:icon="@drawable/ic_outline_delete_24"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
</menu>
|
@ -7,19 +7,54 @@
|
|||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/nav_home"
|
android:id="@+id/nav_home"
|
||||||
android:name="com.dzeio.openhealth.ui.home.HomeFragment"
|
android:name="com.dzeio.openhealth.ui.main.home.HomeFragment"
|
||||||
android:label="@string/menu_home"
|
android:label="@string/menu_home"
|
||||||
tools:layout="@layout/fragment_home" />
|
tools:layout="@layout/fragment_home" >
|
||||||
|
<action
|
||||||
<fragment
|
android:id="@+id/action_nav_home_to_nav_list_weight"
|
||||||
android:id="@+id/nav_gallery"
|
app:destination="@id/nav_list_weight"
|
||||||
android:name="com.dzeio.openhealth.ui.gallery.GalleryFragment"
|
app:enterAnim="@android:anim/slide_in_left"
|
||||||
android:label="@string/menu_gallery"
|
app:exitAnim="@android:anim/slide_out_right"
|
||||||
tools:layout="@layout/fragment_gallery" />
|
app:popEnterAnim="@android:anim/slide_in_left"
|
||||||
|
app:popExitAnim="@android:anim/slide_out_right" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_nav_home_to_nav_import"
|
||||||
|
app:destination="@id/nav_import"
|
||||||
|
app:enterAnim="@android:anim/slide_in_left"
|
||||||
|
app:exitAnim="@android:anim/slide_out_right"
|
||||||
|
app:popEnterAnim="@android:anim/slide_in_left"
|
||||||
|
app:popExitAnim="@android:anim/slide_out_right" />
|
||||||
|
</fragment>
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/nav_import"
|
android:id="@+id/nav_import"
|
||||||
android:name="com.dzeio.openhealth.ui.import.ImportFragment"
|
android:name="com.dzeio.openhealth.ui.main.import.ImportFragment"
|
||||||
android:label="@string/menu_import"
|
android:label="@string/menu_import"
|
||||||
tools:layout="@layout/fragment_import" />
|
tools:layout="@layout/fragment_import" />
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/nav_list_weight"
|
||||||
|
android:name="com.dzeio.openhealth.ui.main.list_weight.ListWeightFragment"
|
||||||
|
android:label="@string/menu_list_weight"
|
||||||
|
tools:layout="@layout/fragment_list_weight" >
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_nav_list_weight_to_nav_edit_weight"
|
||||||
|
app:destination="@id/nav_edit_weight"
|
||||||
|
app:enterAnim="@android:anim/slide_in_left"
|
||||||
|
app:exitAnim="@android:anim/slide_out_right"
|
||||||
|
app:popEnterAnim="@android:anim/slide_in_left"
|
||||||
|
app:popExitAnim="@android:anim/slide_out_right" />
|
||||||
|
</fragment>
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/nav_edit_weight"
|
||||||
|
android:name="com.dzeio.openhealth.ui.dialogs.EditWeightDialog"
|
||||||
|
android:label="@string/menu_edit_weight"
|
||||||
|
tools:layout="@layout/dialog_edit_weight">
|
||||||
|
|
||||||
|
<argument
|
||||||
|
android:name="id"
|
||||||
|
app:argType="long" />
|
||||||
|
|
||||||
|
</fragment>
|
||||||
</navigation>
|
</navigation>
|
@ -1,16 +1,17 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="Theme.OpenHealth" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
<style name="Theme.OpenHealth" parent="Theme.Material3.DynamicColors.DayNight">
|
||||||
<!-- Primary brand color. -->
|
<!-- <!– Primary brand color. –>-->
|
||||||
<item name="colorPrimary">@color/purple_200</item>
|
<!-- <item name="colorPrimary">@color/purple_200</item>-->
|
||||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
<!-- <item name="colorPrimaryVariant">@color/purple_700</item>-->
|
||||||
<item name="colorOnPrimary">@color/black</item>
|
<!-- <item name="colorOnPrimary">@color/black</item>-->
|
||||||
<!-- Secondary brand color. -->
|
<!-- <!– Secondary brand color. –>-->
|
||||||
<item name="colorSecondary">@color/teal_200</item>
|
<!-- <item name="colorSecondary">@color/teal_200</item>-->
|
||||||
<item name="colorSecondaryVariant">@color/teal_200</item>
|
<!-- <item name="colorSecondaryVariant">@color/teal_200</item>-->
|
||||||
<item name="colorOnSecondary">@color/black</item>
|
<!-- <item name="colorOnSecondary">@color/black</item>-->
|
||||||
<!-- Status bar color. -->
|
<!-- <!– Status bar color. –>-->
|
||||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
<!-- <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>-->
|
||||||
<!-- Customize your theme here. -->
|
<!-- <!– Customize your theme here. –>-->
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
@ -11,4 +11,6 @@
|
|||||||
<string name="menu_gallery">Gallery</string>
|
<string name="menu_gallery">Gallery</string>
|
||||||
<string name="menu_slideshow">Slideshow</string>
|
<string name="menu_slideshow">Slideshow</string>
|
||||||
<string name="menu_import">Import</string>
|
<string name="menu_import">Import</string>
|
||||||
|
<string name="menu_list_weight">Weight List</string>
|
||||||
|
<string name="menu_edit_weight">Edit Weight</string>
|
||||||
</resources>
|
</resources>
|
@ -1,17 +1,20 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="Theme.OpenHealth" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
<style name="Theme.OpenHealth" parent="Theme.Material3.DynamicColors.DayNight">
|
||||||
<!-- Primary brand color. -->
|
<!-- <!– Primary brand color. –>-->
|
||||||
<item name="colorPrimary">@color/purple_500</item>
|
<!-- <item name="colorPrimary">@color/purple_500</item>-->
|
||||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
<!-- <item name="colorPrimaryVariant">@color/purple_700</item>-->
|
||||||
<item name="colorOnPrimary">@color/white</item>
|
<!-- <item name="colorOnPrimary">@color/white</item>-->
|
||||||
<!-- Secondary brand color. -->
|
<!-- <!– Secondary brand color. –>-->
|
||||||
<item name="colorSecondary">@color/teal_200</item>
|
<!-- <item name="colorSecondary">@color/teal_200</item>-->
|
||||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
<!-- <item name="colorSecondaryVariant">@color/teal_700</item>-->
|
||||||
<item name="colorOnSecondary">@color/black</item>
|
<!-- <item name="colorOnSecondary">@color/black</item>-->
|
||||||
<!-- Status bar color. -->
|
<!-- <!– Status bar color. –>-->
|
||||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
<!-- <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>-->
|
||||||
<!-- Customize your theme here. -->
|
<!-- Customize your theme here. -->
|
||||||
|
|
||||||
|
<item name="android:windowTranslucentStatus">true</item>
|
||||||
|
<item name="materialAlertDialogTheme">@style/ThemeOverlay.Material3.MaterialAlertDialog</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Theme.OpenHealth.NoActionBar">
|
<style name="Theme.OpenHealth.NoActionBar">
|
||||||
@ -19,7 +22,8 @@
|
|||||||
<item name="windowNoTitle">true</item>
|
<item name="windowNoTitle">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Theme.OpenHealth.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
<style name="Theme.OpenHealth.AppBarOverlay" parent="ThemeOverlay.MaterialComponents.Dark.ActionBar" />
|
||||||
|
|
||||||
|
<style name="Theme.OpenHealth.PopupOverlay" parent="ThemeOverlay.Material3.MaterialAlertDialog" />
|
||||||
|
|
||||||
<style name="Theme.OpenHealth.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
|
||||||
</resources>
|
</resources>
|
15
build.gradle
15
build.gradle
@ -1,10 +1,21 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
buildscript {
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.38.1'
|
||||||
|
|
||||||
|
|
||||||
|
// Safe Navigation
|
||||||
|
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application' version '7.1.0-beta04' apply false
|
id 'com.android.application' version '7.1.0-beta05' apply false
|
||||||
id 'com.android.library' version '7.1.0-beta04' apply false
|
id 'com.android.library' version '7.1.0-beta05' apply false
|
||||||
id 'org.jetbrains.kotlin.android' version '1.6.0' apply false
|
id 'org.jetbrains.kotlin.android' version '1.6.0' apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
task clean(type: Delete) {
|
task clean(type: Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
|
delete project.buildDir
|
||||||
}
|
}
|
@ -21,3 +21,5 @@ kotlin.code.style=official
|
|||||||
# resources declared in the library itself and none from the library's dependencies,
|
# resources declared in the library itself and none from the library's dependencies,
|
||||||
# thereby reducing the size of the R class for that library
|
# thereby reducing the size of the R class for that library
|
||||||
android.nonTransitiveRClass=true
|
android.nonTransitiveRClass=true
|
||||||
|
|
||||||
|
android.enableJetifier=true
|
@ -5,11 +5,13 @@ pluginManagement {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven { url 'https://jitpack.io' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rootProject.name = "OpenHealth"
|
rootProject.name = "OpenHealth"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user