Skip to content

Commit

Permalink
2024-05-28 lecture notes android mvvm
Browse files Browse the repository at this point in the history
  • Loading branch information
htl-leonding committed May 28, 2024
1 parent 9cf1649 commit 3b93bb6
Show file tree
Hide file tree
Showing 69 changed files with 2,169 additions and 5 deletions.
Binary file added asciidocs/images/viewmodel.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 29 additions & 5 deletions asciidocs/index.adoc
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
= 23/24 4bhif wmc - Lecture Notes
ifndef::imagesdir[:imagesdir: images]
Thomas Stütz
1.0.0, {docdate}: Lecture Notes for Courses at HTL Leonding
:icons: font
:experimental:
:sectnums:
ifndef::imagesdir[:imagesdir: images]
:toc:
ifdef::backend-html5[]

ifdef::backend-html5[]
// https://fontawesome.com/v4.7.0/icons/
icon:file-text-o[link=https://github.com/2324-4bhif-wmc/2324-4bhif-wmc-lecture-notes/main/asciidocs/{docname}.adoc] ‏ ‏ ‎
icon:github-square[link=https://github.com/2324-4bhif-wmc/2324-4bhif-wmc-lecture-notes] ‏ ‏ ‎
Expand Down Expand Up @@ -835,9 +837,9 @@ public class Fahrzeug {
Möglichkeit 3: JOINED -> Attribute der Basisklasse in einer
Tabelle und je eine Tabelle pro
abgeleiteter Klasse (mit
Diskriminator* DTYPE)
Diskriminator DTYPE)

__*Diskriminator = Unterscheidungsmerkmal__
* __Diskriminator: Unterscheidungsmerkmal__

[source, java]
----
Expand Down Expand Up @@ -1287,7 +1289,7 @@ ng g c components/todo

== 2024-04-16 Angular HttpClient

* http-client hat fetch gegenüber den Vorteil, dass gute Infrastruktur wir JWT support und middleware zum debuggen
* http-client hat fetch gegenüber den Vorteil, dass gute Infrastruktur mit JWT support und middleware zum debuggen


== 2024-05-21 Android
Expand Down Expand Up @@ -1364,3 +1366,25 @@ PGADMIN_DEFAULT_EMAIL=<your-email>
PGADMIN_DEFAULT_PASSWORD=<pgadmin-pw>
----

== 2024-05-28 ViewModel in Android

image::viewmodel.png[]


* https://medium.com/sahibinden-technology/package-by-layer-vs-package-by-feature-7e89cde2ae3a[Package by Layer vs Package by Feature^]


* https://medium.com/androiddevelopers/under-the-hood-of-jetpack-compose-part-2-of-2-37b2c20c6cdd[Under the hood of Jetpack Compose — part 2 of 2^]


* Anmerkungen zur https://github.com/caberger/android[Prof. Abergers Android-App^]


. Einführung des MVVM - Design Patterns, ein ViewModel pro View. Damit das verständlich wird vorher unbedingt die ersten 17 Minuten von https://www.youtube.com/watch?v=W1ymVx6dmvc ansehen. Hierbei im Kopf "Swift" durch „Java" ersetzen und "SwiftUI" durch "Jetpack Compose".
. Read Only State: In den ViewModels (Todo, Home, TabScreen) sind die Models jetzt Java Records, also readonly, damit sind die 3 Design Prinzipien OK (https://redux.js.org/understanding/thinking-in-redux/three-principles)
. Es gibt jetzt eine saubere Preview - Architektur für alle Views und das obwohl alles in Java und nicht in Kolin geschrieben ist (mit Ausnahme der Jetpack @Composables).
. Verbesserungen bei der Android - Implementierung (util Package) für:
.. RestEasy Client f. Android,
.. Microprofile config (=application.properties f. Android),
.. Jakarta Dependency Injection (@Inject für Android) und
.. RxJava (https://rxmarbles.com für Android).
33 changes: 33 additions & 0 deletions labs/android-mvvm/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Gradle files
.gradle/
build/

# Local configuration file (sdk path, etc)
local.properties

# Log/OS Files
*.log

# Android Studio generated files and folders
captures/
.externalNativeBuild/
.cxx/
*.apk
output.json

# IntelliJ
*.iml
.idea/
misc.xml
deploymentTargetDropDown.xml
render.experimental.xml

# Keystore files
*.jks
*.keystore

# Google Services (e.g. APIs or Firebase)
google-services.json

# Android Profiling
*.hprof
24 changes: 24 additions & 0 deletions labs/android-mvvm/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <https://unlicense.org>
1 change: 1 addition & 0 deletions labs/android-mvvm/app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
111 changes: 111 additions & 0 deletions labs/android-mvvm/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")

kotlin("kapt") // we have to use kapt, because android hilt is still in alpha although kapt is deprecated already ?! :(
id("com.google.dagger.hilt.android")
}

android {
namespace = "at.htl.leonding"
compileSdk = 34

defaultConfig {
applicationId = "at.htl.leonding"
minSdk = 28
targetSdk = 34
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"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.4.3"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
excludes += "META-INF/INDEX.LIST"
excludes += "META-INF/DEPENDENCIES"
excludes += "META-INF/LICENSE.md"
excludes += "META-INF/NOTICE.md"
excludes += "META-INF/DEPENDENCIES.txt"
}
}
}

dependencies {
implementation(libs.androidx.ktx)
implementation(libs.androidx.lifecycle)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.activity.compose)

implementation(platform(libs.androidx.compose.bom))
implementation(libs.compose.ui.ui)
implementation(libs.compose.ui.graphics)
implementation(libs.compose.ui.ui.tooling.preview)
implementation(libs.compose.material)

testImplementation(libs.test.junit)
androidTestImplementation(libs.androidx.test.ext.junit)
androidTestImplementation(libs.androidx.test.espresso)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.compose.ui.test)

debugImplementation(libs.debug.compose.ui.tooling)
debugImplementation(libs.debug.compose.manifest)

implementation(libs.rxjava.rxjava)
implementation(libs.rxjava.android)
implementation(libs.compose.rxjava)

implementation(libs.dagger.hilt)
kapt(libs.kapt.hilt)
implementation(libs.fasterxml.jackson)
implementation(libs.resteasy.client)
implementation(libs.smallrye.config)
}
kapt {
correctErrorTypes = true
}

/** JavaDoc helper.
* This tasks writes the the class-path to a file that can be used with javadoc
* javadoc ... @javadoc.txt
*/
tasks.register("javadoc-params") {
doLast {
val variant = project.android.applicationVariants
val release = variant.filter{ it.buildType.name == "release" }.first()
val outputFile = project.layout.buildDirectory.file("javadoc.txt").get().asFile
outputFile.printWriter().use { out ->
val classpath = release.compileConfiguration.joinToString(separator=":") { it.toString() }
out.println("--class-path " + classpath)
out.println()
}
println("javadoc options written to " + outputFile.absolutePath)
}
}
21 changes: 21 additions & 0 deletions labs/android-mvvm/app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -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
30 changes: 30 additions & 0 deletions labs/android-mvvm/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?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-permission android:name="android.permission.INTERNET"></uses-permission>
<application
android:name=".ToDoApplication"
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.ToDo"
android:usesCleartextTraffic="true"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.ToDo">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package at.htl.leonding;

import android.os.Bundle;

import javax.inject.Inject;

import androidx.activity.ComponentActivity;
import androidx.compose.ui.platform.ComposeView;
import dagger.hilt.android.AndroidEntryPoint;

/** Our main activity implemented in java.
* We separate the application logic from the view.
* The View is implemented in a separate file (separation of concerns).
* We use Kotlin for the Jetpack Compose views.
*/
@AndroidEntryPoint
public class MainActivity extends ComponentActivity {
@Inject
MainViewRenderer mainViewRenderer;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
var view = new ComposeView(this);
mainViewRenderer.setContent(view);
setContentView(view);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package at.htl.leonding

import android.content.res.Configuration
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Surface
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalConfiguration
import at.htl.leonding.feature.tabscreen.TabView
import at.htl.leonding.model.Store
import at.htl.leonding.model.UIState
import javax.inject.Inject
import javax.inject.Singleton

/** Render the contents of the Main Compose View.
* We also watch orientation changes and forward those to the Model.
*/
@Singleton
class MainViewRenderer @Inject constructor() {
@Inject
lateinit var tabScreenView: TabView
@Inject
lateinit var store: Store

fun setContent(view: ComposeView) {
view.setContent {
Surface(modifier = Modifier.fillMaxSize()) {
var orientation by remember { mutableIntStateOf(Configuration.ORIENTATION_PORTRAIT) }
val configuration = LocalConfiguration.current
LaunchedEffect(configuration) {
orientation = configuration.orientation
val currentOrientation = orientationFromConfiguration(configuration)
store.apply { it.uiState.orientation = currentOrientation }
}
tabScreenView.TabViewLayout()
}
}
}
private fun orientationFromConfiguration(configuration: Configuration): UIState.Orientation {
return when(configuration.orientation) {
Configuration.ORIENTATION_PORTRAIT -> UIState.Orientation.portrait
Configuration.ORIENTATION_LANDSCAPE -> UIState.Orientation.landscape
else -> UIState.Orientation.undefined
}
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package at.htl.leonding;

import android.app.Application;
import android.util.Log;

import javax.inject.Singleton;

import dagger.hilt.android.HiltAndroidApp;

/** Our application entry point.
* Needed as the dependency injection container.
*/
@HiltAndroidApp
@Singleton
public class ToDoApplication extends Application {
}
Loading

0 comments on commit 3b93bb6

Please sign in to comment.