configure ktlint gradle plugin

This commit is contained in:
Konrad Pozniak 2020-06-12 19:58:15 +02:00
parent 93a31af6a6
commit 0e8ad1e9ed
77 changed files with 517 additions and 550 deletions

View File

@ -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"

View File

@ -34,5 +34,4 @@ class PixelcatApplication : DaggerApplication() {
.application(this)
.build()
}
}

View File

@ -40,7 +40,6 @@ class AboutActivity : BaseActivity() {
binding.aboutLicensesButton.setOnClickListener {
startActivity(LicenseActivity.newIntent(this))
}
}
companion object {

View File

@ -38,7 +38,6 @@ class LicenseActivity : BaseActivity() {
}
loadFileIntoTextView(R.raw.apache, binding.licenseApacheTextView)
}
private fun loadFileIntoTextView(@RawRes fileId: Int, textView: TextView) {
@ -61,7 +60,6 @@ class LicenseActivity : BaseActivity() {
br.close()
textView.text = sb.toString()
}
companion object {

View File

@ -37,8 +37,5 @@ class LicenseCard
binding.licenseCardLink.text = link
// setOnClickListener { LinkHelper.openLink(link, context) }
}
}
}

View File

@ -52,9 +52,7 @@ class AccountSelectionAdapter(
}
}
}
}
class AccountSelectionViewHolder(val binding: ItemAccountSelectionBinding) :
RecyclerView.ViewHolder(binding.root)

View File

@ -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?,
@ -58,5 +56,4 @@ class AccountSelectionBottomSheet(
super.onDestroyView()
_binding = null
}
}

View File

