configure ktlint gradle plugin
This commit is contained in:
parent
93a31af6a6
commit
0e8ad1e9ed
|
@ -35,7 +35,6 @@ android {
|
|||
buildFeatures {
|
||||
viewBinding = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
android.sourceSets["main"].java.srcDir("src/main/kotlin")
|
||||
|
@ -47,6 +46,11 @@ tasks {
|
|||
}
|
||||
}
|
||||
|
||||
ktlint {
|
||||
version.set("0.37.1")
|
||||
disabledRules.set(setOf("import-ordering"))
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
val lifecycleVersion = "2.3.0-alpha04"
|
||||
|
@ -94,7 +98,7 @@ dependencies {
|
|||
implementation("com.google.dagger:dagger:2.28")
|
||||
|
||||
implementation("com.fxn769:pix:1.4.4")
|
||||
implementation( "com.github.yalantis:ucrop:2.2.5")
|
||||
implementation("com.github.yalantis:ucrop:2.2.5")
|
||||
|
||||
implementation("me.relex:circleindicator:2.1.4")
|
||||
|
||||
|
|
|
@ -34,5 +34,4 @@ class PixelcatApplication : DaggerApplication() {
|
|||
.application(this)
|
||||
.build()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,10 +40,9 @@ class AboutActivity : BaseActivity() {
|
|||
binding.aboutLicensesButton.setOnClickListener {
|
||||
startActivity(LicenseActivity.newIntent(this))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newIntent(context: Context) = Intent(context, AboutActivity::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@ class LicenseActivity : BaseActivity() {
|
|||
}
|
||||
|
||||
loadFileIntoTextView(R.raw.apache, binding.licenseApacheTextView)
|
||||
|
||||
}
|
||||
|
||||
private fun loadFileIntoTextView(@RawRes fileId: Int, textView: TextView) {
|
||||
|
@ -61,10 +60,9 @@ class LicenseActivity : BaseActivity() {
|
|||
br.close()
|
||||
|
||||
textView.text = sb.toString()
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newIntent(context: Context) = Intent(context, LicenseActivity::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,9 @@ import com.google.android.material.card.MaterialCardView
|
|||
|
||||
class LicenseCard
|
||||
@JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : MaterialCardView(context, attrs, defStyleAttr) {
|
||||
|
||||
private val binding =
|
||||
|
@ -31,14 +31,11 @@ class LicenseCard
|
|||
|
||||
binding.licenseCardName.text = name
|
||||
binding.licenseCardLicense.text = license
|
||||
if(link.isNullOrBlank()) {
|
||||
if (link.isNullOrBlank()) {
|
||||
binding.licenseCardLink.hide()
|
||||
} else {
|
||||
binding.licenseCardLink.text = link
|
||||
// setOnClickListener { LinkHelper.openLink(link, context) }
|
||||
// setOnClickListener { LinkHelper.openLink(link, context) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -52,9 +52,7 @@ class AccountSelectionAdapter(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class AccountSelectionViewHolder(val binding: ItemAccountSelectionBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
|
|
@ -13,7 +13,6 @@ import at.connyduck.pixelcat.db.AccountManager
|
|||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class AccountSelectionBottomSheet(
|
||||
private val accountManager: AccountManager
|
||||
) : BottomSheetDialogFragment() {
|
||||
|
@ -22,7 +21,6 @@ class AccountSelectionBottomSheet(
|
|||
private val binding
|
||||
get() = _binding!!
|
||||
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
|
@ -50,7 +48,7 @@ class AccountSelectionBottomSheet(
|
|||
}
|
||||
|
||||
private fun onNewAccount() {
|
||||
//TODO don't create intent here
|
||||
// TODO don't create intent here
|
||||
startActivity(Intent(requireContext(), LoginActivity::class.java))
|
||||
}
|
||||
|
||||
|
@ -58,5 +56,4 @@ class AccountSelectionBottomSheet(
|
|||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,13 +34,10 @@ class MenuBottomSheet : BottomSheetDialogFragment() {
|
|||
startActivity(AboutActivity.newIntent(it.context))
|
||||
dismiss()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import com.fxn.pix.Pix
|
|||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import javax.inject.Inject
|
||||
|
||||
class ComposeActivity: BaseActivity() {
|
||||
class ComposeActivity : BaseActivity() {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
@ -35,7 +35,6 @@ class ComposeActivity: BaseActivity() {
|
|||
super.onCreate(savedInstanceState)
|
||||
setContentView(binding.root)
|
||||
|
||||
|
||||
binding.root.setOnApplyWindowInsetsListener { _, insets ->
|
||||
val top = insets.systemWindowInsetTop
|
||||
|
||||
|
@ -45,11 +44,10 @@ class ComposeActivity: BaseActivity() {
|
|||
insets.consumeSystemWindowInsets()
|
||||
}
|
||||
|
||||
if(viewModel.images.value.isNullOrEmpty()) {
|
||||
if (viewModel.images.value.isNullOrEmpty()) {
|
||||
viewModel.addImage(intent.getStringExtra(EXTRA_MEDIA_URI)!!)
|
||||
}
|
||||
|
||||
|
||||
setSupportActionBar(binding.composeToolBar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
|
@ -75,23 +73,27 @@ class ComposeActivity: BaseActivity() {
|
|||
changeVisibility(VISIBILITY.FOLLOWERS_ONLY)
|
||||
}
|
||||
|
||||
viewModel.images.observe(this, Observer {
|
||||
adapter.submitList(it)
|
||||
})
|
||||
|
||||
viewModel.visibility.observe(this, Observer {
|
||||
val visibilityString = when(it) {
|
||||
VISIBILITY.PUBLIC -> R.string.compose_visibility_public
|
||||
VISIBILITY.UNLISTED -> R.string.compose_visibility_unlisted
|
||||
VISIBILITY.FOLLOWERS_ONLY -> R.string.compose_visibility_followers_only
|
||||
viewModel.images.observe(
|
||||
this,
|
||||
Observer {
|
||||
adapter.submitList(it)
|
||||
}
|
||||
)
|
||||
|
||||
binding.composeVisibilityButton.text = getString(R.string.compose_visibility, getString(visibilityString))
|
||||
viewModel.visibility.observe(
|
||||
this,
|
||||
Observer {
|
||||
val visibilityString = when (it) {
|
||||
VISIBILITY.PUBLIC -> R.string.compose_visibility_public
|
||||
VISIBILITY.UNLISTED -> R.string.compose_visibility_unlisted
|
||||
VISIBILITY.FOLLOWERS_ONLY -> R.string.compose_visibility_followers_only
|
||||
}
|
||||
|
||||
})
|
||||
binding.composeVisibilityButton.text = getString(R.string.compose_visibility, getString(visibilityString))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_PICK_MEDIA) {
|
||||
|
@ -99,7 +101,6 @@ class ComposeActivity: BaseActivity() {
|
|||
data?.getStringArrayListExtra(Pix.IMAGE_RESULTS)
|
||||
Log.e("Result", returnValue.toString())
|
||||
viewModel.addImage(returnValue?.first()!!)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,7 +109,6 @@ class ComposeActivity: BaseActivity() {
|
|||
visibilityBottomSheet.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private const val REQUEST_CODE_PICK_MEDIA = 123
|
||||
private const val EXTRA_MEDIA_URI = "MEDIA_URI"
|
||||
|
@ -119,4 +119,4 @@ class ComposeActivity: BaseActivity() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,4 +41,4 @@ class ComposeImageAdapter : ListAdapter<String, ComposeImageViewHolder>(
|
|||
}
|
||||
|
||||
class ComposeImageViewHolder(val binding: ItemComposeImageBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
|
|
@ -12,23 +12,17 @@ import javax.inject.Inject
|
|||
class ComposeViewModel @Inject constructor(
|
||||
val context: Context,
|
||||
val accountManager: AccountManager
|
||||
): ViewModel() {
|
||||
|
||||
) : ViewModel() {
|
||||
|
||||
val images = MutableLiveData<List<String>>()
|
||||
|
||||
val visibility = MutableLiveData(VISIBILITY.PUBLIC)
|
||||
|
||||
|
||||
|
||||
fun addImage(imageUri: String) {
|
||||
|
||||
images.value = images.value.orEmpty() + imageUri
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
fun setVisibility(visibility: VISIBILITY) {
|
||||
this.visibility.value = visibility
|
||||
}
|
||||
|
@ -47,14 +41,11 @@ class ComposeViewModel @Inject constructor(
|
|||
val intent = SendStatusService.sendStatusIntent(context, statusToSend)
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
enum class VISIBILITY(val serverName: String) {
|
||||
PUBLIC("public"),
|
||||
UNLISTED("unlisted"),
|
||||
FOLLOWERS_ONLY("private")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,18 +19,24 @@ import at.connyduck.pixelcat.network.FediverseApi
|
|||
import at.connyduck.pixelcat.network.calladapter.NetworkResponseError
|
||||
import dagger.android.DaggerService
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import java.util.Timer
|
||||
import java.util.TimerTask
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
|
||||
class SendStatusService : DaggerService(), CoroutineScope {
|
||||
|
||||
@Inject
|
||||
|
@ -41,12 +47,10 @@ class SendStatusService : DaggerService(), CoroutineScope {
|
|||
private val statusesToSend = ConcurrentHashMap<Int, StatusToSend>()
|
||||
private val sendJobs = ConcurrentHashMap<Int, Job>()
|
||||
|
||||
|
||||
private val timer = Timer()
|
||||
|
||||
private val notificationManager by lazy { getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager }
|
||||
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
@ -55,22 +59,21 @@ class SendStatusService : DaggerService(), CoroutineScope {
|
|||
|
||||
if (intent.hasExtra(KEY_STATUS)) {
|
||||
val tootToSend = intent.getParcelableExtra<StatusToSend>(KEY_STATUS)
|
||||
?: throw IllegalStateException("SendTootService started without $KEY_STATUS extra")
|
||||
?: throw IllegalStateException("SendTootService started without $KEY_STATUS extra")
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = NotificationChannel(CHANNEL_ID, getString(R.string.send_status_notification_channel_name), NotificationManager.IMPORTANCE_LOW)
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
|
||||
}
|
||||
|
||||
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_cat)
|
||||
.setContentTitle(getString(R.string.send_status_notification_title))
|
||||
.setContentText(tootToSend.text)
|
||||
.setProgress(1, 0, true)
|
||||
.setOngoing(true)
|
||||
.setColor(getColorForAttr(android.R.attr.colorPrimary))
|
||||
.addAction(0, getString(android.R.string.cancel), cancelSendingIntent(sendingNotificationId))
|
||||
.setSmallIcon(R.drawable.ic_cat)
|
||||
.setContentTitle(getString(R.string.send_status_notification_title))
|
||||
.setContentText(tootToSend.text)
|
||||
.setProgress(1, 0, true)
|
||||
.setOngoing(true)
|
||||
.setColor(getColorForAttr(android.R.attr.colorPrimary))
|
||||
.addAction(0, getString(android.R.string.cancel), cancelSendingIntent(sendingNotificationId))
|
||||
|
||||
if (statusesToSend.size == 0 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_DETACH)
|
||||
|
@ -81,17 +84,14 @@ class SendStatusService : DaggerService(), CoroutineScope {
|
|||
|
||||
statusesToSend[sendingNotificationId] = tootToSend
|
||||
sendStatus(sendingNotificationId--)
|
||||
|
||||
} else {
|
||||
|
||||
if (intent.hasExtra(KEY_CANCEL)) {
|
||||
cancelSending(intent.getIntExtra(KEY_CANCEL, 0))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return START_NOT_STICKY
|
||||
|
||||
}
|
||||
|
||||
private fun sendStatus(id: Int) {
|
||||
|
@ -115,7 +115,6 @@ class SendStatusService : DaggerService(), CoroutineScope {
|
|||
|
||||
val mediaIds = statusToSend.mediaUris.map {
|
||||
|
||||
|
||||
var type: String? = null
|
||||
val extension = MimeTypeMap.getFileExtensionFromUrl(it)
|
||||
if (extension != null) {
|
||||
|
@ -127,11 +126,14 @@ class SendStatusService : DaggerService(), CoroutineScope {
|
|||
|
||||
val body = MultipartBody.Part.create(filePart)
|
||||
|
||||
api.uploadMedia(body).fold({ attachment ->
|
||||
attachment.id
|
||||
}, {
|
||||
""
|
||||
})
|
||||
api.uploadMedia(body).fold(
|
||||
{ attachment ->
|
||||
attachment.id
|
||||
},
|
||||
{
|
||||
""
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val newStatus = NewStatus(
|
||||
|
@ -147,42 +149,46 @@ class SendStatusService : DaggerService(), CoroutineScope {
|
|||
account.domain,
|
||||
statusToSend.idempotencyKey,
|
||||
newStatus
|
||||
).fold<Any?>({
|
||||
statusesToSend.remove(id)
|
||||
}, {
|
||||
when(it) {
|
||||
is NetworkResponseError.ApiError -> {
|
||||
// the server refused to accept the status, save toot & show error message
|
||||
// TODO saveToDrafts
|
||||
).fold<Any?>(
|
||||
{
|
||||
statusesToSend.remove(id)
|
||||
},
|
||||
{
|
||||
when (it) {
|
||||
is NetworkResponseError.ApiError -> {
|
||||
// the server refused to accept the status, save toot & show error message
|
||||
// TODO saveToDrafts
|
||||
|
||||
val builder = NotificationCompat.Builder(this@SendStatusService, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_cat)
|
||||
.setContentTitle(getString(R.string.send_status_notification_error_title))
|
||||
//.setContentText(getString(R.string.send_toot_notification_saved_content))
|
||||
.setColor(getColorForAttr(android.R.attr.colorPrimary))
|
||||
val builder = NotificationCompat.Builder(this@SendStatusService, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_cat)
|
||||
.setContentTitle(getString(R.string.send_status_notification_error_title))
|
||||
// .setContentText(getString(R.string.send_toot_notification_saved_content))
|
||||
.setColor(getColorForAttr(android.R.attr.colorPrimary))
|
||||
|
||||
notificationManager.cancel(id)
|
||||
notificationManager.notify(errorNotificationId--, builder.build())
|
||||
}
|
||||
else -> {
|
||||
var backoff = TimeUnit.SECONDS.toMillis(statusToSend.retries.toLong())
|
||||
if (backoff > MAX_RETRY_INTERVAL) {
|
||||
backoff = MAX_RETRY_INTERVAL
|
||||
notificationManager.cancel(id)
|
||||
notificationManager.notify(errorNotificationId--, builder.build())
|
||||
}
|
||||
|
||||
timer.schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
sendStatus(id)
|
||||
else -> {
|
||||
var backoff = TimeUnit.SECONDS.toMillis(statusToSend.retries.toLong())
|
||||
if (backoff > MAX_RETRY_INTERVAL) {
|
||||
backoff = MAX_RETRY_INTERVAL
|
||||
}
|
||||
}, backoff)
|
||||
|
||||
timer.schedule(
|
||||
object : TimerTask() {
|
||||
override fun run() {
|
||||
sendStatus(id)
|
||||
}
|
||||
},
|
||||
backoff
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
}.apply {
|
||||
sendJobs[id] = this
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private fun stopSelfWhenDone() {
|
||||
|
@ -200,23 +206,25 @@ class SendStatusService : DaggerService(), CoroutineScope {
|
|||
val sendCall = sendJobs.remove(id)
|
||||
sendCall?.cancel()
|
||||
|
||||
// saveTootToDrafts(tootToCancel)
|
||||
// saveTootToDrafts(tootToCancel)
|
||||
|
||||
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_cat)
|
||||
.setContentTitle(getString(R.string.send_status_notification_cancel_title))
|
||||
.setSmallIcon(R.drawable.ic_cat)
|
||||
.setContentTitle(getString(R.string.send_status_notification_cancel_title))
|
||||
// .setContentText(getString(R.string.send_toot_notification_saved_content))
|
||||
.setColor(getColorForAttr(android.R.attr.colorPrimary))
|
||||
.setColor(getColorForAttr(android.R.attr.colorPrimary))
|
||||
|
||||
notificationManager.notify(id, builder.build())
|
||||
|
||||
timer.schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
notificationManager.cancel(id)
|
||||
stopSelfWhenDone()
|
||||
}
|
||||
}, 5000)
|
||||
|
||||
timer.schedule(
|
||||
object : TimerTask() {
|
||||
override fun run() {
|
||||
notificationManager.cancel(id)
|
||||
stopSelfWhenDone()
|
||||
}
|
||||
},
|
||||
5000
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,7 +237,6 @@ class SendStatusService : DaggerService(), CoroutineScope {
|
|||
return PendingIntent.getService(this, tootId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
private const val KEY_STATUS = "status"
|
||||
|
@ -242,15 +249,15 @@ class SendStatusService : DaggerService(), CoroutineScope {
|
|||
private var errorNotificationId = Int.MIN_VALUE // use even more negative ids to not clash with other notis
|
||||
|
||||
@JvmStatic
|
||||
fun sendStatusIntent(context: Context,
|
||||
statusToSend: StatusToSend
|
||||
fun sendStatusIntent(
|
||||
context: Context,
|
||||
statusToSend: StatusToSend
|
||||
): Intent {
|
||||
val intent = Intent(context, SendStatusService::class.java)
|
||||
intent.putExtra(KEY_STATUS, statusToSend)
|
||||
|
||||
return intent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override val coroutineContext: CoroutineContext
|
||||
|
@ -262,11 +269,11 @@ data class StatusToSend(
|
|||
val accountId: Long,
|
||||
val idempotencyKey: String = UUID.randomUUID().toString(),
|
||||
val text: String,
|
||||
val visibility: String,
|
||||
val sensitive: Boolean,
|
||||
val mediaUris: List<String>,
|
||||
val mediaDescriptions: List<String> = emptyList(),
|
||||
val inReplyToId: String? = null,
|
||||
val savedTootUid: Int = 0,
|
||||
var retries: Int = 0
|
||||
val visibility: String,
|
||||
val sensitive: Boolean,
|
||||
val mediaUris: List<String>,
|
||||
val mediaDescriptions: List<String> = emptyList(),
|
||||
val inReplyToId: String? = null,
|
||||
val savedTootUid: Int = 0,
|
||||
var retries: Int = 0
|
||||
) : Parcelable
|
||||
|
|
|
@ -6,7 +6,7 @@ import at.connyduck.pixelcat.components.settings.AppSettings
|
|||
import dagger.android.support.DaggerAppCompatActivity
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class BaseActivity: DaggerAppCompatActivity() {
|
||||
abstract class BaseActivity : DaggerAppCompatActivity() {
|
||||
|
||||
@Inject
|
||||
lateinit var appSettings: AppSettings
|
||||
|
@ -15,10 +15,8 @@ abstract class BaseActivity: DaggerAppCompatActivity() {
|
|||
super.onCreate(savedInstanceState)
|
||||
|
||||
theme.applyStyle(appSettings.getAppColorStyle(), true)
|
||||
if(!appSettings.useSystemFont()) {
|
||||
if (!appSettings.useSystemFont()) {
|
||||
theme.applyStyle(R.style.NunitoFont, true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ import at.connyduck.pixelcat.databinding.ActivityLoginBinding
|
|||
import at.connyduck.pixelcat.util.viewBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class LoginActivity : BaseActivity(), Observer<LoginModel> {
|
||||
|
||||
@Inject
|
||||
|
@ -58,7 +57,7 @@ class LoginActivity : BaseActivity(), Observer<LoginModel> {
|
|||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
@ -72,7 +71,7 @@ class LoginActivity : BaseActivity(), Observer<LoginModel> {
|
|||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when(item.itemId) {
|
||||
when (item.itemId) {
|
||||
R.id.navigation_settings -> {
|
||||
startActivity(SettingsActivity.newIntent(this))
|
||||
return true
|
||||
|
@ -89,17 +88,16 @@ class LoginActivity : BaseActivity(), Observer<LoginModel> {
|
|||
override fun onChanged(loginModel: LoginModel?) {
|
||||
binding.loginInput.setText(loginModel?.input)
|
||||
|
||||
if(loginModel == null) {
|
||||
if (loginModel == null) {
|
||||
return
|
||||
}
|
||||
|
||||
when(loginModel.state) {
|
||||
when (loginModel.state) {
|
||||
LoginState.NO_ERROR -> binding.loginInputLayout.error = null
|
||||
LoginState.AUTH_ERROR -> binding.loginInputLayout.error = "auth error"
|
||||
LoginState.INVALID_DOMAIN -> binding.loginInputLayout.error = "invalid domain"
|
||||
LoginState.NETWORK_ERROR -> binding.loginInputLayout.error = "network error"
|
||||
LoginState.LOADING -> {
|
||||
|
||||
}
|
||||
LoginState.SUCCESS -> {
|
||||
startActivityForResult(LoginWebViewActivity.newIntent(loginModel.domain!!, loginModel.clientId!!, loginModel.clientSecret!!, this), REQUEST_CODE)
|
||||
|
@ -114,5 +112,4 @@ class LoginActivity : BaseActivity(), Observer<LoginModel> {
|
|||
companion object {
|
||||
private const val REQUEST_CODE = 14
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,10 +10,8 @@ data class LoginModel(
|
|||
val domain: String? = null,
|
||||
val clientId: String? = null,
|
||||
val clientSecret: String? = null
|
||||
): 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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -10,14 +10,13 @@ import at.connyduck.pixelcat.db.entitity.AccountAuthData
|
|||
import at.connyduck.pixelcat.network.FediverseApi
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.HttpUrl
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
class LoginViewModel @Inject constructor(
|
||||
private val fediverseApi: FediverseApi,
|
||||
private val accountManager: AccountManager
|
||||
): ViewModel() {
|
||||
|
||||
) : ViewModel() {
|
||||
|
||||
val loginState = MutableLiveData<LoginModel>().apply {
|
||||
value = LoginModel(state = LoginState.NO_ERROR)
|
||||
|
@ -35,18 +34,17 @@ class LoginViewModel @Inject constructor(
|
|||
return
|
||||
}
|
||||
|
||||
val exceptionMatch = Config.domainExceptions.any {exception ->
|
||||
val exceptionMatch = Config.domainExceptions.any { exception ->
|
||||
domainInput.equals(exception, true) || domainInput.endsWith(".$exception", true)
|
||||
}
|
||||
|
||||
if(exceptionMatch) {
|
||||
if (exceptionMatch) {
|
||||
loginState.value = LoginModel(input, LoginState.AUTH_ERROR)
|
||||
return
|
||||
}
|
||||
|
||||
loginState.value = LoginModel(input, LoginState.LOADING)
|
||||
|
||||
|
||||
viewModelScope.launch {
|
||||
fediverseApi.authenticateAppAsync(
|
||||
domain = domainInput,
|
||||
|
@ -54,14 +52,15 @@ class LoginViewModel @Inject constructor(
|
|||
clientWebsite = Config.website,
|
||||
redirectUris = Config.oAuthRedirect,
|
||||
scopes = Config.oAuthScopes
|
||||
).fold({ appData ->
|
||||
loginState.postValue(LoginModel(input, LoginState.SUCCESS, domainInput, appData.clientId, appData.clientSecret))
|
||||
}, {
|
||||
loginState.postValue(LoginModel(input, LoginState.AUTH_ERROR))
|
||||
})
|
||||
).fold(
|
||||
{ appData ->
|
||||
loginState.postValue(LoginModel(input, LoginState.SUCCESS, domainInput, appData.clientId, appData.clientSecret))
|
||||
},
|
||||
{
|
||||
loginState.postValue(LoginModel(input, LoginState.AUTH_ERROR))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@MainThread
|
||||
|
@ -75,25 +74,27 @@ class LoginViewModel @Inject constructor(
|
|||
clientSecret = loginModel.clientSecret!!,
|
||||
redirectUri = Config.oAuthRedirect,
|
||||
code = authCode
|
||||
).fold({ tokenResponse ->
|
||||
val authData = AccountAuthData(
|
||||
accessToken = tokenResponse.accessToken,
|
||||
refreshToken = tokenResponse.refreshToken,
|
||||
tokenExpiresAt = tokenResponse.createdAt ?: 0 + (tokenResponse.expiresIn
|
||||
?: 0),
|
||||
clientId = loginModel.clientId,
|
||||
clientSecret = loginModel.clientSecret
|
||||
)
|
||||
accountManager.addAccount(loginModel.domain, authData)
|
||||
loginState.postValue(loginState.value?.copy(state = LoginState.SUCCESS_FINAL))
|
||||
}, {
|
||||
|
||||
})
|
||||
|
||||
).fold(
|
||||
{ tokenResponse ->
|
||||
val authData = AccountAuthData(
|
||||
accessToken = tokenResponse.accessToken,
|
||||
refreshToken = tokenResponse.refreshToken,
|
||||
tokenExpiresAt = tokenResponse.createdAt ?: 0 + (
|
||||
tokenResponse.expiresIn
|
||||
?: 0
|
||||
),
|
||||
clientId = loginModel.clientId,
|
||||
clientSecret = loginModel.clientSecret
|
||||
)
|
||||
accountManager.addAccount(loginModel.domain, authData)
|
||||
loginState.postValue(loginState.value?.copy(state = LoginState.SUCCESS_FINAL))
|
||||
},
|
||||
{
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun canonicalizeDomain(domain: String): String {
|
||||
// Strip any schemes out.
|
||||
var s = domain.replaceFirst("http://", "")
|
||||
|
@ -105,4 +106,4 @@ class LoginViewModel @Inject constructor(
|
|||
}
|
||||
return s.trim().toLowerCase(Locale.ROOT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import at.connyduck.pixelcat.config.Config
|
|||
import android.webkit.WebViewClient
|
||||
import at.connyduck.pixelcat.databinding.ActivityLoginWebViewBinding
|
||||
|
||||
|
||||
class LoginWebViewActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivityLoginWebViewBinding
|
||||
|
@ -36,10 +35,10 @@ class LoginWebViewActivity : AppCompatActivity() {
|
|||
|
||||
val url = "https://" + domain + endpoint + "?" + toQueryString(parameters)
|
||||
|
||||
binding.loginWebView.webViewClient = object: WebViewClient() {
|
||||
binding.loginWebView.webViewClient = object : WebViewClient() {
|
||||
|
||||
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
|
||||
if(request.url.scheme == Config.oAuthScheme && request.url.host == Config.oAuthHost) {
|
||||
if (request.url.scheme == Config.oAuthScheme && request.url.host == Config.oAuthHost) {
|
||||
loginSuccess(request.url.getQueryParameter("code").orEmpty())
|
||||
return true
|
||||
}
|
||||
|
@ -48,7 +47,6 @@ class LoginWebViewActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
binding.loginWebView.loadUrl(url)
|
||||
|
||||
}
|
||||
|
||||
private fun loginSuccess(authCode: String) {
|
||||
|
@ -59,7 +57,6 @@ class LoginWebViewActivity : AppCompatActivity() {
|
|||
finish()
|
||||
}
|
||||
|
||||
|
||||
private fun toQueryString(parameters: Map<String, String>): String {
|
||||
return parameters.map { "${it.key}=${Uri.encode(it.value)}" }
|
||||
.joinToString("&")
|
||||
|
|
|
@ -61,7 +61,6 @@ class MainActivity : BaseActivity() {
|
|||
true
|
||||
}
|
||||
else -> false
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,7 +86,6 @@ class MainActivity : BaseActivity() {
|
|||
binding.navigation.setOnNavigationItemSelectedListener(onNavigationItemSelectedListener)
|
||||
|
||||
mainViewModel.whatever()
|
||||
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
|
@ -100,7 +98,4 @@ class MainActivity : BaseActivity() {
|
|||
startActivity(ComposeActivity.newIntent(this, returnValue?.firstOrNull()!!))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@ import at.connyduck.pixelcat.components.profile.ProfileFragment
|
|||
import at.connyduck.pixelcat.components.search.SearchFragment
|
||||
import at.connyduck.pixelcat.components.timeline.TimelineFragment
|
||||
|
||||
class MainFragmentAdapter(fragmentActivity: FragmentActivity): FragmentStateAdapter(fragmentActivity) {
|
||||
class MainFragmentAdapter(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) {
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return when(position) {
|
||||
return when (position) {
|
||||
0 -> TimelineFragment.newInstance()
|
||||
1 -> SearchFragment.newInstance()
|
||||
2 -> NotificationsFragment.newInstance()
|
||||
|
@ -23,5 +23,4 @@ class MainFragmentAdapter(fragmentActivity: FragmentActivity): FragmentStateAdap
|
|||
}
|
||||
|
||||
override fun getItemCount() = 4
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,21 +10,21 @@ import javax.inject.Inject
|
|||
class MainViewModel @Inject constructor(
|
||||
private val fediverseApi: FediverseApi,
|
||||
private val accountManager: AccountManager
|
||||
): ViewModel() {
|
||||
) : ViewModel() {
|
||||
|
||||
fun whatever() {
|
||||
|
||||
}
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
|
||||
fediverseApi.accountVerifyCredentials().fold({ account ->
|
||||
fediverseApi.accountVerifyCredentials().fold(
|
||||
{ account ->
|
||||
accountManager.updateActiveAccount(account)
|
||||
}, {
|
||||
|
||||
})
|
||||
},
|
||||
{
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import at.connyduck.pixelcat.dagger.ViewModelFactory
|
|||
import dagger.android.support.DaggerFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
class NotificationsFragment: DaggerFragment(R.layout.fragment_notifications) {
|
||||
class NotificationsFragment : DaggerFragment(R.layout.fragment_notifications) {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
@ -16,5 +16,4 @@ class NotificationsFragment: DaggerFragment(R.layout.fragment_notifications) {
|
|||
companion object {
|
||||
fun newInstance() = NotificationsFragment()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,4 @@ package at.connyduck.pixelcat.components.notifications
|
|||
import androidx.lifecycle.ViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
class NotificationsViewModel @Inject constructor(
|
||||
|
||||
): ViewModel() {
|
||||
|
||||
|
||||
|
||||
}
|
||||
class NotificationsViewModel @Inject constructor() : ViewModel()
|
||||
|
|
|
@ -18,7 +18,7 @@ class GridSpacingItemDecoration(
|
|||
state: RecyclerView.State
|
||||
) {
|
||||
val position = parent.getChildAdapterPosition(view) // item position
|
||||
if(position < topOffset) return
|
||||
if (position < topOffset) return
|
||||
|
||||
val column = (position - topOffset) % spanCount // item column
|
||||
|
||||
|
@ -28,7 +28,5 @@ class GridSpacingItemDecoration(
|
|||
if (position - topOffset >= spanCount) {
|
||||
outRect.top = spacing // item top
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import at.connyduck.pixelcat.R
|
|||
import at.connyduck.pixelcat.components.general.BaseActivity
|
||||
import at.connyduck.pixelcat.databinding.ActivityProfileBinding
|
||||
|
||||
class ProfileActivity: BaseActivity() {
|
||||
class ProfileActivity : BaseActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -30,7 +30,6 @@ class ProfileActivity: BaseActivity() {
|
|||
add(R.id.layoutContainer, ProfileFragment.newInstance(intent.getStringExtra(EXTRA_ACCOUNT_ID)))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -42,4 +41,4 @@ class ProfileActivity: BaseActivity() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,13 +22,12 @@ class ProfileDataSourceFactory(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
class ProfileImageDataSource(
|
||||
private val api: FediverseApi,
|
||||
private val accountId: String?,
|
||||
private val accountManager: AccountManager,
|
||||
private val scope: CoroutineScope
|
||||
): ItemKeyedDataSource<String, Status>() {
|
||||
) : ItemKeyedDataSource<String, Status>() {
|
||||
override fun loadInitial(
|
||||
params: LoadInitialParams<String>,
|
||||
callback: LoadInitialCallback<Status>
|
||||
|
@ -40,11 +39,13 @@ class ProfileImageDataSource(
|
|||
limit = params.requestedLoadSize,
|
||||
onlyMedia = true,
|
||||
excludeReblogs = true
|
||||
).fold({
|
||||
callback.onResult(it)
|
||||
}, {
|
||||
|
||||
})
|
||||
).fold(
|
||||
{
|
||||
callback.onResult(it)
|
||||
},
|
||||
{
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,11 +58,13 @@ class ProfileImageDataSource(
|
|||
limit = params.requestedLoadSize,
|
||||
onlyMedia = true,
|
||||
excludeReblogs = true
|
||||
).fold({
|
||||
callback.onResult(it)
|
||||
}, {
|
||||
|
||||
})
|
||||
).fold(
|
||||
{
|
||||
callback.onResult(it)
|
||||
},
|
||||
{
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,4 +73,4 @@ class ProfileImageDataSource(
|
|||
}
|
||||
|
||||
override fun getKey(item: Status) = item.id
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package at.connyduck.pixelcat.components.profile
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import android.view.View
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
|
@ -43,7 +43,7 @@ class ProfileFragment : DaggerFragment(R.layout.fragment_profile) {
|
|||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
||||
if(activity is MainActivity) {
|
||||
if (activity is MainActivity) {
|
||||
binding.toolbar.inflateMenu(R.menu.secondary_navigation)
|
||||
binding.toolbar.setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
|
@ -58,7 +58,6 @@ class ProfileFragment : DaggerFragment(R.layout.fragment_profile) {
|
|||
MenuBottomSheet()
|
||||
bottomSheetDialog.show(childFragmentManager, "menuBottomSheet")
|
||||
}
|
||||
|
||||
}
|
||||
true
|
||||
}
|
||||
|
@ -74,9 +73,9 @@ class ProfileFragment : DaggerFragment(R.layout.fragment_profile) {
|
|||
val imageSize = (displayWidth - (IMAGE_COLUMN_COUNT - 1) * imageSpacing) / IMAGE_COLUMN_COUNT
|
||||
imageAdapter = ProfileImageAdapter(imageSize)
|
||||
val layoutManager = GridLayoutManager(view.context, IMAGE_COLUMN_COUNT)
|
||||
layoutManager.spanSizeLookup = object: GridLayoutManager.SpanSizeLookup() {
|
||||
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
if(position == 0) return IMAGE_COLUMN_COUNT
|
||||
if (position == 0) return IMAGE_COLUMN_COUNT
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
@ -88,21 +87,30 @@ class ProfileFragment : DaggerFragment(R.layout.fragment_profile) {
|
|||
|
||||
viewModel.setAccountInfo(arg(ACCOUNT_ID))
|
||||
|
||||
viewModel.profile.observe(viewLifecycleOwner, Observer {
|
||||
when (it) {
|
||||
is Success -> onAccountChanged(it.data)
|
||||
is Error -> showError()
|
||||
viewModel.profile.observe(
|
||||
viewLifecycleOwner,
|
||||
Observer {
|
||||
when (it) {
|
||||
is Success -> onAccountChanged(it.data)
|
||||
is Error -> showError()
|
||||
}
|
||||
}
|
||||
})
|
||||
viewModel.relationship.observe(viewLifecycleOwner, Observer {
|
||||
when (it) {
|
||||
is Success -> onRelationshipChanged(it.data)
|
||||
is Error -> showError()
|
||||
)
|
||||
viewModel.relationship.observe(
|
||||
viewLifecycleOwner,
|
||||
Observer {
|
||||
when (it) {
|
||||
is Success -> onRelationshipChanged(it.data)
|
||||
is Error -> showError()
|
||||
}
|
||||
}
|
||||
})
|
||||
viewModel.profileImages.observe(viewLifecycleOwner, Observer {
|
||||
imageAdapter.submitList(it)
|
||||
})
|
||||
)
|
||||
viewModel.profileImages.observe(
|
||||
viewLifecycleOwner,
|
||||
Observer {
|
||||
imageAdapter.submitList(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun onAccountChanged(account: Account?) {
|
||||
|
@ -112,7 +120,6 @@ class ProfileFragment : DaggerFragment(R.layout.fragment_profile) {
|
|||
binding.toolbar.title = account.displayName
|
||||
|
||||
headerAdapter.setAccount(account, viewModel.isSelf)
|
||||
|
||||
}
|
||||
|
||||
private fun onRelationshipChanged(relation: Relationship?) {
|
||||
|
@ -138,5 +145,4 @@ class ProfileFragment : DaggerFragment(R.layout.fragment_profile) {
|
|||
fun newInstance(accountId: String? = null) =
|
||||
ProfileFragment().withArgs { putString(ACCOUNT_ID, accountId) }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import coil.api.load
|
|||
import coil.transform.RoundedCornersTransformation
|
||||
import java.text.NumberFormat
|
||||
|
||||
class ProfileHeaderAdapter: RecyclerView.Adapter<ProfileHeaderViewHolder>() {
|
||||
class ProfileHeaderAdapter : RecyclerView.Adapter<ProfileHeaderViewHolder>() {
|
||||
|
||||
private var account: Account? = null
|
||||
private var isSelf: Boolean = false
|
||||
|
@ -61,7 +61,7 @@ class ProfileHeaderAdapter: RecyclerView.Adapter<ProfileHeaderViewHolder>() {
|
|||
}
|
||||
if (payloads.isEmpty() || payloads.contains(RELATIONSHIP_CHANGED)) {
|
||||
relationship?.let {
|
||||
if(it.following) {
|
||||
if (it.following) {
|
||||
holder.binding.profileFollowButton.setText(R.string.profile_follows_you)
|
||||
} else {
|
||||
holder.binding.profileFollowButton.setText(R.string.profile_action_follow)
|
||||
|
@ -79,5 +79,4 @@ class ProfileHeaderAdapter: RecyclerView.Adapter<ProfileHeaderViewHolder>() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
class ProfileHeaderViewHolder(val binding: ItemProfileHeaderBinding): RecyclerView.ViewHolder(binding.root)
|
||||
class ProfileHeaderViewHolder(val binding: ItemProfileHeaderBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
|
|
|
@ -15,8 +15,8 @@ import coil.api.load
|
|||
|
||||
class ProfileImageAdapter(
|
||||
private val imageSizePx: Int
|
||||
): PagedListAdapter<Status, ProfileImageViewHolder>(
|
||||
object: DiffUtil.ItemCallback<Status>() {
|
||||
) : PagedListAdapter<Status, ProfileImageViewHolder>(
|
||||
object : DiffUtil.ItemCallback<Status>() {
|
||||
override fun areItemsTheSame(old: Status, new: Status): Boolean {
|
||||
return false
|
||||
}
|
||||
|
@ -54,8 +54,6 @@ class ProfileImageAdapter(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class ProfileImageViewHolder(val binding: ItemProfileImageBinding): RecyclerView.ViewHolder(binding.root)
|
||||
class ProfileImageViewHolder(val binding: ItemProfileImageBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
|
|
|
@ -19,11 +19,11 @@ import javax.inject.Inject
|
|||
class ProfileViewModel @Inject constructor(
|
||||
private val fediverseApi: FediverseApi,
|
||||
private val accountManager: AccountManager
|
||||
): ViewModel() {
|
||||
) : ViewModel() {
|
||||
|
||||
val profile = MutableLiveData<UiState<Account>>()
|
||||
val relationship = MutableLiveData<UiState<Relationship>>()
|
||||
val profileImages = MutableLiveData<PagedList<Status>>()
|
||||
val profileImages = MutableLiveData<PagedList<Status>>()
|
||||
|
||||
val isSelf: Boolean
|
||||
get() = accountId == null
|
||||
|
@ -46,11 +46,14 @@ class ProfileViewModel @Inject constructor(
|
|||
private fun loadAccount(reload: Boolean = false) {
|
||||
if (profile.value == null || reload) {
|
||||
viewModelScope.launch {
|
||||
fediverseApi.account(getAccountId()).fold({
|
||||
profile.value = Success(it)
|
||||
}, {
|
||||
profile.value = Error(cause = it)
|
||||
})
|
||||
fediverseApi.account(getAccountId()).fold(
|
||||
{
|
||||
profile.value = Success(it)
|
||||
},
|
||||
{
|
||||
profile.value = Error(cause = it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,24 +61,28 @@ class ProfileViewModel @Inject constructor(
|
|||
private fun loadRelationship(reload: Boolean = false) {
|
||||
if (relationship.value == null || reload) {
|
||||
viewModelScope.launch {
|
||||
fediverseApi.relationships(listOf(getAccountId())).fold({
|
||||
relationship.value = Success(it.first())
|
||||
}, {
|
||||
relationship.value = Error(cause = it)
|
||||
})
|
||||
fediverseApi.relationships(listOf(getAccountId())).fold(
|
||||
{
|
||||
relationship.value = Success(it.first())
|
||||
},
|
||||
{
|
||||
relationship.value = Error(cause = it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadImages(reload: Boolean = false) {
|
||||
if(profileImages.value == null || reload) {
|
||||
if (profileImages.value == null || reload) {
|
||||
profileImages.value = PagedList.Builder(
|
||||
ProfileImageDataSource(
|
||||
fediverseApi,
|
||||
accountId,
|
||||
accountManager,
|
||||
viewModelScope
|
||||
), 20
|
||||
),
|
||||
20
|
||||
).setNotifyExecutor(Executors.mainThreadExecutor())
|
||||
.setFetchExecutor(java.util.concurrent.Executors.newSingleThreadExecutor())
|
||||
.build()
|
||||
|
@ -85,5 +92,4 @@ class ProfileViewModel @Inject constructor(
|
|||
private suspend fun getAccountId(): String {
|
||||
return accountId ?: accountManager.activeAccount()?.accountId!!
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import at.connyduck.pixelcat.dagger.ViewModelFactory
|
|||
import dagger.android.support.DaggerFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
class SearchFragment: DaggerFragment(R.layout.fragment_search) {
|
||||
class SearchFragment : DaggerFragment(R.layout.fragment_search) {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
@ -16,5 +16,4 @@ class SearchFragment: DaggerFragment(R.layout.fragment_search) {
|
|||
companion object {
|
||||
fun newInstance() = SearchFragment()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,4 @@ package at.connyduck.pixelcat.components.search
|
|||
import androidx.lifecycle.ViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
class SearchViewModel @Inject constructor(
|
||||
|
||||
): ViewModel() {
|
||||
|
||||
|
||||
|
||||
}
|
||||
class SearchViewModel @Inject constructor() : ViewModel()
|
||||
|
|
|
@ -4,21 +4,23 @@ import android.content.Context
|
|||
import android.content.SharedPreferences
|
||||
import androidx.annotation.StyleRes
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.appcompat.app.AppCompatDelegate.*
|
||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_AUTO_TIME
|
||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
|
||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
|
||||
import at.connyduck.pixelcat.R
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class AppSettings @Inject constructor (
|
||||
private val sharedPrefs: SharedPreferences,
|
||||
private val context: Context
|
||||
private val sharedPrefs: SharedPreferences,
|
||||
private val context: Context
|
||||
) {
|
||||
|
||||
@StyleRes
|
||||
fun getAppColorStyle(): Int {
|
||||
val appColorPref = sharedPrefs.getString(
|
||||
context.getString(R.string.key_pref_app_color),
|
||||
context.getString(R.string.key_pref_app_color_default)
|
||||
context.getString(R.string.key_pref_app_color),
|
||||
context.getString(R.string.key_pref_app_color_default)
|
||||
)
|
||||
|
||||
return when (appColorPref) {
|
||||
|
@ -26,14 +28,13 @@ class AppSettings @Inject constructor (
|
|||
context.getString(R.string.key_pref_app_color_cold) -> R.style.Cold
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@AppCompatDelegate.NightMode
|
||||
fun getNightMode(): Int {
|
||||
val nightModePref = sharedPrefs.getString(
|
||||
context.getString(R.string.key_pref_night_mode),
|
||||
context.getString(R.string.key_pref_night_mode_default)
|
||||
context.getString(R.string.key_pref_night_mode),
|
||||
context.getString(R.string.key_pref_night_mode_default)
|
||||
)
|
||||
|
||||
return when (nightModePref) {
|
||||
|
@ -47,22 +48,19 @@ class AppSettings @Inject constructor (
|
|||
|
||||
fun isBlackNightMode(): Boolean {
|
||||
return sharedPrefs.getBoolean(
|
||||
context.getString(R.string.key_pref_black_night_mode),
|
||||
context.resources.getBoolean(R.bool.pref_title_black_night_mode_default)
|
||||
context.getString(R.string.key_pref_black_night_mode),
|
||||
context.resources.getBoolean(R.bool.pref_title_black_night_mode_default)
|
||||
)
|
||||
}
|
||||
|
||||
fun useSystemFont(): Boolean {
|
||||
return sharedPrefs.getBoolean(
|
||||
return sharedPrefs.getBoolean(
|
||||
context.getString(R.string.key_pref_system_font),
|
||||
context.resources.getBoolean(R.bool.pref_title_system_font_default)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private fun SharedPreferences.getNonNullString(key: String, default: String): String {
|
||||
return getString(key, default) ?: default
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,11 +48,10 @@ class SettingsActivity : BaseActivity(), SharedPreferences.OnSharedPreferenceCha
|
|||
preferences.registerOnSharedPreferenceChangeListener(this)
|
||||
|
||||
restartActivitiesOnExit = intent.getBooleanExtra(EXTRA_RESTART_ACTIVITIES, false)
|
||||
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
|
||||
when(key) {
|
||||
when (key) {
|
||||
getString(R.string.key_pref_app_color) -> restartCurrentActivity()
|
||||
getString(R.string.key_pref_night_mode) -> AppCompatDelegate.setDefaultNightMode(appSettings.getNightMode())
|
||||
}
|
||||
|
@ -68,7 +67,7 @@ class SettingsActivity : BaseActivity(), SharedPreferences.OnSharedPreferenceCha
|
|||
|
||||
override fun onBackPressed() {
|
||||
val parentActivityName = intent.getStringExtra(EXTRA_PARENT_ACTIVITY)
|
||||
if(restartActivitiesOnExit && parentActivityName != null) {
|
||||
if (restartActivitiesOnExit && parentActivityName != null) {
|
||||
val restartIntent = Intent()
|
||||
restartIntent.component = ComponentName(this, intent.getStringExtra(EXTRA_PARENT_ACTIVITY)!!)
|
||||
restartIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
|
@ -100,6 +99,4 @@ class SettingsActivity : BaseActivity(), SharedPreferences.OnSharedPreferenceCha
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import dagger.android.support.DaggerAppCompatActivity
|
|||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class SplashActivity: DaggerAppCompatActivity() {
|
||||
class SplashActivity : DaggerAppCompatActivity() {
|
||||
|
||||
@Inject
|
||||
lateinit var accountManager: AccountManager
|
||||
|
@ -24,7 +24,7 @@ class SplashActivity: DaggerAppCompatActivity() {
|
|||
Intent(
|
||||
this@SplashActivity,
|
||||
MainActivity::class.java
|
||||
) //TODO don't create intents here
|
||||
) // TODO don't create intents here
|
||||
} else {
|
||||
Intent(this@SplashActivity, LoginActivity::class.java)
|
||||
}
|
||||
|
@ -32,4 +32,4 @@ class SplashActivity: DaggerAppCompatActivity() {
|
|||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package at.connyduck.pixelcat.components.timeline
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
@ -14,13 +13,12 @@ import at.connyduck.pixelcat.dagger.ViewModelFactory
|
|||
import at.connyduck.pixelcat.databinding.FragmentTimelineBinding
|
||||
import at.connyduck.pixelcat.db.entitity.StatusEntity
|
||||
import at.connyduck.pixelcat.util.viewBinding
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import dagger.android.support.DaggerFragment
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimelineFragment: DaggerFragment(R.layout.fragment_timeline), TimeLineActionListener {
|
||||
class TimelineFragment : DaggerFragment(R.layout.fragment_timeline), TimeLineActionListener {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
@ -61,9 +59,7 @@ class TimelineFragment: DaggerFragment(R.layout.fragment_timeline), TimeLineActi
|
|||
binding.timelineSwipeRefresh.isRefreshing = false
|
||||
}
|
||||
|
||||
|
||||
//viewModel.posts.observe(viewLifecycleOwner, Observer { t -> adapter.submitList(t) })
|
||||
|
||||
// viewModel.posts.observe(viewLifecycleOwner, Observer { t -> adapter.submitList(t) })
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -81,5 +77,4 @@ class TimelineFragment: DaggerFragment(R.layout.fragment_timeline), TimeLineActi
|
|||
override fun onReply(status: StatusEntity) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import at.connyduck.pixelcat.databinding.ItemTimelineImageBinding
|
|||
import at.connyduck.pixelcat.model.Attachment
|
||||
import coil.api.load
|
||||
|
||||
class TimelineImageAdapter: RecyclerView.Adapter<TimelineImageViewHolder>() {
|
||||
class TimelineImageAdapter : RecyclerView.Adapter<TimelineImageViewHolder>() {
|
||||
|
||||
var images: List<Attachment> = emptyList()
|
||||
set(value) {
|
||||
|
@ -25,10 +25,7 @@ class TimelineImageAdapter: RecyclerView.Adapter<TimelineImageViewHolder>() {
|
|||
override fun onBindViewHolder(holder: TimelineImageViewHolder, position: Int) {
|
||||
|
||||
holder.binding.timelineImageView.load(images[position].previewUrl)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class TimelineImageViewHolder(val binding: ItemTimelineImageBinding): RecyclerView.ViewHolder(binding.root)
|
||||
class TimelineImageViewHolder(val binding: ItemTimelineImageBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
|
|
|
@ -20,7 +20,7 @@ interface TimeLineActionListener {
|
|||
fun onReply(status: StatusEntity)
|
||||
}
|
||||
|
||||
object TimelineDiffUtil: DiffUtil.ItemCallback<StatusEntity>() {
|
||||
object TimelineDiffUtil : DiffUtil.ItemCallback<StatusEntity>() {
|
||||
override fun areItemsTheSame(oldItem: StatusEntity, newItem: StatusEntity): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ object TimelineDiffUtil: DiffUtil.ItemCallback<StatusEntity>() {
|
|||
override fun areContentsTheSame(oldItem: StatusEntity, newItem: StatusEntity): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TimelineListAdapter(
|
||||
|
@ -83,22 +82,17 @@ class TimelineListAdapter(
|
|||
holder.binding.postDescription.text = status.content.parseAsHtml().trim()
|
||||
|
||||
holder.binding.postDate.text = dateTimeFormatter.format(status.createdAt)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class TimelineViewHolder(val binding: ItemStatusBinding): RecyclerView.ViewHolder(binding.root) {
|
||||
class TimelineViewHolder(val binding: ItemStatusBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
init {
|
||||
binding.postImages.adapter = TimelineImageAdapter()
|
||||
|
||||
|
||||
binding.postIndicator.setViewPager(binding.postImages)
|
||||
(binding.postImages.adapter as TimelineImageAdapter).registerAdapterDataObserver(binding.postIndicator.adapterDataObserver)
|
||||
// val snapHelper = PagerSnapHelper()
|
||||
// snapHelper.attachToRecyclerView(binding.postImages)
|
||||
// val snapHelper = PagerSnapHelper()
|
||||
// snapHelper.attachToRecyclerView(binding.postImages)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ class TimelineRemoteMediator(
|
|||
private val accountId: Long,
|
||||
private val api: FediverseApi,
|
||||
private val db: AppDatabase
|
||||
): RemoteMediator<Int, StatusEntity>() {
|
||||
) : RemoteMediator<Int, StatusEntity>() {
|
||||
|
||||
override suspend fun load(
|
||||
loadType: LoadType,
|
||||
|
@ -36,20 +36,21 @@ class TimelineRemoteMediator(
|
|||
}
|
||||
}
|
||||
|
||||
return apiCall.fold({ statusResult ->
|
||||
return apiCall.fold(
|
||||
{ statusResult ->
|
||||
db.withTransaction {
|
||||
if (loadType == LoadType.REFRESH) {
|
||||
db.statusDao().clearAll(accountId)
|
||||
}
|
||||
db.statusDao().insertOrReplace(statusResult.map { it.toEntity(accountId) })
|
||||
}
|
||||
MediatorResult.Success(endOfPaginationReached = statusResult.isEmpty())
|
||||
}, {
|
||||
MediatorResult.Error(it)
|
||||
})
|
||||
|
||||
|
||||
MediatorResult.Success(endOfPaginationReached = statusResult.isEmpty())
|
||||
},
|
||||
{
|
||||
MediatorResult.Error(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun initialize() = InitializeAction.SKIP_INITIAL_REFRESH
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
package at.connyduck.pixelcat.components.timeline
|
||||
|
||||
import androidx.lifecycle.*
|
||||
import androidx.paging.*
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.ExperimentalPagingApi
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.cachedIn
|
||||
import at.connyduck.pixelcat.db.AccountManager
|
||||
import at.connyduck.pixelcat.db.AppDatabase
|
||||
import at.connyduck.pixelcat.db.entitity.AccountEntity
|
||||
import at.connyduck.pixelcat.db.entitity.StatusEntity
|
||||
import at.connyduck.pixelcat.network.FediverseApi
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimelineViewModel @Inject constructor(
|
||||
// private val repository: TimelineRepo,
|
||||
// private val repository: TimelineRepo,
|
||||
private val accountManager: AccountManager,
|
||||
private val db: AppDatabase,
|
||||
fediverseApi: FediverseApi
|
||||
): ViewModel() {
|
||||
) : ViewModel() {
|
||||
|
||||
private val accountId = MutableLiveData<Long>()
|
||||
|
||||
|
@ -38,8 +41,7 @@ class TimelineViewModel @Inject constructor(
|
|||
|
||||
fun onFavorite(status: StatusEntity) {
|
||||
viewModelScope.launch {
|
||||
// repository.onFavorite(status, accountManager.activeAccount()?.id!!)
|
||||
// repository.onFavorite(status, accountManager.activeAccount()?.id!!)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,8 +6,9 @@ class Loading<T> (override val data: T? = null) : UiState<T>(data)
|
|||
|
||||
class Success<T> (override val data: T? = null) : UiState<T>(data)
|
||||
|
||||
class Error<T> (override val data: T? = null,
|
||||
val errorMessage: String? = null,
|
||||
var consumed: Boolean = false,
|
||||
val cause: Throwable? = null
|
||||
): UiState<T>(data)
|
||||
class Error<T> (
|
||||
override val data: T? = null,
|
||||
val errorMessage: String? = null,
|
||||
var consumed: Boolean = false,
|
||||
val cause: Throwable? = null
|
||||
) : UiState<T>(data)
|
||||
|
|
|
@ -8,8 +8,8 @@ import androidx.annotation.ColorInt
|
|||
@ColorInt
|
||||
fun Context.getColorForAttr(@AttrRes attr: Int): Int {
|
||||
val value = TypedValue()
|
||||
if(theme.resolveAttribute(attr, value, true)) {
|
||||
if (theme.resolveAttribute(attr, value, true)) {
|
||||
return value.data
|
||||
}
|
||||
throw IllegalStateException("Attribute not found")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,4 +22,4 @@ fun Context.getColorForAttr(@AttrRes attr: Int): Int {
|
|||
} else {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,5 +13,5 @@ fun View.hide() {
|
|||
var View.visible
|
||||
get() = visibility == View.VISIBLE
|
||||
set(value) {
|
||||
visibility = if(value) View.VISIBLE else View.GONE
|
||||
}
|
||||
visibility = if (value) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
|
|
@ -10,5 +10,4 @@ object Config {
|
|||
const val oAuthScopes = "read write follow"
|
||||
|
||||
val domainExceptions = arrayOf("gab.com", "gab.ai", "gabfed.com")
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import dagger.android.ContributesAndroidInjector
|
|||
@Module
|
||||
abstract class ActivityModule {
|
||||
|
||||
//TODO order stuff here
|
||||
// TODO order stuff here
|
||||
@ContributesAndroidInjector(modules = [FragmentModule::class])
|
||||
abstract fun contributesMainActivity(): MainActivity
|
||||
|
||||
|
@ -38,5 +38,4 @@ abstract class ActivityModule {
|
|||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun contributesComposeActivity(): ComposeActivity
|
||||
|
||||
}
|
||||
|
|
|
@ -8,14 +8,16 @@ import dagger.android.AndroidInjector
|
|||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
@Component(modules = [
|
||||
AppModule::class,
|
||||
NetworkModule::class,
|
||||
AndroidInjectionModule::class,
|
||||
ActivityModule::class,
|
||||
ViewModelModule::class,
|
||||
ServiceModule::class
|
||||
])
|
||||
@Component(
|
||||
modules = [
|
||||
AppModule::class,
|
||||
NetworkModule::class,
|
||||
AndroidInjectionModule::class,
|
||||
ActivityModule::class,
|
||||
ViewModelModule::class,
|
||||
ServiceModule::class
|
||||
]
|
||||
)
|
||||
interface AppComponent : AndroidInjector<PixelcatApplication> {
|
||||
|
||||
@Component.Builder
|
||||
|
@ -27,4 +29,4 @@ interface AppComponent : AndroidInjector<PixelcatApplication> {
|
|||
}
|
||||
|
||||
override fun inject(app: PixelcatApplication)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ import javax.inject.Singleton
|
|||
class AppModule {
|
||||
|
||||
@Provides
|
||||
fun providesApp(app: PixelcatApplication): Application = app
|
||||
fun providesApp(app: PixelcatApplication): Application = app
|
||||
|
||||
@Provides
|
||||
fun providesContext(app: Application): Context = app
|
||||
|
@ -39,8 +39,4 @@ class AppModule {
|
|||
fun providesAccountManager(db: AppDatabase): AccountManager {
|
||||
return AccountManager(db)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,5 +21,4 @@ abstract class FragmentModule {
|
|||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun profileFragment(): ProfileFragment
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ import okhttp3.logging.HttpLoggingInterceptor
|
|||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
import retrofit2.create
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -34,10 +34,12 @@ class NetworkModule {
|
|||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.writeTimeout(30, TimeUnit.SECONDS)
|
||||
|
||||
if(BuildConfig.DEBUG) {
|
||||
okHttpClientBuilder.addInterceptor(HttpLoggingInterceptor().apply {
|
||||
level = HttpLoggingInterceptor.Level.HEADERS
|
||||
})
|
||||
if (BuildConfig.DEBUG) {
|
||||
okHttpClientBuilder.addInterceptor(
|
||||
HttpLoggingInterceptor().apply {
|
||||
level = HttpLoggingInterceptor.Level.HEADERS
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return okHttpClientBuilder.build()
|
||||
|
@ -61,10 +63,9 @@ class NetworkModule {
|
|||
.addCallAdapterFactory(NetworkResponseAdapterFactory())
|
||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||
.build()
|
||||
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesApi(retrofit: Retrofit): FediverseApi = retrofit.create()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,4 @@ abstract class ServiceModule {
|
|||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun contributesSendStatusService(): SendStatusService
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -47,7 +47,6 @@ abstract class ViewModelModule {
|
|||
@ViewModelKey(MainViewModel::class)
|
||||
internal abstract fun mainViewModel(viewModel: MainViewModel): ViewModel
|
||||
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(TimelineViewModel::class)
|
||||
|
@ -72,5 +71,5 @@ abstract class ViewModelModule {
|
|||
@IntoMap
|
||||
@ViewModelKey(ComposeViewModel::class)
|
||||
internal abstract fun composeViewModel(viewModel: ComposeViewModel): ViewModel
|
||||
//Add more ViewModels here
|
||||
}
|
||||
// Add more ViewModels here
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
package at.connyduck.pixelcat.db
|
||||
|
||||
import androidx.room.*
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import at.connyduck.pixelcat.db.entitity.AccountEntity
|
||||
|
||||
@Dao
|
||||
|
@ -14,5 +18,4 @@ interface AccountDao {
|
|||
|
||||
@Query("SELECT * FROM AccountEntity ORDER BY id ASC")
|
||||
suspend fun loadAll(): List<AccountEntity>
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import at.connyduck.pixelcat.model.Account
|
|||
* @author ConnyDuck
|
||||
*/
|
||||
|
||||
//TODO check if the comments are up to date
|
||||
// TODO check if the comments are up to date
|
||||
|
||||
private const val TAG = "AccountManager"
|
||||
|
||||
|
@ -23,9 +23,8 @@ class AccountManager(db: AppDatabase) {
|
|||
private var accounts: MutableList<AccountEntity> = mutableListOf()
|
||||
private val accountDao: AccountDao = db.accountDao()
|
||||
|
||||
|
||||
suspend fun activeAccount(): AccountEntity? {
|
||||
if(activeAccount == null) {
|
||||
if (activeAccount == null) {
|
||||
accounts = accountDao.loadAll().toMutableList()
|
||||
|
||||
activeAccount = accounts.find { acc ->
|
||||
|
@ -35,7 +34,6 @@ class AccountManager(db: AppDatabase) {
|
|||
return activeAccount
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a new empty account and makes it the active account.
|
||||
* More account information has to be added later with [updateActiveAccount]
|
||||
|
@ -60,7 +58,6 @@ class AccountManager(db: AppDatabase) {
|
|||
auth = authData,
|
||||
isActive = true
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,7 +70,6 @@ class AccountManager(db: AppDatabase) {
|
|||
Log.d(TAG, "saveAccount: saving account with id " + account.id)
|
||||
accountDao.insertOrReplace(account)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -97,9 +93,7 @@ class AccountManager(db: AppDatabase) {
|
|||
activeAccount = null
|
||||
}
|
||||
return activeAccount
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -113,9 +107,9 @@ class AccountManager(db: AppDatabase) {
|
|||
it.username = account.username
|
||||
it.displayName = account.name
|
||||
it.profilePictureUrl = account.avatar
|
||||
// it.defaultPostPrivacy = account.source?.privacy ?: Status.Visibility.PUBLIC
|
||||
// it.defaultPostPrivacy = account.source?.privacy ?: Status.Visibility.PUBLIC
|
||||
it.defaultMediaSensitivity = account.source?.sensitive ?: false
|
||||
// it.emojis = account.emojis ?: emptyList()
|
||||
// it.emojis = account.emojis ?: emptyList()
|
||||
|
||||
Log.d(TAG, "updateActiveAccount: saving account with id " + it.id)
|
||||
it.id = accountDao.insertOrReplace(it)
|
||||
|
@ -123,13 +117,12 @@ class AccountManager(db: AppDatabase) {
|
|||
val accountIndex = accounts.indexOf(it)
|
||||
|
||||
if (accountIndex != -1) {
|
||||
//in case the user was already logged in with this account, remove the old information
|
||||
// in case the user was already logged in with this account, remove the old information
|
||||
accounts.removeAt(accountIndex)
|
||||
accounts.add(accountIndex, it)
|
||||
} else {
|
||||
accounts.add(it)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,5 +180,4 @@ class AccountManager(db: AppDatabase) {
|
|||
acc.id == accountId
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,10 @@ import androidx.room.RoomDatabase
|
|||
import at.connyduck.pixelcat.db.entitity.AccountEntity
|
||||
import at.connyduck.pixelcat.db.entitity.StatusEntity
|
||||
|
||||
|
||||
@Database(entities = [AccountEntity::class, StatusEntity::class], version = 1)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
abstract fun accountDao(): AccountDao
|
||||
|
||||
abstract fun statusDao(): TimelineDao
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
package at.connyduck.pixelcat.db
|
||||
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import at.connyduck.pixelcat.model.Attachment
|
||||
import at.connyduck.pixelcat.model.Status
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
|
||||
class Converters {
|
||||
|
||||
|
@ -25,16 +23,16 @@ class Converters {
|
|||
|
||||
@TypeConverter
|
||||
fun attachmentListToJson(attachmentList: List<Attachment>?): String {
|
||||
val type = Types.newParameterizedType(
|
||||
List::class.java,
|
||||
Attachment::class.java
|
||||
)
|
||||
val type = Types.newParameterizedType(
|
||||
List::class.java,
|
||||
Attachment::class.java
|
||||
)
|
||||
return moshi.adapter<List<Attachment>>(type).toJson(attachmentList)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun jsonToAttachmentList(attachmentListJson: String?): List<Attachment>? {
|
||||
if(attachmentListJson == null) {
|
||||
if (attachmentListJson == null) {
|
||||
return null
|
||||
}
|
||||
val type = Types.newParameterizedType(
|
||||
|
@ -53,5 +51,4 @@ class Converters {
|
|||
fun longToDate(date: Long): Date {
|
||||
return Date(date)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
package at.connyduck.pixelcat.db
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.room.*
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import at.connyduck.pixelcat.db.entitity.StatusEntity
|
||||
|
||||
@Dao
|
||||
|
@ -21,5 +25,4 @@ interface TimelineDao {
|
|||
|
||||
@Query("DELETE FROM StatusEntity WHERE accountId = :accountId")
|
||||
suspend fun clearAll(accountId: Long)
|
||||
|
||||
}
|
||||
|
|
|
@ -2,33 +2,44 @@
|
|||
|
||||
package at.connyduck.pixelcat.db.entitity
|
||||
|
||||
import androidx.room.*
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(indices = [Index(value = ["domain", "accountId"],
|
||||
unique = true)])
|
||||
//@TypeConverters(Converters::class)
|
||||
data class AccountEntity(@field:PrimaryKey(autoGenerate = true) var id: Long,
|
||||
val domain: String,
|
||||
@Embedded(prefix = "auth_") var auth: AccountAuthData,
|
||||
var isActive: Boolean,
|
||||
var accountId: String = "",
|
||||
var username: String = "",
|
||||
var displayName: String = "",
|
||||
var profilePictureUrl: String = "",
|
||||
var notificationsEnabled: Boolean = true,
|
||||
var notificationsMentioned: Boolean = true,
|
||||
var notificationsFollowed: Boolean = true,
|
||||
var notificationsReblogged: Boolean = true,
|
||||
var notificationsFavorited: Boolean = true,
|
||||
var notificationSound: Boolean = true,
|
||||
var notificationVibration: Boolean = true,
|
||||
var notificationLight: Boolean = true,
|
||||
var defaultMediaSensitivity: Boolean = false,
|
||||
var alwaysShowSensitiveMedia: Boolean = false,
|
||||
var mediaPreviewEnabled: Boolean = true,
|
||||
var lastNotificationId: String = "0",
|
||||
var activeNotifications: String = "[]",
|
||||
var notificationsFilter: String = "[]") {
|
||||
@Entity(
|
||||
indices = [
|
||||
Index(
|
||||
value = ["domain", "accountId"],
|
||||
unique = true
|
||||
)
|
||||
]
|
||||
)
|
||||
// @TypeConverters(Converters::class)
|
||||
data class AccountEntity(
|
||||
@field:PrimaryKey(autoGenerate = true) var id: Long,
|
||||
val domain: String,
|
||||
@Embedded(prefix = "auth_") var auth: AccountAuthData,
|
||||
var isActive: Boolean,
|
||||
var accountId: String = "",
|
||||
var username: String = "",
|
||||
var displayName: String = "",
|
||||
var profilePictureUrl: String = "",
|
||||
var notificationsEnabled: Boolean = true,
|
||||
var notificationsMentioned: Boolean = true,
|
||||
var notificationsFollowed: Boolean = true,
|
||||
var notificationsReblogged: Boolean = true,
|
||||
var notificationsFavorited: Boolean = true,
|
||||
var notificationSound: Boolean = true,
|
||||
var notificationVibration: Boolean = true,
|
||||
var notificationLight: Boolean = true,
|
||||
var defaultMediaSensitivity: Boolean = false,
|
||||
var alwaysShowSensitiveMedia: Boolean = false,
|
||||
var mediaPreviewEnabled: Boolean = true,
|
||||
var lastNotificationId: String = "0",
|
||||
var activeNotifications: String = "[]",
|
||||
var notificationsFilter: String = "[]"
|
||||
) {
|
||||
|
||||
val identifier: String
|
||||
get() = "$domain:$accountId"
|
||||
|
|
|
@ -4,24 +4,24 @@ import androidx.room.Entity
|
|||
import at.connyduck.pixelcat.model.Account
|
||||
|
||||
@Entity(
|
||||
primaryKeys = ["serverId", "timelineUserId"]
|
||||
primaryKeys = ["serverId", "timelineUserId"]
|
||||
)
|
||||
data class TimelineAccountEntity(
|
||||
val serverId: Long,
|
||||
val id: String,
|
||||
val localUsername: String,
|
||||
val username: String,
|
||||
val displayName: String,
|
||||
val url: String,
|
||||
val avatar: String
|
||||
val serverId: Long,
|
||||
val id: String,
|
||||
val localUsername: String,
|
||||
val username: String,
|
||||
val displayName: String,
|
||||
val url: String,
|
||||
val avatar: String
|
||||
)
|
||||
|
||||
fun Account.toEntity(serverId: Long) = TimelineAccountEntity(
|
||||
serverId = serverId,
|
||||
id = id,
|
||||
localUsername = localUsername,
|
||||
username = username,
|
||||
displayName = displayName,
|
||||
url = url,
|
||||
avatar = avatar
|
||||
)
|
||||
serverId = serverId,
|
||||
id = id,
|
||||
localUsername = localUsername,
|
||||
username = username,
|
||||
displayName = displayName,
|
||||
url = url,
|
||||
avatar = avatar
|
||||
)
|
||||
|
|
|
@ -8,11 +8,9 @@ import androidx.room.TypeConverters
|
|||
import at.connyduck.pixelcat.db.Converters
|
||||
import at.connyduck.pixelcat.model.Attachment
|
||||
import at.connyduck.pixelcat.model.Status
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
|
||||
@Entity(
|
||||
primaryKeys = ["accountId", "id"]
|
||||
)
|
||||
@Entity(primaryKeys = ["accountId", "id"])
|
||||
@TypeConverters(Converters::class)
|
||||
data class StatusEntity(
|
||||
val accountId: Long,
|
||||
|
|
|
@ -5,8 +5,8 @@ import com.squareup.moshi.JsonClass
|
|||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class AccessToken(
|
||||
@Json(name = "access_token") val accessToken: String,
|
||||
@Json(name = "refresh_token") val refreshToken: String?,
|
||||
@Json(name = "expires_in") val expiresIn: Long?,
|
||||
@Json(name = "created_at") val createdAt: Long?
|
||||
@Json(name = "access_token") val accessToken: String,
|
||||
@Json(name = "refresh_token") val refreshToken: String?,
|
||||
@Json(name = "expires_in") val expiresIn: Long?,
|
||||
@Json(name = "created_at") val createdAt: Long?
|
||||
)
|
||||
|
|
|
@ -8,7 +8,7 @@ import com.squareup.moshi.Json
|
|||
import com.squareup.moshi.JsonClass
|
||||
import kotlinx.android.parcel.Parceler
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Account(
|
||||
|
@ -26,8 +26,8 @@ data class Account(
|
|||
@Json(name = "statuses_count") val statusesCount: Int,
|
||||
val source: AccountSource?,
|
||||
val bot: Boolean,
|
||||
// val emojis: List<Emoji>, // nullable for backward compatibility
|
||||
val fields: List<Field>?, //nullable for backward compatibility
|
||||
// val emojis: List<Emoji>, // nullable for backward compatibility
|
||||
val fields: List<Field>?, // nullable for backward compatibility
|
||||
val moved: Account?
|
||||
|
||||
) {
|
||||
|
@ -43,26 +43,26 @@ data class Account(
|
|||
@JsonClass(generateAdapter = true)
|
||||
@Parcelize
|
||||
data class AccountSource(
|
||||
// val privacy: Status.Visibility,
|
||||
val sensitive: Boolean,
|
||||
val note: String,
|
||||
val fields: List<StringField>?
|
||||
): Parcelable
|
||||
// val privacy: Status.Visibility,
|
||||
val sensitive: Boolean,
|
||||
val note: String,
|
||||
val fields: List<StringField>?
|
||||
) : Parcelable
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Parcelize
|
||||
data class Field (
|
||||
val name: String,
|
||||
// val value: @WriteWith<SpannedParceler>() Spanned,
|
||||
@Json(name = "verified_at") val verifiedAt: Date?
|
||||
): Parcelable
|
||||
data class Field(
|
||||
val name: String,
|
||||
// val value: @WriteWith<SpannedParceler>() Spanned,
|
||||
@Json(name = "verified_at") val verifiedAt: Date?
|
||||
) : Parcelable
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Parcelize
|
||||
data class StringField (
|
||||
val name: String,
|
||||
val value: String
|
||||
): Parcelable
|
||||
data class StringField(
|
||||
val name: 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)
|
||||
|
@ -70,4 +70,4 @@ object SpannedParceler : Parceler<Spanned> {
|
|||
override fun Spanned.write(parcel: Parcel, flags: Int) {
|
||||
parcel.writeString(HtmlCompat.toHtml(this, HtmlCompat.TO_HTML_PARAGRAPH_LINES_INDIVIDUAL))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,6 @@ import com.squareup.moshi.JsonClass
|
|||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class AppCredentials(
|
||||
@Json(name = "client_id") val clientId: String,
|
||||
@Json(name = "client_secret") val clientSecret: String
|
||||
@Json(name = "client_id") val clientId: String,
|
||||
@Json(name = "client_secret") val clientSecret: String
|
||||
)
|
||||
|
|
|
@ -48,9 +48,9 @@ data class Attachment(
|
|||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Parcelize
|
||||
data class MetaData (
|
||||
val focus: Focus?,
|
||||
val duration: Float?
|
||||
data class MetaData(
|
||||
val focus: Focus?,
|
||||
val duration: Float?
|
||||
) : Parcelable
|
||||
|
||||
/**
|
||||
|
@ -61,8 +61,8 @@ data class Attachment(
|
|||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Parcelize
|
||||
data class Focus (
|
||||
val x: Float,
|
||||
val y: Float
|
||||
data class Focus(
|
||||
val x: Float,
|
||||
val y: Float
|
||||
) : Parcelable
|
||||
}
|
||||
|
|
|
@ -6,10 +6,9 @@ import com.squareup.moshi.JsonClass
|
|||
@JsonClass(generateAdapter = true)
|
||||
data class NewStatus(
|
||||
val status: String,
|
||||
//@Json(name = "spoiler_text") val warningText: String,
|
||||
// @Json(name = "spoiler_text") val warningText: String,
|
||||
@Json(name = "in_reply_to_id") val inReplyToId: String?,
|
||||
val visibility: String,
|
||||
val sensitive: Boolean,
|
||||
@Json(name = "media_ids") val mediaIds: List<String>?
|
||||
)
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import com.squareup.moshi.Json
|
|||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Relationship (
|
||||
data class Relationship(
|
||||
val id: String,
|
||||
val following: Boolean,
|
||||
@Json(name = "followed_by") val followedBy: Boolean,
|
||||
|
|
|
@ -3,7 +3,7 @@ package at.connyduck.pixelcat.model
|
|||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Status(
|
||||
|
@ -62,7 +62,7 @@ data class Status(
|
|||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Mention (
|
||||
data class Mention(
|
||||
val id: String,
|
||||
val url: String,
|
||||
val acct: String,
|
||||
|
@ -70,9 +70,8 @@ data class Status(
|
|||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Application (
|
||||
data class Application(
|
||||
val name: String,
|
||||
val website: String?
|
||||
)
|
||||
|
||||
}
|
||||
|
|
|
@ -1,10 +1,24 @@
|
|||
package at.connyduck.pixelcat.network
|
||||
|
||||
import at.connyduck.pixelcat.model.*
|
||||
import at.connyduck.pixelcat.model.AccessToken
|
||||
import at.connyduck.pixelcat.model.Account
|
||||
import at.connyduck.pixelcat.model.AppCredentials
|
||||
import at.connyduck.pixelcat.model.Attachment
|
||||
import at.connyduck.pixelcat.model.NewStatus
|
||||
import at.connyduck.pixelcat.model.Relationship
|
||||
import at.connyduck.pixelcat.model.Status
|
||||
import at.connyduck.pixelcat.network.calladapter.NetworkResponse
|
||||
import okhttp3.MultipartBody
|
||||
import retrofit2.http.*
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.Field
|
||||
import retrofit2.http.FormUrlEncoded
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.Multipart
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Part
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface FediverseApi {
|
||||
|
||||
|
@ -26,12 +40,12 @@ interface FediverseApi {
|
|||
@FormUrlEncoded
|
||||
@POST("oauth/token")
|
||||
suspend fun fetchOAuthToken(
|
||||
@Header(DOMAIN_HEADER) domain: String,
|
||||
@Field("client_id") clientId: String,
|
||||
@Field("client_secret") clientSecret: String,
|
||||
@Field("redirect_uri") redirectUri: String,
|
||||
@Field("code") code: String,
|
||||
@Field("grant_type") grantType: String = "authorization_code"
|
||||
@Header(DOMAIN_HEADER) domain: String,
|
||||
@Field("client_id") clientId: String,
|
||||
@Field("client_secret") clientSecret: String,
|
||||
@Field("redirect_uri") redirectUri: String,
|
||||
@Field("code") code: String,
|
||||
@Field("grant_type") grantType: String = "authorization_code"
|
||||
): NetworkResponse<AccessToken>
|
||||
|
||||
@FormUrlEncoded
|
||||
|
@ -44,7 +58,6 @@ interface FediverseApi {
|
|||
@Field("grant_type") grantType: String = "refresh_token"
|
||||
): NetworkResponse<AccessToken>
|
||||
|
||||
|
||||
@GET("api/v1/accounts/verify_credentials")
|
||||
suspend fun accountVerifyCredentials(): NetworkResponse<Account>
|
||||
|
||||
|
@ -53,7 +66,7 @@ interface FediverseApi {
|
|||
@Query("max_id") maxId: String? = null,
|
||||
@Query("since_id") sinceId: String? = null,
|
||||
@Query("limit") limit: Int? = null
|
||||
): NetworkResponse<List<Status>>
|
||||
): NetworkResponse<List<Status>>
|
||||
|
||||
@GET("api/v1/accounts/{id}/statuses")
|
||||
suspend fun accountTimeline(
|
||||
|
@ -141,4 +154,4 @@ interface FediverseApi {
|
|||
suspend fun unfavouriteStatus(
|
||||
@Path("id") statusId: String
|
||||
): NetworkResponse<Status>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ class InstanceSwitchAuthInterceptor(private val accountManager: AccountManager)
|
|||
builder.url(swapHost(originalRequest.url, instanceHeader))
|
||||
builder.removeHeader(FediverseApi.DOMAIN_HEADER)
|
||||
} else if (currentAccount != null) {
|
||||
//use domain of current account
|
||||
// use domain of current account
|
||||
builder.url(swapHost(originalRequest.url, currentAccount.domain))
|
||||
.header(
|
||||
"Authorization",
|
||||
|
@ -37,7 +37,6 @@ class InstanceSwitchAuthInterceptor(private val accountManager: AccountManager)
|
|||
val newRequest = builder.build()
|
||||
|
||||
return chain.proceed(newRequest)
|
||||
|
||||
} else {
|
||||
return chain.proceed(originalRequest)
|
||||
}
|
||||
|
@ -46,6 +45,4 @@ class InstanceSwitchAuthInterceptor(private val accountManager: AccountManager)
|
|||
private fun swapHost(url: HttpUrl, host: String): HttpUrl {
|
||||
return url.newBuilder().host(host).build()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -10,13 +10,10 @@ import okhttp3.Route
|
|||
class RefreshTokenAuthenticator(private val accountManager: AccountManager) : Authenticator {
|
||||
override fun authenticate(route: Route?, response: Response): Request? {
|
||||
|
||||
|
||||
val currentAccount = runBlocking { accountManager.activeAccount() }
|
||||
|
||||
|
||||
// TODO
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import at.connyduck.pixelcat.BuildConfig
|
|||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
|
||||
class UserAgentInterceptor: Interceptor {
|
||||
class UserAgentInterceptor : Interceptor {
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val requestWithUserAgent = chain.request()
|
||||
|
@ -14,5 +14,4 @@ class UserAgentInterceptor: Interceptor {
|
|||
.build()
|
||||
return chain.proceed(requestWithUserAgent)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,4 @@ class NetworkCallAdapter<S : Any>(
|
|||
override fun adapt(call: Call<S>): Call<NetworkResponse<S>> {
|
||||
return NetworkResponseCall(call)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ sealed class NetworkResponse<out A : Any> {
|
|||
}
|
||||
}
|
||||
|
||||
sealed class NetworkResponseError: Throwable() {
|
||||
sealed class NetworkResponseError : Throwable() {
|
||||
|
||||
data class ApiError(val code: Int) : NetworkResponseError()
|
||||
|
||||
|
@ -27,4 +27,4 @@ sealed class NetworkResponseError: Throwable() {
|
|||
* For example, json parsing error
|
||||
*/
|
||||
data class UnknownError(val error: Throwable?) : NetworkResponseError()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,4 +38,4 @@ class NetworkResponseAdapterFactory : CallAdapter.Factory() {
|
|||
|
||||
return NetworkCallAdapter<Any>(successBodyType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,21 +28,37 @@ internal class NetworkResponseCall<S : Any>(
|
|||
// Response is successful but the body is null
|
||||
callback.onResponse(
|
||||
this@NetworkResponseCall,
|
||||
Response.success(NetworkResponse.Failure(NetworkResponseError.ApiError(response.code())))
|
||||
Response.success(
|
||||
NetworkResponse.Failure(
|
||||
NetworkResponseError.ApiError(
|
||||
response.code()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
callback.onResponse(
|
||||
this@NetworkResponseCall,
|
||||
Response.success(NetworkResponse.Failure(NetworkResponseError.ApiError(response.code())))
|
||||
callback.onResponse(
|
||||
this@NetworkResponseCall,
|
||||
Response.success(
|
||||
NetworkResponse.Failure(
|
||||
NetworkResponseError.ApiError(
|
||||
response.code()
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<S>, throwable: Throwable) {
|
||||
Log.d("NetworkResponseCall", "Network response failed", throwable)
|
||||
val networkResponse = when (throwable) {
|
||||
is IOException -> NetworkResponse.Failure(NetworkResponseError.NetworkError(throwable))
|
||||
is IOException -> NetworkResponse.Failure(
|
||||
NetworkResponseError.NetworkError(
|
||||
throwable
|
||||
)
|
||||
)
|
||||
else -> NetworkResponse.Failure(NetworkResponseError.UnknownError(throwable))
|
||||
}
|
||||
callback.onResponse(this@NetworkResponseCall, Response.success(networkResponse))
|
||||
|
@ -65,4 +81,4 @@ internal class NetworkResponseCall<S : Any>(
|
|||
override fun request(): Request = delegate.request()
|
||||
|
||||
override fun timeout(): Timeout = delegate.timeout()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,14 +7,13 @@ import java.lang.IllegalStateException
|
|||
inline fun <reified T> Fragment.arg(key: String): T {
|
||||
val value = arguments?.get(key)
|
||||
|
||||
if(value !is T) {
|
||||
if (value !is T) {
|
||||
throw IllegalStateException("Argument $key is of wrong type")
|
||||
}
|
||||
return value
|
||||
|
||||
}
|
||||
|
||||
inline fun Fragment.withArgs(argsBuilder: Bundle.() -> Unit): Fragment {
|
||||
this.arguments = Bundle().apply(argsBuilder)
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,10 @@ import kotlin.reflect.KProperty
|
|||
*/
|
||||
|
||||
inline fun <T : ViewBinding> AppCompatActivity.viewBinding(
|
||||
crossinline bindingInflater: (LayoutInflater) -> T) =
|
||||
lazy(LazyThreadSafetyMode.NONE) {
|
||||
bindingInflater(layoutInflater)
|
||||
}
|
||||
crossinline bindingInflater: (LayoutInflater) -> T
|
||||
) = lazy(LazyThreadSafetyMode.NONE) {
|
||||
bindingInflater(layoutInflater)
|
||||
}
|
||||
|
||||
class FragmentViewBindingDelegate<T : ViewBinding>(
|
||||
val fragment: Fragment,
|
||||
|
@ -31,14 +31,16 @@ class FragmentViewBindingDelegate<T : ViewBinding>(
|
|||
init {
|
||||
fragment.lifecycle.addObserver(object : DefaultLifecycleObserver {
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
fragment.viewLifecycleOwnerLiveData.observe(fragment,
|
||||
fragment.viewLifecycleOwnerLiveData.observe(
|
||||
fragment,
|
||||
Observer { t ->
|
||||
t?.lifecycle?.addObserver(object : DefaultLifecycleObserver {
|
||||
override fun onDestroy(owner: LifecycleOwner) {
|
||||
binding = null
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -59,4 +61,4 @@ class FragmentViewBindingDelegate<T : ViewBinding>(
|
|||
}
|
||||
|
||||
fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T) =
|
||||
FragmentViewBindingDelegate(this, viewBindingFactory)
|
||||
FragmentViewBindingDelegate(this, viewBindingFactory)
|
||||
|
|
|
@ -11,14 +11,13 @@ buildscript {
|
|||
}
|
||||
}
|
||||
|
||||
apply(plugin = "org.jlleitschuh.gradle.ktlint")
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven(url = "https://jitpack.io")
|
||||
}
|
||||
apply(plugin = "org.jlleitschuh.gradle.ktlint")
|
||||
}
|
||||
|
||||
tasks.register("clean", Delete::class.java) {
|
||||
|
|
Loading…
Reference in New Issue