Skip to content

Commit

Permalink
Refactor admin contact into reusable component
Browse files Browse the repository at this point in the history
  • Loading branch information
newmanw committed Apr 11, 2024
1 parent 566290e commit b48954f
Show file tree
Hide file tree
Showing 12 changed files with 298 additions and 238 deletions.
1 change: 1 addition & 0 deletions mage/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@
android:theme="@style/AppTheme.NoActionBar"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity android:name=".login.AccountStateActivity"
android:theme="@style/AppTheme3.NoActionBar"
android:launchMode="singleTask">
</activity>
<activity
Expand Down
44 changes: 22 additions & 22 deletions mage/src/main/java/mil/nga/giat/mage/login/AccountStateActivity.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package mil.nga.giat.mage.login

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.graphics.Typeface
import android.net.Uri
import android.os.Bundle
import android.widget.TextView
import mil.nga.giat.mage.R
import mil.nga.giat.mage.databinding.ActivityAccountCreatedBinding

class AccountStateActivity: Activity() {
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.HiltAndroidApp
import mil.nga.giat.mage.ui.setup.AccountState
import mil.nga.giat.mage.ui.setup.AccountStateScreen
import mil.nga.giat.mage.ui.theme.MageTheme3

@AndroidEntryPoint
class AccountStateActivity: AppCompatActivity() {
companion object {
private const val EXTRA_ACCOUNT_ACTIVE = "EXTRA_ACCOUNT_ACTIVE"
private const val EXTRA_ACCOUNT_ENABLED = "EXTRA_ACCOUNT_ENABLED"
Expand All @@ -23,28 +26,25 @@ class AccountStateActivity: Activity() {
}
}

private lateinit var binding: ActivityAccountCreatedBinding

override fun onCreate(savedInstanceBundle: Bundle?) {
super.onCreate(savedInstanceBundle)

binding = ActivityAccountCreatedBinding.inflate(layoutInflater)
setContentView(binding.root)

val appName = findViewById<TextView>(R.id.mage)
appName.typeface = Typeface.createFromAsset(assets, "fonts/GondolaMage-Regular.otf")

val accountActive = intent.getBooleanExtra(EXTRA_ACCOUNT_ACTIVE, false)
val accountEnabled = intent.getBooleanExtra(EXTRA_ACCOUNT_ENABLED, false)

if (!accountActive) {
binding.status.text = getString(R.string.account_inactive_title)
binding.message.text = getString(R.string.account_inactive_message)
val accountState = if (!accountActive) {
AccountState.Inactive(applicationContext)
} else if (!accountEnabled) {
binding.status.text = getString(R.string.account_disabled_title)
binding.message.text = getString(R.string.account_disabled_message)
AccountState.Disabled(applicationContext)
} else AccountState.Unknown(applicationContext)

setContent {
MageTheme3 {
AccountStateScreen(
accountState = accountState,
onDone = { finish() }
)
}
}

binding.ok.setOnClickListener { finish() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import dagger.hilt.android.AndroidEntryPoint
import mil.nga.giat.mage.ui.theme.MageTheme3
import mil.nga.giat.mage.ui.url.ServerUrlScreen
import mil.nga.giat.mage.ui.setup.ServerUrlScreen

@AndroidEntryPoint
class ServerUrlActivity : AppCompatActivity() {
Expand Down
19 changes: 4 additions & 15 deletions mage/src/main/java/mil/nga/giat/mage/login/ServerUrlViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,30 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import mil.nga.giat.mage.R
import mil.nga.giat.mage.data.datasource.observation.AttachmentLocalDataSource
import mil.nga.giat.mage.data.datasource.observation.ObservationLocalDataSource
import mil.nga.giat.mage.database.MageDatabase
import mil.nga.giat.mage.data.repository.api.ApiRepository
import mil.nga.giat.mage.data.repository.api.ApiResponse
import mil.nga.giat.mage.database.dao.MageSqliteOpenHelper
import mil.nga.giat.mage.ui.setup.AdminContact
import javax.inject.Inject

const val ADMIN_EMAIL_PREFERENCE_KEY = "gContactinfoEmail"
const val ADMIN_PHONE_PREFERENCE_KEY = "gContactinfoPhone"

data class ContactInfo(
val email: String? = null,
val phone: String? = null
)

sealed class UrlState {
data object Valid: UrlState()
data object Invalid: UrlState()
data object InProgress: UrlState()
data class Error(val statusCode: Int?, val message: String?): UrlState()
data class Incompatible(val version: String, val contactInfo: ContactInfo): UrlState()
data class Incompatible(val version: String, val contact: AdminContact): UrlState()
}

@HiltViewModel
class ServerUrlViewModel @Inject constructor(
private val application: Application,
private val preferences: SharedPreferences,
private val adminContact: AdminContact,
private val daoStore: MageSqliteOpenHelper,
private val database: MageDatabase,
private val apiRepository: ApiRepository,
Expand Down Expand Up @@ -89,10 +81,7 @@ class ServerUrlViewModel @Inject constructor(
is ApiResponse.Incompatible -> {
val state = UrlState.Incompatible(
version = response.version,
contactInfo = ContactInfo(
email = preferences.getString(ADMIN_EMAIL_PREFERENCE_KEY, null),
phone = preferences.getString(ADMIN_PHONE_PREFERENCE_KEY, null)
)
contact = adminContact
)
_urlState.postValue(state)
}
Expand Down
112 changes: 112 additions & 0 deletions mage/src/main/java/mil/nga/giat/mage/ui/setup/AccountStateScreen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package mil.nga.giat.mage.ui.setup

import android.content.Context
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import mil.nga.giat.mage.R

sealed class AccountState(
val title: String,
val message: String
) {

class Inactive(context: Context): AccountState(
title = context.getString(R.string.account_inactive_title),
message = context.getString(R.string.account_inactive_message)
)

class Disabled(context: Context): AccountState(
title = context.getString(R.string.account_disabled_title),
message = context.getString(R.string.account_disabled_message)
)

class Unknown(context: Context): AccountState(
title = context.getString(R.string.account_unknown_title),
message = context.getString(R.string.account_unknown_message)
)
}

@Composable
fun AccountStateScreen(
accountState: AccountState,
onDone: () -> Unit,
viewModel: AccountStateViewModel = hiltViewModel()
) {
val contact = viewModel.contact

Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 16.dp)

) {

Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.weight(1f)
) {
Icon(
painter = painterResource(R.drawable.ic_wand_white_50dp),
contentDescription = "wand",
tint = MaterialTheme.colorScheme.tertiary,
modifier = Modifier
.padding(bottom = 16.dp)
.size(72.dp)
)

CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface) {
Text(
text = accountState.title,
style = MaterialTheme.typography.displaySmall,
modifier = Modifier.padding(bottom = 4.dp)
)
}
}

Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.weight(2f)
) {

AdminContact(
text = accountState.message,
contact = contact,
style = MaterialTheme.typography.bodyLarge.copy(
color = LocalContentColor.current.copy(alpha = .87f)
).toSpanStyle(),
emailState = EmailState(
subject = "MAGE Account",
body = accountState.message
)
)

Button(
onClick = { onDone() },
modifier = Modifier
.fillMaxWidth()
.padding(top = 48.dp)
) {
Text("OK")
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package mil.nga.giat.mage.ui.setup

import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

@HiltViewModel
class AccountStateViewModel @Inject constructor(
val contact: AdminContact
): ViewModel()
132 changes: 132 additions & 0 deletions mage/src/main/java/mil/nga/giat/mage/ui/setup/AdminContact.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package mil.nga.giat.mage.ui.setup

import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.SecurityUpdateWarning
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
data class AdminContact @Inject constructor(
val preferences: SharedPreferences
) {
val email = preferences.getString(ADMIN_EMAIL_PREFERENCE_KEY, null)
val phone = preferences.getString(ADMIN_PHONE_PREFERENCE_KEY, null)

companion object {
const val ADMIN_EMAIL_PREFERENCE_KEY = "gContactinfoEmail"
const val ADMIN_PHONE_PREFERENCE_KEY = "gContactinfoPhone"
}
}

data class EmailState(
val subject: String,
val body: String
)

@Composable
fun AdminContact(
text: String,
contact: AdminContact,
style: SpanStyle,
emailState: EmailState
) {
val context = LocalContext.current

Column(
Modifier.padding(vertical = 16.dp)
) {
val split = text.split("administrator")
val annotatedString = buildAnnotatedString {
withStyle(style) {
append("${split.first()}administrator")
}

if (contact.phone != null || contact.email != null) {
withStyle(style) { append(" at ") }
}

if (contact.phone != null) {
pushStringAnnotation(
tag = "phone",
annotation = contact.phone
)
withStyle(
style = MaterialTheme.typography.bodyLarge.copy(
color = MaterialTheme.colorScheme.tertiary
).toSpanStyle()
) {
append(contact.phone)
}
pop()
}

if (contact.phone != null && contact.email != null) {
withStyle(style) { append(" or ") }
}

if (contact.email != null) {
pushStringAnnotation(
tag = "email",
annotation = contact.email
)
withStyle(
style = MaterialTheme.typography.bodyLarge.copy(
color = MaterialTheme.colorScheme.tertiary
).toSpanStyle()
) {
append(contact.email)
}
pop()
}

withStyle(style) { append(split.last()) }
}

ClickableText(
text = annotatedString,
style = TextStyle(textAlign = TextAlign.Center),
onClick = { offset ->
annotatedString.getStringAnnotations(
tag = "email", start = offset, end = offset
).firstOrNull()?.let {
val uri = Uri.Builder()
.scheme("mailto")
.opaquePart(contact.email)
.appendQueryParameter("subject", emailState.subject)
.appendQueryParameter("body", emailState.body)
.build()

val intent = Intent(Intent.ACTION_SENDTO, uri)
context.startActivity(intent)
}

annotatedString.getStringAnnotations(
tag = "phone", start = offset, end = offset
).firstOrNull()?.let {
val intent = Intent(Intent.ACTION_DIAL, Uri.parse("tel:${contact.phone}"))
context.startActivity(intent)
}
}
)
}
}
Loading

0 comments on commit b48954f

Please sign in to comment.