@ -34,13 +34,10 @@ class MenuBottomSheet : BottomSheetDialogFragment() {
startActivity(AboutActivity.newIntent(it.context))
dismiss()
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

View File

@ -35,7 +35,6 @@ class ComposeActivity: BaseActivity() {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.root.setOnApplyWindowInsetsListener { _, insets ->
val top = insets.systemWindowInsetTop
@ -49,7 +48,6 @@ class ComposeActivity: BaseActivity() {
viewModel.addImage(intent.getStringExtra(EXTRA_MEDIA_URI)!!)
}
setSupportActionBar(binding.composeToolBar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
@ -75,11 +73,16 @@ class ComposeActivity: BaseActivity() {
changeVisibility(VISIBILITY.FOLLOWERS_ONLY)
}
viewModel.images.observe(this, Observer {
viewModel.images.observe(
this,
Observer {
adapter.submitList(it)
})
}
)
viewModel.visibility.observe(this, Observer {
viewModel.visibility.observe(
this,
Observer {
val visibilityString = when (it) {
VISIBILITY.PUBLIC -> R.string.compose_visibility_public
VISIBILITY.UNLISTED -> R.string.compose_visibility_unlisted
@ -87,10 +90,9 @@ class ComposeActivity: BaseActivity() {
}
binding.composeVisibilityButton.text = getString(R.string.compose_visibility, getString(visibilityString))
})
}
)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
@ -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"

View File

@ -14,21 +14,15 @@ class ComposeViewModel @Inject constructor(
val accountManager: AccountManager
) : 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,10 +41,7 @@ class ComposeViewModel @Inject constructor(
val intent = SendStatusService.sendStatusIntent(context, statusToSend)
ContextCompat.startForegroundService(context, intent)
}
}
}
enum class VISIBILITY(val serverName: String) {

View File

@ -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
}
@ -60,7 +64,6 @@ class SendStatusService : DaggerService(), CoroutineScope {
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)
@ -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 ->
api.uploadMedia(body).fold(
{ attachment ->
attachment.id
}, {
},
{
""
})
}
)
}
val newStatus = NewStatus(
@ -147,9 +149,11 @@ class SendStatusService : DaggerService(), CoroutineScope {
account.domain,
statusToSend.idempotencyKey,
newStatus
).fold<Any?>({
).fold<Any?>(
{
statusesToSend.remove(id)
}, {
},
{
when (it) {
is NetworkResponseError.ApiError -> {
// the server refused to accept the status, save toot & show error message
@ -170,19 +174,21 @@ class SendStatusService : DaggerService(), CoroutineScope {
backoff = MAX_RETRY_INTERVAL
}
timer.schedule(object : TimerTask() {
timer.schedule(
object : TimerTask() {
override fun run() {
sendStatus(id)
}
}, backoff)
},
backoff
)
}
}
})
}
)
}.apply {
sendJobs[id] = this
}
}
private fun stopSelfWhenDone() {
@ -210,13 +216,15 @@ class SendStatusService : DaggerService(), CoroutineScope {
notificationManager.notify(id, builder.build())
timer.schedule(object : TimerTask() {
timer.schedule(
object : TimerTask() {
override fun run() {
notificationManager.cancel(id)
stopSelfWhenDone()
}
}, 5000)
},
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,7 +249,8 @@ 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,
fun sendStatusIntent(
context: Context,
statusToSend: StatusToSend
): Intent {
val intent = Intent(context, SendStatusService::class.java)
@ -250,7 +258,6 @@ class SendStatusService : DaggerService(), CoroutineScope {
return intent
}
}
override val coroutineContext: CoroutineContext

View File

@ -18,7 +18,5 @@ abstract class BaseActivity: DaggerAppCompatActivity() {
if (!appSettings.useSystemFont()) {
theme.applyStyle(R.style.NunitoFont, true)
}
}
}

View File

@ -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
@ -99,7 +98,6 @@ class LoginActivity : BaseActivity(), Observer<LoginModel> {
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
}
}

View File

@ -15,5 +15,3 @@ data class LoginModel(
enum class LoginState { // TODO rename this stuff so it makes sense
LOADING, NO_ERROR, NETWORK_ERROR, INVALID_DOMAIN, AUTH_ERROR, SUCCESS, SUCCESS_FINAL
}

View File

@ -10,7 +10,7 @@ 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(
@ -18,7 +18,6 @@ class LoginViewModel @Inject constructor(
private val accountManager: AccountManager
) : ViewModel() {
val loginState = MutableLiveData<LoginModel>().apply {
value = LoginModel(state = LoginState.NO_ERROR)
}
@ -46,7 +45,6 @@ class LoginViewModel @Inject constructor(
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 ->
).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 ->
).fold(
{ tokenResponse ->
val authData = AccountAuthData(
accessToken = tokenResponse.accessToken,
refreshToken = tokenResponse.refreshToken,
tokenExpiresAt = tokenResponse.createdAt ?: 0 + (tokenResponse.expiresIn
?: 0),
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://", "")

View File

@ -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
@ -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("&")

View File

@ -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()!!))
}
}
}

View File

@ -23,5 +23,4 @@ class MainFragmentAdapter(fragmentActivity: FragmentActivity): FragmentStateAdap
}
override fun getItemCount() = 4
}

View File

@ -13,18 +13,18 @@ class MainViewModel @Inject constructor(
) : ViewModel() {
fun whatever() {
}
init {
viewModelScope.launch {
fediverseApi.accountVerifyCredentials().fold({ account ->
fediverseApi.accountVerifyCredentials().fold(
{ account ->
accountManager.updateActiveAccount(account)
}, {
})
},
{
}
)
}
}
}

View File

@ -16,5 +16,4 @@ class NotificationsFragment: DaggerFragment(R.layout.fragment_notifications) {
companion object {
fun newInstance() = NotificationsFragment()
}
}

View File

@ -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()

View File

@ -28,7 +28,5 @@ class GridSpacingItemDecoration(
if (position - topOffset >= spanCount) {
outRect.top = spacing // item top
}
}
}

View File

