commit a99d61810231bb320c107095bb4f9622bf58d5fb Author: DeaBaasithian Date: Mon Aug 3 11:34:15 2026 +0700 Upload source code aplikasi KopiKaoCare diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..2e1df5f --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +Projek TA \ No newline at end of file diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml new file mode 100644 index 0000000..4a53bee --- /dev/null +++ b/.idea/AndroidProjectSystem.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b86273d --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b268ef3 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/deviceManager.xml b/.idea/deviceManager.xml new file mode 100644 index 0000000..005825e --- /dev/null +++ b/.idea/deviceManager.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..02c4aa5 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..c19f363 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..9f6ad50 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,112 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + + namespace = "com.example.projekta" + compileSdk = 36 + + defaultConfig { + + applicationId = "com.example.projekta" + minSdk = 24 + targetSdk = 33 + + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = + "androidx.test.runner.AndroidJUnitRunner" + } + + packaging { + resources { + excludes += setOf( + "META-INF/INDEX.LIST", + "META-INF/DEPENDENCIES", + "META-INF/io.netty.versions.properties", + "META-INF/LICENSE*", + "META-INF/NOTICE*" + ) + } + } + + buildTypes { + + release { + + isMinifyEnabled = false + + proguardFiles( + getDefaultProguardFile( + "proguard-android-optimize.txt" + ), + "proguard-rules.pro" + ) + } + } + + compileOptions { + + sourceCompatibility = + JavaVersion.VERSION_11 + + targetCompatibility = + JavaVersion.VERSION_11 + } + + buildFeatures { + + viewBinding = true + } +} + +dependencies { + + // ANDROID + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.material) + implementation(libs.androidx.activity) + implementation(libs.androidx.constraintlayout) + + // RECYCLERVIEW + implementation( + "androidx.recyclerview:recyclerview:1.3.2" + ) + + // CIRCLE IMAGE + implementation( + "de.hdodenhof:circleimageview:3.1.0" + ) + + // IMAGE LOADER + implementation( + "io.coil-kt:coil:2.6.0" + ) + + // VOLLEY + implementation( + "com.android.volley:volley:1.2.1" + ) + + // SPLASH SCREEN + implementation( + "androidx.core:core-splashscreen:1.0.1" + ) + + // MQTT + implementation("com.hivemq:hivemq-mqtt-client:1.3.3") + + // TEST + testImplementation(libs.junit) + + androidTestImplementation( + libs.androidx.junit + ) + + androidTestImplementation( + libs.androidx.espresso.core + ) +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/projekta/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/example/projekta/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..23875dc --- /dev/null +++ b/app/src/androidTest/java/com/example/projekta/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.projekta + +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.projekta", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..7f478cb --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/projekta/HistoriActivity.kt b/app/src/main/java/com/example/projekta/HistoriActivity.kt new file mode 100644 index 0000000..39a8720 --- /dev/null +++ b/app/src/main/java/com/example/projekta/HistoriActivity.kt @@ -0,0 +1,120 @@ +package com.example.projekta + +import android.os.Bundle +import android.widget.ImageView +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.android.volley.Request +import com.android.volley.toolbox.JsonArrayRequest +import com.android.volley.toolbox.Volley +import org.json.JSONObject + +class HistoriActivity : AppCompatActivity() { + + private lateinit var recyclerHistori: RecyclerView + + private lateinit var historiList: + ArrayList + + private lateinit var historiAdapter: + HistoriAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + + super.onCreate(savedInstanceState) + + setContentView(R.layout.activity_histori) + + // ================= BACK ================= + val btnBack = + findViewById(R.id.btnBack) + + btnBack.setOnClickListener { + finish() + } + + // ================= RECYCLERVIEW ================= + recyclerHistori = + findViewById(R.id.recyclerHistori) + + recyclerHistori.layoutManager = + LinearLayoutManager(this) + + historiList = ArrayList() + + historiAdapter = + HistoriAdapter( + this, + historiList + ) + + recyclerHistori.adapter = + historiAdapter + + // ================= LOAD HISTORI ================= + loadHistori() + } + + private fun loadHistori() { + + val url = + "http://192.168.43.191/projekta/get_histori.php" + + val request = JsonArrayRequest( + + Request.Method.GET, + url, + null, + + { response -> + + historiList.clear() + + for (i in 0 until response.length()) { + + val data: JSONObject = + response.getJSONObject(i) + + val histori = HistoriModel( + + createdAt = + data.getString("created_at"), + + jamPemupukan = + data.getString("jam_pemupukan"), + + mode = + data.getString("mode"), + + durasiPompa = + data.getString("durasi_pompa"), + + intervalHari = + data.getString("interval_hari"), + + statusTanah = + data.getString("status_tanah") + ) + + historiList.add(histori) + } + + historiAdapter.notifyDataSetChanged() + }, + + { + + Toast.makeText( + this, + "Gagal mengambil histori", + Toast.LENGTH_SHORT + ).show() + } + ) + + Volley.newRequestQueue(this) + .add(request) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/projekta/HistoriAdapter.kt b/app/src/main/java/com/example/projekta/HistoriAdapter.kt new file mode 100644 index 0000000..e9b36df --- /dev/null +++ b/app/src/main/java/com/example/projekta/HistoriAdapter.kt @@ -0,0 +1,95 @@ +package com.example.projekta + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.cardview.widget.CardView +import androidx.recyclerview.widget.RecyclerView + +class HistoriAdapter( + + private val context: Context, + private val historiList: ArrayList + +) : RecyclerView.Adapter() { + + class ViewHolder(itemView: View) + : RecyclerView.ViewHolder(itemView) { + + val tvTanggal: TextView = + itemView.findViewById(R.id.tvTanggal) + + val tvJam: TextView = + itemView.findViewById(R.id.tvJam) + + val tvMode: TextView = + itemView.findViewById(R.id.tvMode) + + val tvDurasi: TextView = + itemView.findViewById(R.id.tvDurasi) + + val tvInterval: TextView = + itemView.findViewById(R.id.tvInterval) + + val tvStatusTanah: TextView = + itemView.findViewById(R.id.tvStatusTanah) + + val cardHistori: CardView = + itemView.findViewById(R.id.cardHistori) + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ViewHolder { + + val view = LayoutInflater.from(context) + .inflate( + R.layout.item_histori, + parent, + false + ) + + return ViewHolder(view) + } + + override fun onBindViewHolder( + holder: ViewHolder, + position: Int + ) { + + val histori = + historiList[position] + + // TANGGAL + holder.tvTanggal.text = + histori.createdAt + + // JAM + holder.tvJam.text = + histori.jamPemupukan + + // MODE + holder.tvMode.text = + "• Mode: ${histori.mode}" + + // DURASI + holder.tvDurasi.text = + "• Durasi: ${histori.durasiPompa}" + + // INTERVAL + holder.tvInterval.text = + "• Interval: ${histori.intervalHari}" + + // STATUS TANAH + holder.tvStatusTanah.text = + "🔥 Status Tanah: ${histori.statusTanah}" + } + + override fun getItemCount(): Int { + + return historiList.size + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/projekta/HistoriModel.kt b/app/src/main/java/com/example/projekta/HistoriModel.kt new file mode 100644 index 0000000..9f85213 --- /dev/null +++ b/app/src/main/java/com/example/projekta/HistoriModel.kt @@ -0,0 +1,17 @@ +package com.example.projekta + +data class HistoriModel( + + val createdAt: String = "", + + val jamPemupukan: String = "", + + val mode: String = "", + + val durasiPompa: String = "", + + val intervalHari: String = "", + + val statusTanah: String = "" + +) \ No newline at end of file diff --git a/app/src/main/java/com/example/projekta/LoginActivity.kt b/app/src/main/java/com/example/projekta/LoginActivity.kt new file mode 100644 index 0000000..7cbea86 --- /dev/null +++ b/app/src/main/java/com/example/projekta/LoginActivity.kt @@ -0,0 +1,216 @@ +package com.example.projekta + +import android.content.Intent +import android.os.Bundle +import android.text.InputType +import android.util.Patterns +import android.view.MotionEvent +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import com.android.volley.Request +import com.android.volley.toolbox.StringRequest +import com.android.volley.toolbox.Volley +import com.example.projekta.databinding.ActivityLoginBinding +import org.json.JSONObject + +class LoginActivity : AppCompatActivity() { + + private lateinit var binding: ActivityLoginBinding + private var isPasswordVisible = false + + private val url = "http://192.168.43.191/projekta/login.php" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = ActivityLoginBinding.inflate(layoutInflater) + setContentView(binding.root) + + // ========================= + // CEK SESSION LOGIN + // ========================= + val sharedPref = getSharedPreferences("LOGIN", MODE_PRIVATE) + + if (sharedPref.getBoolean("isLogin", false)) { + startActivity(Intent(this, MenuActivity::class.java)) + finish() + } + + binding.btnBack.setOnClickListener { + finish() + } + + setupPasswordToggle() + + binding.btnLogin.setOnClickListener { + + val email = binding.etEmail.text.toString().trim() + val password = binding.etPassword.text.toString() + + // VALIDASI EMAIL + if (email.isEmpty() || !Patterns.EMAIL_ADDRESS.matcher(email).matches()) { + binding.etEmail.error = "Email tidak valid" + return@setOnClickListener + } + + // VALIDASI PASSWORD + if (password.length < 6) { + binding.etPassword.error = "Password minimal 6 karakter" + return@setOnClickListener + } + + loginUser(email, password) + } + + binding.txtRegister.setOnClickListener { + startActivity(Intent(this, RegisterActivity::class.java)) + } + } + + // ========================= + // LOGIN USER + // ========================= + private fun loginUser(email: String, password: String) { + + val request = object : StringRequest( + Request.Method.POST, + url, + + { response -> + + try { + + val json = JSONObject(response) + + if (json.getString("status") == "success") { + + val idUser = json.getString("id") + val nama = json.getString("nama") + val emailUser = json.getString("email") + + // SIMPAN SESSION LOGIN + val sharedPref = getSharedPreferences("LOGIN", MODE_PRIVATE) + + sharedPref.edit() + .putBoolean("isLogin", true) + .putString("id_user", idUser) + .putString("nama", nama) + .putString("email", emailUser) + .apply() + + Toast.makeText( + this, + "Login berhasil", + Toast.LENGTH_SHORT + ).show() + + startActivity(Intent(this, MenuActivity::class.java)) + finish() + + } else { + + Toast.makeText( + this, + "Email / Password salah", + Toast.LENGTH_SHORT + ).show() + + } + + } catch (e: Exception) { + + Toast.makeText( + this, + "Response server tidak valid", + Toast.LENGTH_LONG + ).show() + + e.printStackTrace() + } + + }, + + { + + Toast.makeText( + this, + "Gagal koneksi server", + Toast.LENGTH_LONG + ).show() + + } + + ) { + + override fun getParams(): MutableMap { + + val params = HashMap() + + params["email"] = email + params["password"] = password + + return params + } + + } + + Volley.newRequestQueue(this).add(request) + + } + + // ========================= + // PASSWORD TOGGLE + // ========================= + private fun setupPasswordToggle() { + + binding.etPassword.setOnTouchListener { _, event -> + + val drawableEnd = 2 + + if (event.action == MotionEvent.ACTION_UP) { + + val drawable = binding.etPassword.compoundDrawables[drawableEnd] + + if (drawable != null && + event.rawX >= (binding.etPassword.right - drawable.bounds.width()) + ) { + + isPasswordVisible = !isPasswordVisible + + if (isPasswordVisible) { + + binding.etPassword.inputType = + InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD + + binding.etPassword.setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_lock, + 0, + R.drawable.ic_eye_off, + 0 + ) + + } else { + + binding.etPassword.inputType = + InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD + + binding.etPassword.setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_lock, + 0, + R.drawable.ic_eye, + 0 + ) + + } + + binding.etPassword.setSelection(binding.etPassword.text.length) + + return@setOnTouchListener true + } + + } + + false + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/projekta/MainActivity.kt b/app/src/main/java/com/example/projekta/MainActivity.kt new file mode 100644 index 0000000..6fef265 --- /dev/null +++ b/app/src/main/java/com/example/projekta/MainActivity.kt @@ -0,0 +1,22 @@ +package com.example.projekta + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.example.projekta.databinding.ActivityMainBinding + +class MainActivity : AppCompatActivity() { + + private lateinit var binding: ActivityMainBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + // tes klik layar + binding.root.setOnClickListener { + println("App jalan 🔥") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/projekta/MenuActivity.kt b/app/src/main/java/com/example/projekta/MenuActivity.kt new file mode 100644 index 0000000..b0deb49 --- /dev/null +++ b/app/src/main/java/com/example/projekta/MenuActivity.kt @@ -0,0 +1,136 @@ +package com.example.projekta + +import android.content.Context +import android.content.Intent +import android.graphics.BitmapFactory +import android.os.Bundle +import android.util.Base64 +import androidx.activity.OnBackPressedCallback +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.GravityCompat +import com.example.projekta.databinding.ActivityMenuBinding + +class MenuActivity : AppCompatActivity() { + + private lateinit var binding: ActivityMenuBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = ActivityMenuBinding.inflate(layoutInflater) + setContentView(binding.root) + + loadUserSidebar() + + // Buka Drawer + binding.btnMenu.setOnClickListener { + binding.drawerLayout.openDrawer(GravityCompat.START) + } + + // Menu Profile + binding.menuProfile.setOnClickListener { + startActivity(Intent(this, ProfileActivity::class.java)) + binding.drawerLayout.closeDrawer(GravityCompat.START) + } + + // Menu Logout + binding.menuLogout.setOnClickListener { + + getSharedPreferences("LOGIN", MODE_PRIVATE).edit().clear().apply() + getSharedPreferences("USER_DATA", MODE_PRIVATE).edit().clear().apply() + + val intent = Intent(this, LoginActivity::class.java) + intent.flags = + Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + + startActivity(intent) + finish() + } + + // Menu Pemupukan + binding.menuPemupukan.setOnClickListener { + startActivity(Intent(this, PemupukanActivity::class.java)) + } + + // Menu Panel Surya + binding.menuPanelSurya.setOnClickListener { + startActivity(Intent(this, PanelSuryaActivity::class.java)) + } + + // Menu Panduan + binding.menuPanduan.setOnClickListener { + startActivity(Intent(this, PanduanActivity::class.java)) + } + + // Menu Tentang + binding.menuTentang.setOnClickListener { + startActivity(Intent(this, TentangActivity::class.java)) + } + + // Menu Histori + binding.menuHistori.setOnClickListener { + startActivity(Intent(this, HistoriActivity::class.java)) + } + + // Tombol Back + onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + + if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) { + binding.drawerLayout.closeDrawer(GravityCompat.START) + } else { + finishAffinity() + } + } + } + ) + } + + // Refresh data sidebar setiap Activity kembali dibuka + override fun onResume() { + super.onResume() + loadUserSidebar() + } + + // Memuat nama dan foto profil pada sidebar + private fun loadUserSidebar() { + + val sharedPref = getSharedPreferences("USER_DATA", Context.MODE_PRIVATE) + + val nama = sharedPref.getString("nama", "Nama Pengguna") + val foto = sharedPref.getString("foto", "") + + binding.txtSidebarName.text = nama + + if (!foto.isNullOrEmpty()) { + + try { + + val cleanBase64 = foto + .replace("\n", "") + .replace("\r", "") + .replace(" ", "") + + val imageBytes = Base64.decode(cleanBase64, Base64.DEFAULT) + + val bitmap = BitmapFactory.decodeByteArray( + imageBytes, + 0, + imageBytes.size + ) + + binding.imgSidebarProfile.setImageBitmap(bitmap) + + } catch (e: Exception) { + + binding.imgSidebarProfile.setImageResource(R.drawable.profileman) + } + + } else { + + binding.imgSidebarProfile.setImageResource(R.drawable.profileman) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/projekta/MetodePemupukanActivity.kt b/app/src/main/java/com/example/projekta/MetodePemupukanActivity.kt new file mode 100644 index 0000000..fef0bd2 --- /dev/null +++ b/app/src/main/java/com/example/projekta/MetodePemupukanActivity.kt @@ -0,0 +1,389 @@ +package com.example.projekta + +import android.app.TimePickerDialog +import android.graphics.Color +import android.os.Bundle +import android.widget.Button +import android.widget.EditText +import android.widget.ImageView +import android.widget.TextView +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.cardview.widget.CardView +import com.android.volley.Request +import com.android.volley.toolbox.JsonObjectRequest +import com.android.volley.toolbox.StringRequest +import com.android.volley.toolbox.Volley +import java.util.Calendar +import java.util.Locale + +class MetodePemupukanActivity : AppCompatActivity() { + + private lateinit var btnBack: ImageView + + private lateinit var cardAuto: CardView + private lateinit var cardManual: CardView + + private lateinit var etInterval: EditText + private lateinit var etDurasi: EditText + + private lateinit var tvJam: TextView + + private lateinit var btnPilihJam: Button + private lateinit var btnSimpan: Button + + private lateinit var btnPompaManual: Button + private lateinit var tvStatusManual: TextView + + private var mode = "otomatis" + private var isPompaOn = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_metode_pemupukan) + + initView() + setupListener() + loadPengaturan() + } + + private fun initView() { + + btnBack = findViewById(R.id.btnBack) + + cardAuto = findViewById(R.id.cardAuto) + cardManual = findViewById(R.id.cardManual) + + etInterval = findViewById(R.id.etInterval) + etDurasi = findViewById(R.id.etDurasi) + + tvJam = findViewById(R.id.tvJam) + + btnPilihJam = findViewById(R.id.btnPilihJam) + btnSimpan = findViewById(R.id.btnSimpan) + + btnPompaManual = findViewById(R.id.btnPompaManual) + tvStatusManual = findViewById(R.id.tvStatusManual) + } + + private fun setupListener() { + + btnBack.setOnClickListener { + finish() + } + + cardAuto.setOnClickListener { + setModeOtomatis() + } + + cardManual.setOnClickListener { + setModeManual() + } + + btnPilihJam.setOnClickListener { + + val calendar = Calendar.getInstance() + + TimePickerDialog( + this, + { _, hour, minute -> + + tvJam.text = String.format( + Locale.getDefault(), + "%02d:%02d:00", + hour, + minute + ) + }, + calendar.get(Calendar.HOUR_OF_DAY), + calendar.get(Calendar.MINUTE), + true + ).show() + } + + btnPompaManual.setOnClickListener { + togglePompaManual() + } + + btnSimpan.setOnClickListener { + simpanPengaturan() + } + } + + private fun setModeOtomatis() { + + mode = "otomatis" + + etInterval.isEnabled = true + etDurasi.isEnabled = true + btnPilihJam.isEnabled = true + + cardAuto.cardElevation = 12f + cardManual.cardElevation = 4f + } + + private fun setModeManual() { + + mode = "manual" + + etInterval.isEnabled = false + etDurasi.isEnabled = false + btnPilihJam.isEnabled = false + + cardAuto.cardElevation = 4f + cardManual.cardElevation = 12f + } + + private fun togglePompaManual() { + + if (mode != "manual") { + + Toast.makeText( + this, + "Pilih MODE MANUAL terlebih dahulu", + Toast.LENGTH_SHORT + ).show() + + return + } + + isPompaOn = !isPompaOn + + if (isPompaOn) { + + tvStatusManual.text = "STATUS: ON" + tvStatusManual.setTextColor( + Color.parseColor("#2E7D32") + ) + + btnPompaManual.text = "MATIKAN POMPA" + btnPompaManual.setBackgroundColor( + Color.parseColor("#D32F2F") + ) + + simpanHistori("manual", "ON") + + } else { + + tvStatusManual.text = "STATUS: OFF" + tvStatusManual.setTextColor( + Color.parseColor("#D32F2F") + ) + + btnPompaManual.text = "NYALAKAN POMPA" + btnPompaManual.setBackgroundColor( + Color.parseColor("#2E7D32") + ) + + simpanHistori("manual", "OFF") + } + } + + private fun simpanPengaturan() { + + if (mode == "otomatis") { + + if (etInterval.text.toString().trim().isEmpty()) { + etInterval.error = "Isi interval hari" + return + } + + if (etDurasi.text.toString().trim().isEmpty()) { + etDurasi.error = "Isi durasi detik" + return + } + } + + val url = + "http://192.168.43.191/projekta/save_pengaturan.php" + + val request = object : StringRequest( + Method.POST, + url, + + { response -> + + Toast.makeText( + this, + response, + Toast.LENGTH_SHORT + ).show() + }, + + { + + Toast.makeText( + this, + "Gagal koneksi server", + Toast.LENGTH_SHORT + ).show() + } + ) { + + override fun getParams(): MutableMap { + + val params = HashMap() + + params["mode"] = mode + + if (mode == "otomatis") { + + params["interval_hari"] = + etInterval.text.toString().trim() + + params["durasi"] = + etDurasi.text.toString().trim() + + params["jam"] = + tvJam.text.toString() + + } else { + + params["status_manual"] = + if (isPompaOn) "ON" else "OFF" + } + + return params + } + } + + Volley.newRequestQueue(this) + .add(request) + } + + private fun simpanHistori( + modePemupukan: String, + statusPompa: String + ) { + + val url = + "http://192.168.43.191/projekta/insert_histori.php" + + val request = object : StringRequest( + Request.Method.POST, + url, + {}, + {} + ) { + + override fun getParams(): MutableMap { + + val params = HashMap() + + params["mode"] = modePemupukan + + params["status"] = statusPompa + + return params + } + } + + Volley.newRequestQueue(this) + .add(request) + + } + + private fun loadPengaturan() { + + val url = + "http://192.168.43.191/projekta/get_pengaturan.php" + + val request = JsonObjectRequest( + Request.Method.GET, + url, + null, + + { response -> + + try { + + if (response.getBoolean("success")) { + + mode = + response.getString("mode") + + val interval = + response.getInt("interval_hari") + + val durasi = + response.getInt("durasi") + + val jam = + response.getString("jam") + + val statusManual = + response.getString("status_manual") + + tvJam.text = jam + + etInterval.setText( + interval.toString() + ) + + etDurasi.setText( + durasi.toString() + ) + + if (mode == "manual") { + + setModeManual() + + if (statusManual == "ON") { + + isPompaOn = true + + tvStatusManual.text = + "STATUS: ON" + + tvStatusManual.setTextColor( + Color.parseColor("#2E7D32") + ) + + btnPompaManual.text = + "MATIKAN POMPA" + + btnPompaManual.setBackgroundColor( + Color.parseColor("#D32F2F") + ) + + } else { + + isPompaOn = false + + tvStatusManual.text = + "STATUS: OFF" + + tvStatusManual.setTextColor( + Color.parseColor("#D32F2F") + ) + + btnPompaManual.text = + "NYALAKAN POMPA" + + btnPompaManual.setBackgroundColor( + Color.parseColor("#2E7D32") + ) + } + + } else { + + setModeOtomatis() + } + } + + } catch (e: Exception) { + + setModeOtomatis() + } + }, + + { + + setModeOtomatis() + } + ) + + Volley.newRequestQueue(this) + .add(request) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/projekta/PanduanActivity.kt b/app/src/main/java/com/example/projekta/PanduanActivity.kt new file mode 100644 index 0000000..6d4df5f --- /dev/null +++ b/app/src/main/java/com/example/projekta/PanduanActivity.kt @@ -0,0 +1,18 @@ +package com.example.projekta + +import android.os.Bundle +import android.widget.ImageView +import androidx.appcompat.app.AppCompatActivity + +class PanduanActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_panduan) + + val btnBack = findViewById(R.id.btnBack) + btnBack.setOnClickListener { + finish() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/projekta/PanelSuryaActivity.kt b/app/src/main/java/com/example/projekta/PanelSuryaActivity.kt new file mode 100644 index 0000000..877e1fb --- /dev/null +++ b/app/src/main/java/com/example/projekta/PanelSuryaActivity.kt @@ -0,0 +1,145 @@ +package com.example.projekta + +import android.os.Bundle +import android.widget.Button +import android.widget.ImageView +import android.widget.ProgressBar +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import com.android.volley.Request +import com.android.volley.toolbox.JsonObjectRequest +import com.android.volley.toolbox.Volley + +class PanelSuryaActivity : AppCompatActivity() { + + private lateinit var tvStatusPanel: TextView + private lateinit var tvDaya: TextView + private lateinit var tvStatusDaya: TextView + private lateinit var tvSumberEnergi: TextView + private lateinit var tvStatusSistem: TextView + private lateinit var tvStatusPompa: TextView + private lateinit var tvUpdate: TextView + private lateinit var progressDaya: ProgressBar + private lateinit var btnRefresh: Button + + private val url = + "http://192.168.43.191/projekta/get_panel_surya.php" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_panel_surya) + + findViewById(R.id.btnBack).setOnClickListener { + finish() + } + + tvStatusPanel = findViewById(R.id.tvStatusPanel) + tvDaya = findViewById(R.id.tvDaya) + tvStatusDaya = findViewById(R.id.tvStatusDaya) + tvSumberEnergi = findViewById(R.id.tvSumberEnergi) + tvStatusSistem = findViewById(R.id.tvStatusSistem) + tvStatusPompa = findViewById(R.id.tvStatusPompa) + tvUpdate = findViewById(R.id.tvUpdate) + progressDaya = findViewById(R.id.progressDaya) + btnRefresh = findViewById(R.id.btnRefresh) + + ambilDataPanelSurya() + + btnRefresh.setOnClickListener { + ambilDataPanelSurya() + } + } + + private fun ambilDataPanelSurya() { + + val request = JsonObjectRequest( + Request.Method.GET, + url, + null, + + { response -> + + val statusPanel = + response.optString("status_panel", "-") + + val daya = + response.optInt("daya", 0) + + val statusDaya = + response.optString("status_daya", "-") + + val sumberEnergi = + response.optString("sumber_energi", "-") + + val statusSistem = + response.optString("status_sistem", "-") + + val statusPompa = + response.optString("status_pompa", "-") + + val waktuUpdate = + response.optString("waktu_update", "-") + + tvStatusPanel.text = "🟢 $statusPanel" + + tvDaya.text = "$daya%" + + progressDaya.progress = daya + + when { + daya >= 80 -> { + tvStatusDaya.text = "🟢 $statusDaya" + } + + daya >= 40 -> { + tvStatusDaya.text = "🟡 $statusDaya" + } + + else -> { + tvStatusDaya.text = "🔴 $statusDaya" + } + } + + tvSumberEnergi.text = + "🔆 Sumber Energi : $sumberEnergi" + + tvStatusSistem.text = + "⚙️ Status Sistem : $statusSistem" + + tvStatusPompa.text = + "🚰 Status Pompa : $statusPompa" + + tvUpdate.text = + "🕒 Update Terakhir : $waktuUpdate" + + }, + + { error -> + + tvStatusPanel.text = "❌ Gagal mengambil data" + + tvDaya.text = "0%" + + progressDaya.progress = 0 + + tvStatusDaya.text = "-" + + tvSumberEnergi.text = + "🔆 Sumber Energi : -" + + tvStatusSistem.text = + "⚙️ Status Sistem : -" + + tvStatusPompa.text = + "🚰 Status Pompa : -" + + tvUpdate.text = + "🕒 Update Terakhir : -" + + error.printStackTrace() + } + ) + + Volley.newRequestQueue(this).add(request) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/projekta/PemupukanActivity.kt b/app/src/main/java/com/example/projekta/PemupukanActivity.kt new file mode 100644 index 0000000..c876353 --- /dev/null +++ b/app/src/main/java/com/example/projekta/PemupukanActivity.kt @@ -0,0 +1,344 @@ +package com.example.projekta + +import android.content.Intent +import android.graphics.Color +import android.os.Bundle +import android.util.Log +import android.widget.Button +import android.widget.ImageView +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import com.hivemq.client.mqtt.MqttClient +import com.hivemq.client.mqtt.mqtt3.Mqtt3AsyncClient +import org.json.JSONObject +import kotlin.math.roundToInt + +class PemupukanActivity : AppCompatActivity() { + + private lateinit var tvSuhuUdara: TextView + private lateinit var tvKelembapanUdara: TextView + private lateinit var tvSuhuTanah: TextView + private lateinit var tvKelembapanTanah: TextView + private lateinit var tvStatusTanah: TextView + private lateinit var tvStatusSistem: TextView + private lateinit var tvStatusPompa: TextView + + private lateinit var mqttClient: Mqtt3AsyncClient + + private val mqttUser = "DeaBaasithian" + private val mqttPassword = "Lekmamok123#" + + private val topicSensor = + "pemupukan/sensor" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView( + R.layout.activity_pemupukan + ) + + val btnBack = + findViewById( + R.id.btnBack + ) + + val btnMetode = + findViewById