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

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

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

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

View File

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

View File

@ -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,22 +73,26 @@ 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 {
val visibilityString = when(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
}
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

@ -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,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,10 +149,12 @@ class SendStatusService : DaggerService(), CoroutineScope {
account.domain,
statusToSend.idempotencyKey,
newStatus
).fold<Any?>({
).fold<Any?>(
{
statusesToSend.remove(id)
}, {
when(it) {
},
{
when (it) {
is NetworkResponseError.ApiError -> {
// the server refused to accept the status, save toot & show error message
// TODO saveToDrafts
@ -158,7 +162,7 @@ class SendStatusService : DaggerService(), CoroutineScope {
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))
// .setContentText(getString(R.string.send_toot_notification_saved_content))
.setColor(getColorForAttr(android.R.attr.colorPrimary))
notificationManager.cancel(id)
@ -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

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

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

View File

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

View File

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

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

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

View File

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

View File

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

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

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

View File

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

View File

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

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

View File

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

View File

@ -19,7 +19,7 @@ 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>>()
@ -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,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({
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!!
}
}

View File

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

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

View File

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

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,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")
}
}

View File

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

View File

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

@ -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,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
@ -16,7 +19,7 @@ class TimelineViewModel @Inject constructor(
private val accountManager: AccountManager,
private val db: AppDatabase,
fediverseApi: FediverseApi
): ViewModel() {
) : ViewModel() {
private val accountId = MutableLiveData<Long>()
@ -41,5 +44,4 @@ class TimelineViewModel @Inject constructor(
// repository.onFavorite(status, accountManager.activeAccount()?.id!!)
}
}
}

View File

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

View File

@ -8,7 +8,7 @@ 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")

View File

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

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

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

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
@ -34,10 +34,12 @@ class NetworkModule {
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
if(BuildConfig.DEBUG) {
okHttpClientBuilder.addInterceptor(HttpLoggingInterceptor().apply {
if (BuildConfig.DEBUG) {
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)
@ -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
}

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

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

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 {
@ -34,7 +32,7 @@ class Converters {
@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)
}
}

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)])
//@TypeConverters(Converters::class)
data class AccountEntity(@field:PrimaryKey(autoGenerate = true) var id: Long,
@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,
@ -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(
@ -27,7 +27,7 @@ data class Account(
val source: AccountSource?,
val bot: Boolean,
// val emojis: List<Emoji>, // nullable for backward compatibility
val fields: List<Field>?, //nullable for backward compatibility
val fields: List<Field>?, // nullable for backward compatibility
val moved: Account?
) {
@ -47,22 +47,22 @@ data class AccountSource(
val sensitive: Boolean,
val note: String,
val fields: List<StringField>?
): Parcelable
) : Parcelable
@JsonClass(generateAdapter = true)
@Parcelize
data class Field (
data class Field(
val name: String,
// val value: @WriteWith<SpannedParceler>() Spanned,
@Json(name = "verified_at") val verifiedAt: Date?
): Parcelable
) : Parcelable
@JsonClass(generateAdapter = true)
@Parcelize
data class StringField (
data class StringField(
val name: String,
val value: String
): Parcelable
) : Parcelable
object SpannedParceler : Parceler<Spanned> {
override fun create(parcel: Parcel): Spanned = HtmlCompat.fromHtml(parcel.readString() ?: "", HtmlCompat.FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH)

View File

@ -48,7 +48,7 @@ data class Attachment(
*/
@JsonClass(generateAdapter = true)
@Parcelize
data class MetaData (
data class MetaData(
val focus: Focus?,
val duration: Float?
) : Parcelable
@ -61,7 +61,7 @@ data class Attachment(
*/
@JsonClass(generateAdapter = true)
@Parcelize
data class Focus (
data class Focus(
val x: Float,
val y: Float
) : Parcelable

View File

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

View File

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

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

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

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

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

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

View File

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

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

@ -7,11 +7,10 @@ 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 {

View File

@ -17,10 +17,10 @@ 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)
}
}
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
}
})
})
}
)
}
})
}

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