@ -30,7 +30,6 @@ class ProfileActivity: BaseActivity() {
add(R.id.layoutContainer, ProfileFragment.newInstance(intent.getStringExtra(EXTRA_ACCOUNT_ID)))
}
}
}
companion object {

View File

@ -22,7 +22,6 @@ class ProfileDataSourceFactory(
}
}
class ProfileImageDataSource(
private val api: FediverseApi,
private val accountId: String?,
@ -40,11 +39,13 @@ class ProfileImageDataSource(
limit = params.requestedLoadSize,
onlyMedia = true,
excludeReblogs = true
).fold({
).fold(
{
callback.onResult(it)
}, {
})
},
{
}
)
}
}
@ -57,11 +58,13 @@ class ProfileImageDataSource(
limit = params.requestedLoadSize,
onlyMedia = true,
excludeReblogs = true
).fold({
).fold(
{
callback.onResult(it)
}, {
})
},
{
}
)
}
}

View File

@ -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
@ -58,7 +58,6 @@ class ProfileFragment : DaggerFragment(R.layout.fragment_profile) {
MenuBottomSheet()
bottomSheetDialog.show(childFragmentManager, "menuBottomSheet")
}
}
true
}
@ -88,21 +87,30 @@ class ProfileFragment : DaggerFragment(R.layout.fragment_profile) {
viewModel.setAccountInfo(arg(ACCOUNT_ID))
viewModel.profile.observe(viewLifecycleOwner, Observer {
viewModel.profile.observe(
viewLifecycleOwner,
Observer {
when (it) {
is Success -> onAccountChanged(it.data)
is Error -> showError()
}
})
viewModel.relationship.observe(viewLifecycleOwner, Observer {
}
)
viewModel.relationship.observe(
viewLifecycleOwner,
Observer {
when (it) {
is Success -> onRelationshipChanged(it.data)
is Error -> showError()
}
})
viewModel.profileImages.observe(viewLifecycleOwner, Observer {
}
)
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) }
}
}

View File

@ -79,5 +79,4 @@ class ProfileHeaderAdapter: RecyclerView.Adapter<ProfileHeaderViewHolder>() {
}
}
class ProfileHeaderViewHolder(val binding: ItemProfileHeaderBinding) : RecyclerView.ViewHolder(binding.root)

View File

@ -54,8 +54,6 @@ class ProfileImageAdapter(
}
}
}
}
class ProfileImageViewHolder(val binding: ItemProfileImageBinding) : RecyclerView.ViewHolder(binding.root)

View File

@ -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({
fediverseApi.account(getAccountId()).fold(
{
profile.value = Success(it)
}, {
},
{
profile.value = Error(cause = it)
})
}
)
}
}
}
@ -58,11 +61,14 @@ class ProfileViewModel @Inject constructor(
private fun loadRelationship(reload: Boolean = false) {
if (relationship.value == null || reload) {
viewModelScope.launch {
fediverseApi.relationships(listOf(getAccountId())).fold({
fediverseApi.relationships(listOf(getAccountId())).fold(
{
relationship.value = Success(it.first())
}, {
},
{
relationship.value = Error(cause = it)
})
}
)
}
}
}
@ -75,7 +81,8 @@ class ProfileViewModel @Inject constructor(
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!!
}
}

View File

@ -16,5 +16,4 @@ class SearchFragment: DaggerFragment(R.layout.fragment_search) {
companion object {
fun newInstance() = SearchFragment()
}
}

View File

@ -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()

View File

@ -4,11 +4,13 @@ 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
@ -26,7 +28,6 @@ class AppSettings @Inject constructor (
context.getString(R.string.key_pref_app_color_cold) -> R.style.Cold
else -> throw IllegalStateException()
}
}
@AppCompatDelegate.NightMode
@ -58,11 +59,8 @@ class AppSettings @Inject constructor (
context.resources.getBoolean(R.bool.pref_title_system_font_default)
)
}
}
private fun SharedPreferences.getNonNullString(key: String, default: String): String {
return getString(key, default) ?: default
}

View File

