mirror of
https://github.com/dzeiocom/OpenHealth.git
synced 2025-07-06 19:49:18 +00:00
73
app/src/main/java/com/dzeio/openhealth/MainActivity.kt
Normal file
73
app/src/main/java/com/dzeio/openhealth/MainActivity.kt
Normal file
@ -0,0 +1,73 @@
|
||||
package com.dzeio.openhealth
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.navigateUp
|
||||
import androidx.navigation.ui.setupActionBarWithNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import com.dzeio.openhealth.databinding.ActivityMainBinding
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
setSupportActionBar(binding.appBarMain.toolbar)
|
||||
|
||||
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(
|
||||
setOf(
|
||||
R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow
|
||||
), drawerLayout
|
||||
)
|
||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||
navView.setupWithNavController(navController)
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
val navController = findNavController(R.id.nav_host_fragment_content_main)
|
||||
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>,
|
||||
grantResults: IntArray) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
Log.d("MainActivity", "Result $requestCode")
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
for (fragment in supportFragmentManager.primaryNavigationFragment!!.childFragmentManager.fragments) {
|
||||
fragment.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
}
|
264
app/src/main/java/com/dzeio/openhealth/connectors/GoogleFit.kt
Normal file
264
app/src/main/java/com/dzeio/openhealth/connectors/GoogleFit.kt
Normal file
@ -0,0 +1,264 @@
|
||||
package com.dzeio.openhealth.connectors
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.app.ActivityCompat
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
||||
import com.google.android.gms.fitness.Fitness
|
||||
import com.google.android.gms.fitness.FitnessOptions
|
||||
import com.google.android.gms.fitness.data.DataPoint
|
||||
import com.google.android.gms.fitness.data.DataSet
|
||||
import com.google.android.gms.fitness.data.DataSource
|
||||
import com.google.android.gms.fitness.data.DataType
|
||||
import com.google.android.gms.fitness.request.DataReadRequest
|
||||
import com.google.android.gms.fitness.request.DataSourcesRequest
|
||||
import com.google.android.gms.fitness.request.OnDataPointListener
|
||||
import com.google.android.gms.fitness.request.SensorRequest
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
enum class ActionRequestCode {
|
||||
FIND_DATA_SOURCES
|
||||
}
|
||||
|
||||
class GoogleFit(
|
||||
private val activity: Activity,
|
||||
) {
|
||||
companion object {
|
||||
const val TAG = "GoogleFitConnector"
|
||||
}
|
||||
private val fitnessOptions = FitnessOptions.builder()
|
||||
.addDataType(DataType.TYPE_STEP_COUNT_CUMULATIVE)
|
||||
.addDataType(DataType.TYPE_ACTIVITY_SEGMENT)
|
||||
.addDataType(DataType.TYPE_SLEEP_SEGMENT)
|
||||
.addDataType(DataType.TYPE_CALORIES_EXPENDED)
|
||||
.addDataType(DataType.TYPE_BASAL_METABOLIC_RATE)
|
||||
.addDataType(DataType.TYPE_POWER_SAMPLE)
|
||||
.addDataType(DataType.TYPE_HEART_RATE_BPM)
|
||||
.addDataType(DataType.TYPE_LOCATION_SAMPLE)
|
||||
.addDataType(DataType.TYPE_WEIGHT)
|
||||
.build()
|
||||
|
||||
private val runningQOrLater =
|
||||
android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q
|
||||
|
||||
// [START dataPointListener_variable_reference]
|
||||
// Need to hold a reference to this listener, as it's passed into the "unregister"
|
||||
// method in order to stop all sensors from sending data to this listener.
|
||||
private var dataPointListener: OnDataPointListener? = null
|
||||
|
||||
fun import() {
|
||||
checkPermissionsAndRun(ActionRequestCode.FIND_DATA_SOURCES)
|
||||
}
|
||||
|
||||
private fun checkPermissionsAndRun(actionRequestCode: ActionRequestCode) {
|
||||
if (permissionApproved()) {
|
||||
signIn(actionRequestCode)
|
||||
} else {
|
||||
requestRuntimePermissions(actionRequestCode)
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestRuntimePermissions(requestCode: ActionRequestCode) {
|
||||
val shouldProvideRationale =
|
||||
ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
|
||||
// Provide an additional rationale to the user. This would happen if the user denied the
|
||||
// request previously, but didn't check the "Don't ask again" checkbox.
|
||||
requestCode.let {
|
||||
if (shouldProvideRationale) {
|
||||
Log.i(TAG, "Displaying permission rationale to provide additional context.")
|
||||
// ProgressDialog.show(activity, "Waiting for authorization...", "")
|
||||
ActivityCompat.requestPermissions(activity,
|
||||
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
|
||||
requestCode.ordinal)
|
||||
} else {
|
||||
Log.i(TAG, "Requesting permission")
|
||||
// Request permission. It's possible this can be auto answered if device policy
|
||||
// sets the permission in a given state or the user denied the permission
|
||||
// previously and checked "Never ask again".
|
||||
ActivityCompat.requestPermissions(activity,
|
||||
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
|
||||
requestCode.ordinal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun permissionApproved(): Boolean {
|
||||
val approved = if (runningQOrLater) {
|
||||
PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(
|
||||
activity,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
return approved
|
||||
}
|
||||
|
||||
fun signIn(requestCode: ActionRequestCode) {
|
||||
if (oAuthPermissionsApproved()) {
|
||||
performActionForRequestCode(requestCode)
|
||||
} else {
|
||||
requestCode.let {
|
||||
GoogleSignIn.requestPermissions(
|
||||
activity,
|
||||
it.ordinal,
|
||||
getGoogleAccount(), fitnessOptions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun oAuthPermissionsApproved() = GoogleSignIn.hasPermissions(getGoogleAccount(), fitnessOptions)
|
||||
|
||||
/**
|
||||
* Gets a Google account for use in creating the Fitness client. This is achieved by either
|
||||
* using the last signed-in account, or if necessary, prompting the user to sign in.
|
||||
* `getAccountForExtension` is recommended over `getLastSignedInAccount` as the latter can
|
||||
* return `null` if there has been no sign in before.
|
||||
*/
|
||||
private fun getGoogleAccount() = GoogleSignIn.getAccountForExtension(activity, fitnessOptions)
|
||||
|
||||
/**
|
||||
* Runs the desired method, based on the specified request code. The request code is typically
|
||||
* passed to the Fit sign-in flow, and returned with the success callback. This allows the
|
||||
* caller to specify which method, post-sign-in, should be called.
|
||||
*
|
||||
* @param requestCode The code corresponding to the action to perform.
|
||||
*/
|
||||
fun performActionForRequestCode(requestCode: ActionRequestCode) = when (requestCode) {
|
||||
ActionRequestCode.FIND_DATA_SOURCES -> findFitnessDataSources()
|
||||
}
|
||||
|
||||
/** Finds available data sources and attempts to register on a specific [DataType]. */
|
||||
private fun findFitnessDataSources() { // [START find_data_sources]
|
||||
// Note: Fitness.SensorsApi.findDataSources() requires the ACCESS_FINE_LOCATION permission.
|
||||
Fitness.getSensorsClient(activity, getGoogleAccount())
|
||||
.findDataSources(
|
||||
DataSourcesRequest.Builder()
|
||||
.setDataTypes(DataType.TYPE_LOCATION_SAMPLE)
|
||||
.setDataSourceTypes(DataSource.TYPE_RAW)
|
||||
.build())
|
||||
.addOnSuccessListener { dataSources ->
|
||||
for (dataSource in dataSources) {
|
||||
Log.i(TAG, "Data source found: $dataSource")
|
||||
Log.i(TAG, "Data Source type: " + dataSource.dataType.name)
|
||||
// Let's register a listener to receive Activity data!
|
||||
if (dataSource.dataType == DataType.TYPE_LOCATION_SAMPLE && dataPointListener == null) {
|
||||
Log.i(TAG, "Data source for LOCATION_SAMPLE found! Registering.")
|
||||
registerFitnessDataListener(dataSource, DataType.TYPE_LOCATION_SAMPLE)
|
||||
}
|
||||
}
|
||||
}
|
||||
.addOnFailureListener { e -> Log.e(TAG, "failed", e) }
|
||||
// [END find_data_sources]
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener with the Sensors API for the provided [DataSource] and [DataType] combo.
|
||||
*/
|
||||
private fun registerFitnessDataListener(dataSource: DataSource, dataType: DataType) {
|
||||
// [START register_data_listener]
|
||||
dataPointListener = OnDataPointListener { dataPoint ->
|
||||
for (field in dataPoint.dataType.fields) {
|
||||
val value = dataPoint.getValue(field)
|
||||
Log.i(TAG, "Detected DataPoint field: ${field.name}")
|
||||
Log.i(TAG, "Detected DataPoint value: $value")
|
||||
}
|
||||
}
|
||||
Fitness.getSensorsClient(activity, getGoogleAccount())
|
||||
.add(
|
||||
SensorRequest.Builder()
|
||||
.setDataSource(dataSource) // Optional but recommended for custom data sets.
|
||||
.setDataType(dataType) // Can't be omitted.
|
||||
.setSamplingRate(10, TimeUnit.SECONDS)
|
||||
.build(),
|
||||
dataPointListener!!
|
||||
)
|
||||
.addOnCompleteListener { task ->
|
||||
if (task.isSuccessful) {
|
||||
Log.i(TAG, "Listener registered!")
|
||||
} else {
|
||||
Log.e(TAG, "Listener not registered.", task.exception)
|
||||
}
|
||||
}
|
||||
// [END register_data_listener]
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
fun getHistory() {
|
||||
|
||||
val endTime = LocalDateTime.now().atZone(ZoneId.systemDefault())
|
||||
val readRequest = DataReadRequest.Builder()
|
||||
.aggregate(DataType.AGGREGATE_CALORIES_EXPENDED)
|
||||
.bucketByActivityType(1, TimeUnit.SECONDS)
|
||||
.setTimeRange(endTime.minusWeeks(1).toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
||||
Fitness.getHistoryClient(activity, GoogleSignIn.getAccountForExtension(activity, fitnessOptions))
|
||||
.readData(readRequest)
|
||||
.addOnSuccessListener { response ->
|
||||
for (dataSet in response.buckets.flatMap { it.dataSets }) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
dumpDataSet(dataSet)
|
||||
} else {
|
||||
Log.e(TAG, "DUMB SHIT")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
fun dumpDataSet(dataSet: DataSet) {
|
||||
Log.i(TAG, "Data returned for Data type: ${dataSet.dataType.name}")
|
||||
for (dp in dataSet.dataPoints) {
|
||||
Log.i(TAG,"Data point:")
|
||||
Log.i(TAG,"\tType: ${dp.dataType.name}")
|
||||
Log.i(TAG,"\tStart: ${dp.getStartTimeString()}")
|
||||
Log.i(TAG,"\tEnd: ${dp.getEndTimeString()}")
|
||||
for (field in dp.dataType.fields) {
|
||||
Log.i(TAG,"\tField: ${field.name.toString()} Value: ${dp.getValue(field)}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
fun DataPoint.getStartTimeString() = Instant.ofEpochSecond(this.getStartTime(TimeUnit.SECONDS))
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.toLocalDateTime().toString()
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
fun DataPoint.getEndTimeString() = Instant.ofEpochSecond(this.getEndTime(TimeUnit.SECONDS))
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.toLocalDateTime().toString()
|
||||
|
||||
|
||||
/** Unregisters the listener with the Sensors API. */
|
||||
private fun unregisterFitnessDataListener() {
|
||||
if (dataPointListener == null) {
|
||||
// This code only activates one listener at a time. If there's no listener, there's
|
||||
// nothing to unregister.
|
||||
return
|
||||
}
|
||||
// [START unregister_data_listener]
|
||||
// Waiting isn't actually necessary as the unregister call will complete regardless,
|
||||
// even if called from within onStop, but a callback can still be added in order to
|
||||
// inspect the results.
|
||||
Fitness.getSensorsClient(activity, getGoogleAccount())
|
||||
.remove(dataPointListener!!)
|
||||
.addOnCompleteListener { task ->
|
||||
if (task.isSuccessful && task.result!!) {
|
||||
Log.i(TAG, "Listener was removed!")
|
||||
} else {
|
||||
Log.i(TAG, "Listener was not removed.")
|
||||
}
|
||||
}
|
||||
// [END unregister_data_listener]
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
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
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
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
|
||||
}
|
120
app/src/main/java/com/dzeio/openhealth/ui/home/HomeFragment.kt
Normal file
120
app/src/main/java/com/dzeio/openhealth/ui/home/HomeFragment.kt
Normal file
@ -0,0 +1,120 @@
|
||||
package com.dzeio.openhealth.ui.home
|
||||
|
||||
import android.app.Activity.RESULT_OK
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.dzeio.openhealth.connectors.ActionRequestCode
|
||||
import com.dzeio.openhealth.connectors.GoogleFit
|
||||
import com.dzeio.openhealth.databinding.FragmentHomeBinding
|
||||
|
||||
class HomeFragment : Fragment() {
|
||||
|
||||
companion object {
|
||||
const val TAG = "HomeFragment"
|
||||
}
|
||||
|
||||
private var _binding: FragmentHomeBinding? = null
|
||||
|
||||
// This property is only valid between onCreateView and
|
||||
// onDestroyView.
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private lateinit var fit: GoogleFit
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
val homeViewModel =
|
||||
ViewModelProvider(this).get(HomeViewModel::class.java)
|
||||
|
||||
_binding = FragmentHomeBinding.inflate(inflater, container, false)
|
||||
val root: View = binding.root
|
||||
|
||||
binding.button.setOnClickListener {
|
||||
fit = GoogleFit(requireActivity())
|
||||
fit.import()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
fit.getHistory()
|
||||
}
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
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,13 @@
|
||||
package com.dzeio.openhealth.ui.home
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class HomeViewModel : ViewModel() {
|
||||
|
||||
private val _text = MutableLiveData<String>().apply {
|
||||
value = "This is home Fragment"
|
||||
}
|
||||
val text: LiveData<String> = _text
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.dzeio.openhealth.ui.slideshow
|
||||
|
||||
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.FragmentSlideshowBinding
|
||||
|
||||
class SlideshowFragment : Fragment() {
|
||||
|
||||
private var _binding: FragmentSlideshowBinding? = 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 slideshowViewModel =
|
||||
ViewModelProvider(this).get(SlideshowViewModel::class.java)
|
||||
|
||||
_binding = FragmentSlideshowBinding.inflate(inflater, container, false)
|
||||
val root: View = binding.root
|
||||
|
||||
val textView: TextView = binding.textSlideshow
|
||||
slideshowViewModel.text.observe(viewLifecycleOwner) {
|
||||
textView.text = it
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.dzeio.openhealth.ui.slideshow
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class SlideshowViewModel : ViewModel() {
|
||||
|
||||
private val _text = MutableLiveData<String>().apply {
|
||||
value = "This is slideshow Fragment"
|
||||
}
|
||||
val text: LiveData<String> = _text
|
||||
}
|
Reference in New Issue
Block a user