diff --git a/app/build.gradle b/app/build.gradle
index 55ca3e78..5a0dc49a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -47,6 +47,9 @@ dependencies {
testImplementation "com.github.tomakehurst:wiremock-jre8:2.26.2"
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
+ implementation 'com.google.android.material:material:1.1.0'
+ implementation 'androidx.annotation:annotation:1.1.0'
+ implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
@@ -73,4 +76,4 @@ task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest', 'crea
executionData = fileTree(dir: project.buildDir, includes: [
'jacoco/testDebugUnitTest.exec', 'outputs/code-coverage/debugAndroidTest/connected/*coverage.ec'
])
-}
\ No newline at end of file
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2d63dcda..09cf7eba 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -9,6 +9,9 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
+
diff --git a/app/src/main/java/com/h/pixeldroid/data/LoginDataSource.kt b/app/src/main/java/com/h/pixeldroid/data/LoginDataSource.kt
new file mode 100644
index 00000000..7600b90a
--- /dev/null
+++ b/app/src/main/java/com/h/pixeldroid/data/LoginDataSource.kt
@@ -0,0 +1,25 @@
+package com.h.pixeldroid.data
+
+import com.h.pixeldroid.data.model.LoggedInUser
+import java.io.IOException
+
+/**
+ * Class that handles authentication w/ login credentials and retrieves user information.
+ */
+class LoginDataSource {
+
+ fun login(username: String, password: String): Result {
+ try {
+ // TODO: handle loggedInUser authentication
+ 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
+ }
+}
+
diff --git a/app/src/main/java/com/h/pixeldroid/data/LoginRepository.kt b/app/src/main/java/com/h/pixeldroid/data/LoginRepository.kt
new file mode 100644
index 00000000..e9d33973
--- /dev/null
+++ b/app/src/main/java/com/h/pixeldroid/data/LoginRepository.kt
@@ -0,0 +1,46 @@
+package com.h.pixeldroid.data
+
+import com.h.pixeldroid.data.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: LoginDataSource) {
+
+ // in-memory cache of the loggedInUser object
+ var user: LoggedInUser? = 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
+ user = null
+ }
+
+ fun logout() {
+ user = null
+ dataSource.logout()
+ }
+
+ 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 setLoggedInUser(loggedInUser: LoggedInUser) {
+ this.user = loggedInUser
+ // If user credentials will be cached in local storage, it is recommended it be encrypted
+ // @see https://developer.android.com/training/articles/keystore
+ }
+}
diff --git a/app/src/main/java/com/h/pixeldroid/data/Result.kt b/app/src/main/java/com/h/pixeldroid/data/Result.kt
new file mode 100644
index 00000000..d6094c18
--- /dev/null
+++ b/app/src/main/java/com/h/pixeldroid/data/Result.kt
@@ -0,0 +1,18 @@
+package com.h.pixeldroid.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]"
+ }
+ }
+}
diff --git a/app/src/main/java/com/h/pixeldroid/data/model/LoggedInUser.kt b/app/src/main/java/com/h/pixeldroid/data/model/LoggedInUser.kt
new file mode 100644
index 00000000..0f2eaed6
--- /dev/null
+++ b/app/src/main/java/com/h/pixeldroid/data/model/LoggedInUser.kt
@@ -0,0 +1,9 @@
+package com.h.pixeldroid.data.model
+
+/**
+ * Data class that captures user information for logged in users retrieved from LoginRepository
+ */
+data class LoggedInUser(
+ val userId: String,
+ val displayName: String
+)
diff --git a/app/src/main/java/com/h/pixeldroid/ui/login/LoggedInUserView.kt b/app/src/main/java/com/h/pixeldroid/ui/login/LoggedInUserView.kt
new file mode 100644
index 00000000..68255c4c
--- /dev/null
+++ b/app/src/main/java/com/h/pixeldroid/ui/login/LoggedInUserView.kt
@@ -0,0 +1,9 @@
+package com.h.pixeldroid.ui.login
+
+/**
+ * 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/h/pixeldroid/ui/login/LoginActivity.kt b/app/src/main/java/com/h/pixeldroid/ui/login/LoginActivity.kt
new file mode 100644
index 00000000..d8dbed38
--- /dev/null
+++ b/app/src/main/java/com/h/pixeldroid/ui/login/LoginActivity.kt
@@ -0,0 +1,129 @@
+package com.h.pixeldroid.ui.login
+
+import android.app.Activity
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
+import android.os.Bundle
+import androidx.annotation.StringRes
+import androidx.appcompat.app.AppCompatActivity
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.View
+import android.view.inputmethod.EditorInfo
+import android.widget.Button
+import android.widget.EditText
+import android.widget.ProgressBar
+import android.widget.Toast
+
+import com.h.pixeldroid.R
+
+class LoginActivity : AppCompatActivity() {
+
+ private lateinit var loginViewModel: LoginViewModel
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.activity_login)
+
+ val username = findViewById(R.id.username)
+ val password = findViewById(R.id.password)
+ val login = findViewById