@ -48,7 +48,6 @@ class SettingsActivity : BaseActivity(), SharedPreferences.OnSharedPreferenceCha
preferences.registerOnSharedPreferenceChangeListener(this)
restartActivitiesOnExit = intent.getBooleanExtra(EXTRA_RESTART_ACTIVITIES, false)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
@ -100,6 +99,4 @@ class SettingsActivity : BaseActivity(), SharedPreferences.OnSharedPreferenceCha
}
}
}
}

View File

@ -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,7 +13,6 @@ 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
@ -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) })
}
companion object {
@ -81,5 +77,4 @@ class TimelineFragment: DaggerFragment(R.layout.fragment_timeline), TimeLineActi
override fun onReply(status: StatusEntity) {
TODO("Not yet implemented")
}
}

View File

@ -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)

View File

@ -28,7 +28,6 @@ object TimelineDiffUtil: DiffUtil.ItemCallback<StatusEntity>() {
override fun areContentsTheSame(oldItem: StatusEntity, newItem: StatusEntity): Boolean {
return oldItem == newItem
}
}
class TimelineListAdapter(
@ -83,19 +82,14 @@ 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) {
init {
binding.postImages.adapter = TimelineImageAdapter()
binding.postIndicator.setViewPager(binding.postImages)
(binding.postImages.adapter as TimelineImageAdapter).registerAdapterDataObserver(binding.postIndicator.adapterDataObserver)
// val snapHelper = PagerSnapHelper()

View File

@ -36,7 +36,8 @@ class TimelineRemoteMediator(
}
}
return apiCall.fold({ statusResult ->
return apiCall.fold(
{ statusResult ->
db.withTransaction {
if (loadType == LoadType.REFRESH) {
db.statusDao().clearAll(accountId)
@ -44,11 +45,11 @@ class TimelineRemoteMediator(
db.statusDao().insertOrReplace(statusResult.map { it.toEntity(accountId) })
}
MediatorResult.Success(endOfPaginationReached = statusResult.isEmpty())
}, {
},
{
MediatorResult.Error(it)
})
}
)
}
override suspend fun initialize() = InitializeAction.SKIP_INITIAL_REFRESH

View File

@ -1,13 +1,16 @@
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
@ -41,5 +44,4 @@ class TimelineViewModel @Inject constructor(
// repository.onFavorite(status, accountManager.activeAccount()?.id!!)
}
}
}

View File

