diff --git a/app/build.gradle b/app/build.gradle index 9e99c57..72ed63a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -37,7 +37,7 @@ android { compose true } composeOptions { - kotlinCompilerExtensionVersion '1.2.0' + kotlinCompilerExtensionVersion "$compose_compiler" } packagingOptions { resources { @@ -53,11 +53,11 @@ dependencies { // other dependencies implementation 'androidx.core:core-ktx:1.9.0' - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' - implementation 'androidx.activity:activity-compose:1.6.1' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1' + implementation 'androidx.activity:activity-compose:1.7.0' implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" - implementation 'androidx.compose.material3:material3:1.0.0-alpha11' + implementation 'androidx.compose.material3:material3:1.0.1' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' diff --git a/app/src/main/java/com/rocqjones/mesdk/MainActivity.kt b/app/src/main/java/com/rocqjones/mesdk/MainActivity.kt index b267025..2de46b7 100644 --- a/app/src/main/java/com/rocqjones/mesdk/MainActivity.kt +++ b/app/src/main/java/com/rocqjones/mesdk/MainActivity.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.rocqjones.me_design.screens.ListActivity +import com.rocqjones.me_design.screens.bottomNavAdaptive.AdaptiveNavActivity import com.rocqjones.me_design.ui.theme.MeSDKTheme /** @@ -33,18 +34,29 @@ class MainActivity : ComponentActivity() { color = MaterialTheme.colorScheme.background ) { FirstScreen( - onMoveToListClicked = { navigateToList() } + onMoveToListClicked = { navigateToList() }, + onMoveToNavClicked = { navigateToAdaptiveUI() } ) } } } } + private fun navigateToAdaptiveUI() { + try { + val b = Intent(this, AdaptiveNavActivity::class.java) + b.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(b) + } catch (e: Exception) { + e.printStackTrace() + } + } + private fun navigateToList() { try { - val i = Intent(this, ListActivity::class.java) - i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - startActivity(i) + val a = Intent(this, ListActivity::class.java) + a.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(a) } catch (e: Exception) { e.printStackTrace() } @@ -54,6 +66,7 @@ class MainActivity : ComponentActivity() { @Composable fun FirstScreen( onMoveToListClicked: () -> Unit, + onMoveToNavClicked: () -> Unit, modifier: Modifier = Modifier ) { /** @@ -64,12 +77,13 @@ fun FirstScreen( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { - val padding = 24.dp + val padding = 8.dp Text( "Welcome to the Me SDK!", style = MaterialTheme.typography.headlineMedium, color =MaterialTheme.colorScheme.secondary, ) + // list Button( modifier = Modifier.padding(padding).fillMaxWidth(), shape = MaterialTheme.shapes.medium, @@ -80,6 +94,18 @@ fun FirstScreen( style = MaterialTheme.typography.bodyLarge ) } + + // Adaptive Navigation + Button( + modifier = Modifier.padding(padding).fillMaxWidth(), + shape = MaterialTheme.shapes.medium, + onClick = onMoveToNavClicked // sets the sate to true + ) { + Text( + "Adaptive Navigation", + style = MaterialTheme.typography.bodyLarge + ) + } } } @@ -97,6 +123,9 @@ fun FirstScreen( @Composable fun DefaultPreview() { MeSDKTheme { - FirstScreen(onMoveToListClicked = {}) // empty lambda expression means "Do nothing" on click + FirstScreen( + onMoveToListClicked = {}, + onMoveToNavClicked = {} + ) // empty lambda expression means "Do nothing" on click } } \ No newline at end of file diff --git a/build.gradle b/build.gradle index 03d0726..62f09f0 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,11 @@ buildscript { ext { - compose_version = '1.2.0' + compose_version = '1.4.0' // compose.runtime + compose_compiler = '1.4.4' // Release: https://developer.android.com/jetpack/androidx/releases/compose-kotlin } }// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '7.4.1' apply false - id 'com.android.library' version '7.4.1' apply false - id 'org.jetbrains.kotlin.android' version '1.7.0' apply false + id 'com.android.application' version '7.4.2' apply false + id 'com.android.library' version '7.4.2' apply false + id 'org.jetbrains.kotlin.android' version '1.8.10' apply false } \ No newline at end of file diff --git a/me-design/build.gradle b/me-design/build.gradle index 15af3c0..4a0d0c9 100644 --- a/me-design/build.gradle +++ b/me-design/build.gradle @@ -2,7 +2,6 @@ plugins { id 'com.android.library' id 'org.jetbrains.kotlin.android' id 'kotlin-android' - id 'kotlin-android-extensions' id 'kotlin-kapt' } @@ -27,18 +26,23 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + kotlinOptions { jvmTarget = '1.8' } + buildFeatures { compose true } + composeOptions { - kotlinCompilerExtensionVersion '1.2.0' + // Release: https://developer.android.com/jetpack/androidx/releases/compose-kotlin + kotlinCompilerExtensionVersion "$compose_compiler" } packagingOptions { resources { @@ -51,19 +55,22 @@ dependencies { // Me Android SDK - logic implementation project(":me-logic") + implementation 'androidx.core:core-ktx:1.9.0' + // other dependencies implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.8.0' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' - implementation 'androidx.activity:activity-compose:1.6.1' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1' + implementation 'androidx.activity:activity-compose:1.7.0' implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" - implementation 'androidx.compose.material3:material3:1.0.0-alpha11' + implementation 'androidx.compose.material3:material3:1.0.1' androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" diff --git a/me-design/src/main/AndroidManifest.xml b/me-design/src/main/AndroidManifest.xml index dd8bced..149f57d 100644 --- a/me-design/src/main/AndroidManifest.xml +++ b/me-design/src/main/AndroidManifest.xml @@ -2,11 +2,21 @@ + + \ No newline at end of file diff --git a/me-design/src/main/java/com/rocqjones/me_design/base/BaseActivity.kt b/me-design/src/main/java/com/rocqjones/me_design/base/BaseActivity.kt new file mode 100644 index 0000000..f2559fd --- /dev/null +++ b/me-design/src/main/java/com/rocqjones/me_design/base/BaseActivity.kt @@ -0,0 +1,33 @@ +package com.rocqjones.me_design.base + +import android.app.Activity +import android.os.Bundle +import androidx.activity.ComponentActivity +import com.rocqjones.me_logic.utils.ToastUtils + +/** + * This will be the base class of our Design library where we'll be defining all our common behaviour used across our Activities. + * We can have a lot more abstract methods, for every bit we want specific to our subclasses. + * Example Usage: 'CustomToastUtils' demonstrates this well + */ +abstract class BaseActivity : ComponentActivity() { + + private var activityContext: Activity? = null + var toastUtils: ToastUtils? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + initialize() + } + + private fun initialize() { + try { + activityContext = activityContext() + toastUtils = ToastUtils(activityContext!!) + } catch (e: Exception) { + e.printStackTrace() + } + } + + protected abstract fun activityContext(): Activity +} \ No newline at end of file diff --git a/me-design/src/main/java/com/rocqjones/me_design/screens/ListActivity.kt b/me-design/src/main/java/com/rocqjones/me_design/screens/ListActivity.kt index fe94ab7..f6d3b68 100644 --- a/me-design/src/main/java/com/rocqjones/me_design/screens/ListActivity.kt +++ b/me-design/src/main/java/com/rocqjones/me_design/screens/ListActivity.kt @@ -59,7 +59,6 @@ fun GenerateLazyList( } } -@OptIn(ExperimentalMaterial3Api::class) @Composable private fun ItemCard(name: String) { Card( diff --git a/me-design/src/main/java/com/rocqjones/me_design/screens/bottomNavAdaptive/AdaptiveNavActivity.kt b/me-design/src/main/java/com/rocqjones/me_design/screens/bottomNavAdaptive/AdaptiveNavActivity.kt new file mode 100644 index 0000000..e3521d2 --- /dev/null +++ b/me-design/src/main/java/com/rocqjones/me_design/screens/bottomNavAdaptive/AdaptiveNavActivity.kt @@ -0,0 +1,57 @@ +package com.rocqjones.me_design.screens.bottomNavAdaptive + +import android.app.Activity +import android.os.Bundle +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.rocqjones.me_design.base.BaseActivity +import com.rocqjones.me_design.ui.theme.MeSDKTheme + +class AdaptiveNavActivity : BaseActivity() { + + override fun activityContext(): Activity { + return this + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + MeSDKTheme { + // A surface container using the 'background' color from the theme + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background + ) { + Greeting("Check Android CI & Branch protection") + try { + // Test custom snackBar + toastUtils?.showSnackBar("This screen successfully lunched") + } catch (e: Exception) { + e.printStackTrace() + } + } + } + } + + + } +} + +@Composable +fun Greeting(name: String) { + Text(text = "Test $name!") +} + +@Preview(showBackground = true) +@Composable +fun DefaultPreview2() { + MeSDKTheme { + Greeting("Android") + } +} \ No newline at end of file diff --git a/me-design/src/main/res/values/strings.xml b/me-design/src/main/res/values/strings.xml index 67d8234..96b6faa 100644 --- a/me-design/src/main/res/values/strings.xml +++ b/me-design/src/main/res/values/strings.xml @@ -1,5 +1,7 @@ Show less Show more - ListActivity + List Activity + Adaptive Nav Activity + Base Activity \ No newline at end of file diff --git a/me-logic/build.gradle b/me-logic/build.gradle index 2458cd5..08ac9ee 100644 --- a/me-logic/build.gradle +++ b/me-logic/build.gradle @@ -2,7 +2,6 @@ plugins { id 'com.android.library' id 'org.jetbrains.kotlin.android' id 'kotlin-android' - id 'kotlin-android-extensions' id 'kotlin-kapt' } @@ -24,10 +23,12 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } + compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + kotlinOptions { jvmTarget = '1.8' } @@ -37,6 +38,8 @@ dependencies { implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'androidx.core:core-ktx:1.9.0' + implementation 'com.google.android.material:material:1.8.0' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' diff --git a/me-logic/src/main/java/com/rocqjones/me_logic/models/EmailModel.kt b/me-logic/src/main/java/com/rocqjones/me_logic/models/EmailModel.kt new file mode 100644 index 0000000..51d81ee --- /dev/null +++ b/me-logic/src/main/java/com/rocqjones/me_logic/models/EmailModel.kt @@ -0,0 +1,63 @@ +package com.rocqjones.me_logic.models + +import androidx.annotation.DrawableRes +import com.rocqjones.me_logic.providers.LocalAccountsDataProvider + +/** + * A simple data class to represent an Email. + */ +data class Email( + val id: Long, + val sender: Account, + val recipients: List = emptyList(), + val subject: String = "", + val body: String = "", + val attachments: List = emptyList(), + var isImportant: Boolean = false, + var isStarred: Boolean = false, + var mailbox: MailboxType = MailboxType.INBOX, + var createAt: String, + val threads: List = emptyList() +) { + val senderPreview: String = "${sender.fullName} - 4 hrs ago" + val hasBody: Boolean = body.isNotBlank() + val hasAttachments: Boolean = attachments.isNotEmpty() + val recipientsPreview: String = recipients + .map { it.firstName } + .fold("") { name, acc -> "$acc, $name" } + val nonUserAccountRecipients = recipients + .filterNot { LocalAccountsDataProvider.isUserAccount(it.uid) } +} + +/** + * An object which represents an account which can belong to a user. A single user can have + * multiple accounts. + */ +data class Account( + val id: Long, + val uid: Long, + val firstName: String, + val lastName: String, + val email: String, + val altEmail: String, + @DrawableRes val avatar: Int, + var isCurrentAccount: Boolean = false +) { + val fullName: String = "$firstName $lastName" + +} + +/** + * An object class to define an attachment to email object. + */ +data class EmailAttachment( + @DrawableRes val resId: Int, + val contentDesc: String +) + +/** + * An enum class to define different types of email folders or categories. + */ +enum class MailboxType { + INBOX, DRAFTS, SENT, SPAM, TRASH +} \ No newline at end of file diff --git a/me-logic/src/main/java/com/rocqjones/me_logic/providers/LocalAccountsDataProvider.kt b/me-logic/src/main/java/com/rocqjones/me_logic/providers/LocalAccountsDataProvider.kt new file mode 100644 index 0000000..74a928c --- /dev/null +++ b/me-logic/src/main/java/com/rocqjones/me_logic/providers/LocalAccountsDataProvider.kt @@ -0,0 +1,154 @@ +package com.rocqjones.me_logic.providers + +import com.rocqjones.me_logic.R +import com.rocqjones.me_logic.models.Account + +/** + * An static data store of [Account]s. This includes both [Account]s owned by the current user and + * all [Account]s of the current user's contacts. + */ +object LocalAccountsDataProvider { + + val allUserAccounts = mutableListOf( + Account( + 1L, + 0L, + "Jeff", + "Hansen", + "hikingfan@gmail.com", + "hkngfan@outside.com", + R.drawable.avatar_10, + true + ), + Account( + 2L, + 0L, + "Jeff", + "H", + "jeffersonloveshiking@gmail.com", + "jeffersonloveshiking@work.com", + R.drawable.avatar_2 + ), + Account( + 3L, + 0L, + "Jeff", + "Hansen", + "jeffersonc@google.com", + "jeffersonc@gmail.com", + R.drawable.avatar_9 + ) + ) + + private val allUserContactAccounts = listOf( + Account( + 4L, + 1L, + "Tracy", + "Alvarez", + "tracealvie@gmail.com", + "tracealvie@gravity.com", + R.drawable.avatar_1 + ), + Account( + 5L, + 2L, + "Allison", + "Trabucco", + "atrabucco222@gmail.com", + "atrabucco222@work.com", + R.drawable.avatar_3 + ), + Account( + 6L, + 3L, + "Ali", + "Connors", + "aliconnors@gmail.com", + "aliconnors@android.com", + R.drawable.avatar_5 + ), + Account( + 7L, + 4L, + "Alberto", + "Williams", + "albertowilliams124@gmail.com", + "albertowilliams124@chromeos.com", + R.drawable.avatar_0 + ), + Account( + 8L, + 5L, + "Kim", + "Alen", + "alen13@gmail.com", + "alen13@mountainview.gov", + R.drawable.avatar_7 + ), + Account( + 9L, + 6L, + "Google", + "Express", + "express@google.com", + "express@gmail.com", + R.drawable.avatar_express + ), + Account( + 10L, + 7L, + "Sandra", + "Adams", + "sandraadams@gmail.com", + "sandraadams@textera.com", + R.drawable.avatar_2 + ), + Account( + 11L, + 8L, + "Trevor", + "Hansen", + "trevorhandsen@gmail.com", + "trevorhandsen@express.com", + R.drawable.avatar_8 + ), + Account( + 12L, + 9L, + "Sean", + "Holt", + "sholt@gmail.com", + "sholt@art.com", + R.drawable.avatar_6 + ), + Account( + 13L, + 10L, + "Frank", + "Hawkins", + "fhawkank@gmail.com", + "fhawkank@thisisme.com", + R.drawable.avatar_4 + ) + ) + + /** + * Get the current user's default account. + */ + fun getDefaultUserAccount() = allUserAccounts.first() + + /** + * Whether or not the given [Account.id] uid is an account owned by the current user. + */ + fun isUserAccount(uid: Long): Boolean = allUserAccounts.any { it.uid == uid } + + + /** + * Get the contact of the current user with the given [accountId]. + */ + fun getContactAccountByUid(accountId: Long): Account { + return allUserContactAccounts.firstOrNull { it.id == accountId } + ?: allUserContactAccounts.first() + } +} \ No newline at end of file diff --git a/me-logic/src/main/java/com/rocqjones/me_logic/utils/ToastUtils.kt b/me-logic/src/main/java/com/rocqjones/me_logic/utils/ToastUtils.kt new file mode 100644 index 0000000..7fa758e --- /dev/null +++ b/me-logic/src/main/java/com/rocqjones/me_logic/utils/ToastUtils.kt @@ -0,0 +1,42 @@ +package com.rocqjones.me_logic.utils + +import android.app.Activity +import android.view.View +import androidx.core.content.ContextCompat +import com.google.android.material.snackbar.Snackbar +import com.rocqjones.me_logic.R + +/** + * This will be used instead of normal boring Toast + */ +class ToastUtils(activity : Activity) { + + private var snackBar: Snackbar? = null + private var activityContext: Activity? = null + + init { + this.activityContext = activity + } + + fun showSnackBar(message: String) { + try { + val contentView = activityContext!!.findViewById(android.R.id.content) + snackBar = Snackbar.make(contentView, message, Snackbar.LENGTH_LONG) + .setBackgroundTint(ContextCompat.getColor(activityContext!!, R.color.blue)) + snackBar!!.show() + } catch (e: Exception) { + e.printStackTrace() + } + } + + fun showSnackBarError(message: String) { + try { + val contentView = activityContext!!.findViewById(android.R.id.content) + snackBar = Snackbar.make(contentView, message, Snackbar.LENGTH_LONG) + .setBackgroundTint(ContextCompat.getColor(activityContext!!, R.color.red)) + snackBar!!.show() + } catch (e: Exception) { + e.printStackTrace() + } + } +} \ No newline at end of file diff --git a/me-logic/src/main/res/drawable/avatar_0.jpg b/me-logic/src/main/res/drawable/avatar_0.jpg new file mode 100644 index 0000000..dcf2608 Binary files /dev/null and b/me-logic/src/main/res/drawable/avatar_0.jpg differ diff --git a/me-logic/src/main/res/drawable/avatar_1.jpeg b/me-logic/src/main/res/drawable/avatar_1.jpeg new file mode 100644 index 0000000..9dc59cf Binary files /dev/null and b/me-logic/src/main/res/drawable/avatar_1.jpeg differ diff --git a/me-logic/src/main/res/drawable/avatar_10.jpeg b/me-logic/src/main/res/drawable/avatar_10.jpeg new file mode 100644 index 0000000..27b8dc6 Binary files /dev/null and b/me-logic/src/main/res/drawable/avatar_10.jpeg differ diff --git a/me-logic/src/main/res/drawable/avatar_2.jpg b/me-logic/src/main/res/drawable/avatar_2.jpg new file mode 100644 index 0000000..54c74a8 Binary files /dev/null and b/me-logic/src/main/res/drawable/avatar_2.jpg differ diff --git a/me-logic/src/main/res/drawable/avatar_3.jpg b/me-logic/src/main/res/drawable/avatar_3.jpg new file mode 100644 index 0000000..a63f8ce Binary files /dev/null and b/me-logic/src/main/res/drawable/avatar_3.jpg differ diff --git a/me-logic/src/main/res/drawable/avatar_4.jpg b/me-logic/src/main/res/drawable/avatar_4.jpg new file mode 100644 index 0000000..279b70d Binary files /dev/null and b/me-logic/src/main/res/drawable/avatar_4.jpg differ diff --git a/me-logic/src/main/res/drawable/avatar_5.jpeg b/me-logic/src/main/res/drawable/avatar_5.jpeg new file mode 100644 index 0000000..d665a3d Binary files /dev/null and b/me-logic/src/main/res/drawable/avatar_5.jpeg differ diff --git a/me-logic/src/main/res/drawable/avatar_6.jpg b/me-logic/src/main/res/drawable/avatar_6.jpg new file mode 100644 index 0000000..0b32267 Binary files /dev/null and b/me-logic/src/main/res/drawable/avatar_6.jpg differ diff --git a/me-logic/src/main/res/drawable/avatar_7.jpeg b/me-logic/src/main/res/drawable/avatar_7.jpeg new file mode 100644 index 0000000..1cccbb9 Binary files /dev/null and b/me-logic/src/main/res/drawable/avatar_7.jpeg differ diff --git a/me-logic/src/main/res/drawable/avatar_8.jpeg b/me-logic/src/main/res/drawable/avatar_8.jpeg new file mode 100644 index 0000000..5b387af Binary files /dev/null and b/me-logic/src/main/res/drawable/avatar_8.jpeg differ diff --git a/me-logic/src/main/res/drawable/avatar_9.jpg b/me-logic/src/main/res/drawable/avatar_9.jpg new file mode 100644 index 0000000..087bf93 Binary files /dev/null and b/me-logic/src/main/res/drawable/avatar_9.jpg differ diff --git a/me-logic/src/main/res/drawable/avatar_express.png b/me-logic/src/main/res/drawable/avatar_express.png new file mode 100644 index 0000000..f05790f Binary files /dev/null and b/me-logic/src/main/res/drawable/avatar_express.png differ diff --git a/me-logic/src/main/res/values/colors.xml b/me-logic/src/main/res/values/colors.xml new file mode 100644 index 0000000..0649d04 --- /dev/null +++ b/me-logic/src/main/res/values/colors.xml @@ -0,0 +1,7 @@ + + + + #053961 + #FFF44336 + + \ No newline at end of file