diff --git a/JokeApp/app/build.gradle b/JokeApp/app/build.gradle index b66e789..9242f30 100644 --- a/JokeApp/app/build.gradle +++ b/JokeApp/app/build.gradle @@ -19,9 +19,10 @@ android { targetSdk 30 versionCode 1 versionName "1.0" - + multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true + manifestPlaceholders = [auth0Domain: "dev-18w6525q.us.auth0.com", auth0Scheme: "demo"] } buildTypes { @@ -50,15 +51,20 @@ android { dependencies { - implementation 'androidx.core:core-ktx:1.6.0' implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'com.google.android.material:material:1.4.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.test.ext:junit-ktx:1.1.3' + implementation 'androidx.room:room-common:2.3.0' + implementation 'androidx.room:room-ktx:2.3.0' + implementation 'com.auth0.android:auth0:2.+' + implementation 'androidx.security:security-crypto-ktx:1.1.0-alpha02' testImplementation 'junit:junit:4.+' testImplementation 'org.hamcrest:hamcrest:2.2' + testImplementation "androidx.room:room-testing:2.2.6" androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' testImplementation "org.robolectric:robolectric:4.5.1" @@ -67,6 +73,7 @@ dependencies { implementation "android.arch.navigation:navigation-ui-ktx:$version_navigation" implementation 'com.jakewharton.timber:timber:5.0.1' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' + kapt "androidx.room:room-compiler:2.3.0" } diff --git a/JokeApp/app/src/androidTest/java/com/example/jokeapp/JokeDatabaseTest.kt b/JokeApp/app/src/androidTest/java/com/example/jokeapp/JokeDatabaseTest.kt new file mode 100644 index 0000000..aa11cf8 --- /dev/null +++ b/JokeApp/app/src/androidTest/java/com/example/jokeapp/JokeDatabaseTest.kt @@ -0,0 +1,58 @@ +package com.example.jokeapp + +import android.util.Log +import androidx.room.Room +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry + +import com.example.jokeapp.database.jokes.Joke +import com.example.jokeapp.database.jokes.JokeDatabase +import com.example.jokeapp.database.jokes.JokeDatabaseDao +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.io.IOException + +/** + * This is not meant to be a full set of tests. For simplicity, most of your samples do not + * include tests. However, when building the Room, it is helpful to make sure it works before + * adding the UI. + */ + +@RunWith(AndroidJUnit4::class) +class JokeDatabaseTest { + + private lateinit var jokeDao: JokeDatabaseDao + private lateinit var db: JokeDatabase + + @Before + fun createDb() { + Log.i("before", "running before") + val context = InstrumentationRegistry.getInstrumentation().targetContext + // Using an in-memory database because the information stored here disappears when the + // process is killed. + db = Room.inMemoryDatabaseBuilder(context, JokeDatabase::class.java) + // Allowing main thread queries, just for testing. + .allowMainThreadQueries() + .build() + jokeDao = db.jokeDatabaseDao + } + + @After + @Throws(IOException::class) + fun closeDb() { + db.close() + } + + @Test + @Throws(Exception::class) + fun insertAndGetJoke() = runBlocking { + val joke = Joke() + jokeDao.insert(joke) + val lastJoke = jokeDao.getLastJoke() + assertEquals(lastJoke?.punchline, "") + } +} \ No newline at end of file diff --git a/JokeApp/app/src/main/AndroidManifest.xml b/JokeApp/app/src/main/AndroidManifest.xml index e0f9cb8..f18dcec 100644 --- a/JokeApp/app/src/main/AndroidManifest.xml +++ b/JokeApp/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + + + //get the joke with the highest ID (last joke added) + @Query("SELECT * FROM custom_joke_table ORDER BY jokeId DESC LIMIT 1") + suspend fun getLastJoke(): Joke? + + //get the number of jokes present + @Query("SELECT COUNT(*) FROM custom_joke_table") + suspend fun numberOfJokes(): Int +} diff --git a/JokeApp/app/src/main/java/com/example/jokeapp/login/CredentialsManager.kt b/JokeApp/app/src/main/java/com/example/jokeapp/login/CredentialsManager.kt new file mode 100644 index 0000000..aee71b9 --- /dev/null +++ b/JokeApp/app/src/main/java/com/example/jokeapp/login/CredentialsManager.kt @@ -0,0 +1,45 @@ +package com.example.jokeapp.login + +import android.content.Context +import com.auth0.android.result.Credentials +import android.content.SharedPreferences +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKeys + + +object CredentialsManager { + private val ACCESS_TOKEN = "access_token" + + private lateinit var editor: SharedPreferences.Editor + + fun saveCredentials(context: Context, credentials: Credentials) { + + val masterKeyAlias: String = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) + + val sp: SharedPreferences = EncryptedSharedPreferences.create( + "secret_shared_prefs", + masterKeyAlias, + context, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + + editor = sp.edit() + editor.putString(ACCESS_TOKEN, credentials.accessToken) + .apply() + } + + fun getAccessToken(context: Context): String? { + val masterKeyAlias: String = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC) + + val sp: SharedPreferences = EncryptedSharedPreferences.create( + "secret_shared_prefs", + masterKeyAlias, + context, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + + return sp.getString(ACCESS_TOKEN, null) + } +} \ No newline at end of file diff --git a/JokeApp/app/src/main/java/com/example/jokeapp/login/LoginFragment.kt b/JokeApp/app/src/main/java/com/example/jokeapp/login/LoginFragment.kt new file mode 100644 index 0000000..b0f6db9 --- /dev/null +++ b/JokeApp/app/src/main/java/com/example/jokeapp/login/LoginFragment.kt @@ -0,0 +1,154 @@ +package com.example.jokeapp.login + +import android.app.Activity +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.TextView +import android.widget.Toast +import com.example.jokeapp.MainActivity +import com.example.jokeapp.R +import com.auth0.android.Auth0 +import com.auth0.android.authentication.AuthenticationAPIClient +import com.auth0.android.authentication.AuthenticationException +import com.auth0.android.callback.Callback +import com.auth0.android.provider.WebAuthProvider +import com.auth0.android.result.Credentials +import com.auth0.android.result.UserProfile +import timber.log.Timber + +/** + * A simple [Fragment] subclass. + * Use the [LoginFragment.newInstance] factory method to + * create an instance of this fragment. + */ +class LoginFragment : Fragment() { + + private lateinit var account : Auth0 + private lateinit var loggedInText: TextView + private var loggedIn = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // Inflate the layout for this fragment + val view = inflater.inflate(R.layout.fragment_login, container, false) + + //OAUTH + account = Auth0( + getString(R.string.auth_client_id), + getString(R.string.auth_domain) + ) + + + val button = view.findViewById