@ -6,7 +6,8 @@ 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,
class Error<T> (
override val data: T? = null,
val errorMessage: String? = null,
var consumed: Boolean = false,
val cause: Throwable? = null

View File

@ -10,5 +10,4 @@ object Config {
const val oAuthScopes = "read write follow"
val domainExceptions = arrayOf("gab.com", "gab.ai", "gabfed.com")
}

View File

@ -38,5 +38,4 @@ abstract class ActivityModule {
@ContributesAndroidInjector
abstract fun contributesComposeActivity(): ComposeActivity
}

View File

@ -8,14 +8,16 @@ import dagger.android.AndroidInjector
import javax.inject.Singleton
@Singleton
@Component(modules = [
@Component(
modules = [
AppModule::class,
NetworkModule::class,
AndroidInjectionModule::class,
ActivityModule::class,
ViewModelModule::class,
ServiceModule::class
])
]
)
interface AppComponent : AndroidInjector<PixelcatApplication> {
@Component.Builder

View File

@ -39,8 +39,4 @@ class AppModule {
fun providesAccountManager(db: AppDatabase): AccountManager {
return AccountManager(db)
}
}

View File

@ -21,5 +21,4 @@ abstract class FragmentModule {
@ContributesAndroidInjector
abstract fun profileFragment(): ProfileFragment
}

View File

@ -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
@ -35,9 +35,11 @@ class NetworkModule {
.writeTimeout(30, TimeUnit.SECONDS)
if (BuildConfig.DEBUG) {
okHttpClientBuilder.addInterceptor(HttpLoggingInterceptor().apply {
okHttpClientBuilder.addInterceptor(
HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.HEADERS
})
}
)
}
return okHttpClientBuilder.build()
@ -61,7 +63,6 @@ class NetworkModule {
.addCallAdapterFactory(NetworkResponseAdapterFactory())
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()
}
@Provides

View File

@ -9,6 +9,4 @@ abstract class ServiceModule {
@ContributesAndroidInjector
abstract fun contributesSendStatusService(): SendStatusService
}

View File

@ -47,7 +47,6 @@ abstract class ViewModelModule {
@ViewModelKey(MainViewModel::class)
internal abstract fun mainViewModel(viewModel: MainViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(TimelineViewModel::class)

View File

@ -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>
}

View File

@ -23,7 +23,6 @@ class AccountManager(db: AppDatabase) {
private var accounts: MutableList<AccountEntity> = mutableListOf()
private val accountDao: AccountDao = db.accountDao()
suspend fun activeAccount(): AccountEntity? {
if (activeAccount == null) {
accounts = accountDao.loadAll().toMutableList()
@ -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
}
}
/**
@ -129,7 +123,6 @@ class AccountManager(db: AppDatabase) {
} else {
accounts.add(it)
}
}
}
@ -187,5 +180,4 @@ class AccountManager(db: AppDatabase) {
acc.id == accountId
}
}
}

View File

@ -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
}

View File

@ -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 {
@ -53,5 +51,4 @@ class Converters {
fun longToDate(date: Long): Date {
return Date(date)
}
}

View File

@ -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)
}

View File

@ -2,12 +2,22 @@
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)])
@Entity(
indices = [
Index(
value = ["domain", "accountId"],
unique = true
)
]
)
// @TypeConverters(Converters::class)
data class AccountEntity(@field:PrimaryKey(autoGenerate = true) var id: Long,
data class AccountEntity(
@field:PrimaryKey(autoGenerate = true) var id: Long,
val domain: String,
@Embedded(prefix = "auth_") var auth: AccountAuthData,
var isActive: Boolean,
@ -28,7 +38,8 @@ data class AccountEntity(@field:PrimaryKey(autoGenerate = true) var id: Long,
var mediaPreviewEnabled: Boolean = true,
var lastNotificationId: String = "0",
var activeNotifications: String = "[]",
var notificationsFilter: String = "[]") {
var notificationsFilter: String = "[]"
) {
val identifier: String
get() = "$domain:$accountId"

View File

@ -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,

View File

@ -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(

View File

@ -12,4 +12,3 @@ data class NewStatus(
val sensitive: Boolean,
@Json(name = "media_ids") val mediaIds: List<String>?
)

View File

@ -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(
@ -74,5 +74,4 @@ data class Status(
val name: String,
val website: String?
)
}

View File

@ -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 {
@ -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>

View File

@ -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()
}
}

View File

@ -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
}
}

View File

@ -14,5 +14,4 @@ class UserAgentInterceptor: Interceptor {
.build()
return chain.proceed(requestWithUserAgent)
}
}

View File

@ -28,13 +28,25 @@ 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())))
Response.success(
NetworkResponse.Failure(
NetworkResponseError.ApiError(
response.code()
)
)
)
)
}
}
@ -42,7 +54,11 @@ internal class NetworkResponseCall<S : Any>(
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))

View File

@ -11,7 +11,6 @@ inline fun <reified T> Fragment.arg(key: String): T {
throw IllegalStateException("Argument $key is of wrong type")
}
return value
}
inline fun Fragment.withArgs(argsBuilder: Bundle.() -> Unit): Fragment {

View File

@ -17,8 +17,8 @@ import kotlin.reflect.KProperty
*/
inline fun <T : ViewBinding> AppCompatActivity.viewBinding(
crossinline bindingInflater: (LayoutInflater) -> T) =
lazy(LazyThreadSafetyMode.NONE) {
crossinline bindingInflater: (LayoutInflater) -> T
) = lazy(LazyThreadSafetyMode.NONE) {
bindingInflater(layoutInflater)
}
@ -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
}
})
})
}
)
}
})
}

View File

@ -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) {