From c10d563e3fd2f981048654598556c857a8226b23 Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Sun, 17 Nov 2024 18:42:25 +0200 Subject: [PATCH] finish lab1 --- Lab1/.gitignore | 15 ++ Lab1/.idea/.gitignore | 3 + Lab1/.idea/.name | 1 + Lab1/.idea/compiler.xml | 6 + Lab1/.idea/deploymentTargetSelector.xml | 13 ++ Lab1/.idea/gradle.xml | 20 ++ Lab1/.idea/kotlinc.xml | 6 + Lab1/.idea/migrations.xml | 10 + Lab1/.idea/misc.xml | 9 + Lab1/.idea/runConfigurations.xml | 17 ++ Lab1/app/.gitignore | 1 + Lab1/app/build.gradle.kts | 81 ++++++++ Lab1/app/proguard-rules.pro | 21 ++ .../example/lab_v4/ExampleInstrumentedTest.kt | 24 +++ Lab1/app/src/main/AndroidManifest.xml | 26 +++ Lab1/app/src/main/assets/database/LDB.sqlite3 | Bin 0 -> 57344 bytes .../com/example/lab_v4/GlobalViewModel.kt | 44 +++++ .../java/com/example/lab_v4/MainActivity.kt | 83 ++++++++ .../com/example/lab_v4/MeasurementPoint.kt | 7 + .../main/java/com/example/lab_v4/Vec2Int.kt | 6 + .../java/com/example/lab_v4/db/Matavimas.kt | 13 ++ .../com/example/lab_v4/db/MatavimasDao.kt | 12 ++ .../java/com/example/lab_v4/db/MyDatabase.kt | 38 ++++ .../java/com/example/lab_v4/db/Stiprumas.kt | 13 ++ .../com/example/lab_v4/db/StiprumasDao.kt | 12 ++ .../java/com/example/lab_v4/db/Vartotojas.kt | 13 ++ .../com/example/lab_v4/db/VartotojasDao.kt | 20 ++ .../example/lab_v4/fragments/AddFragment.kt | 51 +++++ .../example/lab_v4/fragments/ListAdapter.kt | 54 +++++ .../example/lab_v4/fragments/ListFragment.kt | 43 ++++ .../example/lab_v4/fragments/MapFragment.kt | 132 +++++++++++++ .../com/example/lab_v4/fragments/MyMapView.kt | 108 ++++++++++ .../res/drawable/ic_launcher_background.xml | 170 ++++++++++++++++ .../res/drawable/ic_launcher_foreground.xml | 30 +++ .../app/src/main/res/layout/activity_main.xml | 34 ++++ Lab1/app/src/main/res/layout/custom_row.xml | 66 +++++++ Lab1/app/src/main/res/layout/fragment_add.xml | 77 ++++++++ .../app/src/main/res/layout/fragment_list.xml | 32 +++ Lab1/app/src/main/res/layout/fragment_map.xml | 22 +++ .../app/src/main/res/menu/bottom_nav_menu.xml | 12 ++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 6 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes Lab1/app/src/main/res/navigation/my_nav.xml | 35 ++++ Lab1/app/src/main/res/values-night/themes.xml | 7 + Lab1/app/src/main/res/values/colors.xml | 5 + Lab1/app/src/main/res/values/strings.xml | 5 + Lab1/app/src/main/res/values/themes.xml | 9 + Lab1/app/src/main/res/xml/backup_rules.xml | 13 ++ .../main/res/xml/data_extraction_rules.xml | 19 ++ .../com/example/lab_v4/ExampleUnitTest.kt | 17 ++ Lab1/build.gradle.kts | 5 + Lab1/gradle.properties | 23 +++ Lab1/gradle/libs.versions.toml | 30 +++ Lab1/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes Lab1/gradle/wrapper/gradle-wrapper.properties | 6 + Lab1/gradlew | 185 ++++++++++++++++++ Lab1/gradlew.bat | 89 +++++++++ Lab1/settings.gradle.kts | 23 +++ 68 files changed, 1828 insertions(+) create mode 100644 Lab1/.gitignore create mode 100644 Lab1/.idea/.gitignore create mode 100644 Lab1/.idea/.name create mode 100644 Lab1/.idea/compiler.xml create mode 100644 Lab1/.idea/deploymentTargetSelector.xml create mode 100644 Lab1/.idea/gradle.xml create mode 100644 Lab1/.idea/kotlinc.xml create mode 100644 Lab1/.idea/migrations.xml create mode 100644 Lab1/.idea/misc.xml create mode 100644 Lab1/.idea/runConfigurations.xml create mode 100644 Lab1/app/.gitignore create mode 100644 Lab1/app/build.gradle.kts create mode 100644 Lab1/app/proguard-rules.pro create mode 100644 Lab1/app/src/androidTest/java/com/example/lab_v4/ExampleInstrumentedTest.kt create mode 100644 Lab1/app/src/main/AndroidManifest.xml create mode 100644 Lab1/app/src/main/assets/database/LDB.sqlite3 create mode 100644 Lab1/app/src/main/java/com/example/lab_v4/GlobalViewModel.kt create mode 100644 Lab1/app/src/main/java/com/example/lab_v4/MainActivity.kt create mode 100644 Lab1/app/src/main/java/com/example/lab_v4/MeasurementPoint.kt create mode 100644 Lab1/app/src/main/java/com/example/lab_v4/Vec2Int.kt create mode 100644 Lab1/app/src/main/java/com/example/lab_v4/db/Matavimas.kt create mode 100644 Lab1/app/src/main/java/com/example/lab_v4/db/MatavimasDao.kt create mode 100644 Lab1/app/src/main/java/com/example/lab_v4/db/MyDatabase.kt create mode 100644 Lab1/app/src/main/java/com/example/lab_v4/db/Stiprumas.kt create mode 100644 Lab1/app/src/main/java/com/example/lab_v4/db/StiprumasDao.kt create mode 100644 Lab1/app/src/main/java/com/example/lab_v4/db/Vartotojas.kt create mode 100644 Lab1/app/src/main/java/com/example/lab_v4/db/VartotojasDao.kt create mode 100644 Lab1/app/src/main/java/com/example/lab_v4/fragments/AddFragment.kt create mode 100644 Lab1/app/src/main/java/com/example/lab_v4/fragments/ListAdapter.kt create mode 100644 Lab1/app/src/main/java/com/example/lab_v4/fragments/ListFragment.kt create mode 100644 Lab1/app/src/main/java/com/example/lab_v4/fragments/MapFragment.kt create mode 100644 Lab1/app/src/main/java/com/example/lab_v4/fragments/MyMapView.kt create mode 100644 Lab1/app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 Lab1/app/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 Lab1/app/src/main/res/layout/activity_main.xml create mode 100644 Lab1/app/src/main/res/layout/custom_row.xml create mode 100644 Lab1/app/src/main/res/layout/fragment_add.xml create mode 100644 Lab1/app/src/main/res/layout/fragment_list.xml create mode 100644 Lab1/app/src/main/res/layout/fragment_map.xml create mode 100644 Lab1/app/src/main/res/menu/bottom_nav_menu.xml create mode 100644 Lab1/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 Lab1/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 Lab1/app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 Lab1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 Lab1/app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 Lab1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 Lab1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 Lab1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 Lab1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 Lab1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 Lab1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 Lab1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 Lab1/app/src/main/res/navigation/my_nav.xml create mode 100644 Lab1/app/src/main/res/values-night/themes.xml create mode 100644 Lab1/app/src/main/res/values/colors.xml create mode 100644 Lab1/app/src/main/res/values/strings.xml create mode 100644 Lab1/app/src/main/res/values/themes.xml create mode 100644 Lab1/app/src/main/res/xml/backup_rules.xml create mode 100644 Lab1/app/src/main/res/xml/data_extraction_rules.xml create mode 100644 Lab1/app/src/test/java/com/example/lab_v4/ExampleUnitTest.kt create mode 100644 Lab1/build.gradle.kts create mode 100644 Lab1/gradle.properties create mode 100644 Lab1/gradle/libs.versions.toml create mode 100644 Lab1/gradle/wrapper/gradle-wrapper.jar create mode 100644 Lab1/gradle/wrapper/gradle-wrapper.properties create mode 100644 Lab1/gradlew create mode 100644 Lab1/gradlew.bat create mode 100644 Lab1/settings.gradle.kts diff --git a/Lab1/.gitignore b/Lab1/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/Lab1/.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/Lab1/.idea/.gitignore b/Lab1/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/Lab1/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/Lab1/.idea/.name b/Lab1/.idea/.name new file mode 100644 index 0000000..84725a2 --- /dev/null +++ b/Lab1/.idea/.name @@ -0,0 +1 @@ +Lab_v4 \ No newline at end of file diff --git a/Lab1/.idea/compiler.xml b/Lab1/.idea/compiler.xml new file mode 100644 index 0000000..b86273d --- /dev/null +++ b/Lab1/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Lab1/.idea/deploymentTargetSelector.xml b/Lab1/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..fc8303f --- /dev/null +++ b/Lab1/.idea/deploymentTargetSelector.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Lab1/.idea/gradle.xml b/Lab1/.idea/gradle.xml new file mode 100644 index 0000000..7b3006b --- /dev/null +++ b/Lab1/.idea/gradle.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/Lab1/.idea/kotlinc.xml b/Lab1/.idea/kotlinc.xml new file mode 100644 index 0000000..148fdd2 --- /dev/null +++ b/Lab1/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/Lab1/.idea/migrations.xml b/Lab1/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/Lab1/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/Lab1/.idea/misc.xml b/Lab1/.idea/misc.xml new file mode 100644 index 0000000..b2c751a --- /dev/null +++ b/Lab1/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/Lab1/.idea/runConfigurations.xml b/Lab1/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/Lab1/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/Lab1/app/.gitignore b/Lab1/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/Lab1/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/Lab1/app/build.gradle.kts b/Lab1/app/build.gradle.kts new file mode 100644 index 0000000..55f171b --- /dev/null +++ b/Lab1/app/build.gradle.kts @@ -0,0 +1,81 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + id("kotlin-android") + id("kotlin-kapt") +} + +android { + namespace = "com.example.lab_v4" + compileSdk = 35 + + defaultConfig { + applicationId = "com.example.lab_v4" + minSdk = 24 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = "11" + } + buildFeatures { + viewBinding = true + } +} + +dependencies { + + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.material) + implementation(libs.androidx.activity) + implementation(libs.androidx.constraintlayout) + implementation(libs.androidx.navigation.fragment.ktx) + implementation(libs.androidx.navigation.ui.ktx) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + + // Material Design + implementation("com.google.android.material:material:1.12.0") + + // Navigation Component + implementation("androidx.navigation:navigation-fragment-ktx:2.8.3") + implementation("androidx.navigation:navigation-ui-ktx:2.8.3") + + // Room components + implementation("androidx.room:room-runtime:2.6.1") + kapt("androidx.room:room-compiler:2.6.1") + implementation("androidx.room:room-ktx:2.6.1") + + // Lifecycle components + implementation("androidx.lifecycle:lifecycle-extensions:2.2.0") + implementation("androidx.lifecycle:lifecycle-common-java8:2.8.7") + implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7") + + // Kotlin components + //implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72") + //api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9") + //api("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9") + // implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0") + + //DataBinding + //kapt("com.android.databinding:compiler:3.2.0-alpha10") +} \ No newline at end of file diff --git a/Lab1/app/proguard-rules.pro b/Lab1/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/Lab1/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/Lab1/app/src/androidTest/java/com/example/lab_v4/ExampleInstrumentedTest.kt b/Lab1/app/src/androidTest/java/com/example/lab_v4/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..4c5a2c8 --- /dev/null +++ b/Lab1/app/src/androidTest/java/com/example/lab_v4/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.lab_v4 + +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.lab_v4", appContext.packageName) + } +} \ No newline at end of file diff --git a/Lab1/app/src/main/AndroidManifest.xml b/Lab1/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..15e1965 --- /dev/null +++ b/Lab1/app/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Lab1/app/src/main/assets/database/LDB.sqlite3 b/Lab1/app/src/main/assets/database/LDB.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..aa02c647daacf71d6c3f2fd82fe94a0101a74cf9 GIT binary patch literal 57344 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 SX>gDsCR$L!Q^*;*`u_z;gnaz~ literal 0 HcmV?d00001 diff --git a/Lab1/app/src/main/java/com/example/lab_v4/GlobalViewModel.kt b/Lab1/app/src/main/java/com/example/lab_v4/GlobalViewModel.kt new file mode 100644 index 0000000..ad2d541 --- /dev/null +++ b/Lab1/app/src/main/java/com/example/lab_v4/GlobalViewModel.kt @@ -0,0 +1,44 @@ +package com.example.lab_v4 + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.switchMap +import androidx.lifecycle.viewModelScope +import com.example.lab_v4.db.Matavimas +import com.example.lab_v4.db.MatavimasDao +import com.example.lab_v4.db.MyDatabase +import com.example.lab_v4.db.Stiprumas +import com.example.lab_v4.db.StiprumasDao +import com.example.lab_v4.db.Vartotojas +import com.example.lab_v4.db.VartotojasDao +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class GlobalViewModel(application: Application): AndroidViewModel(application) { + + val vartotojai: LiveData> + val stiprumai: LiveData> + val matavimai: LiveData> + + private val vartotojaiDao: VartotojasDao + private val matavimaiDao: MatavimasDao + private val stiprumaiDao: StiprumasDao + + init { + var db = MyDatabase.getDatabase(application) + vartotojaiDao = db.vartotojasDao() + matavimaiDao = db.matavimasDao() + stiprumaiDao = db.stiprumasDao() + + vartotojai = vartotojaiDao.list() + matavimai = matavimaiDao.list() + stiprumai = stiprumaiDao.list() + } + + fun addVartotojas(vartotojas: Vartotojas) { + viewModelScope.launch(Dispatchers.IO) { + vartotojaiDao.insert(vartotojas) + } + } +} \ No newline at end of file diff --git a/Lab1/app/src/main/java/com/example/lab_v4/MainActivity.kt b/Lab1/app/src/main/java/com/example/lab_v4/MainActivity.kt new file mode 100644 index 0000000..d8a678a --- /dev/null +++ b/Lab1/app/src/main/java/com/example/lab_v4/MainActivity.kt @@ -0,0 +1,83 @@ +package com.example.lab_v4 + +import android.os.Bundle +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.fragment.app.Fragment +import androidx.fragment.app.replace +import androidx.navigation.findNavController +import androidx.navigation.ui.AppBarConfiguration +import androidx.navigation.ui.NavigationUI.setupWithNavController +import androidx.navigation.ui.setupActionBarWithNavController +import androidx.navigation.ui.setupWithNavController +import com.example.lab_v4.databinding.ActivityMainBinding +import com.example.lab_v4.fragments.ListFragment +import com.example.lab_v4.fragments.MapFragment +import com.google.android.material.bottomnavigation.BottomNavigationView + +class MainActivity : AppCompatActivity() { + lateinit var binding: ActivityMainBinding; + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + //setupActionBarWithNavController(findNavController(R.id.fragmentContainerView)) +// ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> +// val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) +// v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) +// insets +// } + +// val navController = findNavController(R.id.fragmentContainerView) +// val appBarConfiguration = AppBarConfiguration(setOf(R.id.mapFragment, R.id.listFragment)) +// setupActionBarWithNavController(navController, appBarConfiguration) + +// var bottomNavigationView = binding.bottomNavigationView; +// var navController = this.findNavController(R.id.fragmentContainerView); +// setupWithNavController(bottomNavigationView, navController); + +// replaceFragment(MapFragment()) + + } + + private fun replaceFragment(fragment: Fragment) { +// var fragmentManager = supportFragmentManager +// var fragmentTransaction = fragmentManager.beginTransaction() +// fragmentTransaction.replace(R.id.frame_layout, fragment) +// fragmentTransaction.commit() + } + + override fun onPostCreate(savedInstanceState: Bundle?) { + super.onPostCreate(savedInstanceState) + + val navView: BottomNavigationView = binding.bottomNavigationView + val navController = findNavController(R.id.fragmentContainerView) + //setupActionBarWithNavController(findNavController(R.id.fragmentContainerView)) + navView.setupWithNavController(navController) + binding.bottomNavigationView.setOnItemSelectedListener { item -> + when (item.itemId) { + R.id.navigation_map -> navController.navigate(R.id.mapFragment) + R.id.navigation_list -> navController.navigate(R.id.listFragment) + } + true; + } + +// val navView: BottomNavigationView = binding.bottomNavigationView +// val navController = findNavController(R.id.fragmentContainerView) +// // Passing each menu ID as a set of Ids because each +// // menu should be considered as top level destinations. +// val appBarConfiguration = AppBarConfiguration(setOf(R.id.navigation_map, R.id.navigation_list)) +// setupActionBarWithNavController(navController, appBarConfiguration) +// navView.setupWithNavController(navController) + } + + override fun onSupportNavigateUp(): Boolean { + val navController = findNavController(R.id.fragmentContainerView) + return navController.navigateUp() || super.onSupportNavigateUp() + } +} \ No newline at end of file diff --git a/Lab1/app/src/main/java/com/example/lab_v4/MeasurementPoint.kt b/Lab1/app/src/main/java/com/example/lab_v4/MeasurementPoint.kt new file mode 100644 index 0000000..6702edb --- /dev/null +++ b/Lab1/app/src/main/java/com/example/lab_v4/MeasurementPoint.kt @@ -0,0 +1,7 @@ +package com.example.lab_v4 + +data class MeasurementPoint( + val x: Int, + val y: Int, + val strengths: Triple +); diff --git a/Lab1/app/src/main/java/com/example/lab_v4/Vec2Int.kt b/Lab1/app/src/main/java/com/example/lab_v4/Vec2Int.kt new file mode 100644 index 0000000..f9170a6 --- /dev/null +++ b/Lab1/app/src/main/java/com/example/lab_v4/Vec2Int.kt @@ -0,0 +1,6 @@ +package com.example.lab_v4 + +data class Vec2Int( + val x: Int, + val y: Int +) \ No newline at end of file diff --git a/Lab1/app/src/main/java/com/example/lab_v4/db/Matavimas.kt b/Lab1/app/src/main/java/com/example/lab_v4/db/Matavimas.kt new file mode 100644 index 0000000..60bf619 --- /dev/null +++ b/Lab1/app/src/main/java/com/example/lab_v4/db/Matavimas.kt @@ -0,0 +1,13 @@ +package com.example.lab_v4.db + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "matavimai") +data class Matavimas( + @PrimaryKey(autoGenerate = true) + val matavimas: Int, + val x: Int, + val y: Int, + val atstumas: Float +) diff --git a/Lab1/app/src/main/java/com/example/lab_v4/db/MatavimasDao.kt b/Lab1/app/src/main/java/com/example/lab_v4/db/MatavimasDao.kt new file mode 100644 index 0000000..29c9e5c --- /dev/null +++ b/Lab1/app/src/main/java/com/example/lab_v4/db/MatavimasDao.kt @@ -0,0 +1,12 @@ +package com.example.lab_v4.db + +import androidx.lifecycle.LiveData +import androidx.room.Dao +import androidx.room.Query + +@Dao +interface MatavimasDao { + + @Query("SELECT * FROM matavimai") + fun list(): LiveData> +} \ No newline at end of file diff --git a/Lab1/app/src/main/java/com/example/lab_v4/db/MyDatabase.kt b/Lab1/app/src/main/java/com/example/lab_v4/db/MyDatabase.kt new file mode 100644 index 0000000..2abaf56 --- /dev/null +++ b/Lab1/app/src/main/java/com/example/lab_v4/db/MyDatabase.kt @@ -0,0 +1,38 @@ +package com.example.lab_v4.db + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase + +@Database( + entities = [Vartotojas::class, Matavimas::class, Stiprumas::class], + version = 2, + exportSchema = false +) +abstract class MyDatabase : RoomDatabase() { + abstract fun vartotojasDao(): VartotojasDao + abstract fun matavimasDao(): MatavimasDao + abstract fun stiprumasDao(): StiprumasDao + + companion object { + @Volatile + private var INSTANCE: MyDatabase? = null + + fun getDatabase(context: Context): MyDatabase { + if (INSTANCE != null) { + return INSTANCE!!; + } + + synchronized(this){ + val instance = Room.databaseBuilder( + context.applicationContext, + MyDatabase::class.java, + "LDB" + ).createFromAsset("database/LDB.sqlite3").build() + INSTANCE = instance + return instance + } + } + } +} \ No newline at end of file diff --git a/Lab1/app/src/main/java/com/example/lab_v4/db/Stiprumas.kt b/Lab1/app/src/main/java/com/example/lab_v4/db/Stiprumas.kt new file mode 100644 index 0000000..72e8c7c --- /dev/null +++ b/Lab1/app/src/main/java/com/example/lab_v4/db/Stiprumas.kt @@ -0,0 +1,13 @@ +package com.example.lab_v4.db + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "stiprumai") +data class Stiprumas( + @PrimaryKey(autoGenerate = true) + val id: Int, + val sensorius: String, + val stiprumas: Int, + val matavimas: Int +) diff --git a/Lab1/app/src/main/java/com/example/lab_v4/db/StiprumasDao.kt b/Lab1/app/src/main/java/com/example/lab_v4/db/StiprumasDao.kt new file mode 100644 index 0000000..df37cee --- /dev/null +++ b/Lab1/app/src/main/java/com/example/lab_v4/db/StiprumasDao.kt @@ -0,0 +1,12 @@ +package com.example.lab_v4.db + +import androidx.lifecycle.LiveData +import androidx.room.Dao +import androidx.room.Query + +@Dao +interface StiprumasDao { + + @Query("SELECT * FROM stiprumai") + fun list(): LiveData> +} \ No newline at end of file diff --git a/Lab1/app/src/main/java/com/example/lab_v4/db/Vartotojas.kt b/Lab1/app/src/main/java/com/example/lab_v4/db/Vartotojas.kt new file mode 100644 index 0000000..b1cf8e4 --- /dev/null +++ b/Lab1/app/src/main/java/com/example/lab_v4/db/Vartotojas.kt @@ -0,0 +1,13 @@ +package com.example.lab_v4.db + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "vartotojai") +data class Vartotojas( + @PrimaryKey(autoGenerate = true) + val id: Int, + val mac: String, + val sensorius: String, + val stiprumas: Int +) diff --git a/Lab1/app/src/main/java/com/example/lab_v4/db/VartotojasDao.kt b/Lab1/app/src/main/java/com/example/lab_v4/db/VartotojasDao.kt new file mode 100644 index 0000000..ac98cae --- /dev/null +++ b/Lab1/app/src/main/java/com/example/lab_v4/db/VartotojasDao.kt @@ -0,0 +1,20 @@ +package com.example.lab_v4.db + +import androidx.lifecycle.LiveData +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.Query + +@Dao +interface VartotojasDao { + + @Insert + fun insert(vartotojas: Vartotojas) + + @Delete + fun delete(vartotojas: Vartotojas) + + @Query("SELECT * FROM vartotojai") + fun list(): LiveData> +} \ No newline at end of file diff --git a/Lab1/app/src/main/java/com/example/lab_v4/fragments/AddFragment.kt b/Lab1/app/src/main/java/com/example/lab_v4/fragments/AddFragment.kt new file mode 100644 index 0000000..62a65df --- /dev/null +++ b/Lab1/app/src/main/java/com/example/lab_v4/fragments/AddFragment.kt @@ -0,0 +1,51 @@ +package com.example.lab_v4.fragments + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.lifecycle.ViewModelProvider +import androidx.navigation.fragment.findNavController +import com.example.lab_v4.GlobalViewModel +import com.example.lab_v4.R +import com.example.lab_v4.databinding.FragmentAddBinding +import com.example.lab_v4.databinding.FragmentListBinding +import com.example.lab_v4.db.Vartotojas + +class AddFragment : Fragment() { + private lateinit var globalViewModel: GlobalViewModel + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + val binding = FragmentAddBinding.inflate(inflater, container, false) + globalViewModel = ViewModelProvider(requireActivity()).get(GlobalViewModel::class.java) + + binding.button.setOnClickListener { + if (binding.editMac.text.isEmpty() || binding.editSensorius1.text.isEmpty() || binding.editSensorius2.text.isEmpty() || binding.editSensorius3.text.isEmpty()) { + Toast.makeText(requireContext(), "ERROR: All fields are required", Toast.LENGTH_LONG).show() + return@setOnClickListener + } + + try { + var mac = binding.editMac.text.toString() + var sensorius1 = Integer.parseInt(binding.editSensorius1.text.toString()) + var sensorius2 = Integer.parseInt(binding.editSensorius2.text.toString()) + var sensorius3 = Integer.parseInt(binding.editSensorius3.text.toString()) + globalViewModel.addVartotojas(Vartotojas(0, mac, "wiliboxas1", sensorius1)) + globalViewModel.addVartotojas(Vartotojas(0, mac, "wiliboxas2", sensorius2)) + globalViewModel.addVartotojas(Vartotojas(0, mac, "wiliboxas3", sensorius3)) + + Toast.makeText(requireContext(), "Added", Toast.LENGTH_LONG).show() + findNavController().navigate(R.id.action_addFragment_to_listFragment) + } catch (_: NumberFormatException) { + Toast.makeText(requireContext(), "ERROR: Failed to parse numbers", Toast.LENGTH_LONG).show() + } + } + + return binding.root; + } +} \ No newline at end of file diff --git a/Lab1/app/src/main/java/com/example/lab_v4/fragments/ListAdapter.kt b/Lab1/app/src/main/java/com/example/lab_v4/fragments/ListAdapter.kt new file mode 100644 index 0000000..ec07779 --- /dev/null +++ b/Lab1/app/src/main/java/com/example/lab_v4/fragments/ListAdapter.kt @@ -0,0 +1,54 @@ +package com.example.lab_v4.fragments + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.example.lab_v4.R +import com.example.lab_v4.db.Vartotojas + +class ListAdapter : RecyclerView.Adapter() +{ + private var vartotojai = emptyList>(); + + class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) { + val rowNumber: TextView + val mac: TextView + val sensor1: TextView + val sensor2: TextView + val sensor3: TextView + + init { + rowNumber = view.findViewById(R.id.rowNumber) + mac = view.findViewById(R.id.mac) + sensor1 = view.findViewById(R.id.sensor1) + sensor2 = view.findViewById(R.id.sensor2) + sensor3 = view.findViewById(R.id.sensor3) + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { + return MyViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.custom_row, parent, false)) + } + + override fun getItemCount(): Int { + return vartotojai.size + } + + override fun onBindViewHolder(holder: MyViewHolder, position: Int) { + var currentItem = vartotojai[position] + assert(currentItem.size == 3) + + holder.rowNumber.setText((position+1).toString()) + holder.mac.text = currentItem[0].mac + holder.sensor1.text = currentItem[0].stiprumas.toString() + holder.sensor2.text = currentItem[1].stiprumas.toString() + holder.sensor3.text = currentItem[2].stiprumas.toString() + } + + fun setData(data: List>) { + this.vartotojai = data + notifyDataSetChanged() + } +} \ No newline at end of file diff --git a/Lab1/app/src/main/java/com/example/lab_v4/fragments/ListFragment.kt b/Lab1/app/src/main/java/com/example/lab_v4/fragments/ListFragment.kt new file mode 100644 index 0000000..308d395 --- /dev/null +++ b/Lab1/app/src/main/java/com/example/lab_v4/fragments/ListFragment.kt @@ -0,0 +1,43 @@ +package com.example.lab_v4.fragments + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.LinearLayoutManager +import com.example.lab_v4.GlobalViewModel +import com.example.lab_v4.R +import com.example.lab_v4.databinding.FragmentListBinding + + +class ListFragment : Fragment() { + private lateinit var globalViewModel: GlobalViewModel + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + val binding = FragmentListBinding.inflate(inflater, container, false) + + val adapter = ListAdapter() + val recyclerView = binding.recycler + recyclerView.adapter = adapter + recyclerView.layoutManager = LinearLayoutManager(requireContext()) + + globalViewModel = ViewModelProvider(requireActivity()).get(GlobalViewModel::class.java) + globalViewModel.vartotojai.observe(viewLifecycleOwner, { vartotojai -> + var groupedVartotojai = vartotojai.sortedBy { v -> v.sensorius }.groupBy { v -> v.mac } + adapter.setData(groupedVartotojai.values.toList()) + }) + + binding.floatingActionButton.setOnClickListener { + findNavController().navigate(R.id.action_listFragment_to_addFragment) + } + + return binding.root + } +} \ No newline at end of file diff --git a/Lab1/app/src/main/java/com/example/lab_v4/fragments/MapFragment.kt b/Lab1/app/src/main/java/com/example/lab_v4/fragments/MapFragment.kt new file mode 100644 index 0000000..8b43274 --- /dev/null +++ b/Lab1/app/src/main/java/com/example/lab_v4/fragments/MapFragment.kt @@ -0,0 +1,132 @@ +package com.example.lab_v4.fragments + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.ViewModelProvider +import com.example.lab_v4.GlobalViewModel +import com.example.lab_v4.MeasurementPoint +import com.example.lab_v4.Vec2Int +import com.example.lab_v4.databinding.FragmentMapBinding +import kotlin.math.pow + + +class MapFragment : Fragment() { + + lateinit var binding: FragmentMapBinding + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentMapBinding.inflate(inflater, container, false) + + val globalViewModel = ViewModelProvider(requireActivity()).get(GlobalViewModel::class.java) + globalViewModel.stiprumai.observe(viewLifecycleOwner) { + updateMapDatapoints() + updateMapHighlights() + binding.myMapView.invalidate() + } + + globalViewModel.matavimai.observe(viewLifecycleOwner) { + updateMapDatapoints() + updateMapHighlights() + binding.myMapView.invalidate() + } + + globalViewModel.vartotojai.observe(viewLifecycleOwner, { vartotojai -> + updateMapHighlights() + binding.myMapView.invalidate() + }) + return binding.root + } + + private fun updateMapDatapoints() + { + val globalViewModel = ViewModelProvider(requireActivity()).get(GlobalViewModel::class.java) + var matavimai = globalViewModel.matavimai.value + + var datapoints = mutableListOf(); + if (matavimai != null) { + for (matavimas in matavimai) { + datapoints.add(Vec2Int(matavimas.x, matavimas.y)) + } + } + + binding.myMapView.datapoints = datapoints; + } + + private fun updateMapHighlights() + { + val globalViewModel = ViewModelProvider(requireActivity()).get(GlobalViewModel::class.java) + + var datapoints = mutableListOf(); + + var matavimai = globalViewModel.matavimai.value + var stiprumai = globalViewModel.stiprumai.value + if (matavimai != null && stiprumai != null) { + for (matavimas in matavimai) { + var distances = mutableListOf>() + for (stiprumas in stiprumai.filter { stiprumas -> stiprumas.matavimas == matavimas.matavimas }) { + distances.add(Pair(stiprumas.stiprumas.toFloat(), stiprumas.sensorius)); + } + if (distances.size != 3) continue + + var distance1 = distances.find { p -> p.second == "wiliboxas1" } + if (distance1 == null) continue + var distance2 = distances.find { p -> p.second == "wiliboxas2" } + if (distance2 == null) continue + var distance3 = distances.find { p -> p.second == "wiliboxas3" } + if (distance3 == null) continue + + datapoints.add(MeasurementPoint(matavimas.x, matavimas.y, Triple(distance1.first, distance2.first, distance3.first))) + } + } + + var highlights = mutableListOf() + + var vartotojai = globalViewModel.vartotojai.value + if (vartotojai != null) { + var groupedVartotojai = vartotojai.sortedBy { v -> v.sensorius }.groupBy { v -> v.mac } + for (group in groupedVartotojai.values) { + + var strength1 = group.find { p -> p.sensorius == "wiliboxas1" } + if (strength1 == null) continue + var strength2 = group.find { p -> p.sensorius == "wiliboxas2" } + if (strength2 == null) continue + var strength3 = group.find { p -> p.sensorius == "wiliboxas3" } + if (strength3 == null) continue + + var nearestPoint = findNearest(datapoints, Triple(strength1.stiprumas.toFloat(), strength2.stiprumas.toFloat(), strength3.stiprumas.toFloat())) + highlights.add(nearestPoint) + } + } + + binding.myMapView.highlights = highlights + } + + private fun findNearest(datapoints: List, strengths: Triple): Vec2Int? + { + var nearest: MeasurementPoint? = null; + var nearestDistance = 0f + + for (datapoint in datapoints) { + var distance = (datapoint.strengths.first - strengths.first).pow(2) + + (datapoint.strengths.second - strengths.second).pow(2) + + (datapoint.strengths.third - strengths.third).pow(2) + + if (nearest == null || (nearestDistance > distance)) { + nearest = datapoint + nearestDistance = distance + } + } + + if (nearest != null) { + return Vec2Int(nearest.x, nearest.y) + } else { + return null; + } + } +} \ No newline at end of file diff --git a/Lab1/app/src/main/java/com/example/lab_v4/fragments/MyMapView.kt b/Lab1/app/src/main/java/com/example/lab_v4/fragments/MyMapView.kt new file mode 100644 index 0000000..168043f --- /dev/null +++ b/Lab1/app/src/main/java/com/example/lab_v4/fragments/MyMapView.kt @@ -0,0 +1,108 @@ +package com.example.lab_v4 + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.util.AttributeSet +import android.view.View + +class MyMapView(context: Context?, attrs: AttributeSet?) : View(context, attrs) { + + var datapoints: List? = null + var highlights: List = listOf(); + + var bgPaint = Paint() + init { + bgPaint.color = Color.GRAY; + bgPaint.strokeWidth = 2f; + } + + var datapointPaint = Paint() + init { + datapointPaint.color = Color.RED; + datapointPaint.strokeWidth = 2f; + } + + var highlightPaint = Paint() + init { + highlightPaint.color = Color.CYAN; + highlightPaint.strokeWidth = 2f; + } + + var textPaint = Paint() + init { + textPaint.color = Color.BLACK; + textPaint.strokeWidth = 3f; + textPaint.textSize = 25f; + textPaint.textAlign = Paint.Align.CENTER; + } + + private fun getDimensions(): Pair, Pair>? { + if (datapoints == null) return null + if (datapoints!!.size <= 1) return null; + + var minX = datapoints!![0].x + var maxX = datapoints!![0].x + var minY = datapoints!![0].y + var maxY = datapoints!![0].y + + for (matavimas in datapoints!!) { + minX = Math.min(minX, matavimas.x) + maxX = Math.max(maxX, matavimas.x) + minY = Math.min(minY, matavimas.y) + maxY = Math.max(maxY, matavimas.y) + } + + return Pair(Pair(minX, maxX), Pair(minY, maxY)) + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + if (datapoints == null) return + + var dimentions = getDimensions() + if (dimentions == null) return; + + var minX = dimentions.first.first; + var maxX = dimentions.first.second; + var minY = dimentions.second.first; + var maxY = dimentions.second.second; + + var myWidth = (maxX - minX) + 1; + var myHeight = (maxY - minY) + 1; + + var margin = 20f; + var gapX = (width - margin * 2) / myWidth; + var gapY = (height - margin * 2) / myHeight; + + for (oy in 0.. point != null && point.x == x && point.y == y }; + if (highlightIndex != -1) { + paint = highlightPaint; + } else if (datapoints!!.find { point -> point.x == x && point.y == y } != null) { + paint = datapointPaint; + } + var circleX = gapX * (ox + 0.5f) + margin; + var circleY = gapY * (myHeight - oy + 0.5f - 1) + margin; + canvas.drawCircle(circleX, circleY, 12.5f, paint); + + if (highlightIndex != -1) { + canvas.drawText("${highlightIndex+1}", circleX, circleY + 8, textPaint); + } + } + } + + canvas.drawLine(0f, 0f, width.toFloat(), 0f, bgPaint); + canvas.drawLine(0f, height.toFloat(), width.toFloat(), height.toFloat(), bgPaint); + + canvas.drawLine(0f, 0f, 0f, height.toFloat(), bgPaint); + canvas.drawLine(width.toFloat(), 0f, width.toFloat(), height.toFloat(), bgPaint); + + } +} \ No newline at end of file diff --git a/Lab1/app/src/main/res/drawable/ic_launcher_background.xml b/Lab1/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/Lab1/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Lab1/app/src/main/res/drawable/ic_launcher_foreground.xml b/Lab1/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/Lab1/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Lab1/app/src/main/res/layout/activity_main.xml b/Lab1/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..0aa18c8 --- /dev/null +++ b/Lab1/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,34 @@ + + + + + + + + \ No newline at end of file diff --git a/Lab1/app/src/main/res/layout/custom_row.xml b/Lab1/app/src/main/res/layout/custom_row.xml new file mode 100644 index 0000000..2de9cda --- /dev/null +++ b/Lab1/app/src/main/res/layout/custom_row.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Lab1/app/src/main/res/layout/fragment_add.xml b/Lab1/app/src/main/res/layout/fragment_add.xml new file mode 100644 index 0000000..8def09a --- /dev/null +++ b/Lab1/app/src/main/res/layout/fragment_add.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + +