diff --git a/README.md b/README.md
index 17e3bd2..4b0b165 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,24 @@
# M1-Projet
+
+Cette application est la partie cliente du projet Palto.
+
+## Fonctionnalités
+
+- Se connecter sur un serveur Palto avec ses identifiants, ou bien se connecter en anonyme.
+- Sur le menu :
+ - Voir la liste des sessions créées.
+ - Créer une nouvelle session en lui donnant un nom.
+ - Ouvrir une session en cliquant sur l’item.
+- Lorsqu’une session est ouverte :
+ - Voir la list des présences.
+ - Scanner une carte NFC et créer un nouvel étudiant associé à cette carte.
+ - Ajouter manuellement un étudiant à la liste.
+
+## Prérequis
+
+- Android 8.1 SDK 27.
+- Support du NFC
+
+## Utilisation
+
+Importer le projet dans Android Studio.
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 39d63cb..e50f1e9 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -29,7 +29,6 @@ android {
kotlinOptions {
jvmTarget = "1.8"
}
- buildToolsVersion = "33.0.1"
buildFeatures {
viewBinding = true
}
@@ -38,16 +37,21 @@ android {
dependencies {
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
- implementation("com.google.android.material:material:1.10.0")
+ implementation("com.google.android.material:material:1.11.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
- implementation("androidx.navigation:navigation-fragment-ktx:2.7.5")
- implementation("androidx.navigation:navigation-ui-ktx:2.7.5")
- implementation("androidx.annotation:annotation:1.7.0")
+ implementation("androidx.navigation:navigation-fragment-ktx:2.7.6")
+ implementation("androidx.navigation:navigation-ui-ktx:2.7.6")
+ implementation("androidx.annotation:annotation:1.7.1")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.2")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
implementation("androidx.legacy:legacy-support-v4:1.0.0")
- implementation("androidx.recyclerview:recyclerview:1.3.0")
+ implementation("androidx.recyclerview:recyclerview:1.3.2")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
+
+ // Retrofit and Moshi for API requests.
+ implementation("com.squareup.retrofit2:retrofit:2.9.0")
+ implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
+ implementation("com.squareup.moshi:moshi-kotlin:1.13.0")
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index fc2cb02..cf82b15 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,6 +5,7 @@
+
>()
+ val tagId: LiveData> = _tagId
+
+ @OptIn(ExperimentalStdlibApi::class)
+ fun setTag(tag: Tag) {
+ Log.d("Nfc", "A new tag has been set.")
+ _tagId.postValue(Event(tag.id.toHexString()))
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/PaltoActivity.kt b/app/src/main/java/com/example/palto/PaltoActivity.kt
index 8a70afa..02773bd 100644
--- a/app/src/main/java/com/example/palto/PaltoActivity.kt
+++ b/app/src/main/java/com/example/palto/PaltoActivity.kt
@@ -1,64 +1,96 @@
package com.example.palto
import android.nfc.NfcAdapter
-import android.nfc.Tag
import android.os.Bundle
import android.util.Log
-import androidx.appcompat.app.AppCompatActivity
+import android.view.Menu
+import android.view.MenuInflater
import androidx.activity.viewModels
+import androidx.appcompat.app.AppCompatActivity
+import androidx.navigation.fragment.NavHostFragment
+import androidx.navigation.ui.AppBarConfiguration
+import androidx.navigation.ui.setupWithNavController
+import com.example.palto.databinding.ActivityPaltoBinding
+import com.example.palto.ui.CardViewModel
class PaltoActivity : AppCompatActivity() {
private var nfcAdapter: NfcAdapter? = null
- private val paltoViewModel: PaltoViewModel by viewModels()
+ private val nfcViewModel: NfcViewModel by viewModels()
+
+ private lateinit var binding: ActivityPaltoBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- // get the NFC Adapter
- nfcAdapter = NfcAdapter.getDefaultAdapter(this)
+ binding = ActivityPaltoBinding.inflate(layoutInflater)
+ setContentView(binding.root)
- // check if NFC is supported
+ //
+ // Toolbar
+ //
+
+ // Set the toolbar as the app bar for the activity.
+ setSupportActionBar(binding.paltoToolbar)
+
+ // Configure the app bar
+ val navHostFragment =
+ supportFragmentManager.findFragmentById(R.id.palto_nav_host_fragment) as NavHostFragment
+ val navController = navHostFragment.navController
+ val appBarConfiguration = AppBarConfiguration(
+ setOf(
+ R.id.menuFragment, R.id.loginFragment
+ )
+ )
+ binding.paltoToolbar.setupWithNavController(navController, appBarConfiguration)
+
+ //
+ // NFC Adapter
+ //
+
+ nfcAdapter = NfcAdapter.getDefaultAdapter(this)
+ // Check if NFC is supported (already checked in the app manifest).
if (nfcAdapter == null) {
Log.e("NFC", "NFC is not supported")
- return
}
-
- // check if NFC is disabled
if (nfcAdapter?.isEnabled == false) {
Log.w("NFC", "NFC is not enabled")
}
-
- setContentView(R.layout.activity_palto)
- /*
- val url = URL("https://www.faraphel.fr/palto/api/auth/token/")
- val connection = url.openConnection()
- val auth_data = Json.decodeFromString(connection.content)
- */
}
+ /**
+ * Specify the options menu for the Activity.
+ */
+ override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+ val inflater: MenuInflater = menuInflater
+ inflater.inflate(R.menu.palto_menu, menu)
+ return true
+ }
+
+ /**
+ * Just before the application is displayed.
+ */
override fun onResume() {
super.onResume()
+ // Begin to read NFC Cards.
nfcAdapter?.enableReaderMode(
this,
- paltoViewModel.tagLiveData::postValue,
+ nfcViewModel::setTag,
NfcAdapter.FLAG_READER_NFC_A or NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK,
null
)
}
+ /**
+ * Just after the application has been quit.
+ */
override fun onPause() {
super.onPause()
- // disable the NFC discovery
+ // Disable the NFC discovery.
nfcAdapter?.disableReaderMode(this)
}
-
- @OptIn(ExperimentalStdlibApi::class)
- fun processTag(tag: Tag) {
- Log.d("NFC", "Tag ID : " + tag.id.toHexString())
- }
}
diff --git a/app/src/main/java/com/example/palto/PaltoViewModel.kt b/app/src/main/java/com/example/palto/PaltoViewModel.kt
deleted file mode 100644
index 9d69dc4..0000000
--- a/app/src/main/java/com/example/palto/PaltoViewModel.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.example.palto
-
-import android.nfc.Tag
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
-
-class PaltoViewModel: ViewModel() {
- val tagLiveData = MutableLiveData()
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/data/Result.kt b/app/src/main/java/com/example/palto/data/Result.kt
deleted file mode 100644
index be27f93..0000000
--- a/app/src/main/java/com/example/palto/data/Result.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.example.palto.data
-
-/**
- * A generic class that holds a value with its loading status.
- * @param
- */
-sealed class Result {
-
- data class Success(val data: T) : Result()
- data class Error(val exception: Exception) : Result()
-
- override fun toString(): String {
- return when (this) {
- is Success<*> -> "Success[data=$data]"
- is Error -> "Error[exception=$exception]"
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/data/local/LocalDataSource.kt b/app/src/main/java/com/example/palto/data/local/LocalDataSource.kt
deleted file mode 100644
index d05487e..0000000
--- a/app/src/main/java/com/example/palto/data/local/LocalDataSource.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.example.palto.data.local
-
-/**
- * Class that handles authentication w/ login credentials and retrieves user information.
- */
-class LocalDataSource {
- /*
- fun login(username: String, password: String): Result {
- try {
-
-
- val fakeUser = LoggedInUser(java.util.UUID.randomUUID().toString(), "Jane Doe")
- return Result.Success(fakeUser)
-
- } catch (e: Throwable) {
- return Result.Error(IOException("Error logging in", e))
- }
- }
-
- fun logout() {
- // TODO: revoke authentication
- }
- */
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/data/network/PaltoApiService.kt b/app/src/main/java/com/example/palto/data/network/PaltoApiService.kt
new file mode 100644
index 0000000..21cde23
--- /dev/null
+++ b/app/src/main/java/com/example/palto/data/network/PaltoApiService.kt
@@ -0,0 +1,46 @@
+package com.example.palto.data.network
+
+import com.example.palto.data.network.model.UserCredentials
+import com.example.palto.domain.Tokens
+import com.squareup.moshi.Moshi
+import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
+import retrofit2.Retrofit
+import retrofit2.converter.moshi.MoshiConverterFactory
+import retrofit2.http.Body
+import retrofit2.http.POST
+
+/**
+ * A public Api object that exposes the lazy-initialized Retrofit service
+ */
+object PaltoApi {
+
+ // Build the Moshi object that Retrofit will be using, making sure to add the Kotlin adapter for
+ // full Kotlin compatibility.
+ private val moshi = Moshi.Builder()
+ .add(KotlinJsonAdapterFactory())
+ .build()
+
+ fun createService(url: String) {
+ // Use the Retrofit builder to build a retrofit object using a Moshi converter
+ // with our Moshi object.
+ val retrofit = Retrofit.Builder()
+ .addConverterFactory(MoshiConverterFactory.create(moshi))
+ .baseUrl(url)
+ .build()
+ retrofitService = retrofit.create(PaltoApiService::class.java)
+ }
+
+ // Retrofit service that Palto will use to do requests.
+ // Make sure to call createService once before using it.
+ lateinit var retrofitService: PaltoApiService
+}
+
+
+/**
+ * Functions to query the API.
+ */
+interface PaltoApiService {
+
+ @POST("api/auth/jwt/token/")
+ suspend fun getTokens(@Body userCredentials: UserCredentials): Tokens
+}
diff --git a/app/src/main/java/com/example/palto/data/network/ServerDataSource.kt b/app/src/main/java/com/example/palto/data/network/ServerDataSource.kt
deleted file mode 100644
index 8c68892..0000000
--- a/app/src/main/java/com/example/palto/data/network/ServerDataSource.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.example.palto.data.network
-
-import com.example.palto.data.Result
-import com.example.palto.model.LoggedInUser
-import com.example.palto.model.Tokens
-import java.io.IOException
-import java.util.UUID
-
-/**
- * Class that handles API calls.
- */
-class ServerDataSource {
-
- private var hostname: String? = null
-
- fun requestToken(
- hostname: String,
- username: String,
- password: String
- ): Result {
- try {
- val tokens = Tokens(
- refresh = "aa",
- access = "bb"
- )
- return Result.Success(tokens)
- } catch (e: Throwable) {
- return Result.Error(IOException("Error logging in", e))
- }
- }
-
- fun refreshToken(current_tokens: Tokens): Result {
- return Result.Success(current_tokens)
- }
-
- fun verifyToken(): Boolean {
- return true
- }
-
- fun login(
- hostname: String,
- username: String,
- password: String
- ): Result {
- try {
- val fakeUser = LoggedInUser(
- UUID.randomUUID().toString(),
- "dede",
- "Lucie",
- "Doe",
- "aa@free.fr",
- )
- return Result.Success(fakeUser)
- } catch (e: Throwable) {
- return Result.Error(IOException("Error logging in", e))
- }
- }
-
- fun logout() { }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/data/network/model/UserCredentials.kt b/app/src/main/java/com/example/palto/data/network/model/UserCredentials.kt
new file mode 100644
index 0000000..50b3ffd
--- /dev/null
+++ b/app/src/main/java/com/example/palto/data/network/model/UserCredentials.kt
@@ -0,0 +1,6 @@
+package com.example.palto.data.network.model
+
+data class UserCredentials(
+ val username: String,
+ val password: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/data/repository/AttendanceRepository.kt b/app/src/main/java/com/example/palto/data/repository/AttendanceRepository.kt
index 13a8631..5ceb649 100644
--- a/app/src/main/java/com/example/palto/data/repository/AttendanceRepository.kt
+++ b/app/src/main/java/com/example/palto/data/repository/AttendanceRepository.kt
@@ -1,10 +1,3 @@
package com.example.palto.data.repository
-import com.example.palto.data.network.ServerDataSource
-
-/**
- *
- */
-class AttendanceRepository(val dataSource: ServerDataSource) {
- // private val cards
-}
\ No newline at end of file
+class AttendanceRepository
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/data/repository/LoginRepository.kt b/app/src/main/java/com/example/palto/data/repository/LoginRepository.kt
deleted file mode 100644
index ad61888..0000000
--- a/app/src/main/java/com/example/palto/data/repository/LoginRepository.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.example.palto.data.repository
-
-import com.example.palto.data.Result
-import com.example.palto.data.network.ServerDataSource
-import com.example.palto.model.LoggedInUser
-
-/**
- * Class that requests authentication and user information from the remote data source and
- * maintains an in-memory cache of login status and user credentials information.
- */
-
-class LoginRepository(val dataSource: ServerDataSource) {
-
- var user: LoggedInUser? = null
- private set
-
- val isLoggedIn: Boolean
- get() = user != null
-
- init {
- user = null
- }
-
- fun logout() {
- user = null
- dataSource.logout()
- }
-
- fun login(
- hostname: String,
- username: String,
- password: String
- ): Result {
- // handle login
- val result = dataSource.login(hostname, username, password)
-
- if (result is Result.Success) {
- setLoggedInUser(result.data)
- }
-
- return result
- }
-
- private fun setLoggedInUser(loggedInUser: LoggedInUser) {
- this.user = loggedInUser
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/data/repository/SessionRepository.kt b/app/src/main/java/com/example/palto/data/repository/SessionRepository.kt
index 3b4b4a7..45e122c 100644
--- a/app/src/main/java/com/example/palto/data/repository/SessionRepository.kt
+++ b/app/src/main/java/com/example/palto/data/repository/SessionRepository.kt
@@ -1,10 +1,3 @@
package com.example.palto.data.repository
-import com.example.palto.data.network.ServerDataSource
-
-/**
- *
- */
-class SessionRepository(val dataSource: ServerDataSource) {
- // private val cards
-}
\ No newline at end of file
+class SessionRepository
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/data/repository/TokenRepository.kt b/app/src/main/java/com/example/palto/data/repository/TokenRepository.kt
new file mode 100644
index 0000000..c1bb243
--- /dev/null
+++ b/app/src/main/java/com/example/palto/data/repository/TokenRepository.kt
@@ -0,0 +1,23 @@
+package com.example.palto.data.repository
+
+import com.example.palto.data.network.PaltoApi
+import com.example.palto.data.network.model.UserCredentials
+import com.example.palto.domain.Tokens
+
+/**
+ * Class that requests authentication tokens from Palto server.
+ */
+class TokenRepository() {
+
+ private var tokens: Tokens? = null
+
+ suspend fun authenticate(
+ hostname: String,
+ username: String,
+ password: String
+ ) {
+ PaltoApi.createService("http://$hostname:8000/")
+ val tokens = PaltoApi.retrofitService.getTokens((UserCredentials(username, password)))
+ this.tokens = tokens
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/data/repository/TokensRepository.kt b/app/src/main/java/com/example/palto/data/repository/TokensRepository.kt
deleted file mode 100644
index 184f04c..0000000
--- a/app/src/main/java/com/example/palto/data/repository/TokensRepository.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.example.palto.data.repository
-
-import com.example.palto.data.network.ServerDataSource
-import com.example.palto.model.Tokens
-
-/**
- * Class that requests authentication and user information from the remote data source and
- * maintains an in-memory cache of login status and user credentials information.
- */
-
-class TokensRepository(val dataSource: ServerDataSource) {
-
- var tokens: Tokens? = null
- private set
-
- /*
- val isLoggedIn: Boolean
- get() = user != null
- */
-
- init {
- // If user credentials will be cached in local storage, it is recommended it be encrypted
- // @see https://developer.android.com/training/articles/keystore
- tokens = null
- }
-
- /*
- fun login(username: String, password: String): Result {
- // handle login
- val result = dataSource.login(username, password)
-
- if (result is Result.Success) {
- setLoggedInUser(result.data)
- }
-
- return result
- }
- */
-
- private fun setTokens(tokens: Tokens) {
- this.tokens = tokens
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/data/repository/UserRepository.kt b/app/src/main/java/com/example/palto/data/repository/UserRepository.kt
new file mode 100644
index 0000000..2c58c10
--- /dev/null
+++ b/app/src/main/java/com/example/palto/data/repository/UserRepository.kt
@@ -0,0 +1,3 @@
+package com.example.palto.data.repository
+
+class UserRepository
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/model/Attendance.kt b/app/src/main/java/com/example/palto/domain/Attendance.kt
similarity index 56%
rename from app/src/main/java/com/example/palto/model/Attendance.kt
rename to app/src/main/java/com/example/palto/domain/Attendance.kt
index 1288725..1d6c321 100644
--- a/app/src/main/java/com/example/palto/model/Attendance.kt
+++ b/app/src/main/java/com/example/palto/domain/Attendance.kt
@@ -1,10 +1,12 @@
-package com.example.palto.model
+package com.example.palto.domain
import java.io.Serializable
+import java.time.LocalTime
/**
* Data class that captures tokens for logged in users retrieved from LoginRepository
*/
data class Attendance(
- val date: String,
- val access: String
+ val id: Int,
+ val student: User,
+ val date: LocalTime
) : Serializable
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/domain/Card.kt b/app/src/main/java/com/example/palto/domain/Card.kt
new file mode 100644
index 0000000..0520b42
--- /dev/null
+++ b/app/src/main/java/com/example/palto/domain/Card.kt
@@ -0,0 +1,7 @@
+package com.example.palto.domain
+
+data class Card(
+ val id: Int,
+ val tagId: String,
+ val user: User,
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/domain/Event.kt b/app/src/main/java/com/example/palto/domain/Event.kt
new file mode 100644
index 0000000..0b72683
--- /dev/null
+++ b/app/src/main/java/com/example/palto/domain/Event.kt
@@ -0,0 +1,18 @@
+package com.example.palto.domain
+
+class Event(private val content: T) {
+
+ var hasBeenHandled = false
+ private set
+
+ fun getContentIfNotHandled(): T? {
+ return if (hasBeenHandled) {
+ null
+ } else {
+ hasBeenHandled = true
+ content
+ }
+ }
+
+ fun peekContent(): T = content
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/domain/Session.kt b/app/src/main/java/com/example/palto/domain/Session.kt
new file mode 100644
index 0000000..4e2116e
--- /dev/null
+++ b/app/src/main/java/com/example/palto/domain/Session.kt
@@ -0,0 +1,12 @@
+package com.example.palto.domain
+import java.io.Serializable
+
+/**
+ * Data class that captures tokens for logged in users retrieved from LoginRepository
+ */
+data class Session(
+ val id: Int,
+ val name: String,
+ var attendances: List
+ // When the list is updated, it is replaced by a new one.
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/model/Tokens.kt b/app/src/main/java/com/example/palto/domain/Tokens.kt
similarity index 79%
rename from app/src/main/java/com/example/palto/model/Tokens.kt
rename to app/src/main/java/com/example/palto/domain/Tokens.kt
index fa90475..4e29846 100644
--- a/app/src/main/java/com/example/palto/model/Tokens.kt
+++ b/app/src/main/java/com/example/palto/domain/Tokens.kt
@@ -1,4 +1,4 @@
-package com.example.palto.model
+package com.example.palto.domain
import java.io.Serializable
/**
@@ -7,4 +7,4 @@ import java.io.Serializable
data class Tokens(
val refresh: String,
val access: String
-) : Serializable
\ No newline at end of file
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/domain/User.kt b/app/src/main/java/com/example/palto/domain/User.kt
new file mode 100644
index 0000000..7e36833
--- /dev/null
+++ b/app/src/main/java/com/example/palto/domain/User.kt
@@ -0,0 +1,12 @@
+package com.example.palto.domain
+
+/**
+ * Data class that captures user information for logged in users retrieved from LoginRepository
+ */
+data class User(
+ val id: Int,
+ val username: String,
+ val firstName: String,
+ val lastName: String,
+ val email: String
+)
diff --git a/app/src/main/java/com/example/palto/model/Card.kt b/app/src/main/java/com/example/palto/model/Card.kt
deleted file mode 100644
index 8c55422..0000000
--- a/app/src/main/java/com/example/palto/model/Card.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.example.palto.model
-import java.io.Serializable
-
-data class Card(
- val id: String,
- val uid: ByteArray,
- val department: String,
- val owner: String
-) {
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
-
- other as Card
-
- return uid.contentEquals(other.uid)
- }
-
- override fun hashCode(): Int {
- return uid.contentHashCode()
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/model/LoggedInUser.kt b/app/src/main/java/com/example/palto/model/LoggedInUser.kt
deleted file mode 100644
index 6f60811..0000000
--- a/app/src/main/java/com/example/palto/model/LoggedInUser.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.example.palto.model
-
-import java.io.Serializable
-
-/**
- * Data class that captures user information for logged in users retrieved from LoginRepository
- */
-data class LoggedInUser(
- val id: String,
- val username: String,
- val first_name: String,
- val last_name: String,
- val email: String
-) : Serializable
diff --git a/app/src/main/java/com/example/palto/model/Session.kt b/app/src/main/java/com/example/palto/model/Session.kt
deleted file mode 100644
index 8a6827f..0000000
--- a/app/src/main/java/com/example/palto/model/Session.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.example.palto.model
-import java.io.Serializable
-
-/**
- * Data class that captures tokens for logged in users retrieved from LoginRepository
- */
-data class Session(
- val id: String
-) : Serializable
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/CardViewModel.kt b/app/src/main/java/com/example/palto/ui/CardViewModel.kt
new file mode 100644
index 0000000..7223020
--- /dev/null
+++ b/app/src/main/java/com/example/palto/ui/CardViewModel.kt
@@ -0,0 +1,34 @@
+package com.example.palto.ui
+
+import android.nfc.Tag
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.example.palto.domain.Card
+import com.example.palto.domain.User
+
+/**
+ * CardViewModel maintain a list of cards application wide.
+ * May be converted in a repository.
+ */
+class CardViewModel: ViewModel() {
+
+ private var _cards = MutableLiveData>()
+ private val cards: LiveData> = _cards
+
+ fun createCard(user: User, tagId: String): Card {
+ val list = _cards.value ?: emptyList()
+ val card = Card(list.size, tagId, user)
+ _cards.value = list + card
+ Log.d("Palto", "CardViewModel: a card has been added into the list.")
+ return card
+ }
+
+ fun getCard(tagId: String): Card? {
+ val card = _cards.value?.find() {
+ it.tagId == tagId
+ }
+ return card
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/UserViewModel.kt b/app/src/main/java/com/example/palto/ui/UserViewModel.kt
new file mode 100644
index 0000000..ac791c4
--- /dev/null
+++ b/app/src/main/java/com/example/palto/ui/UserViewModel.kt
@@ -0,0 +1,31 @@
+package com.example.palto.ui
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.example.palto.domain.Card
+import com.example.palto.domain.User
+
+/**
+ * UserViewModel maintain a list of users application wide.
+ * May be converted into a repository.
+ */
+class UserViewModel: ViewModel() {
+
+ private var _users = MutableLiveData>()
+ val users : LiveData> = _users
+
+ fun createUser(username: String): User {
+ val list = _users.value ?: emptyList()
+ val user = User(
+ id = list.size,
+ username = username,
+ firstName = "",
+ lastName = "",
+ email = "")
+ _users.value = list + user
+ Log.d("Palto", "UserViewModel: a user has been added into the list.")
+ return user
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/attendanceList/AttendanceListAdapter.kt b/app/src/main/java/com/example/palto/ui/attendanceList/AttendanceListAdapter.kt
deleted file mode 100644
index a7788b1..0000000
--- a/app/src/main/java/com/example/palto/ui/attendanceList/AttendanceListAdapter.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.example.palto.ui.attendanceList
-
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import android.widget.TextView
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.ListAdapter
-import androidx.recyclerview.widget.RecyclerView
-import com.example.palto.databinding.FragmentAttendanceItemBinding
-import com.example.palto.model.Card
-
-/**
- *
- */
-class AttendanceListAdapter :
- ListAdapter(CardDiffCallback) {
-
- override fun onCreateViewHolder(
- parent: ViewGroup,
- viewType: Int
- ): ViewHolder {
-
- return ViewHolder(
- FragmentAttendanceItemBinding.inflate(
- LayoutInflater.from(parent.context),
- parent,
- false
- )
- )
- }
-
- @OptIn(ExperimentalStdlibApi::class)
- override fun onBindViewHolder(holder: ViewHolder, position: Int) {
- val item = getItem(position)
- holder.cardId.text = item.uid.toHexString()
- //holder.contentView.text = item.content
- }
-
- inner class ViewHolder(
- binding: FragmentAttendanceItemBinding
- ) : RecyclerView.ViewHolder(binding.root) {
-
- val cardId: TextView = binding.cardId
- override fun toString(): String {
- return super.toString() + " '" + cardId.text + "'"
- }
- }
-}
-
-object CardDiffCallback : DiffUtil.ItemCallback() {
- override fun areItemsTheSame(oldItem: Card, newItem: Card): Boolean {
- return oldItem == newItem
- }
-
- override fun areContentsTheSame(oldItem: Card, newItem: Card): Boolean {
- return oldItem.id == newItem.id
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/attendanceList/AttendanceListFragment.kt b/app/src/main/java/com/example/palto/ui/attendanceList/AttendanceListFragment.kt
deleted file mode 100644
index 68773ab..0000000
--- a/app/src/main/java/com/example/palto/ui/attendanceList/AttendanceListFragment.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.example.palto.ui.attendanceList
-
-import android.nfc.NfcAdapter
-import android.nfc.Tag
-import android.os.Bundle
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Observer
-import androidx.navigation.navGraphViewModels
-import com.example.palto.PaltoViewModel
-import com.example.palto.R
-import com.example.palto.databinding.FragmentAttendanceListBinding
-
-/**
- * A fragment representing a list of attendances.
- */
-class AttendanceListFragment : Fragment() {
-
- private val attendanceListViewModel: AttendanceListViewModel by
- navGraphViewModels(R.id.nav_graph) { AttendanceListViewModel.Factory }
-
- private val paltoViewModel: PaltoViewModel by
- activityViewModels()
-
- private var _binding: FragmentAttendanceListBinding? = null
- // This property is only valid between onCreateView and onDestroyView
- private val binding get() = _binding!!
-
- /**
- * Only inflate the view.
- */
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
-
- _binding = FragmentAttendanceListBinding.inflate(inflater, container, false)
- return binding.root
- }
-
- /**
- * Logic on the returned view of onCreateView.
- */
- override fun onViewCreated(
- view: View,
- savedInstanceState: Bundle?
- ) {
- super.onViewCreated(view, savedInstanceState)
-
- // Set the adapter of the view for managing automatically the list of items on the screen.
- val adapter = AttendanceListAdapter()
- binding.list.adapter = adapter
- attendanceListViewModel.cardsLiveData.observe(viewLifecycleOwner) {
- Log.d("NFC", "A card has been had to the list")
- adapter.submitList(it)
- }
-
- // Set the listener for a new NFC tag.
- paltoViewModel.tagLiveData.observe(viewLifecycleOwner) {
- Log.d("NFC", "tag observer has been notified")
- attendanceListViewModel.insertCard(it)
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/attendanceList/AttendanceListViewModel.kt b/app/src/main/java/com/example/palto/ui/attendanceList/AttendanceListViewModel.kt
deleted file mode 100644
index a4efa38..0000000
--- a/app/src/main/java/com/example/palto/ui/attendanceList/AttendanceListViewModel.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.example.palto.ui.attendanceList
-
-import android.nfc.Tag
-import android.util.Log
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-import com.example.palto.data.network.ServerDataSource
-import com.example.palto.data.repository.AttendanceRepository
-import com.example.palto.model.Card
-
-/**
- * ViewModel of a session which has a list of attendances.
- */
-class AttendanceListViewModel(
- private val attendanceRepository: AttendanceRepository
-) : ViewModel() {
-
- val cardsLiveData: MutableLiveData> = MutableLiveData(emptyList())
-
- fun insertCard(tag: Tag) {
- val card = Card(
- "0",
- tag.id,
- "tmp",
- "tmp"
- )
- cardsLiveData.value = (cardsLiveData.value ?: emptyList()) + card
- Log.d("NFC", "view model: A card has been had to the list")
- }
-
- /**
- * ViewModel Factory.
- */
- companion object {
-
- val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
- @Suppress("UNCHECKED_CAST")
- override fun create(
- modelClass: Class
- ): T {
- return AttendanceListViewModel(
- AttendanceRepository(ServerDataSource())
- ) as T
- }
- }
- }
-}
diff --git a/app/src/main/java/com/example/palto/ui/login/LoggedInUserView.kt b/app/src/main/java/com/example/palto/ui/login/LoggedInUserView.kt
deleted file mode 100644
index 8cddb61..0000000
--- a/app/src/main/java/com/example/palto/ui/login/LoggedInUserView.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.example.palto.ui.login
-
-/* Est-ce que c’est util ?
- * Updater la vue dans le fragment
- */
-
-/**
- * User details post authentication that is exposed to the UI
- */
-data class LoggedInUserView(
- val displayName: String
- //... other data fields that may be accessible to the UI
-)
diff --git a/app/src/main/java/com/example/palto/ui/login/LoginFragment.kt b/app/src/main/java/com/example/palto/ui/login/LoginFragment.kt
index acf12be..92d524c 100644
--- a/app/src/main/java/com/example/palto/ui/login/LoginFragment.kt
+++ b/app/src/main/java/com/example/palto/ui/login/LoginFragment.kt
@@ -1,42 +1,62 @@
package com.example.palto.ui.login
-import androidx.lifecycle.Observer
-import androidx.annotation.StringRes
-import androidx.fragment.app.Fragment
+import android.annotation.SuppressLint
import android.os.Bundle
-import android.text.Editable
-import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.view.inputmethod.EditorInfo
import android.widget.Toast
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
-import androidx.navigation.navGraphViewModels
import com.example.palto.databinding.FragmentLoginBinding
-import com.example.palto.R
-
class LoginFragment : Fragment() {
- private val loginViewModel: LoginViewModel by
- navGraphViewModels(R.id.nav_graph) { LoginViewModelFactory() }
+ // userViewModel is where the user is logged in, at the activity level.
+ private val loginViewModel: LoginViewModel by activityViewModels() { LoginViewModel.Factory }
- private var _binding: FragmentLoginBinding? = null
-
- // This property is only valid between onCreateView and onDestroyView.
- private val binding get() = _binding!!
+ private lateinit var binding: FragmentLoginBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
+ binding = FragmentLoginBinding.inflate(inflater, container, false)
+
+ val navController = findNavController()
+
+ // Bind the login button.
+ binding.login.setOnClickListener {
+ binding.loading.visibility = View.VISIBLE
+ loginViewModel.login(
+ binding.hostname.text.toString(),
+ binding.username.text.toString(),
+ binding.password.text.toString()
+ )
+ }
+
+ // Bind anonymous login clickable text.
+ binding.loginAnonymous.setOnClickListener {
+ loginViewModel.loginAnonymous()
+ }
+
+ // On result of logging.
+ loginViewModel.result.observe(viewLifecycleOwner) {
+ binding.loading.visibility = View.GONE
+ if (it.success) {
+ navController.popBackStack()
+ } else if (it.error != null) {
+ binding.loginError.text = "Exception : ${it.exception.toString()}"
+ Toast.makeText(activity, it.error, Toast.LENGTH_LONG).show()
+ }
+ }
- _binding = FragmentLoginBinding.inflate(inflater, container, false)
return binding.root
}
+ /*
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -46,23 +66,21 @@ class LoginFragment : Fragment() {
val loginButton = binding.login
val loadingProgressBar = binding.loading
- //
- loginViewModel.loginFormState.observe(viewLifecycleOwner,
- Observer { loginFormState ->
- if (loginFormState == null) {
- return@Observer
- }
- loginButton.isEnabled = loginFormState.isDataValid
- loginFormState.hostnameError?.let {
- hostnameEditText.error = getString(it)
- }
- loginFormState.usernameError?.let {
- usernameEditText.error = getString(it)
- }
- loginFormState.passwordError?.let {
- passwordEditText.error = getString(it)
- }
- })
+ loginViewModel.loginFormState.observe(viewLifecycleOwner) {
+ if (it == null) {
+ return@Observer
+ }
+ loginButton.isEnabled = it.isDataValid
+ it.hostnameError?.let {
+ hostnameEditText.error = getString(it)
+ }
+ it.usernameError?.let {
+ usernameEditText.error = getString(it)
+ }
+ it.passwordError?.let {
+ passwordEditText.error = getString(it)
+ }
+ }
loginViewModel.loginResult.observe(viewLifecycleOwner,
Observer { loginResult ->
@@ -103,17 +121,8 @@ class LoginFragment : Fragment() {
}
false
}
-
- // Damien : Le setOnClickListener est là !
- loginButton.setOnClickListener {
- loadingProgressBar.visibility = View.VISIBLE
- loginViewModel.login(
- hostnameEditText.text.toString(),
- usernameEditText.text.toString(),
- passwordEditText.text.toString()
- )
- }
}
+ */
/*
private fun updateUiWithUser(model: LoggedInUserView) {
@@ -122,15 +131,11 @@ class LoginFragment : Fragment() {
val appContext = context?.applicationContext ?: return
Toast.makeText(appContext, welcome, Toast.LENGTH_LONG).show()
}
- */
private fun showLoginFailed(@StringRes errorString: Int) {
val appContext = context?.applicationContext ?: return
Toast.makeText(appContext, errorString, Toast.LENGTH_LONG).show()
}
- override fun onDestroyView() {
- super.onDestroyView()
- _binding = null
- }
+ */
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/login/LoginResult.kt b/app/src/main/java/com/example/palto/ui/login/LoginResult.kt
index 9367f89..1d748f2 100644
--- a/app/src/main/java/com/example/palto/ui/login/LoginResult.kt
+++ b/app/src/main/java/com/example/palto/ui/login/LoginResult.kt
@@ -1,9 +1,10 @@
package com.example.palto.ui.login
/**
- * Authentication result : success (user details) or error message.
+ * Authentication result : success is true if connected or error message with exception.
*/
data class LoginResult(
- val success: LoggedInUserView? = null,
- val error: Int? = null
+ val success: Boolean,
+ val error: Int? = null, // Id of the string resource to display to the user.
+ val exception: Exception? = null
)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/login/LoginViewModel.kt b/app/src/main/java/com/example/palto/ui/login/LoginViewModel.kt
index 99ea964..f7365dc 100644
--- a/app/src/main/java/com/example/palto/ui/login/LoginViewModel.kt
+++ b/app/src/main/java/com/example/palto/ui/login/LoginViewModel.kt
@@ -1,84 +1,106 @@
package com.example.palto.ui.login
+import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
-import android.util.Patterns
import androidx.lifecycle.ViewModelProvider
-import com.example.palto.data.repository.LoginRepository
-
+import androidx.lifecycle.viewModelScope
import com.example.palto.R
-import com.example.palto.data.network.ServerDataSource
+import com.example.palto.data.repository.TokenRepository
+import com.example.palto.data.repository.UserRepository
+import com.example.palto.domain.User
+import kotlinx.coroutines.launch
-class LoginViewModel(private val loginRepository: LoginRepository) : ViewModel() {
+/**
+ * LoginViewModel to access information about the logged in user and login form.
+ */
+class LoginViewModel(
+ private val tokenRepository: TokenRepository,
+ private val userRepository: UserRepository
+): ViewModel() {
- private val _loginForm = MutableLiveData()
- val loginFormState: LiveData = _loginForm
+ private var _result = MutableLiveData()
+ val result = _result as LiveData
- private val _loginResult = MutableLiveData()
- val loginResult: LiveData = _loginResult
+ // User is initially set to null to be disconnected.
+ private var _user = MutableLiveData(null)
+ val user = _user as LiveData
+
+ /*
+ private val _loginFormState = MutableLiveData()
+ val loginFormState: LiveData = _loginFormState
+ */
fun login(
hostname: String,
username: String,
password: String) {
- // can be launched in a separate asynchronous job
- val result = loginRepository.login(hostname, username, password)
- /*
- if (result is Result.Success) {
- _loginResult.value =
- LoginResult(success = LoggedInUserView(
- displayName = result.data.displayName))
- } else {
- _loginResult.value = LoginResult(error = R.string.login_failed)
- }
- */
- }
-
- fun loginDataChanged(
- hostname: String,
- username: String,
- password: String) {
- if (!isHostNameValid(hostname)) {
- _loginForm.value = LoginFormState(hostnameError = R.string.invalid_hostname)
- } else if (!isUserNameValid(username)) {
- _loginForm.value = LoginFormState(usernameError = R.string.invalid_username)
- } else if (!isPasswordValid(password)) {
- _loginForm.value = LoginFormState(passwordError = R.string.invalid_password)
- } else {
- _loginForm.value = LoginFormState(isDataValid = true)
- }
- }
-
- private fun isHostNameValid(hostname: String): Boolean {
- return hostname.isNotBlank()
- }
-
- // A placeholder username validation check
- private fun isUserNameValid(username: String): Boolean {
- return if (username.contains("@")) {
- Patterns.EMAIL_ADDRESS.matcher(username).matches()
- } else {
- username.isNotBlank()
- }
- }
-
- // A placeholder password validation check
- private fun isPasswordValid(password: String): Boolean {
- return password.length > 5
- }
-}
-
-class LoginViewModelFactory : ViewModelProvider.Factory {
- override fun create(modelClass: Class): T {
- if (modelClass.isAssignableFrom(LoginViewModel::class.java)) {
- return LoginViewModel(
- loginRepository = LoginRepository(
- dataSource = ServerDataSource()
+ // Coroutine runs in background.
+ viewModelScope.launch {
+ try {
+ tokenRepository.authenticate(hostname, username, password)
+ _user.value = User(-1, username, "", "", "")
+ _result.value = LoginResult(success = true)
+ } catch (e: Exception) {
+ Log.e("Palto", "Connection error: " + e.message)
+ _result.value = LoginResult(
+ success = false,
+ error = R.string.login_failed,
+ exception = e
)
- ) as T
+ }
}
- throw IllegalArgumentException("Unknown ViewModel class")
}
-}
+
+ fun loginAnonymous() {
+ _user.value = User(-2, "anonymous", "", "", "")
+ _result.value = LoginResult(success = true)
+ }
+
+ fun logout() {
+ _user.value = null
+ _result.value = LoginResult(success = false)
+ }
+
+ /*
+ fun loginDataChanged(
+ hostname: String,
+ username: String,
+ password: String) {
+ if (!isHostNameValid(hostname)) {
+ _loginForm.value = LoginFormState(hostnameError = R.string.invalid_hostname)
+ } else if (!isUserNameValid(username)) {
+ _loginForm.value = LoginFormState(usernameError = R.string.invalid_username)
+ } else if (!isPasswordValid(password)) {
+ _loginForm.value = LoginFormState(passwordError = R.string.invalid_password)
+ } else {
+ _loginForm.value = LoginFormState(isDataValid = true)
+ }
+ }
+ private fun isHostNameValid(hostname: String): Boolean {
+ return hostname.isNotBlank()
+ }
+
+ private fun isUserNameValid(username: String): Boolean {
+ return username.isNotBlank()
+ }
+
+ private fun isPasswordValid(password: String): Boolean {
+ return password.length > 5
+ }
+ */
+
+ companion object {
+
+ val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
+ override fun create(modelClass: Class): T {
+ return LoginViewModel(
+ tokenRepository = TokenRepository(),
+ userRepository = UserRepository()
+ ) as T
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/menu/MenuAdapter.kt b/app/src/main/java/com/example/palto/ui/menu/MenuAdapter.kt
new file mode 100644
index 0000000..518c04f
--- /dev/null
+++ b/app/src/main/java/com/example/palto/ui/menu/MenuAdapter.kt
@@ -0,0 +1,58 @@
+package com.example.palto.ui.menu
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.example.palto.databinding.FragmentMenuItemBinding
+import com.example.palto.domain.Session
+
+/**
+ * A [ListAdapter] that can display [Session] items.
+ */
+class MenuAdapter(private val onClick: (Session) -> Unit) :
+ ListAdapter(SessionDiffCallback) {
+ inner class ViewHolder(binding: FragmentMenuItemBinding) :
+ RecyclerView.ViewHolder(binding.root) {
+ private val sessionNameText: TextView = binding.sessionName
+ private var currentSession: Session? = null
+
+ init {
+ binding.root.setOnClickListener {
+ currentSession?.let {
+ onClick(it)
+ }
+ }
+ }
+
+ fun bind(session: Session) {
+ currentSession = session
+ sessionNameText.text = session.name
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ val binding = FragmentMenuItemBinding
+ .inflate(LayoutInflater.from(parent.context), parent, false)
+ return ViewHolder(binding)
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val item = getItem(position)
+ holder.bind(item)
+ }
+
+ override fun getItemCount() = currentList.size
+}
+
+object SessionDiffCallback : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: Session, newItem: Session): Boolean {
+ return oldItem == newItem
+ }
+
+ override fun areContentsTheSame(oldItem: Session, newItem: Session): Boolean {
+ return oldItem.id == newItem.id
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/menu/MenuFragment.kt b/app/src/main/java/com/example/palto/ui/menu/MenuFragment.kt
new file mode 100644
index 0000000..2991f91
--- /dev/null
+++ b/app/src/main/java/com/example/palto/ui/menu/MenuFragment.kt
@@ -0,0 +1,71 @@
+package com.example.palto.ui.menu
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.os.bundleOf
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.navigation.fragment.findNavController
+import androidx.navigation.navGraphViewModels
+import com.example.palto.R
+import com.example.palto.databinding.FragmentMenuListBinding
+import com.example.palto.domain.Session
+import com.example.palto.ui.login.LoginViewModel
+
+/**
+ * A fragment representing a list of Sessions.
+ */
+class MenuFragment : Fragment() {
+
+ private val menuViewModel: MenuViewModel by
+ navGraphViewModels(R.id.nav_graph) { MenuViewModel.Factory }
+
+ private val loginViewModel: LoginViewModel by
+ activityViewModels() { LoginViewModel.Factory }
+
+ // This property is only valid between onCreateView and onDestroyView
+ private lateinit var binding: FragmentMenuListBinding
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ binding = FragmentMenuListBinding.inflate(inflater, container, false)
+
+ val navController = findNavController()
+
+ // Connect the user.
+ loginViewModel.user.observe(viewLifecycleOwner) {
+ if (it != null) {
+ // Get sessions of the user from remote.
+ } else {
+ navController.navigate(R.id.loginFragment)
+ }
+ }
+
+ // Display the list of sessions.
+
+ // Create a new MenuAdapter (list) with the given function when clicking an item.
+ val adapter = MenuAdapter { adapterOnClick(it) }
+ binding.menuList.adapter = adapter
+ // Link the adapter with the session list in the menuViewMode.
+ menuViewModel.sessions.observe(viewLifecycleOwner) {
+ adapter.submitList(it)
+ }
+
+ // Bind the add button.
+ binding.createSession.setOnClickListener {
+ navController.navigate(R.id.action_menuFragment_to_newSessionFragment)
+ }
+
+ return binding.root
+ }
+
+ private fun adapterOnClick(session: Session) {
+ val bundle = bundleOf("session" to session.id)
+ findNavController().navigate(R.id.action_menuFragment_to_sessionFragment, bundle)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/menu/MenuViewModel.kt b/app/src/main/java/com/example/palto/ui/menu/MenuViewModel.kt
new file mode 100644
index 0000000..e263c6d
--- /dev/null
+++ b/app/src/main/java/com/example/palto/ui/menu/MenuViewModel.kt
@@ -0,0 +1,47 @@
+package com.example.palto.ui.menu
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.palto.domain.Attendance
+import com.example.palto.domain.Session
+
+/**
+ * ViewModel for accessing all the sessions created.
+ */
+class MenuViewModel() : ViewModel() {
+
+ // A list of sessions.
+ private var _sessions = MutableLiveData>()
+ val sessions: LiveData> = _sessions
+
+ fun createSession(name: String) {
+ val list = _sessions.value ?: emptyList()
+ val session = Session(
+ id = list.size,
+ name = name,
+ attendances = emptyList()
+ )
+ _sessions.value = list + session
+ Log.d("Palto", "MenuViewModel: A session has been added into the list.")
+ }
+
+ fun getSession(id: Int): Session? {
+ return _sessions.value?.find { it.id == id }
+ }
+
+ fun setAttendanceListSession(id: Int, list: List) {
+ getSession(id)?.let { it.attendances = list }
+ }
+
+ companion object {
+
+ val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
+ override fun create(modelClass: Class): T {
+ return MenuViewModel() as T
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/menu/new_session/NewSessionFragment.kt b/app/src/main/java/com/example/palto/ui/menu/new_session/NewSessionFragment.kt
new file mode 100644
index 0000000..fdbe854
--- /dev/null
+++ b/app/src/main/java/com/example/palto/ui/menu/new_session/NewSessionFragment.kt
@@ -0,0 +1,46 @@
+package com.example.palto.ui.menu.new_session
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.view.menu.MenuView
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.navigation.fragment.findNavController
+import androidx.navigation.navGraphViewModels
+import com.example.palto.R
+import com.example.palto.databinding.FragmentNewSessionBinding
+import com.example.palto.ui.menu.MenuViewModel
+
+/**
+ *
+ */
+class NewSessionFragment : Fragment() {
+
+ private val newSessionViewModel: NewSessionViewModel by viewModels()
+
+ private val menuViewModel: MenuViewModel by
+ navGraphViewModels(R.id.nav_graph) { MenuViewModel.Factory }
+
+ // This property is only valid between onCreateView and onDestroyView
+ private lateinit var binding: FragmentNewSessionBinding
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ binding = FragmentNewSessionBinding.inflate(inflater, container, false)
+
+ // Bind the create button
+ binding.newSessionCreate.setOnClickListener {
+ menuViewModel.createSession(
+ name = binding.newSessionName.text.toString()
+ )
+ findNavController().popBackStack()
+ }
+
+ return binding.root
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/menu/new_session/NewSessionViewModel.kt b/app/src/main/java/com/example/palto/ui/menu/new_session/NewSessionViewModel.kt
new file mode 100644
index 0000000..0058596
--- /dev/null
+++ b/app/src/main/java/com/example/palto/ui/menu/new_session/NewSessionViewModel.kt
@@ -0,0 +1,8 @@
+package com.example.palto.ui.menu.new_session
+
+import androidx.lifecycle.ViewModel
+
+/**
+ * ViewModel of the session creation form. Used for verification.
+ */
+class NewSessionViewModel() : ViewModel()
diff --git a/app/src/main/java/com/example/palto/ui/session/SessionAdapter.kt b/app/src/main/java/com/example/palto/ui/session/SessionAdapter.kt
new file mode 100644
index 0000000..afb2fd0
--- /dev/null
+++ b/app/src/main/java/com/example/palto/ui/session/SessionAdapter.kt
@@ -0,0 +1,52 @@
+package com.example.palto.ui.session
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.example.palto.databinding.FragmentSessionItemBinding
+import com.example.palto.domain.Attendance
+import com.example.palto.domain.Card
+import java.time.format.DateTimeFormatter
+
+/**
+ * A [ListAdapter] that can display [Attendance] items.
+ */
+class SessionAdapter : ListAdapter(AttendanceDiffCallback) {
+
+
+ inner class ViewHolder(binding: FragmentSessionItemBinding) :
+ RecyclerView.ViewHolder(binding.root) {
+ private val attendanceUsernameText: TextView = binding.attendanceUsername
+ private val attendanceDate: TextView = binding.attendanceDate
+
+ fun bind(attendance: Attendance) {
+ attendanceUsernameText.text = attendance.student.username
+ attendanceDate.text = attendance.date.format(DateTimeFormatter.ofPattern("HH:mm:ss"))
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ val binding = FragmentSessionItemBinding
+ .inflate(LayoutInflater.from(parent.context), parent, false)
+ return ViewHolder(binding)
+ }
+
+ @OptIn(ExperimentalStdlibApi::class)
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val item = getItem(position)
+ holder.bind(item)
+ }
+}
+
+object AttendanceDiffCallback : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: Attendance, newItem: Attendance): Boolean {
+ return oldItem == newItem
+ }
+
+ override fun areContentsTheSame(oldItem: Attendance, newItem: Attendance): Boolean {
+ return oldItem.id == newItem.id
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/session/SessionFragment.kt b/app/src/main/java/com/example/palto/ui/session/SessionFragment.kt
new file mode 100644
index 0000000..35e7e82
--- /dev/null
+++ b/app/src/main/java/com/example/palto/ui/session/SessionFragment.kt
@@ -0,0 +1,106 @@
+package com.example.palto.ui.session
+
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Toast
+import androidx.core.os.bundleOf
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Lifecycle
+import androidx.navigation.fragment.findNavController
+import androidx.navigation.navGraphViewModels
+import com.example.palto.NfcViewModel
+import com.example.palto.R
+import com.example.palto.databinding.FragmentSessionListBinding
+import com.example.palto.ui.CardViewModel
+import com.example.palto.ui.menu.MenuViewModel
+
+/**
+ * A fragment representing a list of attendances.
+ */
+class SessionFragment : Fragment() {
+
+ private val sessionViewModel: SessionViewModel by
+ navGraphViewModels(R.id.nav_graph) { SessionViewModel.Factory }
+
+ private val menuViewModel: MenuViewModel by
+ navGraphViewModels(R.id.nav_graph) { MenuViewModel.Factory }
+
+ private val cardViewModel: CardViewModel by
+ navGraphViewModels(R.id.nav_graph)
+
+ private val nfcViewModel: NfcViewModel by activityViewModels()
+
+ // This property is only valid between onCreateView and onDestroyView
+ private lateinit var binding: FragmentSessionListBinding
+
+ /**
+ * Have the fragment instantiate the user interface.
+ */
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ binding = FragmentSessionListBinding.inflate(inflater, container, false)
+
+ // If the bundle value id of key "session" is not empty,
+ // set the attendance list to that of the selected session.
+ arguments?.getInt("session")?.let { id ->
+ val session = menuViewModel.getSession(id)
+ if (session != null) {
+ Log.d("Palto", "SessionFragment: Session id ${session.id} has been found.")
+ sessionViewModel.session = session
+ sessionViewModel.setAttendanceList(session.attendances)
+ }
+ }
+
+ // Set the adapter of the view for managing automatically the list of items on the screen.
+ val adapter = SessionAdapter()
+ binding.sessionList.adapter = adapter
+ sessionViewModel.attendances.observe(viewLifecycleOwner) {
+ adapter.submitList(it)
+ }
+
+ // Set the listener for a new NFC tag.
+ nfcViewModel.tagId.observe(viewLifecycleOwner) {
+
+ // If the NFC tag has not been handled.
+ it.getContentIfNotHandled()?.let { cardId ->
+ val card = cardViewModel.getCard(cardId)
+ // If a card with this tag exists, add this card.
+ if (card != null) {
+ sessionViewModel.addAttendance(card.user)
+ // Else go to the NewStudentFragment to create a new card and student.
+ } else {
+ val bundle = bundleOf("tagId" to cardId)
+ findNavController()
+ .navigate(R.id.action_sessionFragment_to_newStudentFragment, bundle)
+ }
+ }
+ }
+
+ // Manual add student button
+ binding.addStudent.setOnClickListener {
+ findNavController().navigate(R.id.action_sessionFragment_to_newStudentFragment)
+ }
+
+ // Print the result of adding an attendance on the view.
+ sessionViewModel.result.observe(viewLifecycleOwner) {
+ // If the result has not been already shown
+ it.getContentIfNotHandled()?.let { result ->
+ Toast.makeText(
+ activity,
+ getString(result.message, result.username),
+ Toast.LENGTH_LONG).show()
+ }
+ }
+
+ return binding.root
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/session/SessionResult.kt b/app/src/main/java/com/example/palto/ui/session/SessionResult.kt
new file mode 100644
index 0000000..60b0b31
--- /dev/null
+++ b/app/src/main/java/com/example/palto/ui/session/SessionResult.kt
@@ -0,0 +1,10 @@
+package com.example.palto.ui.session
+
+/**
+ * Authentication result : success is true if connected or error message with exception.
+ */
+data class SessionResult(
+ val success: Boolean,
+ val message: Int, // Id of the string resource to display to the user
+ val username: String
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/session/SessionViewModel.kt b/app/src/main/java/com/example/palto/ui/session/SessionViewModel.kt
new file mode 100644
index 0000000..eb81aa5
--- /dev/null
+++ b/app/src/main/java/com/example/palto/ui/session/SessionViewModel.kt
@@ -0,0 +1,84 @@
+package com.example.palto.ui.session
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.palto.R
+import com.example.palto.domain.Attendance
+import com.example.palto.domain.Event
+import com.example.palto.domain.Session
+import com.example.palto.domain.User
+import java.time.LocalTime
+
+/**
+ * ViewModel of a session which has a list of attendances.
+ */
+class SessionViewModel() : ViewModel() {
+
+ private var _result = MutableLiveData>()
+ val result: LiveData> = _result
+
+ private var _attendances = MutableLiveData>()
+ val attendances: LiveData> = _attendances
+
+ // The opened session which have been selected in the menu.
+ var session: Session? = null
+
+ /**
+ * Add the [student] in the attendance list [attendances].
+ * Return true if it has been added, else return false.
+ */
+ fun addAttendance(student: User) {
+ val list = _attendances.value ?: emptyList()
+
+ // If the list already contains the user, return false
+ if (list.any { it.student == student }) {
+ Log.d("Palto", "SessionViewModel: User already in the list.")
+ _result.value = Event(SessionResult(
+ false,
+ R.string.session_user_already_added,
+ student.username
+ ))
+
+ // Else create a new attendance and add it into the list.
+ } else {
+ val attendance = Attendance(
+ id = list.size,
+ student = student,
+ date = LocalTime.now()
+ )
+ // Add the attendance in the attendance list, and trigger the observers.
+ _attendances.value = list + attendance
+ // Saved the list in the session.
+ session?.attendances = list + attendance
+
+ _result.value = Event(SessionResult(
+ true,
+ R.string.session_user_added,
+ student.username
+ ))
+ Log.d("Palto", "SessionViewModel: An attendance has been added into the list.")
+ }
+ }
+
+ fun setAttendanceList(list: List) {
+ _attendances.value = list
+ }
+
+ /**
+ * ViewModel Factory.
+ */
+ companion object {
+
+ val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
+ @Suppress("UNCHECKED_CAST")
+ override fun create(
+ modelClass: Class
+ ): T {
+ return SessionViewModel() as T
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/example/palto/ui/session/new_student/NewStudentFragment.kt b/app/src/main/java/com/example/palto/ui/session/new_student/NewStudentFragment.kt
new file mode 100644
index 0000000..e52675d
--- /dev/null
+++ b/app/src/main/java/com/example/palto/ui/session/new_student/NewStudentFragment.kt
@@ -0,0 +1,69 @@
+package com.example.palto.ui.session.new_student
+
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.fragment.app.viewModels
+import androidx.navigation.fragment.findNavController
+import androidx.navigation.navGraphViewModels
+import com.example.palto.NfcViewModel
+import com.example.palto.R
+import com.example.palto.databinding.FragmentNewSessionBinding
+import com.example.palto.databinding.FragmentNewStudentBinding
+import com.example.palto.ui.CardViewModel
+import com.example.palto.ui.UserViewModel
+import com.example.palto.ui.menu.MenuViewModel
+import com.example.palto.ui.session.SessionViewModel
+
+/**
+ *
+ */
+class NewStudentFragment : Fragment() {
+
+ private val newStudentViewModel: NewStudentViewModel by viewModels()
+
+ private val sessionViewModel: SessionViewModel by navGraphViewModels(R.id.nav_graph)
+
+ private val userViewModel: UserViewModel by navGraphViewModels(R.id.nav_graph)
+
+ private val cardViewModel: CardViewModel by navGraphViewModels(R.id.nav_graph)
+
+ // This property is only valid between onCreateView and onDestroyView
+ private lateinit var binding: FragmentNewStudentBinding
+
+ @OptIn(ExperimentalStdlibApi::class)
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ binding = FragmentNewStudentBinding.inflate(inflater, container, false)
+
+ // If the bundle value tagId of key "tagId" exists,
+ // Set it on the screen.
+ val tagId = arguments?.getString("tagId")
+ if (tagId != null) {
+ binding.newStudentCardId.text = tagId
+ }
+
+ // Bind the create button.
+ binding.newStudentCreate.setOnClickListener {
+ val user = userViewModel.createUser(binding.newStudentName.text.toString())
+
+ // If a tag has been provided, create the card.
+ // The user would not need to create his account afterward.
+ if (tagId != null) {
+ cardViewModel.createCard(user, tagId)
+ }
+
+ sessionViewModel.addAttendance(user)
+ findNavController().popBackStack()
+ }
+
+ return binding.root
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/session/new_student/NewStudentViewModel.kt b/app/src/main/java/com/example/palto/ui/session/new_student/NewStudentViewModel.kt
new file mode 100644
index 0000000..8ae9268
--- /dev/null
+++ b/app/src/main/java/com/example/palto/ui/session/new_student/NewStudentViewModel.kt
@@ -0,0 +1,8 @@
+package com.example.palto.ui.session.new_student
+
+import androidx.lifecycle.ViewModel
+
+/**
+ * ViewModel of the session creation form. Used for verifications.
+ */
+class NewStudentViewModel() : ViewModel()
diff --git a/app/src/main/java/com/example/palto/ui/sessionList/SessionListAdapter.kt b/app/src/main/java/com/example/palto/ui/sessionList/SessionListAdapter.kt
deleted file mode 100644
index 24f492f..0000000
--- a/app/src/main/java/com/example/palto/ui/sessionList/SessionListAdapter.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.example.palto.ui.sessionList
-
-import androidx.recyclerview.widget.RecyclerView
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import android.widget.TextView
-
-import com.example.palto.ui.sessionList.placeholder.PlaceholderContent.PlaceholderItem
-import com.example.palto.databinding.FragmentSessionItemBinding
-
-/**
- * [RecyclerView.Adapter] that can display a [PlaceholderItem].
- */
-class SessionListAdapter(private val values: List) :
- RecyclerView.Adapter() {
-
- class ViewHolder(binding: FragmentSessionItemBinding) :
- RecyclerView.ViewHolder(binding.root) {
-
- val idView: TextView = binding.itemNumber
- val contentView: TextView = binding.content
-
- override fun toString(): String {
- return super.toString() + " '" + contentView.text + "'"
- }
- }
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
- val view = FragmentSessionItemBinding.inflate(
- LayoutInflater.from(parent.context),
- parent,
- false
- )
- return ViewHolder(view)
- }
-
- override fun onBindViewHolder(holder: ViewHolder, position: Int) {
- val item = values[position]
- holder.idView.text = item.id
- holder.contentView.text = item.content
- }
-
- override fun getItemCount(): Int = values.size
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/sessionList/SessionListFragment.kt b/app/src/main/java/com/example/palto/ui/sessionList/SessionListFragment.kt
deleted file mode 100644
index 03424e1..0000000
--- a/app/src/main/java/com/example/palto/ui/sessionList/SessionListFragment.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.example.palto.ui.sessionList
-
-import android.os.Bundle
-import androidx.fragment.app.Fragment
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import com.example.palto.R
-import com.example.palto.ui.sessionList.placeholder.PlaceholderContent
-
-/**
- * A fragment representing a list of Sessions.
- */
-class SessionListFragment : Fragment() {
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- val view = inflater.inflate(R.layout.fragment_session_list, container, false)
-
- if (view is RecyclerView) {
- view.layoutManager = LinearLayoutManager(context)
- view.adapter = SessionListAdapter(PlaceholderContent.ITEMS)
- }
- return view
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/sessionList/SessionListViewModel.kt b/app/src/main/java/com/example/palto/ui/sessionList/SessionListViewModel.kt
deleted file mode 100644
index d127a36..0000000
--- a/app/src/main/java/com/example/palto/ui/sessionList/SessionListViewModel.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.example.palto.ui.sessionList
-
-import androidx.lifecycle.ViewModel
-import com.example.palto.data.repository.LoginRepository
-
-class SessionListViewModel(private val loginRepository: LoginRepository) : ViewModel() {
- /*
- private val _loginForm = MutableLiveData()
- val loginFormState: LiveData = _loginForm
-
- private val _loginResult = MutableLiveData()
- val loginResult: LiveData = _loginResult
- */
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/palto/ui/sessionList/placeholder/PlaceholderContent.kt b/app/src/main/java/com/example/palto/ui/sessionList/placeholder/PlaceholderContent.kt
deleted file mode 100644
index b085b2c..0000000
--- a/app/src/main/java/com/example/palto/ui/sessionList/placeholder/PlaceholderContent.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.example.palto.ui.sessionList.placeholder
-
-import java.util.ArrayList
-import java.util.HashMap
-
-/**
- * Helper class for providing sample content for user interfaces created by
- * Android template wizards.
- *
- * TODO: Replace all uses of this class before publishing your app.
- */
-object PlaceholderContent {
-
- /**
- * An array of sample (placeholder) items.
- */
- val ITEMS: MutableList = ArrayList()
-
- /**
- * A map of sample (placeholder) items, by ID.
- */
- val ITEM_MAP: MutableMap = HashMap()
-
- private val COUNT = 25
-
- init {
- // Add some sample items.
- for (i in 1..COUNT) {
- addItem(createPlaceholderItem(i))
- }
- }
-
- private fun addItem(item: PlaceholderItem) {
- ITEMS.add(item)
- ITEM_MAP.put(item.id, item)
- }
-
- private fun createPlaceholderItem(position: Int): PlaceholderItem {
- return PlaceholderItem(position.toString(), "Item " + position, makeDetails(position))
- }
-
- private fun makeDetails(position: Int): String {
- val builder = StringBuilder()
- builder.append("Details about Item: ").append(position)
- for (i in 0..position - 1) {
- builder.append("\nMore details information here.")
- }
- return builder.toString()
- }
-
- /**
- * A placeholder item representing a piece of content.
- */
- data class PlaceholderItem(val id: String, val content: String, val details: String) {
- override fun toString(): String = content
- }
-}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_palto.xml b/app/src/main/res/layout/activity_palto.xml
index 3a70781..cebfacb 100644
--- a/app/src/main/res/layout/activity_palto.xml
+++ b/app/src/main/res/layout/activity_palto.xml
@@ -1,12 +1,21 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_attendance_list.xml b/app/src/main/res/layout/fragment_attendance_list.xml
deleted file mode 100644
index 318836a..0000000
--- a/app/src/main/res/layout/fragment_attendance_list.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_login.xml b/app/src/main/res/layout/fragment_login.xml
index 96f7159..7e7c0c7 100644
--- a/app/src/main/res/layout/fragment_login.xml
+++ b/app/src/main/res/layout/fragment_login.xml
@@ -13,11 +13,8 @@
@@ -77,33 +71,38 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
- android:layout_marginStart="48dp"
- android:layout_marginTop="8dp"
- android:layout_marginEnd="48dp"
- android:layout_marginBottom="64dp"
- android:enabled="false"
+ android:enabled="true"
android:text="@string/action_sign_in"
+ app:layout_constraintBottom_toTopOf="@+id/login_anonymous"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.5"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/login_error" />
+
+
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0.5" />
-
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.5"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/login" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_attendance_item.xml b/app/src/main/res/layout/fragment_menu_item.xml
similarity index 61%
rename from app/src/main/res/layout/fragment_attendance_item.xml
rename to app/src/main/res/layout/fragment_menu_item.xml
index b975c33..92c931f 100644
--- a/app/src/main/res/layout/fragment_attendance_item.xml
+++ b/app/src/main/res/layout/fragment_menu_item.xml
@@ -4,10 +4,17 @@
android:layout_height="wrap_content"
android:orientation="horizontal">
-
+ android:textAppearance="?attr/textAppearanceListItem" />-->
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_menu_list.xml b/app/src/main/res/layout/fragment_menu_list.xml
new file mode 100644
index 0000000..645676c
--- /dev/null
+++ b/app/src/main/res/layout/fragment_menu_list.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_new_session.xml b/app/src/main/res/layout/fragment_new_session.xml
new file mode 100644
index 0000000..d419ed9
--- /dev/null
+++ b/app/src/main/res/layout/fragment_new_session.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_new_student.xml b/app/src/main/res/layout/fragment_new_student.xml
new file mode 100644
index 0000000..c4e2eb2
--- /dev/null
+++ b/app/src/main/res/layout/fragment_new_student.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_session_item.xml b/app/src/main/res/layout/fragment_session_item.xml
index 1877568..dc3831f 100644
--- a/app/src/main/res/layout/fragment_session_item.xml
+++ b/app/src/main/res/layout/fragment_session_item.xml
@@ -5,14 +5,14 @@
android:orientation="horizontal">
-
\ No newline at end of file
+ android:layout_height="match_parent">
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/palto_menu.xml b/app/src/main/res/menu/palto_menu.xml
new file mode 100644
index 0000000..a018d03
--- /dev/null
+++ b/app/src/main/res/menu/palto_menu.xml
@@ -0,0 +1,8 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml
index cee746c..7dd725c 100644
--- a/app/src/main/res/mipmap-anydpi/ic_launcher.xml
+++ b/app/src/main/res/mipmap-anydpi/ic_launcher.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
index cee746c..7dd725c 100644
--- a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
+++ b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml
index 4b9c3fd..6c958f5 100644
--- a/app/src/main/res/navigation/nav_graph.xml
+++ b/app/src/main/res/navigation/nav_graph.xml
@@ -3,20 +3,47 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
- app:startDestination="@id/attendanceListFragment">
+ app:startDestination="@id/menuFragment">
+
+
+
+
+
+
+
+ android:id="@+id/newSessionFragment"
+ android:name="com.example.palto.ui.menu.new_session.NewSessionFragment"
+ android:label="Nouvelle fiche"
+ tools:layout="@layout/fragment_new_session"/>
+
+ android:id="@+id/sessionFragment"
+ android:name="com.example.palto.ui.session.SessionFragment"
+ android:label="Fiche de présence"
+ tools:layout="@layout/fragment_session_list" >
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
deleted file mode 100644
index 7de5518..0000000
--- a/app/src/main/res/values-night/themes.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index c8524cd..ee293c9 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,5 +1,9 @@
+ #00345f
+ #ffffff
+ #ef800a
+ #ffffff
#FF000000
#FFFFFFFF
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 51b4ac6..aaa0bcf 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -5,11 +5,23 @@
Nom d’utilisateur
Mot de passe
Connexion
- Sign in
+ Connexion anonyme
+ Connexion
"Bienvenue !"
Serveur inaccessible
Nom d’utilisateur non valide
Mot de passe invalide
"Erreur de connexion !"
Identifiants Invalides
+ Créer une session
+ Créer une présence
+ Nouvelle Fiche
+ Nom de la fiche
+ Créer
+ Déconnexion
+ Ajouter
+ Nom de l’étudiant
+ ID Carte : Pas de carte
+ L’étudiant %1$s existe déjà dans la liste !\n
+ L’étudiant %1$s a été ajouté
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 73557f8..9fc7cdb 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -1,8 +1,17 @@
-
-
-
+
+
diff --git a/build.gradle.kts b/build.gradle.kts
index 8e8f4ab..55ea678 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,5 +1,5 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
- id("com.android.application") version "8.2.0" apply false
+ id("com.android.application") version "8.2.1" apply false
id("org.jetbrains.kotlin.android") version "1.9.0" apply false
}
\ No newline at end of file