done
1
TomatoLeafCare/app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
100
TomatoLeafCare/app/build.gradle.kts
Normal file
@ -0,0 +1,100 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.jetbrains.kotlin.android)
|
||||
id("kotlin-parcelize")
|
||||
}
|
||||
|
||||
|
||||
android {
|
||||
namespace = "com.example.tomatoleafcare"
|
||||
compileSdk = 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.example.tomatoleafcare"
|
||||
minSdk = 24
|
||||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
aaptOptions {
|
||||
noCompress += "tflite"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.1"
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
dataBinding = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.ui)
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.material3)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
|
||||
implementation("androidx.cardview:cardview:1.0.0")
|
||||
implementation("androidx.recyclerview:recyclerview:1.2.1")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.7.2")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.7.2")
|
||||
|
||||
implementation("androidx.camera:camera-core:1.3.0")
|
||||
implementation("androidx.camera:camera-camera2:1.3.0")
|
||||
implementation("androidx.camera:camera-lifecycle:1.3.0")
|
||||
implementation("androidx.camera:camera-view:1.3.0")
|
||||
implementation("com.google.android.material:material:1.9.0")
|
||||
|
||||
implementation ("org.tensorflow:tensorflow-lite:2.13.0")
|
||||
implementation ("org.tensorflow:tensorflow-lite-support:0.3.1")
|
||||
implementation ("com.squareup.okhttp3:okhttp:4.12.0")
|
||||
|
||||
implementation ("com.squareup.picasso:picasso:2.8")
|
||||
implementation ("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
|
||||
implementation ("androidx.lifecycle:lifecycle-livedata-ktx:2.6.2")
|
||||
|
||||
}
|
21
TomatoLeafCare/app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
@ -0,0 +1,24 @@
|
||||
package com.example.tomatoleafcare
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.example.tomatoleafcare", appContext.packageName)
|
||||
}
|
||||
}
|
63
TomatoLeafCare/app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.TomatoLeafCare"
|
||||
tools:targetApi="31"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:usesCleartextTraffic="true">
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
<activity
|
||||
android:name=".ui.splash.SplashActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.TomatoLeafCare.Splash">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ui.home.MainActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.TomatoLeafCare.Splash"/>
|
||||
|
||||
<activity android:name=".DiseaseDetailFragment" />
|
||||
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
BIN
TomatoLeafCare/app/src/main/ic_launcher-playstore.png
Normal file
After Width: | Height: | Size: 36 KiB |
@ -0,0 +1,48 @@
|
||||
package com.example.tomatoleafcare.adapter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.example.tomatoleafcare.R
|
||||
import com.example.tomatoleafcare.model.Disease
|
||||
|
||||
class DiseaseAdapter(
|
||||
private val diseases: List<Disease>,
|
||||
private val listener: OnItemClickListener
|
||||
) : RecyclerView.Adapter<DiseaseAdapter.DiseaseViewHolder>() {
|
||||
|
||||
interface OnItemClickListener {
|
||||
fun onItemClick(disease: Disease)
|
||||
}
|
||||
|
||||
inner class DiseaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
private val diseaseName: TextView = itemView.findViewById(R.id.diseaseName)
|
||||
private val diseaseImage: ImageView = itemView.findViewById(R.id.diseaseImage)
|
||||
private val diseaseDescription: TextView = itemView.findViewById((R.id.diseaseDescription))
|
||||
|
||||
fun bind(disease: Disease) {
|
||||
diseaseName.text = disease.name
|
||||
diseaseImage.setImageResource(disease.imageResId)
|
||||
diseaseDescription.text = disease.description
|
||||
|
||||
itemView.setOnClickListener {
|
||||
listener.onItemClick(disease)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DiseaseViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.disease_item, parent, false)
|
||||
return DiseaseViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: DiseaseViewHolder, position: Int) {
|
||||
holder.bind(diseases[position])
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = diseases.size
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package com.example.tomatoleafcare.ui.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.example.tomatoleafcare.R
|
||||
import com.example.tomatoleafcare.model.History
|
||||
import com.squareup.picasso.Picasso
|
||||
|
||||
class HistoryAdapter(
|
||||
private val context: Context,
|
||||
private var historyList: List<History>,
|
||||
private val onItemClick: (History) -> Unit,
|
||||
private val onDeleteClick: (History) -> Unit
|
||||
) : RecyclerView.Adapter<HistoryAdapter.HistoryViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HistoryViewHolder {
|
||||
val view = LayoutInflater.from(context).inflate(R.layout.history_item, parent, false)
|
||||
return HistoryViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: HistoryViewHolder, position: Int) {
|
||||
val history = historyList[position]
|
||||
holder.bind(history)
|
||||
|
||||
holder.itemView.setOnClickListener {
|
||||
onItemClick(history)
|
||||
}
|
||||
|
||||
holder.btnDelete.setOnClickListener {
|
||||
onDeleteClick(history)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = historyList.size
|
||||
|
||||
fun updateData(newList: List<History>) {
|
||||
historyList = newList
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
inner class HistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
private val diseaseName: TextView = itemView.findViewById(R.id.txtDiseaseName)
|
||||
private val date: TextView = itemView.findViewById(R.id.txtDate)
|
||||
private val image: ImageView = itemView.findViewById(R.id.imageHistory)
|
||||
val btnDelete: ImageButton = itemView.findViewById(R.id.btnDelete)
|
||||
|
||||
fun bind(history: History) {
|
||||
diseaseName.text = history.diseaseName
|
||||
date.text = history.date
|
||||
Picasso.get().load(history.imagePath)
|
||||
.placeholder(R.drawable.placeholder)
|
||||
.into(image)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.example.tomatoleafcare.adapter
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.example.tomatoleafcare.ui.home.CardSlide1Fragment
|
||||
import com.example.tomatoleafcare.ui.home.CardSlide2Fragment
|
||||
|
||||
class SliderAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
|
||||
override fun getItemCount(): Int = 2
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return when (position) {
|
||||
0 -> CardSlide1Fragment()
|
||||
1 -> CardSlide2Fragment()
|
||||
else -> CardSlide1Fragment()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package com.example.tomatoleafcare.helper
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import com.example.tomatoleafcare.model.History
|
||||
|
||||
class HistoryDatabaseHelper(context: Context) :
|
||||
SQLiteOpenHelper(context, "history.db", null, 1) {
|
||||
|
||||
override fun onCreate(db: SQLiteDatabase) {
|
||||
val createTable = """
|
||||
CREATE TABLE history (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
diseaseName TEXT,
|
||||
date TEXT,
|
||||
imagePath TEXT
|
||||
)
|
||||
""".trimIndent()
|
||||
db.execSQL(createTable)
|
||||
}
|
||||
|
||||
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
db.execSQL("DROP TABLE IF EXISTS history")
|
||||
onCreate(db)
|
||||
}
|
||||
|
||||
fun insertHistory(item: History): Long {
|
||||
val db = writableDatabase
|
||||
val values = ContentValues().apply {
|
||||
put("diseaseName", item.diseaseName)
|
||||
put("date", item.date)
|
||||
put("imagePath", item.imagePath)
|
||||
}
|
||||
return db.insert("history", null, values)
|
||||
}
|
||||
|
||||
fun getAllHistory(): List<History> {
|
||||
val db = readableDatabase
|
||||
val cursor = db.rawQuery("SELECT * FROM history ORDER BY id DESC", null)
|
||||
val items = mutableListOf<History>()
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
items.add(
|
||||
History(
|
||||
id = cursor.getLong(cursor.getColumnIndexOrThrow("id")),
|
||||
diseaseName = cursor.getString(cursor.getColumnIndexOrThrow("diseaseName")),
|
||||
date = cursor.getString(cursor.getColumnIndexOrThrow("date")),
|
||||
imagePath = cursor.getString(cursor.getColumnIndexOrThrow("imagePath"))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cursor.close()
|
||||
return items
|
||||
}
|
||||
|
||||
fun deleteHistory(id: Long): Boolean {
|
||||
val db = this.writableDatabase
|
||||
val result = db.delete("history", "id=?", arrayOf(id.toString()))
|
||||
db.close()
|
||||
return result > 0
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.example.tomatoleafcare.model
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
|
||||
data class Disease(
|
||||
val name: String,
|
||||
val description: String,
|
||||
val cause: String,
|
||||
val symptoms: String,
|
||||
val impact: String,
|
||||
val solution: String,
|
||||
val imageResId: Int
|
||||
): Parcelable
|
@ -0,0 +1,11 @@
|
||||
package com.example.tomatoleafcare.model
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
@Parcelize
|
||||
data class History(
|
||||
val id: Long = 0,
|
||||
val diseaseName: String,
|
||||
val date: String,
|
||||
val imagePath: String
|
||||
) : Parcelable
|
@ -0,0 +1,99 @@
|
||||
package com.example.tomatoleafcare.repository
|
||||
|
||||
import com.example.tomatoleafcare.R
|
||||
import com.example.tomatoleafcare.model.Disease
|
||||
|
||||
object DiseaseRepository {
|
||||
val classList = listOf(
|
||||
Disease(
|
||||
"Bercak Daun Septoria",
|
||||
"Menyebabkan bercak coklat pada daun, pencegahan dengan fungisida dan sirkulasi udara yang baik.",
|
||||
"Jamur Septoria lycopersici, jamur ini sering ditemukan di tanah yang basah dan menyebar melalui percikan air.",
|
||||
"Bercak-bercak kecil berwarna coklat tua atau hitam pada daun yang dimulai dari bagian bawah dan dapat membesar.",
|
||||
"Daun menguning dan rontok., mengurangi produktivitas buah, dan membuat tanaman rentan terhadap penyakit lain.",
|
||||
"Penggunaan fungisida, memastikan sirkulasi udara yang baik, dan menghindari penyiraman berlebihan.",
|
||||
R.drawable.bercakdaunseptoria
|
||||
),
|
||||
Disease(
|
||||
"Virus Mozaik",
|
||||
"Menyebabkan daun terdistorsi dan mosaik, cegah dengan benih sehat dan menghapus tanaman terinfeksi.",
|
||||
"Tomato Mosaic Virus (ToMV) yang menyebar melalui kontak tanaman terinfeksi, alat pertanian, dan serangga.",
|
||||
"Pola mosaik bercak hijau muda dan gelap pada daun, daun menjadi keriput atau terdistorsi",
|
||||
"Menghambat fotosintesis, menyebabkan pertumbuhan yang tidak optimal, serta menurunkan kualitas dan kuantitas buah.",
|
||||
"Menggunakan benih sehat, menjaga kebersihan alat, dan menghilangkan tanaman yang terinfeksi.",
|
||||
R.drawable.virusmozaik
|
||||
),
|
||||
Disease(
|
||||
"Jamur Daun",
|
||||
"Menyebabkan bercak kuning dan lapisan beludru di bawah daun, cegah dengan kelembapan rendah dan fungisida.",
|
||||
"Jamur Cladosporium fulvum yang berkembang di lingkungan lembap dan sirkulasi udara buruk.",
|
||||
"Bercak kuning pada bagian atas daun dan lapisan beludru hijau atau coklat pada bagian bawah daun",
|
||||
"Mengurangi kemampuan fotosintesis, dapat menyebabkan daun mengering dan rontok.",
|
||||
"Menjaga kelembapan rendah, meningkatkan sirkulasi udara, menghindari penyiraman langsung pada daun, dan menggunakan fungisida.",
|
||||
R.drawable.jamurdaun
|
||||
),
|
||||
Disease(
|
||||
"Virus Keriting Daun",
|
||||
"Menyebabkan daun keriting dan menguning, cegah dengan insektisida dan penghapusan tanaman terinfeksi.",
|
||||
"Virus yang ditularkan oleh kutu kebul (Bemisia tabaci).",
|
||||
"Daun menjadi keriting, tebal, dan berubah warna menjadi kuning.",
|
||||
"Menghambat pertumbuhan tanaman, menurunkan kualitas dan kuantitas buah.",
|
||||
"Menggunakan insektisida, dan segera hapus tanaman yang terinfeksi.",
|
||||
R.drawable.viruskeriting
|
||||
),
|
||||
Disease(
|
||||
"Hawar Daun",
|
||||
"Menyebabkan bercak hitam, pembusukan buah, cegah dengan fungisida dan rotasi tanaman.",
|
||||
"Jamur Phytophthora infestans, yang menyebar cepat dalam kondisi lembap dan dingin.",
|
||||
"Bercak coklat gelap atau hitam menyebar cepat pada daun, dengan lapisan putih pada bagian bawah daun.",
|
||||
"Bisa dengan cepat menghancurkan seluruh tanaman jika tidak segera diatasi, buah juga bisa membusuk.",
|
||||
"Gunakan fungisida terutama saat musim hujan, dan lakukan rotasi tanaman untuk mencegah infeksi ulang",
|
||||
R.drawable.lateblight
|
||||
),
|
||||
Disease(
|
||||
"Bercak Bakteri",
|
||||
"Menyebabkan bercak hitam pada daun dan buah, cegah dengan benih sehat dan sanitasi.",
|
||||
"Bakteri Xanthomonas campestris pv. vesicatoria, yang menyebar melalui air, angin, dan alat pertanian.",
|
||||
"Bercak hitam kecil muncul pada daun, batang, dan buah.",
|
||||
"Merusak jaringan daun, Menghambat fotositesis dan Menurunkan kualitas buah,",
|
||||
"Gunakan benih sehat, jaga sanitasi, dan gunakan bakterisida.",
|
||||
R.drawable.bercakbakteri
|
||||
),
|
||||
Disease(
|
||||
"Tanaman Sehat",
|
||||
"Tidak ditemukan penyakit pada daun ini.",
|
||||
"tidak ada",
|
||||
"tidak ada",
|
||||
"tidak ada",
|
||||
"tidak ada",
|
||||
R.drawable.sehat
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
fun getData(): List<Disease> {
|
||||
return classList
|
||||
}
|
||||
|
||||
fun findDiseaseByName(name: String): Disease? {
|
||||
return classList.find { it.name.equals(name, ignoreCase = true) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object DiseaseMatcher {
|
||||
fun matchDisease(className: String): Disease? {
|
||||
return when (className) {
|
||||
"bacterial_spot" -> DiseaseRepository.classList.find { it.name.contains("Bercak Bakteri", ignoreCase = true) }
|
||||
"healthy" -> DiseaseRepository.classList.find { it.name.contains("Tanaman Sehat", ignoreCase = true) }
|
||||
"late_blight" -> DiseaseRepository.classList.find { it.name.contains("Hawar Daun", ignoreCase = true) }
|
||||
"leaf_curl_virus" -> DiseaseRepository.classList.find { it.name.contains("Virus Keriting Daun", ignoreCase = true) }
|
||||
"leaf_mold" -> DiseaseRepository.classList.find { it.name.contains("Jamur Daun", ignoreCase = true) }
|
||||
"mosaic_virus" -> DiseaseRepository.classList.find { it.name.contains("Virus Mozaik", ignoreCase = true) }
|
||||
"septoria_leaf_spot" -> DiseaseRepository.classList.find { it.name.contains("Bercak Daun Septoria", ignoreCase = true) }
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,431 @@
|
||||
package com.example.tomatoleafcare.ui.camera
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Debug
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.example.tomatoleafcare.DiseaseDetailFragment
|
||||
import com.example.tomatoleafcare.R
|
||||
import com.example.tomatoleafcare.repository.DiseaseMatcher
|
||||
import com.example.tomatoleafcare.model.History
|
||||
import com.example.tomatoleafcare.databinding.CameraFragmentBinding
|
||||
import com.example.tomatoleafcare.helper.HistoryDatabaseHelper
|
||||
import okhttp3.*
|
||||
import org.json.JSONObject
|
||||
import java.io.*
|
||||
import java.nio.channels.FileChannel
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.core.content.ContextCompat
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import org.tensorflow.lite.Interpreter
|
||||
import java.nio.MappedByteBuffer
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
class CameraFragment : Fragment() {
|
||||
|
||||
private var _binding: CameraFragmentBinding? = null
|
||||
|
||||
private val binding get() = _binding ?: throw IllegalStateException("Binding belum diinisialisasi")
|
||||
|
||||
private lateinit var imageUri: Uri
|
||||
private lateinit var photoFile: File
|
||||
|
||||
private val classLabels = arrayOf(
|
||||
"bacterial_spot",
|
||||
"healthy",
|
||||
"late_blight",
|
||||
"leaf_curl_virus",
|
||||
"leaf_mold",
|
||||
"mosaic_virus",
|
||||
"septoria_leaf_spot"
|
||||
)
|
||||
|
||||
private val labelKelas = arrayOf(
|
||||
"Bercak Bakteri",
|
||||
"Daun Sehat",
|
||||
"Hawar Daun",
|
||||
"Virus Keriting",
|
||||
"Jamur Daun",
|
||||
"Virus Mozaik",
|
||||
"Bercak Daun Septoria"
|
||||
)
|
||||
|
||||
private var apiAvailable = false
|
||||
private val credentials = "rahasia:tomat"
|
||||
private val basicAuth = "Basic " + Base64.encodeToString(credentials.toByteArray(), Base64.NO_WRAP)
|
||||
|
||||
private val galleryLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
|
||||
uri?.let {
|
||||
try {
|
||||
val inputStream = requireContext().contentResolver.openInputStream(it)
|
||||
val tempFile = File.createTempFile("gallery_", ".jpg", requireContext().cacheDir)
|
||||
val outputStream = FileOutputStream(tempFile)
|
||||
|
||||
inputStream?.copyTo(outputStream)
|
||||
inputStream?.close()
|
||||
outputStream.close()
|
||||
|
||||
imageUri = Uri.fromFile(tempFile)
|
||||
binding.imageView.setImageURI(imageUri)
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Toast.makeText(requireContext(), "Gagal memuat gambar dari galeri", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val cameraLauncher = registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
|
||||
if (success) {
|
||||
binding.imageView.setImageURI(imageUri)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
_binding = CameraFragmentBinding.inflate(inflater, container, false)
|
||||
|
||||
binding.btnKamera.setOnClickListener {
|
||||
requestCameraPermissionLauncher.launch(android.Manifest.permission.CAMERA)
|
||||
}
|
||||
|
||||
binding.btnGaleri.setOnClickListener {
|
||||
openGallery()
|
||||
}
|
||||
|
||||
binding.btnPindai.setOnClickListener {
|
||||
if (::imageUri.isInitialized) {
|
||||
if (apiAvailable) {
|
||||
sendImageToApi(imageUri)
|
||||
} else {
|
||||
runLocalModel(imageUri, requireContext())
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(requireContext(), "Silakan pilih gambar terlebih dahulu", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
handler.post(updateNetworkStatusRunnable)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
private fun openGallery() {
|
||||
galleryLauncher.launch("image/*")
|
||||
}
|
||||
|
||||
private fun openCamera() {
|
||||
try {
|
||||
photoFile = File.createTempFile("photo_", ".jpg", requireContext().cacheDir)
|
||||
imageUri = FileProvider.getUriForFile(requireContext(), "${requireContext().packageName}.fileprovider", photoFile)
|
||||
cameraLauncher.launch(imageUri)
|
||||
} catch (e: IOException) {
|
||||
Toast.makeText(requireContext(), "Gagal membuat file foto", Toast.LENGTH_SHORT).show()
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val requestCameraPermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
) { isGranted ->
|
||||
if (isGranted) {
|
||||
openCamera()
|
||||
} else {
|
||||
Toast.makeText(requireContext(), "Izin kamera diperlukan untuk fitur ini", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun runLocalModel(uri: Uri, context: Context) {
|
||||
// val startTime = System.currentTimeMillis()
|
||||
// val beforeMemory = Debug.getNativeHeapAllocatedSize() / 1024.0
|
||||
|
||||
val model = Interpreter(loadModelFile("ResNet-50_tomato-leaf-disease.tflite", context))
|
||||
val inputStream = context.contentResolver.openInputStream(uri)
|
||||
val bitmap = BitmapFactory.decodeStream(inputStream)
|
||||
val resizedBitmap = Bitmap.createScaledBitmap(bitmap, 224, 224, true)
|
||||
|
||||
val input = Array(1) { Array(224) { Array(224) { FloatArray(3) } } }
|
||||
for (y in 0 until 224) {
|
||||
for (x in 0 until 224) {
|
||||
val pixel = resizedBitmap.getPixel(x, y)
|
||||
input[0][y][x][0] = Color.red(pixel).toFloat()
|
||||
input[0][y][x][1] = Color.green(pixel).toFloat()
|
||||
input[0][y][x][2] = Color.blue(pixel).toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
val output = Array(1) { FloatArray(classLabels.size) }
|
||||
model.run(input, output)
|
||||
|
||||
val probabilities = output[0]
|
||||
|
||||
// Softmax normalization (optional, if model doesn't do it)
|
||||
val expValues = probabilities.map { Math.exp(it.toDouble()) }
|
||||
val sumExp = expValues.sum()
|
||||
val softmax = expValues.map { (it / sumExp).toFloat() }
|
||||
|
||||
val predictedIndex = probabilities.indices.maxByOrNull { probabilities[it] } ?: -1
|
||||
val prediction = classLabels[predictedIndex]
|
||||
|
||||
val resultBuilder = StringBuilder()
|
||||
resultBuilder.append("Confidence:\n")
|
||||
for (i in labelKelas.indices) {
|
||||
val label = labelKelas[i]
|
||||
val confidence = softmax[i] * 100
|
||||
resultBuilder.append("$label: ${"%.2f".format(confidence)}%\n")
|
||||
}
|
||||
|
||||
// val endTime = System.currentTimeMillis()
|
||||
// val afterMemory = Debug.getNativeHeapAllocatedSize() / 1024.0
|
||||
// val duration = endTime - startTime
|
||||
// val memoryUsed = (afterMemory - beforeMemory).roundToInt()
|
||||
// Log.d("MODEL_RESULT", resultBuilder.toString())
|
||||
val confidenceText = resultBuilder.toString()
|
||||
|
||||
//Toast.makeText(context, "Local: $prediction\nTime: $duration ms\nMemory: $memoryUsed KB", Toast.LENGTH_LONG).show()
|
||||
// Toast.makeText(context, "Local: $prediction", Toast.LENGTH_LONG).show()
|
||||
// Log.d("MODEL_METRICS", "Local - Time: $duration ms, Memory: $memoryUsed KB")
|
||||
|
||||
navigateToNoteFragment(prediction, uri, confidenceText)
|
||||
}
|
||||
|
||||
private fun loadModelFile(modelName: String, context: Context): MappedByteBuffer {
|
||||
val fileDescriptor = context.assets.openFd(modelName)
|
||||
val inputStream = FileInputStream(fileDescriptor.fileDescriptor)
|
||||
val fileChannel = inputStream.channel
|
||||
val startOffset = fileDescriptor.startOffset
|
||||
val declaredLength = fileDescriptor.declaredLength
|
||||
return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)
|
||||
}
|
||||
|
||||
private fun sendImageToApi(uri: Uri) {
|
||||
// val startTime = System.currentTimeMillis()
|
||||
// val beforeMemory = Debug.getNativeHeapAllocatedSize() / 1024.0
|
||||
|
||||
val contentResolver = requireContext().contentResolver
|
||||
val inputStream = contentResolver.openInputStream(uri) ?: return
|
||||
val imageBytes = inputStream.readBytes()
|
||||
val requestBody = imageBytes.toRequestBody("image/*".toMediaTypeOrNull(), 0)
|
||||
val body = MultipartBody.Part.createFormData("image", "image.jpg", requestBody)
|
||||
|
||||
val requestBodyMultipart = MultipartBody.Builder()
|
||||
.setType(MultipartBody.FORM)
|
||||
.addPart(body)
|
||||
.build()
|
||||
|
||||
val client = OkHttpClient()
|
||||
val request = Request.Builder()
|
||||
.url("http://robotika.upnvj.ac.id:8000/tomatoleafcare/classify")
|
||||
// .url("http://api.simdoks.web.id:5000/tomatoleafcare/classify")
|
||||
.post(requestBodyMultipart)
|
||||
.addHeader("Authorization", basicAuth)
|
||||
.build()
|
||||
|
||||
client.newCall(request).enqueue(object : Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
requireActivity().runOnUiThread {
|
||||
Toast.makeText(requireContext(), "Gagal terhubung ke server", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
val responseBody = response.body?.string()
|
||||
if (!response.isSuccessful || responseBody == null) {
|
||||
requireActivity().runOnUiThread {
|
||||
Toast.makeText(requireContext(), "Respons tidak valid dari server", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
return
|
||||
}
|
||||
try {
|
||||
val jsonObject = JSONObject(responseBody)
|
||||
|
||||
val predictedClassObj = jsonObject.getJSONObject("predictedClass")
|
||||
val prediction = predictedClassObj.getString("class")
|
||||
|
||||
val confidencesArray = predictedClassObj.getJSONArray("confidences")
|
||||
val confidenceTextBuilder = StringBuilder()
|
||||
confidenceTextBuilder.append("\nConfidence:\n")
|
||||
|
||||
for (i in 0 until confidencesArray.length()) {
|
||||
val item = confidencesArray.getJSONObject(i)
|
||||
val className = item.getString("class")
|
||||
val confidence = item.getDouble("confidence")
|
||||
confidenceTextBuilder.append("$className: ${"%.2f".format(confidence)}%\n")
|
||||
}
|
||||
|
||||
val confidenceText = confidenceTextBuilder.toString()
|
||||
|
||||
// val endTime = System.currentTimeMillis()
|
||||
// val duration = endTime - startTime
|
||||
// val afterMemory = Debug.getNativeHeapAllocatedSize() / 1024.0
|
||||
// val memoryUsed = (afterMemory - beforeMemory).roundToInt()
|
||||
|
||||
requireActivity().runOnUiThread {
|
||||
// Toast.makeText(requireContext(), "Online: $prediction\nTime: $duration ms\nMemory: $memoryUsed KB", Toast.LENGTH_LONG).show()
|
||||
// Toast.makeText(requireContext(), "Online: $prediction", Toast.LENGTH_LONG).show()
|
||||
// Log.d("MODEL_METRICS", "Online - Time: $duration ms, Memory: $memoryUsed KB")
|
||||
navigateToNoteFragment(prediction, uri, confidenceText)
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e("PARSE_ERROR", e.message ?: "Unknown error")
|
||||
Log.e("RAW_JSON", responseBody ?: "No body")
|
||||
e.printStackTrace()
|
||||
requireActivity().runOnUiThread {
|
||||
Toast.makeText(requireContext(), "Gagal parsing data", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private val updateInterval: Long = 1000
|
||||
|
||||
private val updateNetworkStatusRunnable = object : Runnable {
|
||||
override fun run() {
|
||||
checkApiAvailability()
|
||||
handler.postDelayed(this, updateInterval)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkApiAvailability() {
|
||||
val client = OkHttpClient()
|
||||
val request = Request.Builder()
|
||||
.url("http://robotika.upnvj.ac.id:8000/tomatoleafcare/check")
|
||||
// .url("http://api.simdoks.web.id:5000/tomatoleafcare/check")
|
||||
.get()
|
||||
.addHeader("Authorization", basicAuth)
|
||||
.build()
|
||||
|
||||
client.newCall(request).enqueue(object : Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
apiAvailable = false
|
||||
updateApiStatus()
|
||||
Log.e("API_RESPONSE", "Request failed: ${e.message}")
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
apiAvailable = response.isSuccessful
|
||||
updateApiStatus()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun updateApiStatus() {
|
||||
if (_binding == null || !isAdded) return
|
||||
|
||||
requireActivity().runOnUiThread {
|
||||
_binding?.let { binding ->
|
||||
if (apiAvailable) {
|
||||
binding.textStatus.text = getString(R.string.online)
|
||||
binding.textStatus.setTextColor(ContextCompat.getColor(requireContext(), R.color.green))
|
||||
binding.ledStatus.setBackgroundResource(R.drawable.bg_led_online)
|
||||
} else {
|
||||
binding.textStatus.text = getString(R.string.offline)
|
||||
binding.textStatus.setTextColor(ContextCompat.getColor(requireContext(), R.color.blue))
|
||||
binding.ledStatus.setBackgroundResource(R.drawable.bg_led_offline)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToNoteFragment(
|
||||
className: String,
|
||||
imageUri: Uri,
|
||||
confidenceText: String
|
||||
) {
|
||||
confidenceText.lines().forEachIndexed { index, line ->
|
||||
Log.d("CONFIDENCE_LINE", "[$index] $line")
|
||||
}
|
||||
|
||||
val maxConfidence = confidenceText.lines()
|
||||
.mapNotNull { line ->
|
||||
Regex("[a-zA-Z_]+:\\s*([\\d.,]+)%").find(line)
|
||||
?.groupValues?.get(1)
|
||||
?.replace(",", ".")
|
||||
?.toFloatOrNull()
|
||||
}
|
||||
.maxOrNull() ?: 0f
|
||||
|
||||
|
||||
val isConfident = maxConfidence >= 70f
|
||||
Log.d("ParsedConfidence", "confidence: $confidenceText")
|
||||
Log.d("ParsedConfidence", "Max confidence: $maxConfidence")
|
||||
|
||||
|
||||
// matchedDisease hanya dipakai jika confidence cukup
|
||||
val matchedDisease = if (isConfident) DiseaseMatcher.matchDisease(className) else null
|
||||
|
||||
// Simpan ke database hanya jika confidence >= 70%
|
||||
if (isConfident) {
|
||||
val dbHelper = HistoryDatabaseHelper(requireContext())
|
||||
val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault())
|
||||
val now = formatter.format(Date())
|
||||
|
||||
val historyItem = History(
|
||||
diseaseName = matchedDisease?.name ?: "Unknown",
|
||||
date = now,
|
||||
imagePath = imageUri.toString()
|
||||
)
|
||||
dbHelper.insertHistory(historyItem)
|
||||
}
|
||||
|
||||
// Siapkan Bundle untuk dikirim ke DiseaseDetailFragment
|
||||
val bundle = Bundle().apply {
|
||||
if (isConfident) {
|
||||
putString("disease_name", matchedDisease?.name)
|
||||
putString("disease_description", matchedDisease?.description)
|
||||
putString("disease_symptoms", matchedDisease?.symptoms)
|
||||
putString("disease_causes", matchedDisease?.cause)
|
||||
putString("disease_impact", matchedDisease?.impact)
|
||||
putString("disease_solution", matchedDisease?.solution)
|
||||
} else {
|
||||
putString("disease_name", "Penyakit daun tidak terdeteksi")
|
||||
putString("disease_description", "-")
|
||||
putString("disease_symptoms", "-")
|
||||
putString("disease_causes", "-")
|
||||
putString("disease_impact", "-")
|
||||
putString("disease_solution", "-")
|
||||
}
|
||||
|
||||
putString("image_uri", imageUri.toString())
|
||||
putString("confidence_text", confidenceText)
|
||||
}
|
||||
|
||||
// Navigasi ke DiseaseDetailFragment
|
||||
val noteFragment = DiseaseDetailFragment().apply {
|
||||
arguments = bundle
|
||||
}
|
||||
|
||||
requireActivity().supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_container, noteFragment)
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
}
|
||||
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
handler.removeCallbacks(updateNetworkStatusRunnable)
|
||||
_binding = null
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package com.example.tomatoleafcare.ui.history
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.example.tomatoleafcare.R
|
||||
import com.example.tomatoleafcare.repository.DiseaseRepository
|
||||
import com.example.tomatoleafcare.model.History
|
||||
import com.example.tomatoleafcare.ui.home.MainActivity
|
||||
import com.squareup.picasso.Picasso
|
||||
|
||||
class HistoryDetailFragment : Fragment() {
|
||||
|
||||
private var history: History? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(R.layout.history_detail_fragment, container, false)
|
||||
|
||||
(activity as? MainActivity)?.showBottomNav(false)
|
||||
|
||||
val btnBack = view.findViewById<ImageView>(R.id.btnBack)
|
||||
btnBack.setOnClickListener {
|
||||
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
history = arguments?.getParcelable<History>("history") as? History
|
||||
|
||||
|
||||
history?.let {
|
||||
showDetails(view, it)
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
private fun showDetails(view: View, history: History) {
|
||||
val detailDiseaseName = view.findViewById<TextView>(R.id.detailDiseaseName)
|
||||
val detailDiseaseCauses = view.findViewById<TextView>(R.id.detailDiseaseCauses)
|
||||
val detailDiseaseSymptoms = view.findViewById<TextView>(R.id.detailDiseaseSymptoms)
|
||||
val detailDiseaseImpact = view.findViewById<TextView>(R.id.detailDiseaseImpact)
|
||||
val detailDiseaseSolution = view.findViewById<TextView>(R.id.detailDiseaseSolution)
|
||||
val detailDiseaseImage = view.findViewById<ImageView>(R.id.detailDiseaseImage)
|
||||
|
||||
Picasso.get().load(history.imagePath).into(detailDiseaseImage)
|
||||
detailDiseaseName.text = history.diseaseName
|
||||
|
||||
val matchedDisease = DiseaseRepository.findDiseaseByName(history.diseaseName)
|
||||
|
||||
matchedDisease?.let {
|
||||
detailDiseaseCauses.text = it.cause
|
||||
detailDiseaseSymptoms.text = it.symptoms
|
||||
detailDiseaseImpact.text = it.impact
|
||||
detailDiseaseSolution.text = it.solution
|
||||
} ?: run {
|
||||
detailDiseaseCauses.text = "-"
|
||||
detailDiseaseSymptoms.text = "-"
|
||||
detailDiseaseImpact.text = "-"
|
||||
detailDiseaseSolution.text = "-"
|
||||
}
|
||||
}
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
(activity as? MainActivity)?.showBottomNav(true)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.example.tomatoleafcare.ui.history
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.example.tomatoleafcare.R
|
||||
import com.example.tomatoleafcare.ui.adapter.HistoryAdapter
|
||||
import com.example.tomatoleafcare.viewmodel.HistoryViewModel
|
||||
|
||||
class HistoryFragment : Fragment() {
|
||||
|
||||
private lateinit var recyclerView: RecyclerView
|
||||
private lateinit var historyAdapter: HistoryAdapter
|
||||
private val viewModel: HistoryViewModel by viewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(R.layout.history_fragment, container, false)
|
||||
|
||||
recyclerView = view.findViewById(R.id.recyclerViewHistory)
|
||||
recyclerView.layoutManager = LinearLayoutManager(requireContext())
|
||||
|
||||
historyAdapter = HistoryAdapter(
|
||||
context = requireContext(),
|
||||
historyList = listOf(),
|
||||
onItemClick = { history ->
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable("history", history)
|
||||
|
||||
val detailFragment = HistoryDetailFragment().apply {
|
||||
arguments = bundle
|
||||
}
|
||||
|
||||
requireActivity().supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_container, detailFragment)
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
},
|
||||
onDeleteClick = { item ->
|
||||
viewModel.deleteHistoryById(item.id.toInt())
|
||||
}
|
||||
)
|
||||
|
||||
recyclerView.adapter = historyAdapter
|
||||
|
||||
observeViewModel()
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
private fun observeViewModel() {
|
||||
viewModel.historyList.observe(viewLifecycleOwner, Observer { list ->
|
||||
historyAdapter.updateData(list)
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.example.tomatoleafcare.ui.home
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.example.tomatoleafcare.R
|
||||
|
||||
class CardSlide1Fragment : Fragment() {
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.card_slide_1, container, false)
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.example.tomatoleafcare.ui.home
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.example.tomatoleafcare.R
|
||||
|
||||
class CardSlide2Fragment : Fragment() {
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.card_slide_2, container, false)
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
package com.example.tomatoleafcare.ui.home
|
||||
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.example.tomatoleafcare.DiseaseDetailFragment
|
||||
import com.example.tomatoleafcare.R
|
||||
import com.example.tomatoleafcare.adapter.DiseaseAdapter
|
||||
import com.example.tomatoleafcare.adapter.SliderAdapter
|
||||
import com.example.tomatoleafcare.model.Disease
|
||||
import com.example.tomatoleafcare.repository.DiseaseRepository
|
||||
import com.example.tomatoleafcare.ui.note.NoteFragment
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
|
||||
class HomeFragment : Fragment() {
|
||||
|
||||
private lateinit var recyclerView: RecyclerView
|
||||
private lateinit var adapter: DiseaseAdapter
|
||||
private lateinit var viewPager: ViewPager2
|
||||
private val sliderHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
private val sliderRunnable = object : Runnable {
|
||||
override fun run() {
|
||||
val itemCount = viewPager.adapter?.itemCount ?: 0
|
||||
if (itemCount > 0) {
|
||||
val nextItem = (viewPager.currentItem + 1) % itemCount
|
||||
viewPager.setCurrentItem(nextItem, true)
|
||||
sliderHandler.postDelayed(this, 10000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(R.layout.home_fragment, container, false)
|
||||
|
||||
val classList = DiseaseRepository.classList.filter { it.name != "Tanaman Sehat" }
|
||||
val topThreeDiseases = classList.take(3)
|
||||
|
||||
viewPager = view.findViewById(R.id.viewPager)
|
||||
viewPager.adapter = SliderAdapter(this)
|
||||
|
||||
val space = resources.getDimensionPixelSize(R.dimen.slider_gap)
|
||||
viewPager.addItemDecoration(SliderItemDecoration(space))
|
||||
|
||||
recyclerView = view.findViewById(R.id.recyclerView)
|
||||
recyclerView.layoutManager = LinearLayoutManager(requireContext())
|
||||
|
||||
val seeMoreText: TextView = view.findViewById(R.id.seeMoreText)
|
||||
seeMoreText.paintFlags = seeMoreText.paintFlags or Paint.UNDERLINE_TEXT_FLAG
|
||||
|
||||
seeMoreText.setOnClickListener {
|
||||
val noteFragment = NoteFragment()
|
||||
requireActivity().supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_container, noteFragment)
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
|
||||
val bottomNavigationView = requireActivity().findViewById<BottomNavigationView>(R.id.bottomNavigationView)
|
||||
bottomNavigationView.selectedItemId = R.id.navigation_note
|
||||
}
|
||||
|
||||
adapter = DiseaseAdapter(topThreeDiseases, object : DiseaseAdapter.OnItemClickListener {
|
||||
override fun onItemClick(disease: Disease) {
|
||||
val detailFragment = DiseaseDetailFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString("disease_name", disease.name)
|
||||
putString("disease_description", disease.description)
|
||||
putString("disease_symptoms", disease.symptoms)
|
||||
putString("disease_causes", disease.cause)
|
||||
putString("disease_impact", disease.impact)
|
||||
putString("disease_solution", disease.solution)
|
||||
putInt("disease_image", disease.imageResId)
|
||||
}
|
||||
}
|
||||
|
||||
requireActivity().supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_container, detailFragment)
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
}
|
||||
})
|
||||
|
||||
recyclerView.adapter = adapter
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
sliderHandler.postDelayed(sliderRunnable, 1000)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
sliderHandler.removeCallbacks(sliderRunnable)
|
||||
}
|
||||
|
||||
class SliderItemDecoration(private val space: Int) : RecyclerView.ItemDecoration() {
|
||||
override fun getItemOffsets(
|
||||
outRect: Rect,
|
||||
view: View,
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
) {
|
||||
super.getItemOffsets(outRect, view, parent, state)
|
||||
outRect.left = space
|
||||
outRect.right = space
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.example.tomatoleafcare.ui.home
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.example.tomatoleafcare.R
|
||||
import com.example.tomatoleafcare.databinding.ActivityMainBinding
|
||||
import com.example.tomatoleafcare.ui.note.NoteFragment
|
||||
import com.example.tomatoleafcare.ui.camera.CameraFragment
|
||||
import com.example.tomatoleafcare.ui.history.HistoryFragment
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
replaceFragment(HomeFragment())
|
||||
}
|
||||
|
||||
binding.bottomNavigationView.setOnItemSelectedListener { item ->
|
||||
when (item.itemId) {
|
||||
R.id.navigation_home -> replaceFragment(HomeFragment())
|
||||
R.id.navigation_camera -> replaceFragment(CameraFragment())
|
||||
R.id.navigation_note -> replaceFragment(NoteFragment())
|
||||
R.id.navigation_history -> replaceFragment(HistoryFragment())
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fun showBottomNav(show: Boolean) {
|
||||
val bottomNav = findViewById<View>(R.id.bottomNavigationView)
|
||||
bottomNav.visibility = if (show) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
|
||||
private fun replaceFragment(fragment: Fragment) {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_container, fragment)
|
||||
.commit()
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
package com.example.tomatoleafcare
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import com.example.tomatoleafcare.ui.home.MainActivity
|
||||
import com.example.tomatoleafcare.viewmodel.DiseaseViewModel
|
||||
import com.google.android.material.imageview.ShapeableImageView
|
||||
|
||||
class DiseaseDetailFragment : Fragment() {
|
||||
|
||||
private val diseaseViewModel: DiseaseViewModel by activityViewModels()
|
||||
|
||||
private lateinit var imageView: ShapeableImageView
|
||||
private lateinit var nameTextView: TextView
|
||||
private lateinit var causesTextView: TextView
|
||||
private lateinit var symptomsTextView: TextView
|
||||
private lateinit var impactTextView: TextView
|
||||
private lateinit var solutionTextView: TextView
|
||||
private lateinit var backButton: ImageButton
|
||||
private lateinit var infoConfidenceButton: ImageButton
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(R.layout.disease_detail_fragment, container, false)
|
||||
|
||||
(activity as? MainActivity)?.showBottomNav(false)
|
||||
|
||||
imageView = view.findViewById(R.id.detailDiseaseImage)
|
||||
nameTextView = view.findViewById(R.id.detailDiseaseName)
|
||||
causesTextView = view.findViewById(R.id.detailDiseaseCauses)
|
||||
symptomsTextView = view.findViewById(R.id.detailDiseaseSymptoms)
|
||||
impactTextView = view.findViewById(R.id.detailDiseaseImpact)
|
||||
solutionTextView = view.findViewById(R.id.detailDiseaseSolution)
|
||||
backButton = view.findViewById(R.id.btnBack)
|
||||
infoConfidenceButton = view.findViewById(R.id.infoConfidenceButton)
|
||||
|
||||
val diseaseName = arguments?.getString("disease_name")
|
||||
val imageUriString = arguments?.getString("image_uri")
|
||||
val confidenceText = arguments?.getString("confidence_text")
|
||||
|
||||
if (!imageUriString.isNullOrEmpty()) {
|
||||
val imageUri = Uri.parse(imageUriString)
|
||||
imageView.setImageURI(imageUri)
|
||||
}
|
||||
|
||||
if (diseaseName?.trim()?.lowercase() == "penyakit daun tidak terdeteksi") {
|
||||
// Jangan load data dari ViewModel
|
||||
nameTextView.text = "Tidak terdeteksi"
|
||||
causesTextView.text = "-"
|
||||
symptomsTextView.text = "-"
|
||||
impactTextView.text = "-"
|
||||
solutionTextView.text = "-"
|
||||
} else {
|
||||
// Load dan tampilkan data penyakit
|
||||
diseaseViewModel.loadDiseaseByName(diseaseName ?: "")
|
||||
diseaseViewModel.disease.observe(viewLifecycleOwner, Observer { disease ->
|
||||
if (disease != null) {
|
||||
nameTextView.text = disease.name
|
||||
causesTextView.text = disease.cause
|
||||
symptomsTextView.text = disease.symptoms
|
||||
impactTextView.text = disease.impact
|
||||
solutionTextView.text = disease.solution
|
||||
|
||||
if (imageUriString.isNullOrEmpty()) {
|
||||
imageView.setImageResource(disease.imageResId)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
infoConfidenceButton.setOnClickListener {
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle("Confidence per Label")
|
||||
.setMessage(confidenceText ?: "Confidence tidak tersedia.")
|
||||
.show()
|
||||
}
|
||||
|
||||
backButton.setOnClickListener {
|
||||
requireActivity().supportFragmentManager.popBackStack()
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
(activity as? MainActivity)?.showBottomNav(true)
|
||||
diseaseViewModel.clearDisease()
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package com.example.tomatoleafcare.ui.note
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.example.tomatoleafcare.DiseaseDetailFragment
|
||||
import com.example.tomatoleafcare.R
|
||||
import com.example.tomatoleafcare.adapter.DiseaseAdapter
|
||||
import com.example.tomatoleafcare.repository.DiseaseRepository
|
||||
import com.example.tomatoleafcare.model.Disease
|
||||
import com.example.tomatoleafcare.viewmodel.DiseaseViewModel
|
||||
|
||||
class NoteFragment : Fragment() {
|
||||
|
||||
private lateinit var recyclerView: RecyclerView
|
||||
private lateinit var adapter: DiseaseAdapter
|
||||
private val viewModel: DiseaseViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(R.layout.note_fragment, container, false)
|
||||
|
||||
val classList = DiseaseRepository.classList.filter { it.name != "Tanaman Sehat" }
|
||||
|
||||
recyclerView = view.findViewById(R.id.recyclerViewNote)
|
||||
recyclerView.layoutManager = LinearLayoutManager(requireContext())
|
||||
|
||||
adapter = DiseaseAdapter(classList, object : DiseaseAdapter.OnItemClickListener {
|
||||
override fun onItemClick(disease: Disease) {
|
||||
viewModel.loadDiseaseByName(disease.name)
|
||||
|
||||
requireActivity().supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_container, DiseaseDetailFragment())
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
}
|
||||
})
|
||||
|
||||
recyclerView.adapter = adapter
|
||||
|
||||
return view
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.example.tomatoleafcare.ui.splash
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.example.tomatoleafcare.R
|
||||
import com.example.tomatoleafcare.ui.home.MainActivity
|
||||
|
||||
class SplashActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.splash_screen)
|
||||
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
startActivity(Intent(this, MainActivity::class.java))
|
||||
finish()
|
||||
}, 2000)
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.example.tomatoleafcare.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
@ -0,0 +1,58 @@
|
||||
package com.example.tomatoleafcare.ui.theme
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color(0xFFFFFBFE),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onTertiary = Color.White,
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
*/
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun TomatoLeafCareTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package com.example.tomatoleafcare.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
/* Other default text styles to override
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
letterSpacing = 0.sp
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
*/
|
||||
)
|
@ -0,0 +1,25 @@
|
||||
package com.example.tomatoleafcare.viewmodel
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.example.tomatoleafcare.model.Disease
|
||||
import com.example.tomatoleafcare.repository.DiseaseRepository
|
||||
|
||||
class DiseaseViewModel : ViewModel() {
|
||||
|
||||
private val _disease = MutableLiveData<Disease?>()
|
||||
val disease: MutableLiveData<Disease?> get() = _disease
|
||||
|
||||
fun loadDiseaseByName(name: String) {
|
||||
val result = DiseaseRepository.findDiseaseByName(name)
|
||||
result?.let {
|
||||
_disease.value = it
|
||||
}
|
||||
}
|
||||
|
||||
fun clearDisease() {
|
||||
_disease.value = null
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package com.example.tomatoleafcare.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.example.tomatoleafcare.helper.HistoryDatabaseHelper
|
||||
import com.example.tomatoleafcare.model.History
|
||||
|
||||
class HistoryViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
private val dbHelper = HistoryDatabaseHelper(application)
|
||||
|
||||
private val _historyList = MutableLiveData<List<History>>()
|
||||
val historyList: LiveData<List<History>> get() = _historyList
|
||||
|
||||
init {
|
||||
loadAllHistory()
|
||||
}
|
||||
|
||||
fun loadAllHistory() {
|
||||
val allHistory = dbHelper.getAllHistory()
|
||||
_historyList.postValue(allHistory)
|
||||
}
|
||||
|
||||
fun deleteHistoryById(id: Int) {
|
||||
val success = dbHelper.deleteHistory(id.toLong())
|
||||
if (success) {
|
||||
loadAllHistory()
|
||||
}
|
||||
}
|
||||
|
||||
fun insertHistory(item: History) {
|
||||
dbHelper.insertHistory(item)
|
||||
loadAllHistory()
|
||||
}
|
||||
}
|
5
TomatoLeafCare/app/src/main/res/color/nav_item_color.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/active_color" android:state_checked="true"/>
|
||||
<item android:color="@color/inactive_color"/>
|
||||
</selector>
|
BIN
TomatoLeafCare/app/src/main/res/drawable/bercakbakteri.jpg
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
TomatoLeafCare/app/src/main/res/drawable/bercakdaunseptoria.jpg
Normal file
After Width: | Height: | Size: 60 KiB |
@ -0,0 +1,4 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||
<solid android:color="#0000FF"/> <!-- Biru -->
|
||||
<size android:width="16dp" android:height="16dp"/>
|
||||
</shape>
|
@ -0,0 +1,4 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||
<solid android:color="#00FF00"/> <!-- Hijau -->
|
||||
<size android:width="16dp" android:height="16dp"/>
|
||||
</shape>
|
BIN
TomatoLeafCare/app/src/main/res/drawable/healthy.jpg
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
TomatoLeafCare/app/src/main/res/drawable/ic_camera.png
Normal file
After Width: | Height: | Size: 871 B |
@ -0,0 +1,7 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@android:color/white" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/green_primary" />
|
||||
</shape>
|
BIN
TomatoLeafCare/app/src/main/res/drawable/ic_close.png
Normal file
After Width: | Height: | Size: 395 B |
BIN
TomatoLeafCare/app/src/main/res/drawable/ic_delete.png
Normal file
After Width: | Height: | Size: 268 B |
BIN
TomatoLeafCare/app/src/main/res/drawable/ic_galeri.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
TomatoLeafCare/app/src/main/res/drawable/ic_history.png
Normal file
After Width: | Height: | Size: 998 B |
BIN
TomatoLeafCare/app/src/main/res/drawable/ic_home.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
TomatoLeafCare/app/src/main/res/drawable/ic_info.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
android:height="108dp"
|
||||
android:width="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
</vector>
|
@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
BIN
TomatoLeafCare/app/src/main/res/drawable/ic_note.png
Normal file
After Width: | Height: | Size: 785 B |
BIN
TomatoLeafCare/app/src/main/res/drawable/ic_pindai.png
Normal file
After Width: | Height: | Size: 739 B |
BIN
TomatoLeafCare/app/src/main/res/drawable/icon.png
Normal file
After Width: | Height: | Size: 166 KiB |
BIN
TomatoLeafCare/app/src/main/res/drawable/jamurdaun.jpeg
Normal file
After Width: | Height: | Size: 169 KiB |
BIN
TomatoLeafCare/app/src/main/res/drawable/lateblight.jpg
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
TomatoLeafCare/app/src/main/res/drawable/logo.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
TomatoLeafCare/app/src/main/res/drawable/placeholder.png
Normal file
After Width: | Height: | Size: 16 KiB |
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="16dp"/>
|
||||
<solid android:color="@android:color/white"/>
|
||||
</shape>
|
BIN
TomatoLeafCare/app/src/main/res/drawable/sehat.jpg
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
TomatoLeafCare/app/src/main/res/drawable/tomato.png
Normal file
After Width: | Height: | Size: 897 KiB |
BIN
TomatoLeafCare/app/src/main/res/drawable/tomatoleafcare.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
TomatoLeafCare/app/src/main/res/drawable/viruskeriting.jpg
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
TomatoLeafCare/app/src/main/res/drawable/virusmozaik.jpg
Normal file
After Width: | Height: | Size: 29 KiB |
24
TomatoLeafCare/app/src/main/res/layout/activity_main.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
android:id="@+id/bottomNavigationView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="70dp"
|
||||
app:menu="@menu/nav_menu"
|
||||
app:itemIconTint="@color/nav_item_color"
|
||||
app:itemBackground="@color/light_green"
|
||||
app:itemActiveIndicatorStyle="@color/light_green"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
162
TomatoLeafCare/app/src/main/res/layout/camera_fragment.xml
Normal file
@ -0,0 +1,162 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#FFFDEB">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingStart="8dp"
|
||||
android:layout_marginBottom="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logoImage"
|
||||
android:layout_width="210dp"
|
||||
android:layout_height="54dp"
|
||||
android:src="@drawable/tomatoleafcare"
|
||||
android:contentDescription="@string/logo"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/statusLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true">
|
||||
|
||||
<View
|
||||
android:id="@+id/ledStatus"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:background="@drawable/bg_led_offline"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginBottom="2dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/offline"
|
||||
android:textColor="@color/blue"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginBottom="2dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/judul"
|
||||
android:textAlignment="center"
|
||||
android:textStyle="bold"
|
||||
android:textSize="18sp"
|
||||
android:textColor="#3C553E"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/rounded_corner_bg"
|
||||
android:padding="12dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/penjelasan"
|
||||
android:textSize="14sp"
|
||||
android:justificationMode="inter_word"
|
||||
android:textColor="#3C3C3C" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:paddingHorizontal="12dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnKamera"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/kamera"
|
||||
app:icon="@drawable/ic_camera"
|
||||
app:iconPadding="8dp"
|
||||
app:iconGravity="textStart"
|
||||
app:iconTint="@color/black"
|
||||
android:textColor="@color/black"
|
||||
app:cornerRadius="12dp"
|
||||
app:backgroundTint="@color/light_green"
|
||||
android:layout_marginEnd="8dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnGaleri"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/galeri"
|
||||
android:textColor="@color/black"
|
||||
app:backgroundTint="@color/light_green"
|
||||
app:cornerRadius="12dp"
|
||||
app:icon="@drawable/ic_galeri"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="8dp"
|
||||
app:iconTint="@color/black"
|
||||
android:layout_marginStart="8dp" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="#D1D1D1">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:src="@drawable/placeholder"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="centerCrop"
|
||||
android:contentDescription="@string/placeholder_gambar" />
|
||||
</FrameLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnPindai"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/pindai"
|
||||
|
||||
app:icon="@drawable/ic_pindai"
|
||||
app:iconPadding="8dp"
|
||||
app:iconGravity="textStart"
|
||||
app:iconTint="@color/black"
|
||||
android:textColor="@color/black"
|
||||
app:cornerRadius="12dp"
|
||||
app:backgroundTint="@color/light_green"/>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
50
TomatoLeafCare/app/src/main/res/layout/card_slide_1.xml
Normal file
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardBackgroundColor="@color/yellow_accent"
|
||||
android:layout_margin="16dp">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="180dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:padding="20dp"
|
||||
android:text="@string/tanam_rawat_nikmati"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textSubtitle"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_below="@id/textTitle"
|
||||
android:layout_alignStart="@id/textTitle"
|
||||
android:layout_marginStart="1dp"
|
||||
android:layout_marginTop="-4dp"
|
||||
android:padding="20dp"
|
||||
android:text="@string/quotes"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageTomato"
|
||||
android:layout_width="148dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:contentDescription="@string/gambar_tomat"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/tomato" />
|
||||
</RelativeLayout>
|
||||
</androidx.cardview.widget.CardView>
|
155
TomatoLeafCare/app/src/main/res/layout/card_slide_2.xml
Normal file
@ -0,0 +1,155 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
app:cardCornerRadius="16dp"
|
||||
android:background="@color/green_primary"
|
||||
app:cardElevation="8dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:background="@color/green_primary">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/tanam_rawat_panen"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:padding="10dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/cardStep1"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/textTitle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/cardStep2"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/yellow_accent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Tanam"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center"
|
||||
android:text="Pilih bibit unggul untuk hasil terbaik"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/cardStep2"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/textTitle"
|
||||
app:layout_constraintStart_toEndOf="@id/cardStep1"
|
||||
app:layout_constraintEnd_toStartOf="@+id/cardStep3"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/yellow_accent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Jaga"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center"
|
||||
android:text="Siram secara teratur dan rutin"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/cardStep3"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:cardCornerRadius="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/textTitle"
|
||||
app:layout_constraintStart_toEndOf="@id/cardStep2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/yellow_accent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Panen"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center"
|
||||
android:text="Nikmati tomat segar dan sehat"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.cardview.widget.CardView>
|
@ -0,0 +1,179 @@
|
||||
<?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:orientation="vertical"
|
||||
android:background="@color/cream_background"
|
||||
android:padding="16dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logoImage"
|
||||
android:layout_width="210dp"
|
||||
android:layout_height="54dp"
|
||||
android:src="@drawable/tomatoleafcare"
|
||||
android:contentDescription="@string/logotomatoleafcare"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnBack"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/kembali"
|
||||
android:src="@drawable/ic_close"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tint="@color/green_primary" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/cream_background"
|
||||
android:scrollbars="none"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/detailDiseaseImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp"
|
||||
android:contentDescription="@string/disease_image"
|
||||
android:scaleType="centerCrop"
|
||||
app:shapeAppearanceOverlay="@style/CornerImageStyle" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardElevation="6dp"
|
||||
app:strokeColor="@color/green_primary"
|
||||
app:strokeWidth="1dp"
|
||||
android:backgroundTint="@android:color/white">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp">
|
||||
|
||||
<!-- Nama Penyakit (Tetap di Tengah) -->
|
||||
<TextView
|
||||
android:id="@+id/detailDiseaseName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/nama_penyakit"
|
||||
android:textSize="22sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/green_primary"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<!-- Tombol Info (i) di Pojok Kanan Atas -->
|
||||
<ImageButton
|
||||
android:id="@+id/infoConfidenceButton"
|
||||
android:layout_width="28dp"
|
||||
android:layout_height="28dp"
|
||||
android:background="@drawable/ic_circle_background"
|
||||
android:src="@drawable/ic_info"
|
||||
android:scaleType="centerInside"
|
||||
android:tint="@color/green_primary"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:ignore="UseAppTint" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/penyebab"
|
||||
android:textStyle="bold"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@color/green_primary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailDiseaseCauses"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/penyebab"
|
||||
android:justificationMode="inter_word"
|
||||
android:textColor="@color/black" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/gejala"
|
||||
android:textStyle="bold"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@color/green_primary"
|
||||
android:paddingTop="12dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailDiseaseSymptoms"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:justificationMode="inter_word"
|
||||
android:textColor="@color/black"
|
||||
android:text="@string/gejala" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dampak"
|
||||
android:textStyle="bold"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@color/green_primary"
|
||||
android:paddingTop="12dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailDiseaseImpact"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:justificationMode="inter_word"
|
||||
android:textColor="@color/black"
|
||||
android:text="@string/dampak" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/solusi"
|
||||
android:textStyle="bold"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@color/green_primary"
|
||||
android:paddingTop="12dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailDiseaseSolution"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:justificationMode="inter_word"
|
||||
android:textColor="@color/black"
|
||||
android:text="@string/solusi" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
55
TomatoLeafCare/app/src/main/res/layout/disease_item.xml
Normal file
@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
app:cardBackgroundColor="@color/green_primary"
|
||||
app:cardCornerRadius="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/diseaseImage"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/placeholder"
|
||||
android:contentDescription="@string/gambar_penyakit_daun"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginEnd="2dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/diseaseName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/nama_penyakit"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/yellow_accent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/diseaseDescription"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/deskripsi_singkat_penyakit"
|
||||
android:justificationMode="inter_word"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/white"
|
||||
android:layout_marginTop="4dp"
|
||||
android:paddingEnd="2dp"
|
||||
android:layout_marginBottom="2dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
@ -0,0 +1,156 @@
|
||||
<?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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/cream_background"
|
||||
android:padding="16dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logoImage"
|
||||
android:layout_width="210dp"
|
||||
android:layout_height="54dp"
|
||||
android:src="@drawable/tomatoleafcare"
|
||||
android:contentDescription="@string/logotomatoleafcare"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnBack"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:src="@drawable/ic_close"
|
||||
android:contentDescription="@string/kembali"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tint="@color/green_primary" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/cream_background"
|
||||
android:scrollbars="none"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/detailDiseaseImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:contentDescription="@string/disease_image"
|
||||
app:shapeAppearanceOverlay="@style/CornerImageStyle" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardElevation="6dp"
|
||||
app:strokeColor="@color/green_primary"
|
||||
app:strokeWidth="1dp"
|
||||
android:backgroundTint="@android:color/white">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailDiseaseName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/nama_penyakit"
|
||||
android:textSize="22sp"
|
||||
android:textStyle="bold"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/green_primary" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/penyebab"
|
||||
android:textStyle="bold"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@color/green_primary"
|
||||
android:paddingTop="12dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailDiseaseCauses"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/penyebab"
|
||||
android:justificationMode="inter_word"
|
||||
android:textColor="@color/black" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/gejala"
|
||||
android:textStyle="bold"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@color/green_primary"
|
||||
android:paddingTop="12dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailDiseaseSymptoms"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:justificationMode="inter_word"
|
||||
android:textColor="@color/black"
|
||||
android:text="@string/gejala" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dampak"
|
||||
android:textStyle="bold"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@color/green_primary"
|
||||
android:paddingTop="12dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailDiseaseImpact"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:justificationMode="inter_word"
|
||||
android:textColor="@color/black"
|
||||
android:text="@string/dampak" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/solusi"
|
||||
android:textStyle="bold"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@color/green_primary"
|
||||
android:paddingTop="12dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/detailDiseaseSolution"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:justificationMode="inter_word"
|
||||
android:textColor="@color/black"
|
||||
android:text="@string/solusi" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
28
TomatoLeafCare/app/src/main/res/layout/history_fragment.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/history_root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/cream_background"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="15dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="210dp"
|
||||
android:layout_height="54dp"
|
||||
android:src="@drawable/tomatoleafcare"
|
||||
android:contentDescription="@string/logotomatoleafcare" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerViewHistory"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
</LinearLayout>
|
64
TomatoLeafCare/app/src/main/res/layout/history_item.xml
Normal file
@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
app:cardBackgroundColor="@color/green_primary"
|
||||
app:cardCornerRadius="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageHistory"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/placeholder"
|
||||
android:contentDescription="@string/gambar_riwayat_penyakit" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtDiseaseName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/nama_penyakit"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/yellow_accent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtDate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/tanggal"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/white"
|
||||
android:layout_marginTop="4dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnDelete"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:src="@drawable/ic_delete"
|
||||
android:contentDescription="@string/hapus_riwayat"
|
||||
android:background="@android:color/transparent"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="8dp"
|
||||
app:tint="@android:color/white" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
82
TomatoLeafCare/app/src/main/res/layout/home_fragment.xml
Normal file
@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/cream_background"
|
||||
tools:context=".ui.home.HomeFragment">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="210dp"
|
||||
android:layout_height="54dp"
|
||||
android:src="@drawable/tomatoleafcare"
|
||||
android:contentDescription="@string/logotomatoleafcare" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:fillViewport="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/viewPager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="10dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_marginTop="4dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/jenis_penyakit_daun_tomat"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/seeMoreText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/selengkapnya"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:textColor="@color/green_primary"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:paddingLeft="8dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</LinearLayout>
|
27
TomatoLeafCare/app/src/main/res/layout/note_fragment.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/cream_background"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="210dp"
|
||||
android:layout_height="54dp"
|
||||
android:src="@drawable/tomatoleafcare"
|
||||
android:contentDescription="@string/logotomatoleafcare" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerViewNote"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false" />
|
||||
</LinearLayout>
|
25
TomatoLeafCare/app/src/main/res/layout/splash_screen.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/green_primary"
|
||||
android:gravity="center"
|
||||
android:paddingBottom="20dp">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_gravity="center"
|
||||
app:cardCornerRadius="20dp"
|
||||
app:cardElevation="4dp"
|
||||
app:cardBackgroundColor="@color/yellow_accent"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_gravity="center"
|
||||
android:contentDescription="@string/logo"
|
||||
android:src="@drawable/logo"
|
||||
android:elevation="8dp"/>
|
||||
</FrameLayout>
|
21
TomatoLeafCare/app/src/main/res/menu/nav_menu.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/navigation_home"
|
||||
android:icon="@drawable/ic_home"
|
||||
android:title="Home" />
|
||||
|
||||
<item
|
||||
android:id="@+id/navigation_camera"
|
||||
android:icon="@drawable/ic_camera"
|
||||
android:title="Scan" />
|
||||
|
||||
<item
|
||||
android:id="@+id/navigation_note"
|
||||
android:icon="@drawable/ic_note"
|
||||
android:title="Note" />
|
||||
|
||||
<item
|
||||
android:id="@+id/navigation_history"
|
||||
android:icon="@drawable/ic_history"
|
||||
android:title="History"/>
|
||||
</menu>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
BIN
TomatoLeafCare/app/src/main/res/mipmap-hdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 3.6 KiB |
BIN
TomatoLeafCare/app/src/main/res/mipmap-mdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.2 KiB |
BIN
TomatoLeafCare/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 5.3 KiB |
BIN
TomatoLeafCare/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 8.3 KiB |
BIN
TomatoLeafCare/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 12 KiB |
23
TomatoLeafCare/app/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
|
||||
<color name="green_primary">#626F47</color>
|
||||
<color name="light_green">#A4B465</color>
|
||||
<color name="yellow_accent">#FFCF50</color>
|
||||
<color name="cream_background">#FEFAE0</color>
|
||||
|
||||
<color name="active_color">#FFCF50</color>
|
||||
<color name="inactive_color">#626F47</color>
|
||||
<color name="green">#00FF00</color>
|
||||
<color name="red">#FF0000</color>
|
||||
<color name="blue">#0000FF</color>
|
||||
|
||||
|
||||
</resources>
|
4
TomatoLeafCare/app/src/main/res/values/dimens.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="slider_gap">16dp</dimen>
|
||||
</resources>
|
32
TomatoLeafCare/app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<resources>
|
||||
<string name="app_name">Tomato Leaf Care</string>
|
||||
<string name="placeholder_gambar">Placeholder gambar</string>
|
||||
<string name="penjelasan">Pastikan foto yang anda kirimkan memiliki kualitas yang cukup jelas dan hindari foto yang buram, terpotong atau terlalu gelap sehingga sulit untuk mengidentifikasi penyakit daun dengan jelas.</string>
|
||||
<string name="kamera">Kamera</string>
|
||||
<string name="galeri">Galeri</string>
|
||||
<string name="logo">Logo</string>
|
||||
<string name="judul">Masukkan gambar penyakit daun\nyang ingin dideteksi</string>
|
||||
<string name="pindai">Pindai</string>
|
||||
<string name="logotomatoleafcare">LogoTomatoLeafCare</string>
|
||||
<string name="disease_image">Disease Image</string>
|
||||
<string name="nama_penyakit">Nama Penyakit</string>
|
||||
<string name="penyebab">Penyebab</string>
|
||||
<string name="gejala">Gejala</string>
|
||||
<string name="dampak">Dampak</string>
|
||||
<string name="solusi">Solusi</string>
|
||||
<string name="gambar_penyakit_daun">gambar penyakit daun</string>
|
||||
<string name="deskripsi_singkat_penyakit">Deskripsi singkat penyakit</string>
|
||||
<string name="gambar_riwayat_penyakit">Gambar riwayat penyakit</string>
|
||||
<string name="tanggal">Tanggal</string>
|
||||
<string name="hapus_riwayat">Hapus riwayat</string>
|
||||
<string name="tanam_rawat_nikmati">🌱 Tanam, Rawat, Nikmati! 🍅</string>
|
||||
<string name="tanam_rawat_panen">🌱 Tanam, Rawat, Panen! 🍅</string>
|
||||
<string name="quotes">Rawat tanaman tomatmu dan panen hasil terbaik!</string>
|
||||
<string name="jenis_penyakit_daun_tomat">Jenis Penyakit Daun Tomat</string>
|
||||
<string name="online">Online</string>
|
||||
<string name="offline">Offline</string>
|
||||
<string name="kembali">backbutton</string>
|
||||
<string name="gambar_tomat">gambar-tomat</string>
|
||||
<string name="lihat_lebih_banyak">Lihat lebih banyak</string>
|
||||
<string name="selengkapnya">selengkapnya</string>
|
||||
</resources>
|
7
TomatoLeafCare/app/src/main/res/values/styles.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="CornerImageStyle">
|
||||
<item name="cornerFamily">rounded</item>
|
||||
<item name="cornerSize">16dp</item>
|
||||
</style>
|
||||
</resources>
|
14
TomatoLeafCare/app/src/main/res/values/themes.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<style name="Theme.TomatoLeafCare" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<item name="colorPrimary">@color/green_primary</item>
|
||||
<item name="colorSecondary">@color/yellow_accent</item>
|
||||
<item name="android:colorBackground">@color/white</item>
|
||||
<item name="android:statusBarColor">?attr/colorPrimary</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.TomatoLeafCare.Splash" parent="Theme.Material3.Light.NoActionBar">
|
||||
<item name="android:statusBarColor">@color/green_primary</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
13
TomatoLeafCare/app/src/main/res/xml/backup_rules.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample backup rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/guide/topics/data/autobackup
|
||||
for details.
|
||||
Note: This file is ignored for devices older that API 31
|
||||
See https://developer.android.com/about/versions/12/backup-restore
|
||||
-->
|
||||
<full-backup-content>
|
||||
<!--
|
||||
<include domain="sharedpref" path="."/>
|
||||
<exclude domain="sharedpref" path="device.xml"/>
|
||||
-->
|
||||
</full-backup-content>
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample data extraction rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||
for details.
|
||||
-->
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
-->
|
||||
</cloud-backup>
|
||||
<!--
|
||||
<device-transfer>
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
</device-transfer>
|
||||
-->
|
||||
</data-extraction-rules>
|
6
TomatoLeafCare/app/src/main/res/xml/file_paths.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<cache-path
|
||||
name="cache"
|
||||
path="." />
|
||||
</paths>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="true">robotika.upnvj.ac.id</domain>
|
||||
</domain-config>
|
||||
</network-security-config>
|
@ -0,0 +1,17 @@
|
||||
package com.example.tomatoleafcare
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|