improve login
This commit is contained in:
parent
bc8ced0fd2
commit
31794af1fd
|
@ -26,8 +26,8 @@
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
|
||||||
|
<activity android:name=".components.login.LoginWebViewActivity"
|
||||||
<activity android:name=".components.login.LoginWebViewActivity">
|
android:label="@string/title_login">
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".components.settings.SettingsActivity"
|
android:name=".components.settings.SettingsActivity"
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
package at.connyduck.pixelcat.components.login
|
package at.connyduck.pixelcat.components.login
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
|
@ -29,23 +30,30 @@ import androidx.activity.viewModels
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.WindowInsetsCompat.Type.systemBars
|
import androidx.core.view.WindowInsetsCompat.Type.systemBars
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.lifecycleScope
|
||||||
import at.connyduck.pixelcat.components.main.MainActivity
|
import at.connyduck.pixelcat.components.main.MainActivity
|
||||||
import at.connyduck.pixelcat.R
|
import at.connyduck.pixelcat.R
|
||||||
import at.connyduck.pixelcat.components.about.AboutActivity
|
import at.connyduck.pixelcat.components.about.AboutActivity
|
||||||
import at.connyduck.pixelcat.components.general.BaseActivity
|
import at.connyduck.pixelcat.components.general.BaseActivity
|
||||||
import at.connyduck.pixelcat.components.settings.SettingsActivity
|
import at.connyduck.pixelcat.components.settings.SettingsActivity
|
||||||
|
import at.connyduck.pixelcat.components.util.extension.visible
|
||||||
import at.connyduck.pixelcat.dagger.ViewModelFactory
|
import at.connyduck.pixelcat.dagger.ViewModelFactory
|
||||||
import at.connyduck.pixelcat.databinding.ActivityLoginBinding
|
import at.connyduck.pixelcat.databinding.ActivityLoginBinding
|
||||||
import at.connyduck.pixelcat.util.viewBinding
|
import at.connyduck.pixelcat.util.viewBinding
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.FlowPreview
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class LoginActivity : BaseActivity(), Observer<LoginModel> {
|
@FlowPreview
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
|
class LoginActivity : BaseActivity() {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var viewModelFactory: ViewModelFactory
|
lateinit var viewModelFactory: ViewModelFactory
|
||||||
|
|
||||||
private val loginViewModel: LoginViewModel by viewModels { viewModelFactory }
|
private val viewModel: LoginViewModel by viewModels { viewModelFactory }
|
||||||
|
|
||||||
private val binding by viewBinding(ActivityLoginBinding::inflate)
|
private val binding by viewBinding(ActivityLoginBinding::inflate)
|
||||||
|
|
||||||
|
@ -54,7 +62,7 @@ class LoginActivity : BaseActivity(), Observer<LoginModel> {
|
||||||
|
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(binding.loginContainer) { _, insets ->
|
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
|
||||||
val top = insets.getInsets(systemBars()).top
|
val top = insets.getInsets(systemBars()).top
|
||||||
val toolbarParams = binding.loginToolbar.layoutParams as ViewGroup.MarginLayoutParams
|
val toolbarParams = binding.loginToolbar.layoutParams as ViewGroup.MarginLayoutParams
|
||||||
toolbarParams.topMargin = top
|
toolbarParams.topMargin = top
|
||||||
|
@ -67,10 +75,14 @@ class LoginActivity : BaseActivity(), Observer<LoginModel> {
|
||||||
setDisplayShowTitleEnabled(false)
|
setDisplayShowTitleEnabled(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
loginViewModel.loginState.observe(this, this)
|
lifecycleScope.launch {
|
||||||
|
viewModel.observe().collect { loginModel ->
|
||||||
|
onChanged(loginModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
binding.loginButton.setOnClickListener {
|
binding.loginButton.setOnClickListener {
|
||||||
loginViewModel.startLogin(binding.loginInput.text.toString())
|
viewModel.startLogin(binding.loginInput.text.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,13 +90,21 @@ class LoginActivity : BaseActivity(), Observer<LoginModel> {
|
||||||
|
|
||||||
val authCode = data?.getStringExtra(LoginWebViewActivity.RESULT_AUTHORIZATION_CODE)
|
val authCode = data?.getStringExtra(LoginWebViewActivity.RESULT_AUTHORIZATION_CODE)
|
||||||
if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK && !authCode.isNullOrEmpty()) {
|
if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK && !authCode.isNullOrEmpty()) {
|
||||||
loginViewModel.authCode(authCode)
|
viewModel.authCode(authCode)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
|
||||||
|
if (!intent.hasExtra(LoginWebViewActivity.RESULT_AUTHORIZATION_CODE)) {
|
||||||
|
viewModel.removeError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
menuInflater.inflate(R.menu.login, menu)
|
menuInflater.inflate(R.menu.login, menu)
|
||||||
return super.onCreateOptionsMenu(menu)
|
return super.onCreateOptionsMenu(menu)
|
||||||
|
@ -105,7 +125,8 @@ class LoginActivity : BaseActivity(), Observer<LoginModel> {
|
||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onChanged(loginModel: LoginModel?) {
|
private fun onChanged(loginModel: LoginModel?) {
|
||||||
|
|
||||||
binding.loginInput.setText(loginModel?.input)
|
binding.loginInput.setText(loginModel?.input)
|
||||||
|
|
||||||
if (loginModel == null) {
|
if (loginModel == null) {
|
||||||
|
@ -113,23 +134,47 @@ class LoginActivity : BaseActivity(), Observer<LoginModel> {
|
||||||
}
|
}
|
||||||
|
|
||||||
when (loginModel.state) {
|
when (loginModel.state) {
|
||||||
LoginState.NO_ERROR -> binding.loginInputLayout.error = null
|
LoginState.NO_ERROR -> {
|
||||||
LoginState.AUTH_ERROR -> binding.loginInputLayout.error = "auth error"
|
binding.loginInputLayout.error = null
|
||||||
LoginState.INVALID_DOMAIN -> binding.loginInputLayout.error = "invalid domain"
|
setLoading(false)
|
||||||
LoginState.NETWORK_ERROR -> binding.loginInputLayout.error = "network error"
|
}
|
||||||
|
LoginState.AUTH_ERROR -> {
|
||||||
|
binding.loginInputLayout.error = "auth error"
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
LoginState.INVALID_DOMAIN -> {
|
||||||
|
binding.loginInputLayout.error = "invalid domain"
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
LoginState.NETWORK_ERROR -> {
|
||||||
|
binding.loginInputLayout.error = "network error"
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
LoginState.LOADING -> {
|
LoginState.LOADING -> {
|
||||||
|
setLoading(true)
|
||||||
}
|
}
|
||||||
LoginState.SUCCESS -> {
|
LoginState.SUCCESS -> {
|
||||||
|
setLoading(true)
|
||||||
startActivityForResult(LoginWebViewActivity.newIntent(loginModel.domain!!, loginModel.clientId!!, loginModel.clientSecret!!, this), REQUEST_CODE)
|
startActivityForResult(LoginWebViewActivity.newIntent(loginModel.domain!!, loginModel.clientId!!, loginModel.clientSecret!!, this), REQUEST_CODE)
|
||||||
}
|
}
|
||||||
LoginState.SUCCESS_FINAL -> {
|
LoginState.SUCCESS_FINAL -> {
|
||||||
startActivity(Intent(this, MainActivity::class.java)) // TODO dont create intent here
|
setLoading(true)
|
||||||
|
startActivity(MainActivity.newIntent(this))
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setLoading(loading: Boolean) {
|
||||||
|
binding.loginLoading.visible = loading
|
||||||
|
binding.loginImageView.visible = !loading
|
||||||
|
binding.loginInputLayout.visible = !loading
|
||||||
|
binding.loginButton.visible = !loading
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val REQUEST_CODE = 14
|
private const val REQUEST_CODE = 14
|
||||||
|
|
||||||
|
fun newIntent(context: Context) = Intent(context, LoginActivity::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,5 +32,11 @@ data class LoginModel(
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
enum class LoginState { // TODO rename this stuff so it makes sense
|
enum class LoginState { // TODO rename this stuff so it makes sense
|
||||||
LOADING, NO_ERROR, NETWORK_ERROR, INVALID_DOMAIN, AUTH_ERROR, SUCCESS, SUCCESS_FINAL
|
LOADING,
|
||||||
|
NO_ERROR,
|
||||||
|
NETWORK_ERROR,
|
||||||
|
INVALID_DOMAIN,
|
||||||
|
AUTH_ERROR,
|
||||||
|
SUCCESS,
|
||||||
|
SUCCESS_FINAL
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,52 +19,54 @@
|
||||||
|
|
||||||
package at.connyduck.pixelcat.components.login
|
package at.connyduck.pixelcat.components.login
|
||||||
|
|
||||||
import androidx.annotation.MainThread
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import at.connyduck.pixelcat.config.Config
|
import at.connyduck.pixelcat.config.Config
|
||||||
import at.connyduck.pixelcat.db.AccountManager
|
import at.connyduck.pixelcat.db.AccountManager
|
||||||
import at.connyduck.pixelcat.db.entitity.AccountAuthData
|
import at.connyduck.pixelcat.db.entitity.AccountAuthData
|
||||||
import at.connyduck.pixelcat.network.FediverseApi
|
import at.connyduck.pixelcat.network.FediverseApi
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.FlowPreview
|
||||||
|
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
|
||||||
|
import kotlinx.coroutines.flow.asFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@FlowPreview
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
class LoginViewModel @Inject constructor(
|
class LoginViewModel @Inject constructor(
|
||||||
private val fediverseApi: FediverseApi,
|
private val fediverseApi: FediverseApi,
|
||||||
private val accountManager: AccountManager
|
private val accountManager: AccountManager
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
val loginState = MutableLiveData<LoginModel>().apply {
|
private val loginState = ConflatedBroadcastChannel(LoginModel(state = LoginState.NO_ERROR))
|
||||||
value = LoginModel(state = LoginState.NO_ERROR)
|
|
||||||
}
|
fun observe() = loginState.asFlow()
|
||||||
|
|
||||||
@MainThread
|
|
||||||
fun startLogin(input: String) {
|
fun startLogin(input: String) {
|
||||||
|
|
||||||
val domainInput = canonicalizeDomain(input)
|
|
||||||
|
|
||||||
try {
|
|
||||||
HttpUrl.Builder().host(domainInput).scheme("https").build()
|
|
||||||
} catch (e: IllegalArgumentException) {
|
|
||||||
loginState.value = LoginModel(input, LoginState.INVALID_DOMAIN)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val exceptionMatch = Config.domainExceptions.any { exception ->
|
|
||||||
domainInput.equals(exception, true) || domainInput.endsWith(".$exception", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exceptionMatch) {
|
|
||||||
loginState.value = LoginModel(input, LoginState.AUTH_ERROR)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
loginState.value = LoginModel(input, LoginState.LOADING)
|
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
val domainInput = canonicalizeDomain(input)
|
||||||
|
|
||||||
|
try {
|
||||||
|
HttpUrl.Builder().host(domainInput).scheme("https").build()
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
loginState.send(LoginModel(input, LoginState.INVALID_DOMAIN))
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
val exceptionMatch = Config.domainExceptions.any { exception ->
|
||||||
|
domainInput.equals(exception, true) || domainInput.endsWith(".$exception", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exceptionMatch) {
|
||||||
|
loginState.send(LoginModel(input, LoginState.AUTH_ERROR))
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
loginState.send(LoginModel(input, LoginState.LOADING))
|
||||||
|
|
||||||
fediverseApi.authenticateAppAsync(
|
fediverseApi.authenticateAppAsync(
|
||||||
domain = domainInput,
|
domain = domainInput,
|
||||||
clientName = "Pixelcat",
|
clientName = "Pixelcat",
|
||||||
|
@ -73,19 +75,18 @@ class LoginViewModel @Inject constructor(
|
||||||
scopes = Config.oAuthScopes
|
scopes = Config.oAuthScopes
|
||||||
).fold(
|
).fold(
|
||||||
{ appData ->
|
{ appData ->
|
||||||
loginState.postValue(LoginModel(input, LoginState.SUCCESS, domainInput, appData.clientId, appData.clientSecret))
|
loginState.send(LoginModel(input, LoginState.SUCCESS, domainInput, appData.clientId, appData.clientSecret))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
loginState.postValue(LoginModel(input, LoginState.AUTH_ERROR))
|
loginState.send(LoginModel(input, LoginState.AUTH_ERROR))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
|
||||||
fun authCode(authCode: String) {
|
fun authCode(authCode: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val loginModel = loginState.value!!
|
val loginModel = loginState.value
|
||||||
|
|
||||||
fediverseApi.fetchOAuthToken(
|
fediverseApi.fetchOAuthToken(
|
||||||
domain = loginModel.domain!!,
|
domain = loginModel.domain!!,
|
||||||
|
@ -106,14 +107,21 @@ class LoginViewModel @Inject constructor(
|
||||||
clientSecret = loginModel.clientSecret
|
clientSecret = loginModel.clientSecret
|
||||||
)
|
)
|
||||||
accountManager.addAccount(loginModel.domain, authData)
|
accountManager.addAccount(loginModel.domain, authData)
|
||||||
loginState.postValue(loginState.value?.copy(state = LoginState.SUCCESS_FINAL))
|
loginState.send(loginState.value.copy(state = LoginState.SUCCESS_FINAL))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
loginState.send(loginState.value.copy(state = LoginState.AUTH_ERROR))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun removeError() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
loginState.send(loginState.value.copy(state = LoginState.NO_ERROR))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun canonicalizeDomain(domain: String): String {
|
private fun canonicalizeDomain(domain: String): String {
|
||||||
// Strip any schemes out.
|
// Strip any schemes out.
|
||||||
var s = domain.replaceFirst("http://", "")
|
var s = domain.replaceFirst("http://", "")
|
||||||
|
|
|
@ -23,23 +23,35 @@ import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.ViewGroup
|
||||||
import android.webkit.WebResourceRequest
|
import android.webkit.WebResourceRequest
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import at.connyduck.pixelcat.config.Config
|
import at.connyduck.pixelcat.config.Config
|
||||||
import android.webkit.WebViewClient
|
import android.webkit.WebViewClient
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import at.connyduck.pixelcat.components.general.BaseActivity
|
||||||
import at.connyduck.pixelcat.databinding.ActivityLoginWebViewBinding
|
import at.connyduck.pixelcat.databinding.ActivityLoginWebViewBinding
|
||||||
|
|
||||||
class LoginWebViewActivity : AppCompatActivity() {
|
class LoginWebViewActivity : BaseActivity() {
|
||||||
|
|
||||||
private lateinit var binding: ActivityLoginWebViewBinding
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityLoginWebViewBinding.inflate(layoutInflater)
|
val binding = ActivityLoginWebViewBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
|
||||||
|
val top = insets.getInsets(WindowInsetsCompat.Type.systemBars()).top
|
||||||
|
val toolbarParams = binding.loginToolbar.layoutParams as ViewGroup.MarginLayoutParams
|
||||||
|
toolbarParams.topMargin = top
|
||||||
|
WindowInsetsCompat.CONSUMED
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.loginToolbar.setNavigationOnClickListener {
|
||||||
|
onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
val domain = intent.getStringExtra(EXTRA_DOMAIN)!!
|
val domain = intent.getStringExtra(EXTRA_DOMAIN)!!
|
||||||
val clientId = intent.getStringExtra(EXTRA_CLIENT_ID)!!
|
val clientId = intent.getStringExtra(EXTRA_CLIENT_ID)!!
|
||||||
val clientSecret = intent.getStringExtra(EXTRA_CLIENT_SECRET)!!
|
val clientSecret = intent.getStringExtra(EXTRA_CLIENT_SECRET)!!
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
package at.connyduck.pixelcat.components.main
|
package at.connyduck.pixelcat.components.main
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -115,4 +116,8 @@ class MainActivity : BaseActivity() {
|
||||||
startActivity(ComposeActivity.newIntent(this, returnValue?.firstOrNull()!!))
|
startActivity(ComposeActivity.newIntent(this, returnValue?.firstOrNull()!!))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun newIntent(context: Context) = Intent(context, MainActivity::class.java)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,5 +50,4 @@ class NotificationsViewModel @Inject constructor(
|
||||||
).flow
|
).flow
|
||||||
}
|
}
|
||||||
.cachedIn(viewModelScope)
|
.cachedIn(viewModelScope)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
package at.connyduck.pixelcat.components.splash
|
package at.connyduck.pixelcat.components.splash
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import at.connyduck.pixelcat.components.login.LoginActivity
|
import at.connyduck.pixelcat.components.login.LoginActivity
|
||||||
|
@ -40,12 +39,9 @@ class SplashActivity : DaggerAppCompatActivity() {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
|
|
||||||
val intent = if (accountManager.activeAccount() != null) {
|
val intent = if (accountManager.activeAccount() != null) {
|
||||||
Intent(
|
MainActivity.newIntent(this@SplashActivity)
|
||||||
this@SplashActivity,
|
|
||||||
MainActivity::class.java
|
|
||||||
) // TODO don't create intents here
|
|
||||||
} else {
|
} else {
|
||||||
Intent(this@SplashActivity, LoginActivity::class.java)
|
LoginActivity.newIntent(this@SplashActivity)
|
||||||
}
|
}
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
finish()
|
finish()
|
||||||
|
|
|
@ -28,5 +28,5 @@ object Config {
|
||||||
const val oAuthRedirect = "$oAuthScheme://$oAuthHost"
|
const val oAuthRedirect = "$oAuthScheme://$oAuthHost"
|
||||||
const val oAuthScopes = "read write follow"
|
const val oAuthScopes = "read write follow"
|
||||||
|
|
||||||
val domainExceptions = arrayOf("gab.com", "gab.ai", "gabfed.com")
|
val domainExceptions = arrayOf("gab.com", "gab.ai", "spinster.xyz")
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import at.connyduck.pixelcat.components.about.licenses.LicenseActivity
|
||||||
import at.connyduck.pixelcat.components.compose.ComposeActivity
|
import at.connyduck.pixelcat.components.compose.ComposeActivity
|
||||||
import at.connyduck.pixelcat.components.timeline.detail.DetailActivity
|
import at.connyduck.pixelcat.components.timeline.detail.DetailActivity
|
||||||
import at.connyduck.pixelcat.components.login.LoginActivity
|
import at.connyduck.pixelcat.components.login.LoginActivity
|
||||||
|
import at.connyduck.pixelcat.components.login.LoginWebViewActivity
|
||||||
import at.connyduck.pixelcat.components.profile.ProfileActivity
|
import at.connyduck.pixelcat.components.profile.ProfileActivity
|
||||||
import at.connyduck.pixelcat.components.settings.SettingsActivity
|
import at.connyduck.pixelcat.components.settings.SettingsActivity
|
||||||
import at.connyduck.pixelcat.components.splash.SplashActivity
|
import at.connyduck.pixelcat.components.splash.SplashActivity
|
||||||
|
@ -61,4 +62,7 @@ abstract class ActivityModule {
|
||||||
|
|
||||||
@ContributesAndroidInjector
|
@ContributesAndroidInjector
|
||||||
abstract fun contributesDetailActivity(): DetailActivity
|
abstract fun contributesDetailActivity(): DetailActivity
|
||||||
|
|
||||||
|
@ContributesAndroidInjector
|
||||||
|
abstract fun contributesLoginWebViewActivity(): LoginWebViewActivity
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ fun Account.toEntity(accountId: Long) = TimelineAccountEntity(
|
||||||
id = id,
|
id = id,
|
||||||
localUsername = localUsername,
|
localUsername = localUsername,
|
||||||
username = username,
|
username = username,
|
||||||
displayName = displayName,
|
displayName = name,
|
||||||
url = url,
|
url = url,
|
||||||
avatar = avatar
|
avatar = avatar
|
||||||
)
|
)
|
||||||
|
|
|
@ -19,14 +19,8 @@
|
||||||
|
|
||||||
package at.connyduck.pixelcat.model
|
package at.connyduck.pixelcat.model
|
||||||
|
|
||||||
import android.os.Parcel
|
|
||||||
import android.os.Parcelable
|
|
||||||
import android.text.Spanned
|
|
||||||
import androidx.core.text.HtmlCompat
|
|
||||||
import com.squareup.moshi.Json
|
import com.squareup.moshi.Json
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
import kotlinx.android.parcel.Parceler
|
|
||||||
import kotlinx.android.parcel.Parcelize
|
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
|
@ -55,38 +49,25 @@ data class Account(
|
||||||
get() = if (displayName.isEmpty()) {
|
get() = if (displayName.isEmpty()) {
|
||||||
localUsername
|
localUsername
|
||||||
} else displayName
|
} else displayName
|
||||||
|
|
||||||
fun isRemote(): Boolean = this.username != this.localUsername
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
@Parcelize
|
|
||||||
data class AccountSource(
|
data class AccountSource(
|
||||||
// val privacy: Status.Visibility,
|
val privacy: Status.Visibility,
|
||||||
val sensitive: Boolean,
|
val sensitive: Boolean,
|
||||||
val note: String,
|
val note: String,
|
||||||
val fields: List<StringField>?
|
val fields: List<StringField>?
|
||||||
) : Parcelable
|
)
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
@Parcelize
|
|
||||||
data class Field(
|
data class Field(
|
||||||
val name: String,
|
val name: String,
|
||||||
// val value: @WriteWith<SpannedParceler>() Spanned,
|
val value: String,
|
||||||
@Json(name = "verified_at") val verifiedAt: Date?
|
@Json(name = "verified_at") val verifiedAt: Date?
|
||||||
) : Parcelable
|
)
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
@Parcelize
|
|
||||||
data class StringField(
|
data class StringField(
|
||||||
val name: String,
|
val name: String,
|
||||||
val value: String
|
val value: String
|
||||||
) : Parcelable
|
)
|
||||||
|
|
||||||
object SpannedParceler : Parceler<Spanned> {
|
|
||||||
override fun create(parcel: Parcel): Spanned = HtmlCompat.fromHtml(parcel.readString() ?: "", HtmlCompat.FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH)
|
|
||||||
|
|
||||||
override fun Spanned.write(parcel: Parcel, flags: Int) {
|
|
||||||
parcel.writeString(HtmlCompat.toHtml(this, HtmlCompat.TO_HTML_PARAGRAPH_LINES_INDIVIDUAL))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_selected="false" android:color="#6BFFFFFF"/>
|
||||||
|
<item android:state_selected="true" android:color="#fff"/>
|
||||||
|
</selector>
|
|
@ -1,7 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/loginContainer"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@drawable/pixelcat_gradient">
|
android:background="@drawable/pixelcat_gradient">
|
||||||
|
@ -18,6 +17,7 @@
|
||||||
android:id="@+id/loginToolbar"
|
android:id="@+id/loginToolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
@ -36,21 +36,27 @@
|
||||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
|
||||||
android:layout_width="250dp"
|
android:layout_width="250dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/instance_input_hint"
|
||||||
|
app:boxStrokeColor="@color/edit_text_color_white"
|
||||||
|
app:boxStrokeErrorColor="@color/white"
|
||||||
|
app:errorIconTint="@color/white"
|
||||||
|
app:errorTextColor="@color/white"
|
||||||
|
app:hintTextColor="@color/white"
|
||||||
app:layout_constraintBottom_toTopOf="@id/loginButton"
|
app:layout_constraintBottom_toTopOf="@id/loginButton"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
android:hint="@string/instance_input_hint"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/loginImageView">
|
app:layout_constraintTop_toBottomOf="@+id/loginImageView">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
android:id="@+id/loginInput"
|
android:id="@+id/loginInput"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textColor="#fff"
|
|
||||||
android:ems="10"
|
android:ems="10"
|
||||||
android:inputType="textUri" />
|
android:inputType="textUri"
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
android:textColor="@color/white"
|
||||||
|
android:textCursorDrawable="@null" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/loginButton"
|
android:id="@+id/loginButton"
|
||||||
|
@ -69,19 +75,19 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/loginInputLayout" />
|
app:layout_constraintTop_toBottomOf="@+id/loginInputLayout" />
|
||||||
|
|
||||||
<!--ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/loading"
|
android:id="@+id/loginLoading"
|
||||||
android:visibility="gone"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_gravity="center"
|
||||||
android:layout_gravity="center"
|
android:layout_marginStart="32dp"
|
||||||
android:layout_marginStart="32dp"
|
android:layout_marginTop="64dp"
|
||||||
android:layout_marginTop="64dp"
|
android:layout_marginEnd="32dp"
|
||||||
android:layout_marginEnd="32dp"
|
android:layout_marginBottom="64dp"
|
||||||
android:layout_marginBottom="64dp"
|
android:indeterminateTint="@color/white"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="@+id/password"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="@+id/password"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
app:layout_constraintVertical_bias="0.3"/-->
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,15 +1,29 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
android:layout_width="match_parent"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/pixelcat_gradient">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/loginToolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:navigationIcon="@drawable/ic_arrow_back"
|
||||||
|
app:title="@string/title_login"
|
||||||
|
app:titleTextAppearance="@style/TextAppearanceToolbar"
|
||||||
|
app:titleTextColor="#fff" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<WebView
|
||||||
|
android:id="@+id/loginWebView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".components.login.LoginWebViewActivity">
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
||||||
|
|
||||||
<WebView android:id="@+id/loginWebView"
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
|
@ -13,4 +13,12 @@
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="AppTheme.Fullscreen">
|
||||||
|
<item name="android:windowTranslucentNavigation">true</item>
|
||||||
|
|
||||||
|
<item name="android:navigationBarColor">@color/transparent</item>
|
||||||
|
<item name="android:navigationBarDividerColor">@color/transparent</item>
|
||||||
|
<item name="android:windowLightNavigationBar">false</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -87,4 +87,6 @@
|
||||||
<string name="notification_favourited">%1$s liked your post</string>
|
<string name="notification_favourited">%1$s liked your post</string>
|
||||||
<string name="notification_followed">%1$s followed you</string>
|
<string name="notification_followed">%1$s followed you</string>
|
||||||
|
|
||||||
|
<string name="title_login">Login</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="AppTheme.Fullscreen">
|
<style name="AppTheme.Fullscreen">
|
||||||
<item name="android:windowTranslucentNavigation">false</item>
|
<item name="android:windowTranslucentNavigation">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="TextAppearanceToolbar" parent="@style/TextAppearance.MaterialComponents.Headline6">
|
<style name="TextAppearanceToolbar" parent="@style/TextAppearance.MaterialComponents.Headline6">
|
||||||
|
|
Loading…
Reference in New Issue