From 1aa29f0e23884f7023e8c7f7feaf1efbbac10226 Mon Sep 17 00:00:00 2001 From: Diassdp <149257568+Diassdp@users.noreply.github.com> Date: Fri, 16 May 2025 20:17:45 +0700 Subject: [PATCH] Version 3.1(After Revision): Added Lihat Laporan Button in dashboard to move into the new Chart Added Indonesia Full Translation Full --- app/build.gradle.kts | 1 + .../healthjournal/ui/custom/MyMarkerView.kt | 62 +++++ .../ui/dashboard/MainActivity.kt | 64 ++--- .../healthjournal/ui/dashboard/MainAdapter.kt | 90 +++---- .../main/java/com/healthjournal/ui/laporan.kt | 239 +++++++++++++----- app/src/main/res/drawable/color_circle.xml | 4 + .../main/res/drawable/marker_background.xml | 18 ++ .../res/layout/activity_detail_journal.xml | 46 ++-- .../res/layout/activity_journal_input.xml | 39 +-- app/src/main/res/layout/activity_laporan.xml | 235 ++++++++++++++++- app/src/main/res/layout/activity_main.xml | 12 +- .../main/res/layout/item_health_history.xml | 30 ++- app/src/main/res/layout/marker_view.xml | 19 ++ app/src/main/res/values-land/dimens.xml | 3 + app/src/main/res/values-v23/themes.xml | 8 + app/src/main/res/values-w1240dp/dimens.xml | 3 + app/src/main/res/values-w600dp/dimens.xml | 3 + app/src/main/res/values/colors.xml | 8 +- app/src/main/res/values/dimens.xml | 3 + app/src/main/res/values/strings.xml | 9 +- app/src/main/res/values/themes.xml | 15 +- settings.gradle.kts | 1 + 22 files changed, 682 insertions(+), 230 deletions(-) create mode 100644 app/src/main/java/com/healthjournal/ui/custom/MyMarkerView.kt create mode 100644 app/src/main/res/drawable/color_circle.xml create mode 100644 app/src/main/res/drawable/marker_background.xml create mode 100644 app/src/main/res/layout/marker_view.xml create mode 100644 app/src/main/res/values-land/dimens.xml create mode 100644 app/src/main/res/values-v23/themes.xml create mode 100644 app/src/main/res/values-w1240dp/dimens.xml create mode 100644 app/src/main/res/values-w600dp/dimens.xml create mode 100644 app/src/main/res/values/dimens.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5fe3eb4..f66126c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -50,6 +50,7 @@ dependencies { implementation("androidx.navigation:navigation-fragment-ktx:2.7.7") implementation("androidx.navigation:navigation-ui-ktx:2.7.7") + implementation ("com.github.PhilJay:MPAndroidChart:v3.1.0") // Firebase libraries implementation(libs.firebase.database) diff --git a/app/src/main/java/com/healthjournal/ui/custom/MyMarkerView.kt b/app/src/main/java/com/healthjournal/ui/custom/MyMarkerView.kt new file mode 100644 index 0000000..82939b7 --- /dev/null +++ b/app/src/main/java/com/healthjournal/ui/custom/MyMarkerView.kt @@ -0,0 +1,62 @@ +import android.content.Context +import android.content.Intent +import android.graphics.Rect +import android.widget.Button +import android.widget.TextView +import android.widget.Toast +import com.github.mikephil.charting.components.MarkerView +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.highlight.Highlight +import com.github.mikephil.charting.utils.MPPointF +import com.healthjournal.R +import com.healthjournal.data.ResultData +import com.healthjournal.ui.journal.detail.DetailJournalActivity + +class MyMarkerView( + private val context: Context, + private val dataList: List +) : MarkerView(context, R.layout.marker_view) { + + private val tvContent: TextView = findViewById(R.id.tvContent) + private var lastHighlightedIndex: Int? = null + + override fun refreshContent(e: Entry?, highlight: Highlight?) { + e?.let { + val index = e.x.toInt() + lastHighlightedIndex = index + if (index in dataList.indices) { + val item = dataList[index] + val content = """ + Hari: ${getIndonesianDayName(item.date)} + Tanggal: ${item.date} + Gula Darah: ${item.bloodSugar} + BMI: ${item.BMI.toString().substring(0, 4)} + Tekanan Darah: ${item.systolicBP}/${item.diastolicBP} mmHg + """.trimIndent() + tvContent.text = content + } else { + tvContent.text = "" + } + } + super.refreshContent(e, highlight) + } + + override fun getOffset(): MPPointF { + return MPPointF(-(width / 2).toFloat(), -height.toFloat()) + } + + private fun getIndonesianDayName(dateString: String): String { + return try { + val sdf = java.text.SimpleDateFormat("dd/MM/yyyy", java.util.Locale("id", "ID")) + val date = sdf.parse(dateString) + if (date != null) { + val dayFormat = java.text.SimpleDateFormat("EEEE", java.util.Locale("id", "ID")) + dayFormat.format(date) + } else { + "-" + } + } catch (e: Exception) { + "-" + } + } +} diff --git a/app/src/main/java/com/healthjournal/ui/dashboard/MainActivity.kt b/app/src/main/java/com/healthjournal/ui/dashboard/MainActivity.kt index 9cf3ea7..ce266f6 100644 --- a/app/src/main/java/com/healthjournal/ui/dashboard/MainActivity.kt +++ b/app/src/main/java/com/healthjournal/ui/dashboard/MainActivity.kt @@ -23,6 +23,7 @@ import com.healthjournal.data.ResultData import com.healthjournal.databinding.ActivityMainBinding import com.healthjournal.receiver.ReminderReceiver import com.healthjournal.ui.journal.input.JournalInputActivity +import com.healthjournal.ui.laporan import com.healthjournal.ui.login.LoginActivity import com.healthjournal.ui.profile.ProfileActivity import com.healthjournal.ui.recommendation.RecommendationActivity @@ -56,12 +57,12 @@ class MainActivity : AppCompatActivity() { private fun scheduleHealthReminders(context: Context) { val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager - val daysOfWeek = listOf(Calendar.MONDAY, Calendar.WEDNESDAY, Calendar.FRIDAY) + for (day in daysOfWeek.shuffled().take(3)) { val calendar = Calendar.getInstance().apply { set(Calendar.DAY_OF_WEEK, day) - set(Calendar.HOUR_OF_DAY, 9) // Set reminder at 9 AM + set(Calendar.HOUR_OF_DAY, 9) set(Calendar.MINUTE, 0) set(Calendar.SECOND, 0) } @@ -74,13 +75,12 @@ class MainActivity : AppCompatActivity() { alarmManager.setRepeating( AlarmManager.RTC_WAKEUP, calendar.timeInMillis, - AlarmManager.INTERVAL_DAY * 7, // Repeat every week + AlarmManager.INTERVAL_DAY * 7, pendingIntent ) } } - private fun navigationBottomBar() { val bottomNav = findViewById(R.id.nav_view) @@ -102,14 +102,13 @@ class MainActivity : AppCompatActivity() { binding.ivAdd.setOnClickListener { if (dailyReport) { - Toast.makeText(this, "Daily Report Already Created", Toast.LENGTH_SHORT).show() + Toast.makeText(this, "Laporan harian sudah dibuat", Toast.LENGTH_SHORT).show() } else { startActivity(Intent(this, JournalInputActivity::class.java)) } } } - private fun getWeekCount() { val today = Calendar.getInstance() val userID = user.currentUser!!.uid @@ -117,11 +116,9 @@ class MainActivity : AppCompatActivity() { val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()) - // Set start of the week (Sunday) today.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY) val startDate = today.time - // Set end of the week (Saturday) val endDate = today.clone() as Calendar endDate.add(Calendar.DAY_OF_WEEK, 6) val endDateDate = endDate.time @@ -138,7 +135,7 @@ class MainActivity : AppCompatActivity() { } entryDate?.let { it in startDate..endDateDate } ?: false } - Log.d("debug", "Entries found: $count for week: ${dateFormat.format(startDate)} - ${dateFormat.format(endDateDate)}") + Log.d("debug", "Jumlah entri: $count minggu ini: ${dateFormat.format(startDate)} - ${dateFormat.format(endDateDate)}") binding.tvDailyCounter.text = "$count/7" } else { Log.e("error", task.exception?.message.toString()) @@ -147,39 +144,43 @@ class MainActivity : AppCompatActivity() { } } - - private fun setupListener(){ + private fun setupListener() { binding.btnRecomendation.setOnClickListener { startActivity(Intent(this, RecommendationActivity::class.java)) } binding.btnInputData.setOnClickListener { startActivity(Intent(this, JournalInputActivity::class.java)) } + binding.btnReport.setOnClickListener { + val intent = Intent(this, laporan::class.java) + intent.putExtra("healthDataList", ArrayList(healthDataList)) + startActivity(intent) + } } private fun dailycheck() { getWeekCount() - val today = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()).format(android.icu.util.Calendar.getInstance().time).toString() + val today = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()).format(Calendar.getInstance().time) val userID = user.currentUser!!.uid database.getReference("users").child(userID).child("journal").get().addOnCompleteListener { task -> - if (task.isSuccessful) { - task.result.children.forEach { - if (it.child("date").value.toString() == today) { - dailyReport = true - switchLayout() - val referencePath = it.key ?: return@forEach - populateTodayReport(referencePath) - } + if (task.isSuccessful) { + task.result.children.forEach { + if (it.child("date").value.toString() == today) { + dailyReport = true + switchLayout() + val referencePath = it.key ?: return@forEach + populateTodayReport(referencePath) } - } else { - Log.d("error", task.exception!!.message.toString()) - Toast.makeText(this, task.exception!!.message, Toast.LENGTH_SHORT).show() } + } else { + Log.d("error", task.exception!!.message.toString()) + Toast.makeText(this, task.exception!!.message, Toast.LENGTH_SHORT).show() + } } } - private fun switchLayout(){ - if (binding.clDailyReport1.visibility == View.VISIBLE){ + private fun switchLayout() { + if (binding.clDailyReport1.visibility == View.VISIBLE) { binding.clDailyReport1.visibility = View.INVISIBLE binding.clDailyReport2.visibility = View.VISIBLE binding.btnRecomendation.visibility = View.VISIBLE @@ -206,17 +207,17 @@ class MainActivity : AppCompatActivity() { userRef.get().addOnCompleteListener { task -> if (task.isSuccessful) { if (task.result.exists() && task.result.childrenCount > 0) { - Toast.makeText(this, "Selamat Kembali!", Toast.LENGTH_SHORT).show() + Toast.makeText(this, "Selamat datang kembali!", Toast.LENGTH_SHORT).show() dailycheck() getWeekCount() populateHistory() } else { - Toast.makeText(this, "Tidak ada user data kesehatan. Tolong isi data kesehatan profile.", Toast.LENGTH_LONG).show() + Toast.makeText(this, "Data kesehatan belum tersedia. Silakan isi profil terlebih dahulu.", Toast.LENGTH_LONG).show() startActivity(Intent(this@MainActivity, UsersInputActivity::class.java)) finish() } } else { - Toast.makeText(this, "Failed to retrieve user data.", Toast.LENGTH_SHORT).show() + Toast.makeText(this, "Gagal mengambil data pengguna.", Toast.LENGTH_SHORT).show() } } } else { @@ -244,7 +245,7 @@ class MainActivity : AppCompatActivity() { val bloodSugar = data.child("bloodSugar").value.toString().toFloatOrNull() ?: 0f val diastolicBP = data.child("bloodPressureDIA").value.toString().toIntOrNull() ?: 0 val systolicBP = data.child("bloodPressureSYS").value.toString().toIntOrNull() ?: 0 - val BMI = data.child("bmi").value.toString().toFloatOrNull() ?: 0f + val BMI = data.child("BMI").value.toString().toFloatOrNull() ?: 0f val date = data.child("date").value.toString() val task = data.child("recommendation").child("tasks").value as? List> ?: emptyList() @@ -265,10 +266,9 @@ class MainActivity : AppCompatActivity() { }) } - private fun populateTodayReport(referencePath: String) { if (!::user.isInitialized || user.currentUser == null) { - Log.e("MainActivity", "User not logged in, cannot fetch today's report") + Log.e("MainActivity", "Pengguna belum login, tidak bisa ambil laporan hari ini.") return } @@ -284,7 +284,7 @@ class MainActivity : AppCompatActivity() { binding.tvBmiLevel.text = String.format(Locale.getDefault(), "%.2f BMI", bmiValue) binding.tvBmiDesc.text = it.result.child("recommendation").child("BMIAnalysis").value.toString() } else { - Log.e("MainActivity", "Error fetching today's report: ${it.exception?.message}") + Log.e("MainActivity", "Gagal mengambil laporan hari ini: ${it.exception?.message}") Toast.makeText(this, it.exception?.message, Toast.LENGTH_SHORT).show() } } diff --git a/app/src/main/java/com/healthjournal/ui/dashboard/MainAdapter.kt b/app/src/main/java/com/healthjournal/ui/dashboard/MainAdapter.kt index 3ab27ea..19cf06c 100644 --- a/app/src/main/java/com/healthjournal/ui/dashboard/MainAdapter.kt +++ b/app/src/main/java/com/healthjournal/ui/dashboard/MainAdapter.kt @@ -1,7 +1,6 @@ package com.healthjournal.ui.dashboard import android.content.Intent -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -11,80 +10,73 @@ import com.healthjournal.R import com.healthjournal.data.ResultData import com.healthjournal.ui.journal.detail.DetailJournalActivity import java.text.SimpleDateFormat -import java.util.Locale +import java.util.* -class MainAdapter(private val healthDataList: MutableList) : RecyclerView.Adapter() { +class MainAdapter(private val daftarDataKesehatan: MutableList) : RecyclerView.Adapter() { - class HealthViewHolder(view: View) : RecyclerView.ViewHolder(view) { - val tvDay: TextView = view.findViewById(R.id.tv_day) - val tvDate: TextView = view.findViewById(R.id.tv_date) - val tvBloodSugar: TextView = view.findViewById(R.id.tv_BS_Level) - val tvBloodPressure: TextView = view.findViewById(R.id.tv_BP_Level) - val tvGoals: TextView = view.findViewById(R.id.tv_goals_count) + class HolderDataKesehatan(view: View) : RecyclerView.ViewHolder(view) { + val tvHari: TextView = view.findViewById(R.id.tv_day) + val tvTanggal: TextView = view.findViewById(R.id.tv_date) + val tvGulaDarah: TextView = view.findViewById(R.id.tv_BS_Level) + val tvTekananDarah: TextView = view.findViewById(R.id.tv_BP_Level) + val tvTugas: TextView = view.findViewById(R.id.tv_goals_count) } - // Function to update the list and notify the adapter - fun setData(newData: List) { - healthDataList.clear() - healthDataList.addAll(newData) + fun perbaruiData(dataBaru: List) { + daftarDataKesehatan.clear() + daftarDataKesehatan.addAll(dataBaru) notifyDataSetChanged() } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HealthViewHolder { - val view = LayoutInflater.from(parent.context) - .inflate(R.layout.item_health_history, parent, false) - return HealthViewHolder(view) - } - - private fun dayOfWeek(date: String): String { + private fun dapatkanHariDariTanggal(tanggal: String): String { return try { - val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()) - val parsedDate = dateFormat.parse(date) - if (parsedDate != null) { - SimpleDateFormat("EEEE", Locale.getDefault()).format(parsedDate) + val formatTanggal = SimpleDateFormat("dd/MM/yyyy", Locale("id", "ID")) + val tanggalTerurai = formatTanggal.parse(tanggal) + if (tanggalTerurai != null) { + SimpleDateFormat("EEEE", Locale("id", "ID")).format(tanggalTerurai) + .replaceFirstChar { it.uppercaseChar() } } else { - "Invalid Date" + "Tanggal Tidak Valid" } } catch (e: Exception) { - "Invalid Date" + "Tanggal Tidak Valid" } } - private fun countGoals(taskList: List>): String { - Log.d("debug", taskList.toString()) - var completedGoals = 0 - val totalGoals = taskList.size - - for (task in taskList) { - val isCompleted = task["completed"] as? Boolean ?: false - if (isCompleted) { - completedGoals++ - } - } + private fun hitungTugasRekomendasi(daftarTugas: List>): String { + val total = daftarTugas.size + val selesai = daftarTugas.count { (it["completed"] as? Boolean) == true } return when { - totalGoals == 0 -> "No goals available" - completedGoals == 0 -> "No goals completed" - else -> "$completedGoals/$totalGoals goals completed" + total == 0 -> "Tidak ada tugas" + selesai == 0 -> "Belum ada tugas selesai" + else -> "$selesai dari $total tugas selesai" } } - override fun onBindViewHolder(holder: HealthViewHolder, position: Int) { - val healthData = healthDataList[position] + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HolderDataKesehatan { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_health_history, parent, false) + return HolderDataKesehatan(view) + } + + override fun onBindViewHolder(holder: HolderDataKesehatan, posisi: Int) { + val data = daftarDataKesehatan[posisi] + + holder.tvHari.text = dapatkanHariDariTanggal(data.date) + holder.tvTanggal.text = data.date + holder.tvGulaDarah.text = "${data.bloodSugar} mg/dL" + holder.tvTekananDarah.text = "${data.systolicBP}/${data.diastolicBP} mmHg" + holder.tvTugas.text = hitungTugasRekomendasi(data.task) - holder.tvDay.text = dayOfWeek(healthData.date) - holder.tvDate.text = healthData.date - holder.tvBloodSugar.text = "${healthData.bloodSugar} mg/dL" - holder.tvBloodPressure.text = "${healthData.diastolicBP}/${healthData.systolicBP} mm Hg" - holder.tvGoals.text = countGoals(healthData.task) holder.itemView.setOnClickListener { val context = holder.itemView.context val intent = Intent(context, DetailJournalActivity::class.java).apply { - putExtra("JOURNAL_KEY", healthData.journalID) + putExtra("JOURNAL_KEY", data.journalID) } context.startActivity(intent) } } - override fun getItemCount(): Int = healthDataList.size + override fun getItemCount(): Int = daftarDataKesehatan.size } diff --git a/app/src/main/java/com/healthjournal/ui/laporan.kt b/app/src/main/java/com/healthjournal/ui/laporan.kt index 4ec7ff6..3605460 100644 --- a/app/src/main/java/com/healthjournal/ui/laporan.kt +++ b/app/src/main/java/com/healthjournal/ui/laporan.kt @@ -1,96 +1,199 @@ package com.healthjournal.ui +import MyMarkerView +import android.content.Intent import android.os.Bundle -import androidx.activity.enableEdgeToEdge +import android.view.MotionEvent +import android.widget.ArrayAdapter import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat +import androidx.core.content.ContextCompat +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.data.* +import com.github.mikephil.charting.formatter.IndexAxisValueFormatter +import com.github.mikephil.charting.highlight.Highlight +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet +import com.github.mikephil.charting.listener.ChartTouchListener +import com.github.mikephil.charting.listener.OnChartGestureListener +import com.github.mikephil.charting.listener.OnChartValueSelectedListener +import com.google.android.material.textfield.MaterialAutoCompleteTextView import com.healthjournal.R +import com.healthjournal.data.ResultData +import com.healthjournal.ui.journal.detail.DetailJournalActivity +import java.text.SimpleDateFormat +import java.util.* class laporan : AppCompatActivity() { - private lateinit var lineChart: LineChart + private lateinit var chartBMI: LineChart + private lateinit var chartSugar: LineChart + private lateinit var chartPressure: LineChart - private val jsonString = """ - { - "journal": { - "-OI9BcS6G4-Ftau4WJe0": { - "BMI": 22.7731876373291, - "bloodPressureDIA": "76", - "bloodPressureSYS": "113", - "bloodSugar": "80", - "date": "03/02/2025" - }, - "-OIJnaHgm2dP_Mbzu2Ta": { - "BMI": 22.7731876373291, - "bloodPressureDIA": "72", - "bloodPressureSYS": "109", - "bloodSugar": "89", - "date": "05/02/2025" - } - } - } - """ + private lateinit var resultList: List + private val sdf = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()) + + private lateinit var sharedMarkerView: MyMarkerView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) + setContentView(R.layout.activity_laporan) - lineChart = findViewById(R.id.lineChart) - renderChart() + supportActionBar?.title = "Laporan Kesehatan" + + chartBMI = findViewById(R.id.chartBMI) + chartSugar = findViewById(R.id.chartSugar) + chartPressure = findViewById(R.id.chartPressure) + + @Suppress("UNCHECKED_CAST") + resultList = intent.getSerializableExtra("healthDataList") as? List ?: emptyList() + renderCharts(resultList) } - private fun renderChart() { + private fun renderCharts(data: List) { val labels = ArrayList() val bmiEntries = ArrayList() val sugarEntries = ArrayList() - val pressureEntries = ArrayList() // We'll use SYS for simplicity + val systolicEntries = ArrayList() + val diastolicEntries = ArrayList() - val sdf = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()) - val chartData = JSONObject(jsonString).getJSONObject("journal") - val keys = chartData.keys().asSequence().sortedBy { - sdf.parse(chartData.getJSONObject(it).getString("date")) - }.toList() + val sorted = data.sortedBy { sdf.parse(it.date) } - keys.forEachIndexed { index, key -> - val entry = chartData.getJSONObject(key) - val date = entry.getString("date") - val bmi = entry.getDouble("BMI").toFloat() - val sugar = entry.getString("bloodSugar").toFloat() - val pressure = entry.getString("bloodPressureSYS").toFloat() - - labels.add(date) - bmiEntries.add(Entry(index.toFloat(), bmi)) - sugarEntries.add(Entry(index.toFloat(), sugar)) - pressureEntries.add(Entry(index.toFloat(), pressure)) + sorted.forEachIndexed { index, item -> + labels.add("[${getIndonesianDayName(item.date)}] \n${item.date}") + bmiEntries.add(Entry(index.toFloat(), item.BMI)) + sugarEntries.add(Entry(index.toFloat(), item.bloodSugar)) + systolicEntries.add(Entry(index.toFloat(), item.systolicBP.toFloat())) + diastolicEntries.add(Entry(index.toFloat(), item.diastolicBP.toFloat())) } - val bmiDataSet = LineDataSet(bmiEntries, "BMI").apply { - color = resources.getColor(android.R.color.holo_green_dark) + sharedMarkerView = MyMarkerView(this, sorted) + + drawChart(chartBMI, listOf(LineDataSet(bmiEntries, "BMI").apply { + val color = ContextCompat.getColor(this@laporan, R.color.chart_bmi) + setColor(color) setCircleColor(color) - } + lineWidth = 2f + valueTextSize = 10f + }), labels) - val sugarDataSet = LineDataSet(sugarEntries, "Blood Sugar").apply { - color = resources.getColor(android.R.color.holo_red_dark) + drawChart(chartSugar, listOf(LineDataSet(sugarEntries, "Gula Darah").apply { + val color = ContextCompat.getColor(this@laporan, R.color.chart_sugar) + setColor(color) setCircleColor(color) - } + lineWidth = 2f + valueTextSize = 10f + }), labels) - val pressureDataSet = LineDataSet(pressureEntries, "Blood Pressure (SYS)").apply { - color = resources.getColor(android.R.color.holo_blue_dark) - setCircleColor(color) - } + drawChart(chartPressure, listOf( + LineDataSet(systolicEntries, "Sistolik").apply { + val color = ContextCompat.getColor(this@laporan, R.color.chart_systolic) + setColor(color) + setCircleColor(color) + lineWidth = 2f + valueTextSize = 10f + }, + LineDataSet(diastolicEntries, "Diastolik").apply { + val color = ContextCompat.getColor(this@laporan, R.color.chart_diastolic) + setColor(color) + setCircleColor(color) + lineWidth = 2f + valueTextSize = 10f + } + ), labels) - val lineData = LineData(bmiDataSet, sugarDataSet, pressureDataSet) - lineChart.data = lineData - - val xAxis = lineChart.xAxis - xAxis.valueFormatter = IndexAxisValueFormatter(labels) - xAxis.position = XAxis.XAxisPosition.BOTTOM - xAxis.granularity = 1f - xAxis.labelRotationAngle = -45f - - lineChart.axisRight.isEnabled = false - lineChart.description.text = "Health Metrics Over Time" - lineChart.invalidate() + setupHighlightListeners() + setupDoubleTapNavigation(chartBMI, sorted) + setupDoubleTapNavigation(chartSugar, sorted) + setupDoubleTapNavigation(chartPressure, sorted) } -} \ No newline at end of file + + private fun drawChart(chart: LineChart, dataSets: List, labels: List) { + chart.data = LineData(dataSets) + + chart.xAxis.apply { + valueFormatter = IndexAxisValueFormatter(labels) + position = XAxis.XAxisPosition.BOTTOM + granularity = 1f + labelRotationAngle = -45f + } + + chart.axisRight.isEnabled = false + chart.description.isEnabled = false + chart.legend.isEnabled = true + + chart.setTouchEnabled(true) + chart.isDragEnabled = true + chart.setScaleEnabled(true) + chart.setPinchZoom(true) + chart.setDoubleTapToZoomEnabled(false) + chart.setVisibleXRangeMaximum(7f) + chart.moveViewToX(chart.data.entryCount.toFloat()) + + chart.marker = sharedMarkerView + chart.invalidate() + } + + private fun setupHighlightListeners() { + val charts = listOf(chartBMI, chartSugar, chartPressure) + + for (chart in charts) { + chart.setOnChartValueSelectedListener(object : OnChartValueSelectedListener { + override fun onValueSelected(e: Entry?, h: Highlight?) { + charts.filter { it != chart }.forEach { + it.highlightValue(null, true) + } + } + + override fun onNothingSelected() {} + }) + } + } + + private fun setupDoubleTapNavigation(chart: LineChart, data: List) { + var lastTapTime = 0L + var lastEntry: Entry? = null + + chart.setOnChartGestureListener(object : OnChartGestureListener { + override fun onChartSingleTapped(me: MotionEvent?) { + me ?: return + val highlight = chart.getHighlightByTouchPoint(me.x, me.y) + val entry = highlight?.let { chart.data.getEntryForHighlight(it) } + + val currentTime = System.currentTimeMillis() + if (entry != null && lastEntry == entry && currentTime - lastTapTime < 400) { + val index = entry.x.toInt() + if (index in data.indices) { + val item = data[index] + val intent = Intent(this@laporan, DetailJournalActivity::class.java).apply { + putExtra("JOURNAL_KEY", item.journalID) + } + startActivity(intent) + } + } + + lastEntry = entry + lastTapTime = currentTime + } + + override fun onChartGestureStart(me: MotionEvent?, lastPerformedGesture: ChartTouchListener.ChartGesture?) {} + override fun onChartGestureEnd(me: MotionEvent?, lastPerformedGesture: ChartTouchListener.ChartGesture?) {} + override fun onChartLongPressed(me: MotionEvent?) {} + override fun onChartDoubleTapped(me: MotionEvent?) {} + override fun onChartFling(me1: MotionEvent?, me2: MotionEvent?, velocityX: Float, velocityY: Float) {} + override fun onChartScale(me: MotionEvent?, scaleX: Float, scaleY: Float) {} + override fun onChartTranslate(me: MotionEvent?, dX: Float, dY: Float) {} + }) + } + + private fun getIndonesianDayName(dateString: String): String { + return try { + val date = sdf.parse(dateString) ?: return "" + val locale = Locale("id", "ID") + val dayFormat = SimpleDateFormat("EEEE", locale) + dayFormat.format(date) + } catch (e: Exception) { + "" + } + } + +} diff --git a/app/src/main/res/drawable/color_circle.xml b/app/src/main/res/drawable/color_circle.xml new file mode 100644 index 0000000..f1685bc --- /dev/null +++ b/app/src/main/res/drawable/color_circle.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/marker_background.xml b/app/src/main/res/drawable/marker_background.xml new file mode 100644 index 0000000..a82259f --- /dev/null +++ b/app/src/main/res/drawable/marker_background.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_detail_journal.xml b/app/src/main/res/layout/activity_detail_journal.xml index 617a743..c406647 100644 --- a/app/src/main/res/layout/activity_detail_journal.xml +++ b/app/src/main/res/layout/activity_detail_journal.xml @@ -5,14 +5,14 @@ android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/Secondary_Light" + android:background="@color/background" tools:context=".ui.journal.detail.DetailJournalActivity"> @@ -22,7 +22,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" - android:text="JOURNAL DETAIL" + android:text="DETAIL JURNAL" android:textAlignment="center" android:textColor="@color/Primary_Dark" android:textSize="20sp" @@ -35,11 +35,11 @@ android:id="@+id/tv_bloodsugar_level1" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="Blood Sugar Level :" + android:layout_marginTop="8dp" + android:text="Tingkat Gula Darah :" android:textColor="@color/Primary_Dark" android:textSize="16sp" android:textStyle="bold" - android:layout_marginTop="10dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.375" app:layout_constraintStart_toStartOf="parent" @@ -60,29 +60,29 @@ app:layout_constraintTop_toBottomOf="@id/tv_title" tools:text="100 mg/dL" /> - + tools:text="olahraga teratur." /> - + tools:text="Tingkat tekanan darah Anda berada dalam kisaran normal. Pertahankan pola makan seimbang dan olahraga teratur." /> - + tools:text="Ini masih dianggap kisaran yang dapat diterima, dan berhubungan dengan kesehatan yang baik." /> - diff --git a/app/src/main/res/layout/activity_journal_input.xml b/app/src/main/res/layout/activity_journal_input.xml index fb989e2..22685f9 100644 --- a/app/src/main/res/layout/activity_journal_input.xml +++ b/app/src/main/res/layout/activity_journal_input.xml @@ -23,7 +23,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" - android:text="Tolong Masukan Laporan Hari Ini" + android:text="Silakan Masukkan Laporan Hari Ini" android:textStyle="bold" android:textColor="@color/Primary_Dark" app:layout_constraintEnd_toEndOf="parent" @@ -48,7 +48,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:fontFamily="@font/poppins_regular" - android:text="Blood Pressure\n(SYS) (mm/HG)" + android:text="Tekanan Darah\n(Sistolik) \n(mm/Hg)" android:textAlignment="center" android:textColor="@color/Primary_Dark" android:textSize="8sp" @@ -71,7 +71,6 @@ app:layout_constraintEnd_toEndOf="@+id/tv_input_blood_pressure_SYS" app:layout_constraintStart_toStartOf="@+id/tv_input_blood_pressure_SYS" app:layout_constraintTop_toBottomOf="@id/tv_input_blood_pressure_SYS" /> - - + + app:layout_constraintEnd_toEndOf="parent" /> + app:layout_constraintEnd_toEndOf="parent" /> + app:layout_constraintEnd_toEndOf="parent" /> + - \ No newline at end of file + diff --git a/app/src/main/res/layout/activity_laporan.xml b/app/src/main/res/layout/activity_laporan.xml index 476b0bf..028400c 100644 --- a/app/src/main/res/layout/activity_laporan.xml +++ b/app/src/main/res/layout/activity_laporan.xml @@ -1,16 +1,233 @@ - + android:background="@color/background" + android:fillViewport="true"> - - + android:layout_height="wrap_content" + android:padding="16dp"> - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index c0c2140..b279831 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -5,6 +5,7 @@ android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="@color/background" tools:context=".ui.dashboard.MainActivity"> + app:layout_constraintTop_toBottomOf="@+id/cl_Health_Report" + app:layout_constraintVertical_bias="0.0" + tools:itemCount="1" + tools:listitem="@layout/item_health_history" /> + app:layout_constraintVertical_bias="0.388" /> + app:layout_constraintVertical_bias="0.0" /> + app:layout_constraintVertical_bias="0.071" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/marker_view.xml b/app/src/main/res/layout/marker_view.xml new file mode 100644 index 0000000..52b9ffa --- /dev/null +++ b/app/src/main/res/layout/marker_view.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/app/src/main/res/values-land/dimens.xml b/app/src/main/res/values-land/dimens.xml new file mode 100644 index 0000000..10125a3 --- /dev/null +++ b/app/src/main/res/values-land/dimens.xml @@ -0,0 +1,3 @@ + + 48dp + \ No newline at end of file diff --git a/app/src/main/res/values-v23/themes.xml b/app/src/main/res/values-v23/themes.xml new file mode 100644 index 0000000..b635679 --- /dev/null +++ b/app/src/main/res/values-v23/themes.xml @@ -0,0 +1,8 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/values-w1240dp/dimens.xml b/app/src/main/res/values-w1240dp/dimens.xml new file mode 100644 index 0000000..ec434d3 --- /dev/null +++ b/app/src/main/res/values-w1240dp/dimens.xml @@ -0,0 +1,3 @@ + + 200dp + \ No newline at end of file diff --git a/app/src/main/res/values-w600dp/dimens.xml b/app/src/main/res/values-w600dp/dimens.xml new file mode 100644 index 0000000..10125a3 --- /dev/null +++ b/app/src/main/res/values-w600dp/dimens.xml @@ -0,0 +1,3 @@ + + 48dp + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 2e86a12..ef4e780 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -2,6 +2,7 @@ #FF000000 #FFFFFFFF + #E8F4F8 #004343 #006C6C @@ -9,8 +10,13 @@ #7ABDBD #E8F4F8 - #D9D9D9 + #EFEDED #FF5722 #FF0000 + + #388E3C + #D32F2F + #1976D2 + #F57C00 \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..b789d50 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,3 @@ + + 16dp + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 077199f..8ffcdbc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -60,5 +60,12 @@ // Activity Recommendation - // Activity S + + + Daily + Weekly + Monthly + + + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 32469a9..6a0b8a5 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -2,15 +2,18 @@ - \ No newline at end of file + diff --git a/settings.gradle.kts b/settings.gradle.kts index 731d472..d29d02c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -16,6 +16,7 @@ dependencyResolutionManagement { repositories { google() mavenCentral() + maven(url = "https://jitpack.io") } }