style: Require trailing comma, and break lines
Requiring trailing commas on multi-line lists of items (declarations and call sites) reduces future repository churn when those lines are changed, but introduces additional churn now. Bite the bullet and make the change, as well as adjusting lines that were too long / indented incorrectly. The changes were performed automatically, using the `ktlintFormat` task. Based on https://github.com/tuskyapp/Tusky/pull/3968 by https://github.com/tinsukE
This commit is contained in:
parent
cf26af25d8
commit
a441576bf6
|
@ -8,10 +8,13 @@ insert_final_newline = true
|
|||
trim_trailing_whitespace = true
|
||||
|
||||
# Disable wildcard imports
|
||||
[*.{java, kt}]
|
||||
[*.{java,kt}]
|
||||
ij_kotlin_name_count_to_use_star_import = 999
|
||||
ij_kotlin_name_count_to_use_star_import_for_members = 999
|
||||
ij_java_class_count_to_use_import_on_demand = 999
|
||||
# Require trailing comma
|
||||
ij_kotlin_allow_trailing_comma = true
|
||||
ij_kotlin_allow_trailing_comma_on_call_site = true
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -20,7 +20,7 @@ class MigrationsTest {
|
|||
var helper: MigrationTestHelper = MigrationTestHelper(
|
||||
InstrumentationRegistry.getInstrumentation(),
|
||||
AppDatabase::class.java.canonicalName,
|
||||
FrameworkSQLiteOpenHelperFactory()
|
||||
FrameworkSQLiteOpenHelperFactory(),
|
||||
)
|
||||
|
||||
@Test
|
||||
|
@ -37,7 +37,7 @@ class MigrationsTest {
|
|||
id, domain, token, active, accountId, username, "Display Name",
|
||||
"https://picture.url", true, true, true, true, true, true, true,
|
||||
true, "1000", "[]", "[{\"shortcode\": \"emoji\", \"url\": \"yes\"}]", 0, false,
|
||||
false, true
|
||||
false, true,
|
||||
)
|
||||
|
||||
db.execSQL(
|
||||
|
@ -49,7 +49,7 @@ class MigrationsTest {
|
|||
"`defaultPostPrivacy`,`defaultMediaSensitivity`,`alwaysShowSensitiveMedia`," +
|
||||
"`mediaPreviewEnabled`) " +
|
||||
"VALUES (nullif(?, 0),?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
||||
values
|
||||
values,
|
||||
)
|
||||
|
||||
db.close()
|
||||
|
|
|
@ -49,7 +49,7 @@ class AboutActivity : BottomSheetActivity(), Injectable {
|
|||
Build.MANUFACTURER,
|
||||
Build.MODEL,
|
||||
Build.VERSION.RELEASE,
|
||||
Build.VERSION.SDK_INT
|
||||
Build.VERSION.SDK_INT,
|
||||
)
|
||||
|
||||
lifecycleScope.launch {
|
||||
|
@ -59,7 +59,7 @@ class AboutActivity : BottomSheetActivity(), Injectable {
|
|||
R.string.about_account_info,
|
||||
account.username,
|
||||
account.domain,
|
||||
instanceInfo.version
|
||||
instanceInfo.version,
|
||||
)
|
||||
binding.accountInfoTitle.show()
|
||||
binding.accountInfo.show()
|
||||
|
|
|
@ -111,20 +111,22 @@ class AccountsInListFragment : DialogFragment(), Injectable {
|
|||
}
|
||||
|
||||
binding.searchView.isSubmitButtonEnabled = true
|
||||
binding.searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
viewModel.search(query.orEmpty())
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String?): Boolean {
|
||||
// Close event is not sent so we use this instead
|
||||
if (newText.isNullOrBlank()) {
|
||||
viewModel.search("")
|
||||
binding.searchView.setOnQueryTextListener(
|
||||
object : SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
viewModel.search(query.orEmpty())
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
override fun onQueryTextChange(newText: String?): Boolean {
|
||||
// Close event is not sent so we use this instead
|
||||
if (newText.isNullOrBlank()) {
|
||||
viewModel.search("")
|
||||
}
|
||||
return true
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun setupSearchView(state: State) {
|
||||
|
|
|
@ -53,15 +53,17 @@ abstract class BottomSheetActivity : BaseActivity() {
|
|||
val bottomSheetLayout: LinearLayout = findViewById(R.id.item_status_bottom_sheet)
|
||||
bottomSheet = BottomSheetBehavior.from(bottomSheetLayout)
|
||||
bottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
bottomSheet.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
|
||||
cancelActiveSearch()
|
||||
bottomSheet.addBottomSheetCallback(
|
||||
object : BottomSheetBehavior.BottomSheetCallback() {
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
|
||||
cancelActiveSearch()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {}
|
||||
})
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
open fun viewUrl(url: String, lookupFallbackBehavior: PostLookupFallbackBehavior = PostLookupFallbackBehavior.OPEN_IN_BROWSER) {
|
||||
|
@ -72,7 +74,7 @@ abstract class BottomSheetActivity : BaseActivity() {
|
|||
|
||||
mastodonApi.searchObservable(
|
||||
query = url,
|
||||
resolve = true
|
||||
resolve = true,
|
||||
).observeOn(AndroidSchedulers.mainThread())
|
||||
.autoDispose(AndroidLifecycleScopeProvider.from(this, Lifecycle.Event.ON_DESTROY))
|
||||
.subscribe(
|
||||
|
@ -101,7 +103,7 @@ abstract class BottomSheetActivity : BaseActivity() {
|
|||
onEndSearch(url)
|
||||
performUrlFallbackAction(url, lookupFallbackBehavior)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
onBeginSearch(url)
|
||||
|
@ -177,5 +179,5 @@ abstract class BottomSheetActivity : BaseActivity() {
|
|||
|
||||
enum class PostLookupFallbackBehavior {
|
||||
OPEN_IN_BROWSER,
|
||||
DISPLAY_ERROR
|
||||
DISPLAY_ERROR,
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
|
||||
private enum class PickType {
|
||||
AVATAR,
|
||||
HEADER
|
||||
HEADER,
|
||||
}
|
||||
|
||||
private val cropImage = registerForActivityResult(CropImageContract()) { result ->
|
||||
|
@ -105,7 +105,7 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
displayName = binding.displayNameEditText.text.toString(),
|
||||
note = binding.noteEditText.text.toString(),
|
||||
locked = binding.lockedCheckBox.isChecked,
|
||||
fields = accountFieldEditAdapter.getFieldData()
|
||||
fields = accountFieldEditAdapter.getFieldData(),
|
||||
)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -162,7 +162,7 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
.placeholder(R.drawable.avatar_default)
|
||||
.transform(
|
||||
FitCenter(),
|
||||
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_80dp))
|
||||
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_80dp)),
|
||||
)
|
||||
.into(binding.avatarPreview)
|
||||
}
|
||||
|
@ -198,7 +198,7 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
observeImage(viewModel.headerData, binding.headerPreview, false)
|
||||
|
||||
viewModel.saveData.observe(
|
||||
this
|
||||
this,
|
||||
) {
|
||||
when (it) {
|
||||
is Success -> {
|
||||
|
@ -238,10 +238,10 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
private fun observeImage(
|
||||
liveData: LiveData<Uri>,
|
||||
imageView: ImageView,
|
||||
roundedCorners: Boolean
|
||||
roundedCorners: Boolean,
|
||||
) {
|
||||
liveData.observe(
|
||||
this
|
||||
this,
|
||||
) { imageUri ->
|
||||
|
||||
// skipping all caches so we can always reuse the same uri
|
||||
|
@ -253,7 +253,7 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
if (roundedCorners) {
|
||||
glide.transform(
|
||||
FitCenter(),
|
||||
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_80dp))
|
||||
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_80dp)),
|
||||
).into(imageView)
|
||||
} else {
|
||||
glide.into(imageView)
|
||||
|
@ -276,7 +276,7 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
setImageSource(includeGallery = true, includeCamera = false)
|
||||
setOutputUri(viewModel.getAvatarUri())
|
||||
setOutputCompressFormat(Bitmap.CompressFormat.PNG)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
PickType.HEADER -> {
|
||||
|
@ -287,7 +287,7 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
|||
setImageSource(includeGallery = true, includeCamera = false)
|
||||
setOutputUri(viewModel.getHeaderUri())
|
||||
setOutputCompressFormat(Bitmap.CompressFormat.PNG)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
|
|||
binding.listsRecycler.adapter = adapter
|
||||
binding.listsRecycler.layoutManager = LinearLayoutManager(this)
|
||||
binding.listsRecycler.addItemDecoration(
|
||||
DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
|
||||
DividerItemDecoration(this, DividerItemDecoration.VERTICAL),
|
||||
)
|
||||
|
||||
binding.swipeRefreshLayout.setOnRefreshListener { viewModel.retryLoading() }
|
||||
|
@ -132,7 +132,7 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
|
|||
R.string.action_create_list
|
||||
} else {
|
||||
R.string.action_rename_list
|
||||
}
|
||||
},
|
||||
) { _, _ ->
|
||||
onPickedDialogName(binding.nameText.text.toString(), list?.id, binding.exclusiveCheckbox.isChecked)
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
|
|||
binding.messageView.setup(
|
||||
R.drawable.elephant_friend_empty,
|
||||
R.string.message_empty,
|
||||
null
|
||||
null,
|
||||
)
|
||||
} else {
|
||||
binding.messageView.hide()
|
||||
|
@ -202,13 +202,13 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
|
|||
Snackbar.make(
|
||||
binding.listsRecycler,
|
||||
messageId,
|
||||
Snackbar.LENGTH_SHORT
|
||||
Snackbar.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
|
||||
private fun onListSelected(listId: String, listTitle: String) {
|
||||
startActivityWithSlideInAnimation(
|
||||
StatusListActivity.newListIntent(this, listId, listTitle)
|
||||
StatusListActivity.newListIntent(this, listId, listTitle),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -242,7 +242,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
changeAccount(requestedId, intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
} else if (openDrafts) {
|
||||
|
@ -293,7 +293,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
|
||||
setupDrawer(
|
||||
savedInstanceState,
|
||||
addSearchButton = hideTopToolbar
|
||||
addSearchButton = hideTopToolbar,
|
||||
)
|
||||
|
||||
/* Fetch user info while we're doing other things. This has to be done after setting up the
|
||||
|
@ -317,7 +317,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
is ProfileEditedEvent -> onFetchUserInfoSuccess(event.newProfileData)
|
||||
is MainTabsChangedEvent -> {
|
||||
refreshMainDrawerItems(
|
||||
addSearchButton = hideTopToolbar
|
||||
addSearchButton = hideTopToolbar,
|
||||
)
|
||||
|
||||
setupTabs(false)
|
||||
|
@ -354,14 +354,14 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
|
||||
1
|
||||
1,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -404,7 +404,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
Log.d(
|
||||
TAG,
|
||||
"onResume: EmojiPack has been changed from %s to %s"
|
||||
.format(selectedEmojiPack, currentEmojiPack)
|
||||
.format(selectedEmojiPack, currentEmojiPack),
|
||||
)
|
||||
selectedEmojiPack = currentEmojiPack
|
||||
recreate()
|
||||
|
@ -478,7 +478,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
|
||||
private fun setupDrawer(
|
||||
savedInstanceState: Bundle?,
|
||||
addSearchButton: Boolean
|
||||
addSearchButton: Boolean,
|
||||
) {
|
||||
val drawerOpenClickListener = View.OnClickListener { binding.mainDrawerLayout.open() }
|
||||
|
||||
|
@ -497,7 +497,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
descriptionRes = R.string.add_account_description
|
||||
iconicsIcon = GoogleMaterial.Icon.gmd_add
|
||||
},
|
||||
0
|
||||
0,
|
||||
)
|
||||
attachToSliderView(binding.mainDrawer)
|
||||
dividerBelowHeader = false
|
||||
|
@ -511,32 +511,34 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
header.accountHeaderBackground.setBackgroundColor(MaterialColors.getColor(header, R.attr.colorBackgroundAccent))
|
||||
val animateAvatars = preferences.getBoolean("animateGifAvatars", false)
|
||||
|
||||
DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
|
||||
override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) {
|
||||
if (animateAvatars) {
|
||||
glide.load(uri)
|
||||
.placeholder(placeholder)
|
||||
.into(imageView)
|
||||
} else {
|
||||
glide.asBitmap()
|
||||
.load(uri)
|
||||
.placeholder(placeholder)
|
||||
.into(imageView)
|
||||
}
|
||||
}
|
||||
|
||||
override fun cancel(imageView: ImageView) {
|
||||
glide.clear(imageView)
|
||||
}
|
||||
|
||||
override fun placeholder(ctx: Context, tag: String?): Drawable {
|
||||
if (tag == DrawerImageLoader.Tags.PROFILE.name || tag == DrawerImageLoader.Tags.PROFILE_DRAWER_ITEM.name) {
|
||||
return ctx.getDrawable(R.drawable.avatar_default)!!
|
||||
DrawerImageLoader.init(
|
||||
object : AbstractDrawerImageLoader() {
|
||||
override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) {
|
||||
if (animateAvatars) {
|
||||
glide.load(uri)
|
||||
.placeholder(placeholder)
|
||||
.into(imageView)
|
||||
} else {
|
||||
glide.asBitmap()
|
||||
.load(uri)
|
||||
.placeholder(placeholder)
|
||||
.into(imageView)
|
||||
}
|
||||
}
|
||||
|
||||
return super.placeholder(ctx, tag)
|
||||
}
|
||||
})
|
||||
override fun cancel(imageView: ImageView) {
|
||||
glide.clear(imageView)
|
||||
}
|
||||
|
||||
override fun placeholder(ctx: Context, tag: String?): Drawable {
|
||||
if (tag == DrawerImageLoader.Tags.PROFILE.name || tag == DrawerImageLoader.Tags.PROFILE_DRAWER_ITEM.name) {
|
||||
return ctx.getDrawable(R.drawable.avatar_default)!!
|
||||
}
|
||||
|
||||
return super.placeholder(ctx, tag)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
binding.mainDrawer.apply {
|
||||
refreshMainDrawerItems(addSearchButton)
|
||||
|
@ -645,7 +647,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
nameRes = R.string.action_logout
|
||||
iconRes = R.drawable.ic_logout
|
||||
onClick = ::logout
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
if (addSearchButton) {
|
||||
|
@ -657,7 +659,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
onClick = {
|
||||
startActivityWithSlideInAnimation(SearchActivity.getIntent(context))
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -669,7 +671,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
onClick = {
|
||||
startActivityWithSlideInAnimation(TrendingActivity.getIntent(context))
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -686,7 +688,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
onClick = {
|
||||
buildDeveloperToolsDialog().show()
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -699,8 +701,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
.setItems(
|
||||
arrayOf(
|
||||
"Clear home timeline cache",
|
||||
"Remove first 40 statuses"
|
||||
)
|
||||
"Remove first 40 statuses",
|
||||
),
|
||||
) { _, which ->
|
||||
Log.d(TAG, "Developer tools: $which")
|
||||
when (which) {
|
||||
|
@ -912,7 +914,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
},
|
||||
{ throwable ->
|
||||
Log.e(TAG, "Failed to fetch user info. " + throwable.message)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -962,57 +964,61 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
.apply {
|
||||
if (showPlaceholder) placeholder(R.drawable.avatar_default)
|
||||
}
|
||||
.into(object : CustomTarget<Drawable>(navIconSize, navIconSize) {
|
||||
.into(
|
||||
object : CustomTarget<Drawable>(navIconSize, navIconSize) {
|
||||
|
||||
override fun onLoadStarted(placeholder: Drawable?) {
|
||||
placeholder?.let {
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(it, navIconSize, navIconSize)
|
||||
override fun onLoadStarted(placeholder: Drawable?) {
|
||||
placeholder?.let {
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(it, navIconSize, navIconSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Drawable,
|
||||
transition: Transition<in Drawable>?
|
||||
) {
|
||||
if (resource is Animatable) resource.start()
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(resource, navIconSize, navIconSize)
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
placeholder?.let {
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(it, navIconSize, navIconSize)
|
||||
override fun onResourceReady(
|
||||
resource: Drawable,
|
||||
transition: Transition<in Drawable>?,
|
||||
) {
|
||||
if (resource is Animatable) resource.start()
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(resource, navIconSize, navIconSize)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
placeholder?.let {
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(it, navIconSize, navIconSize)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
glide.asBitmap().load(avatarUrl).transform(RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp)))
|
||||
.apply {
|
||||
if (showPlaceholder) placeholder(R.drawable.avatar_default)
|
||||
}
|
||||
.into(object : CustomTarget<Bitmap>(navIconSize, navIconSize) {
|
||||
override fun onLoadStarted(placeholder: Drawable?) {
|
||||
placeholder?.let {
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(it, navIconSize, navIconSize)
|
||||
.into(
|
||||
object : CustomTarget<Bitmap>(navIconSize, navIconSize) {
|
||||
override fun onLoadStarted(placeholder: Drawable?) {
|
||||
placeholder?.let {
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(it, navIconSize, navIconSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Bitmap,
|
||||
transition: Transition<in Bitmap>?
|
||||
) {
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(
|
||||
BitmapDrawable(resources, resource),
|
||||
navIconSize,
|
||||
navIconSize
|
||||
)
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
placeholder?.let {
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(it, navIconSize, navIconSize)
|
||||
override fun onResourceReady(
|
||||
resource: Bitmap,
|
||||
transition: Transition<in Bitmap>?,
|
||||
) {
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(
|
||||
BitmapDrawable(resources, resource),
|
||||
navIconSize,
|
||||
navIconSize,
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
placeholder?.let {
|
||||
activeToolbar.navigationIcon = FixedSizeDrawable(it, navIconSize, navIconSize)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1026,7 +1032,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
},
|
||||
{ throwable ->
|
||||
Log.w(TAG, "Failed to fetch announcements.", throwable)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1114,7 +1120,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
|||
options: ComposeActivity.ComposeOptions,
|
||||
tuskyAccountId: Long = -1,
|
||||
notificationTag: String? = null,
|
||||
notificationId: Int = -1
|
||||
notificationId: Int = -1,
|
||||
): Intent {
|
||||
return accountSwitchIntent(context, tuskyAccountId).apply {
|
||||
action = Intent.ACTION_SEND // so it can be opened via shortcuts
|
||||
|
|
|
@ -114,7 +114,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
},
|
||||
{
|
||||
Log.w(TAG, "Failed to query tag #$tag", it)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
{
|
||||
Snackbar.make(binding.root, getString(R.string.error_following_hashtag_format, tag), Snackbar.LENGTH_SHORT).show()
|
||||
Log.e(TAG, "Failed to follow #$tag", it)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
{
|
||||
Snackbar.make(binding.root, getString(R.string.error_unfollowing_hashtag_format, tag), Snackbar.LENGTH_SHORT).show()
|
||||
Log.e(TAG, "Failed to unfollow #$tag", it)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -193,12 +193,12 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
},
|
||||
{ throwable ->
|
||||
Log.e(TAG, "Error getting filters: $throwable")
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Log.e(TAG, "Error getting filters: $throwable")
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +223,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
title = "#$tag",
|
||||
context = listOf(FilterV1.HOME),
|
||||
filterAction = Filter.Action.WARN.action,
|
||||
expiresInSeconds = null
|
||||
expiresInSeconds = null,
|
||||
).fold(
|
||||
{ filter ->
|
||||
if (mastodonApi.addFilterKeyword(filterId = filter.id, keyword = tag, wholeWord = true).isSuccess) {
|
||||
|
@ -242,7 +242,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
listOf(FilterV1.HOME),
|
||||
irreversible = false,
|
||||
wholeWord = true,
|
||||
expiresInSeconds = null
|
||||
expiresInSeconds = null,
|
||||
).fold(
|
||||
{ filter ->
|
||||
mutedFilterV1 = filter
|
||||
|
@ -252,13 +252,13 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
{ throwable ->
|
||||
Snackbar.make(binding.root, getString(R.string.error_muting_hashtag_format, tag), Snackbar.LENGTH_SHORT).show()
|
||||
Log.e(TAG, "Failed to mute #$tag", throwable)
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Snackbar.make(binding.root, getString(R.string.error_muting_hashtag_format, tag), Snackbar.LENGTH_SHORT).show()
|
||||
Log.e(TAG, "Failed to mute #$tag", throwable)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -274,7 +274,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
// This filter exists in multiple contexts, just remove the home context
|
||||
mastodonApi.updateFilter(
|
||||
id = filter.id,
|
||||
context = filter.context.filter { it != Filter.Kind.HOME.kind }
|
||||
context = filter.context.filter { it != Filter.Kind.HOME.kind },
|
||||
)
|
||||
} else {
|
||||
mastodonApi.deleteFilter(filter.id)
|
||||
|
@ -289,7 +289,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
context = filter.context.filter { it != FilterV1.HOME },
|
||||
irreversible = null,
|
||||
wholeWord = null,
|
||||
expiresInSeconds = null
|
||||
expiresInSeconds = null,
|
||||
)
|
||||
} else {
|
||||
mastodonApi.deleteFilterV1(filter.id)
|
||||
|
@ -309,7 +309,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
{ throwable ->
|
||||
Snackbar.make(binding.root, getString(R.string.error_unmuting_hashtag_format, tag), Snackbar.LENGTH_SHORT).show()
|
||||
Log.e(TAG, "Failed to unmute #$tag", throwable)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ data class TabData(
|
|||
@DrawableRes val icon: Int,
|
||||
val fragment: (List<String>) -> Fragment,
|
||||
val arguments: List<String> = emptyList(),
|
||||
val title: (Context) -> String = { context -> context.getString(text) }
|
||||
val title: (Context) -> String = { context -> context.getString(text) },
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
|
@ -70,49 +70,49 @@ fun createTabDataFromId(id: String, arguments: List<String> = emptyList()): TabD
|
|||
id = HOME,
|
||||
text = R.string.title_home,
|
||||
icon = R.drawable.ic_home_24dp,
|
||||
fragment = { TimelineFragment.newInstance(TimelineKind.Home) }
|
||||
fragment = { TimelineFragment.newInstance(TimelineKind.Home) },
|
||||
)
|
||||
NOTIFICATIONS -> TabData(
|
||||
id = NOTIFICATIONS,
|
||||
text = R.string.title_notifications,
|
||||
icon = R.drawable.ic_notifications_24dp,
|
||||
fragment = { NotificationsFragment.newInstance() }
|
||||
fragment = { NotificationsFragment.newInstance() },
|
||||
)
|
||||
LOCAL -> TabData(
|
||||
id = LOCAL,
|
||||
text = R.string.title_public_local,
|
||||
icon = R.drawable.ic_local_24dp,
|
||||
fragment = { TimelineFragment.newInstance(TimelineKind.PublicLocal) }
|
||||
fragment = { TimelineFragment.newInstance(TimelineKind.PublicLocal) },
|
||||
)
|
||||
FEDERATED -> TabData(
|
||||
id = FEDERATED,
|
||||
text = R.string.title_public_federated,
|
||||
icon = R.drawable.ic_public_24dp,
|
||||
fragment = { TimelineFragment.newInstance(TimelineKind.PublicFederated) }
|
||||
fragment = { TimelineFragment.newInstance(TimelineKind.PublicFederated) },
|
||||
)
|
||||
DIRECT -> TabData(
|
||||
id = DIRECT,
|
||||
text = R.string.title_direct_messages,
|
||||
icon = R.drawable.ic_reblog_direct_24dp,
|
||||
fragment = { ConversationsFragment.newInstance() }
|
||||
fragment = { ConversationsFragment.newInstance() },
|
||||
)
|
||||
TRENDING_TAGS -> TabData(
|
||||
id = TRENDING_TAGS,
|
||||
text = R.string.title_public_trending_hashtags,
|
||||
icon = R.drawable.ic_trending_up_24px,
|
||||
fragment = { TrendingTagsFragment.newInstance() }
|
||||
fragment = { TrendingTagsFragment.newInstance() },
|
||||
)
|
||||
TRENDING_LINKS -> TabData(
|
||||
id = TRENDING_LINKS,
|
||||
text = R.string.title_public_trending_links,
|
||||
icon = R.drawable.ic_trending_up_24px,
|
||||
fragment = { TrendingLinksFragment.newInstance() }
|
||||
fragment = { TrendingLinksFragment.newInstance() },
|
||||
)
|
||||
TRENDING_STATUSES -> TabData(
|
||||
id = TRENDING_STATUSES,
|
||||
text = R.string.title_public_trending_statuses,
|
||||
icon = R.drawable.ic_trending_up_24px,
|
||||
fragment = { TimelineFragment.newInstance(TimelineKind.TrendingStatuses) }
|
||||
fragment = { TimelineFragment.newInstance(TimelineKind.TrendingStatuses) },
|
||||
)
|
||||
HASHTAG -> TabData(
|
||||
id = HASHTAG,
|
||||
|
@ -120,7 +120,7 @@ fun createTabDataFromId(id: String, arguments: List<String> = emptyList()): TabD
|
|||
icon = R.drawable.ic_hashtag,
|
||||
fragment = { args -> TimelineFragment.newInstance(TimelineKind.Tag(args)) },
|
||||
arguments = arguments,
|
||||
title = { context -> arguments.joinToString(separator = " ") { context.getString(R.string.title_tag, it) } }
|
||||
title = { context -> arguments.joinToString(separator = " ") { context.getString(R.string.title_tag, it) } },
|
||||
)
|
||||
LIST -> TabData(
|
||||
id = LIST,
|
||||
|
@ -128,13 +128,13 @@ fun createTabDataFromId(id: String, arguments: List<String> = emptyList()): TabD
|
|||
icon = R.drawable.ic_list,
|
||||
fragment = { args -> TimelineFragment.newInstance(TimelineKind.UserList(args.first(), args.last())) },
|
||||
arguments = arguments,
|
||||
title = { arguments.getOrNull(1).orEmpty() }
|
||||
title = { arguments.getOrNull(1).orEmpty() },
|
||||
)
|
||||
BOOKMARKS -> TabData(
|
||||
id = BOOKMARKS,
|
||||
text = R.string.title_bookmarks,
|
||||
icon = R.drawable.ic_bookmark_active_24dp,
|
||||
fragment = { TimelineFragment.newInstance(TimelineKind.Bookmarks) }
|
||||
fragment = { TimelineFragment.newInstance(TimelineKind.Bookmarks) },
|
||||
)
|
||||
else -> throw IllegalArgumentException("unknown tab type")
|
||||
}
|
||||
|
@ -145,6 +145,6 @@ fun defaultTabs(): List<TabData> {
|
|||
createTabDataFromId(HOME),
|
||||
createTabDataFromId(NOTIFICATIONS),
|
||||
createTabDataFromId(LOCAL),
|
||||
createTabDataFromId(DIRECT)
|
||||
createTabDataFromId(DIRECT),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -115,44 +115,46 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
|
|||
binding.addTabRecyclerView.adapter = addTabAdapter
|
||||
binding.addTabRecyclerView.layoutManager = LinearLayoutManager(this)
|
||||
|
||||
touchHelper = ItemTouchHelper(object : ItemTouchHelper.Callback() {
|
||||
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
|
||||
return makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.END)
|
||||
}
|
||||
|
||||
override fun isLongPressDragEnabled(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun isItemViewSwipeEnabled(): Boolean {
|
||||
return MIN_TAB_COUNT < currentTabs.size
|
||||
}
|
||||
|
||||
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
|
||||
val temp = currentTabs[viewHolder.bindingAdapterPosition]
|
||||
currentTabs[viewHolder.bindingAdapterPosition] = currentTabs[target.bindingAdapterPosition]
|
||||
currentTabs[target.bindingAdapterPosition] = temp
|
||||
|
||||
currentTabsAdapter.notifyItemMoved(viewHolder.bindingAdapterPosition, target.bindingAdapterPosition)
|
||||
saveTabs()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
onTabRemoved(viewHolder.bindingAdapterPosition)
|
||||
}
|
||||
|
||||
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
|
||||
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
|
||||
viewHolder?.itemView?.elevation = selectedItemElevation
|
||||
touchHelper = ItemTouchHelper(
|
||||
object : ItemTouchHelper.Callback() {
|
||||
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
|
||||
return makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.END)
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
|
||||
super.clearView(recyclerView, viewHolder)
|
||||
viewHolder.itemView.elevation = 0f
|
||||
}
|
||||
})
|
||||
override fun isLongPressDragEnabled(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun isItemViewSwipeEnabled(): Boolean {
|
||||
return MIN_TAB_COUNT < currentTabs.size
|
||||
}
|
||||
|
||||
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
|
||||
val temp = currentTabs[viewHolder.bindingAdapterPosition]
|
||||
currentTabs[viewHolder.bindingAdapterPosition] = currentTabs[target.bindingAdapterPosition]
|
||||
currentTabs[target.bindingAdapterPosition] = temp
|
||||
|
||||
currentTabsAdapter.notifyItemMoved(viewHolder.bindingAdapterPosition, target.bindingAdapterPosition)
|
||||
saveTabs()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
onTabRemoved(viewHolder.bindingAdapterPosition)
|
||||
}
|
||||
|
||||
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
|
||||
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
|
||||
viewHolder?.itemView?.elevation = selectedItemElevation
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
|
||||
super.clearView(recyclerView, viewHolder)
|
||||
viewHolder.itemView.elevation = 0f
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
touchHelper.attachToRecyclerView(binding.currentTabsRecyclerView)
|
||||
|
||||
|
@ -327,13 +329,13 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
|
|||
dialog.hide()
|
||||
Log.e("TabPreferenceActivity", "failed to load lists", throwable)
|
||||
Snackbar.make(binding.root, R.string.error_list_load, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getProgressBarJob(progressView: View, delayMs: Long) = this.lifecycleScope.launch(
|
||||
start = CoroutineStart.LAZY
|
||||
start = CoroutineStart.LAZY,
|
||||
) {
|
||||
try {
|
||||
delay(delayMs)
|
||||
|
|
|
@ -105,7 +105,7 @@ class TuskyApplication : Application(), HasAndroidInjector {
|
|||
this,
|
||||
androidx.work.Configuration.Builder()
|
||||
.setWorkerFactory(workerFactory)
|
||||
.build()
|
||||
.build(),
|
||||
)
|
||||
|
||||
// Prune the database every ~ 12 hours when the device is idle.
|
||||
|
@ -115,7 +115,7 @@ class TuskyApplication : Application(), HasAndroidInjector {
|
|||
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
|
||||
PruneCacheWorker.PERIODIC_WORK_TAG,
|
||||
ExistingPeriodicWorkPolicy.KEEP,
|
||||
pruneCacheWorker
|
||||
pruneCacheWorker,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -121,11 +121,13 @@ class ViewMediaActivity : BaseActivity(), HasAndroidInjector, ViewImageFragment.
|
|||
|
||||
binding.viewPager.adapter = adapter
|
||||
binding.viewPager.setCurrentItem(initialPosition, false)
|
||||
binding.viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
binding.toolbar.title = getPageTitle(position)
|
||||
}
|
||||
})
|
||||
binding.viewPager.registerOnPageChangeCallback(
|
||||
object : ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
binding.toolbar.title = getPageTitle(position)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// Setup the toolbar.
|
||||
setSupportActionBar(binding.toolbar)
|
||||
|
@ -149,12 +151,14 @@ class ViewMediaActivity : BaseActivity(), HasAndroidInjector, ViewImageFragment.
|
|||
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE
|
||||
|
||||
window.statusBarColor = Color.BLACK
|
||||
window.sharedElementEnterTransition.addListener(object : NoopTransitionListener {
|
||||
override fun onTransitionEnd(transition: Transition) {
|
||||
adapter.onTransitionEnd(binding.viewPager.currentItem)
|
||||
window.sharedElementEnterTransition.removeListener(this)
|
||||
}
|
||||
})
|
||||
window.sharedElementEnterTransition.addListener(
|
||||
object : NoopTransitionListener {
|
||||
override fun onTransitionEnd(transition: Transition) {
|
||||
adapter.onTransitionEnd(binding.viewPager.currentItem)
|
||||
window.sharedElementEnterTransition.removeListener(this)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
|
@ -192,12 +196,14 @@ class ViewMediaActivity : BaseActivity(), HasAndroidInjector, ViewImageFragment.
|
|||
}
|
||||
|
||||
binding.toolbar.animate().alpha(alpha)
|
||||
.setListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
binding.toolbar.visibility = visibility
|
||||
animation.removeListener(this)
|
||||
}
|
||||
})
|
||||
.setListener(
|
||||
object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
binding.toolbar.visibility = visibility
|
||||
animation.removeListener(this)
|
||||
}
|
||||
},
|
||||
)
|
||||
.start()
|
||||
}
|
||||
|
||||
|
@ -228,7 +234,7 @@ class ViewMediaActivity : BaseActivity(), HasAndroidInjector, ViewImageFragment.
|
|||
showErrorDialog(
|
||||
binding.toolbar,
|
||||
R.string.error_media_download_permission,
|
||||
R.string.action_retry
|
||||
R.string.action_retry,
|
||||
) { requestDownloadMedia() }
|
||||
}
|
||||
}
|
||||
|
@ -263,7 +269,8 @@ class ViewMediaActivity : BaseActivity(), HasAndroidInjector, ViewImageFragment.
|
|||
Attachment.Type.IMAGE -> shareImage(directory, attachment.url)
|
||||
Attachment.Type.AUDIO,
|
||||
Attachment.Type.VIDEO,
|
||||
Attachment.Type.GIFV -> shareMediaFile(directory, attachment.url)
|
||||
Attachment.Type.GIFV,
|
||||
-> shareMediaFile(directory, attachment.url)
|
||||
else -> Log.e(TAG, "Unknown media format for sharing.")
|
||||
}
|
||||
}
|
||||
|
@ -321,7 +328,7 @@ class ViewMediaActivity : BaseActivity(), HasAndroidInjector, ViewImageFragment.
|
|||
invalidateOptionsMenu()
|
||||
binding.progressBarShare.visibility = View.GONE
|
||||
Log.e(TAG, "Failed to download image", error)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ import com.keylesspalace.tusky.util.loadAvatar
|
|||
import com.keylesspalace.tusky.util.visible
|
||||
|
||||
class AccountViewHolder(
|
||||
private val binding: ItemAccountBinding
|
||||
private val binding: ItemAccountBinding,
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
private lateinit var accountId: String
|
||||
|
||||
|
@ -36,19 +36,19 @@ class AccountViewHolder(
|
|||
account: TimelineAccount,
|
||||
animateAvatar: Boolean,
|
||||
animateEmojis: Boolean,
|
||||
showBotOverlay: Boolean
|
||||
showBotOverlay: Boolean,
|
||||
) {
|
||||
accountId = account.id
|
||||
|
||||
binding.accountUsername.text = binding.accountUsername.context.getString(
|
||||
R.string.post_username_format,
|
||||
account.username
|
||||
account.username,
|
||||
)
|
||||
|
||||
val emojifiedName = account.name.emojify(
|
||||
account.emojis,
|
||||
binding.accountDisplayName,
|
||||
animateEmojis
|
||||
animateEmojis,
|
||||
)
|
||||
binding.accountDisplayName.text = emojifiedName
|
||||
|
||||
|
@ -66,7 +66,7 @@ class AccountViewHolder(
|
|||
fun setupLinkListener(listener: LinkListener) {
|
||||
itemView.setOnClickListener {
|
||||
listener.onViewAccount(
|
||||
accountId
|
||||
accountId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import java.util.Locale
|
|||
class EmojiAdapter(
|
||||
emojiList: List<Emoji>,
|
||||
private val onEmojiSelectedListener: OnEmojiSelectedListener,
|
||||
private val animate: Boolean
|
||||
private val animate: Boolean,
|
||||
) : RecyclerView.Adapter<BindingHolder<ItemEmojiButtonBinding>>() {
|
||||
|
||||
private val emojiList: List<Emoji> = emojiList.filter { emoji -> emoji.visibleInPicker == null || emoji.visibleInPicker }
|
||||
|
|
|
@ -43,13 +43,13 @@ class FollowRequestViewHolder(
|
|||
private val binding: ItemFollowRequestBinding,
|
||||
private val accountActionListener: AccountActionListener,
|
||||
private val linkListener: LinkListener,
|
||||
private val showHeader: Boolean
|
||||
private val showHeader: Boolean,
|
||||
) : NotificationsPagingAdapter.ViewHolder, RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
override fun bind(
|
||||
viewData: NotificationViewData,
|
||||
payloads: List<*>?,
|
||||
statusDisplayOptions: StatusDisplayOptions
|
||||
statusDisplayOptions: StatusDisplayOptions,
|
||||
) {
|
||||
// Skip updates with payloads. That indicates a timestamp update, and
|
||||
// this view does not have timestamps.
|
||||
|
@ -59,7 +59,7 @@ class FollowRequestViewHolder(
|
|||
viewData.account,
|
||||
statusDisplayOptions.animateAvatars,
|
||||
statusDisplayOptions.animateEmojis,
|
||||
statusDisplayOptions.showBotOverlay
|
||||
statusDisplayOptions.showBotOverlay,
|
||||
)
|
||||
|
||||
setupActionListener(accountActionListener, viewData.account.id)
|
||||
|
@ -69,26 +69,26 @@ class FollowRequestViewHolder(
|
|||
account: TimelineAccount,
|
||||
animateAvatar: Boolean,
|
||||
animateEmojis: Boolean,
|
||||
showBotOverlay: Boolean
|
||||
showBotOverlay: Boolean,
|
||||
) {
|
||||
val wrappedName = account.name.unicodeWrap()
|
||||
val emojifiedName: CharSequence = wrappedName.emojify(
|
||||
account.emojis,
|
||||
itemView,
|
||||
animateEmojis
|
||||
animateEmojis,
|
||||
)
|
||||
binding.displayNameTextView.text = emojifiedName
|
||||
if (showHeader) {
|
||||
val wholeMessage: String = itemView.context.getString(
|
||||
R.string.notification_follow_request_format,
|
||||
wrappedName
|
||||
wrappedName,
|
||||
)
|
||||
binding.notificationTextView.text = SpannableStringBuilder(wholeMessage).apply {
|
||||
setSpan(
|
||||
StyleSpan(Typeface.BOLD),
|
||||
0,
|
||||
wrappedName.length,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
|
||||
)
|
||||
}.emojify(account.emojis, itemView, animateEmojis)
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ class PollAdapter : RecyclerView.Adapter<BindingHolder<ItemPollBinding>>() {
|
|||
mode: Int,
|
||||
resultClickListener: View.OnClickListener?,
|
||||
animateEmojis: Boolean,
|
||||
enabled: Boolean = true
|
||||
enabled: Boolean = true,
|
||||
) {
|
||||
this.pollOptions = options
|
||||
this.voteCount = voteCount
|
||||
|
|
|
@ -37,13 +37,13 @@ import java.util.Date
|
|||
|
||||
class ReportNotificationViewHolder(
|
||||
private val binding: ItemReportNotificationBinding,
|
||||
private val notificationActionListener: NotificationActionListener
|
||||
private val notificationActionListener: NotificationActionListener,
|
||||
) : NotificationsPagingAdapter.ViewHolder, RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
override fun bind(
|
||||
viewData: NotificationViewData,
|
||||
payloads: List<*>?,
|
||||
statusDisplayOptions: StatusDisplayOptions
|
||||
statusDisplayOptions: StatusDisplayOptions,
|
||||
) {
|
||||
// Skip updates with payloads. That indicates a timestamp update, and
|
||||
// this view does not have timestamps.
|
||||
|
@ -53,13 +53,13 @@ class ReportNotificationViewHolder(
|
|||
viewData.account,
|
||||
viewData.report!!,
|
||||
statusDisplayOptions.animateAvatars,
|
||||
statusDisplayOptions.animateEmojis
|
||||
statusDisplayOptions.animateEmojis,
|
||||
)
|
||||
setupActionListener(
|
||||
notificationActionListener,
|
||||
viewData.report.targetAccount.id,
|
||||
viewData.account.id,
|
||||
viewData.report.id
|
||||
viewData.report.id,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -67,17 +67,17 @@ class ReportNotificationViewHolder(
|
|||
reporter: TimelineAccount,
|
||||
report: Report,
|
||||
animateAvatar: Boolean,
|
||||
animateEmojis: Boolean
|
||||
animateEmojis: Boolean,
|
||||
) {
|
||||
val reporterName = reporter.name.unicodeWrap().emojify(
|
||||
reporter.emojis,
|
||||
binding.root,
|
||||
animateEmojis
|
||||
animateEmojis,
|
||||
)
|
||||
val reporteeName = report.targetAccount.name.unicodeWrap().emojify(
|
||||
report.targetAccount.emojis,
|
||||
itemView,
|
||||
animateEmojis
|
||||
animateEmojis,
|
||||
)
|
||||
val icon = ContextCompat.getDrawable(binding.root.context, R.drawable.ic_flag_24dp)
|
||||
|
||||
|
@ -85,12 +85,12 @@ class ReportNotificationViewHolder(
|
|||
binding.notificationTopText.text = itemView.context.getString(
|
||||
R.string.notification_header_report_format,
|
||||
reporterName,
|
||||
reporteeName
|
||||
reporteeName,
|
||||
)
|
||||
binding.notificationSummary.text = itemView.context.getString(
|
||||
R.string.notification_summary_report_format,
|
||||
getRelativeTimeSpanString(itemView.context, report.createdAt.time, Date().time),
|
||||
report.status_ids?.size ?: 0
|
||||
report.status_ids?.size ?: 0,
|
||||
)
|
||||
binding.notificationCategory.text = getTranslatedCategory(itemView.context, report.category)
|
||||
|
||||
|
@ -102,13 +102,13 @@ class ReportNotificationViewHolder(
|
|||
report.targetAccount.avatar,
|
||||
binding.notificationReporteeAvatar,
|
||||
itemView.context.resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp),
|
||||
animateAvatar
|
||||
animateAvatar,
|
||||
)
|
||||
loadAvatar(
|
||||
reporter.avatar,
|
||||
binding.notificationReporterAvatar,
|
||||
itemView.context.resources.getDimensionPixelSize(R.dimen.avatar_radius_24dp),
|
||||
animateAvatar
|
||||
animateAvatar,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ class ReportNotificationViewHolder(
|
|||
listener: NotificationActionListener,
|
||||
reporteeId: String,
|
||||
reporterId: String,
|
||||
reportId: String
|
||||
reportId: String,
|
||||
) {
|
||||
binding.notificationReporteeAvatar.setOnClickListener {
|
||||
val position = bindingAdapterPosition
|
||||
|
|
|
@ -46,7 +46,7 @@ class TabAdapter(
|
|||
private var data: List<TabData>,
|
||||
private val small: Boolean,
|
||||
private val listener: ItemInteractionListener,
|
||||
private var removeButtonEnabled: Boolean = false
|
||||
private var removeButtonEnabled: Boolean = false,
|
||||
) : RecyclerView.Adapter<BindingHolder<ViewBinding>>() {
|
||||
|
||||
fun updateData(newData: List<TabData>) {
|
||||
|
@ -103,7 +103,7 @@ class TabAdapter(
|
|||
setDrawableTint(
|
||||
holder.itemView.context,
|
||||
binding.removeButton.drawable,
|
||||
(if (removeButtonEnabled) android.R.attr.textColorTertiary else R.attr.textColorDisabled)
|
||||
(if (removeButtonEnabled) android.R.attr.textColorTertiary else R.attr.textColorDisabled),
|
||||
)
|
||||
|
||||
if (tab.id == HASHTAG) {
|
||||
|
|
|
@ -14,7 +14,7 @@ class CacheUpdater @Inject constructor(
|
|||
eventHub: EventHub,
|
||||
accountManager: AccountManager,
|
||||
appDatabase: AppDatabase,
|
||||
gson: Gson
|
||||
gson: Gson,
|
||||
) {
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
|
|
@ -155,7 +155,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
|||
private enum class FollowState {
|
||||
NOT_FOLLOWING,
|
||||
FOLLOWING,
|
||||
REQUESTED
|
||||
REQUESTED,
|
||||
}
|
||||
|
||||
private lateinit var adapter: AccountPagerAdapter
|
||||
|
@ -273,17 +273,19 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
|||
val enableSwipeForTabs = preferences.getBoolean(PrefKeys.ENABLE_SWIPE_FOR_TABS, true)
|
||||
binding.accountFragmentViewPager.isUserInputEnabled = enableSwipeForTabs
|
||||
|
||||
binding.accountTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
|
||||
override fun onTabReselected(tab: TabLayout.Tab?) {
|
||||
tab?.position?.let { position ->
|
||||
(adapter.getFragment(position) as? ReselectableFragment)?.onReselect()
|
||||
binding.accountTabLayout.addOnTabSelectedListener(
|
||||
object : TabLayout.OnTabSelectedListener {
|
||||
override fun onTabReselected(tab: TabLayout.Tab?) {
|
||||
tab?.position?.let { position ->
|
||||
(adapter.getFragment(position) as? ReselectableFragment)?.onReselect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTabUnselected(tab: TabLayout.Tab?) {}
|
||||
override fun onTabUnselected(tab: TabLayout.Tab?) {}
|
||||
|
||||
override fun onTabSelected(tab: TabLayout.Tab?) {}
|
||||
})
|
||||
override fun onTabSelected(tab: TabLayout.Tab?) {}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleWindowInsets() {
|
||||
|
@ -323,14 +325,14 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
|||
binding.accountToolbar.navigationIcon = LayerDrawable(
|
||||
arrayOf(
|
||||
backgroundCircle,
|
||||
binding.accountToolbar.navigationIcon
|
||||
)
|
||||
binding.accountToolbar.navigationIcon,
|
||||
),
|
||||
)
|
||||
binding.accountToolbar.overflowIcon = LayerDrawable(
|
||||
arrayOf(
|
||||
backgroundCircle,
|
||||
binding.accountToolbar.overflowIcon
|
||||
)
|
||||
binding.accountToolbar.overflowIcon,
|
||||
),
|
||||
)
|
||||
|
||||
binding.accountHeaderInfoContainer.background = MaterialShapeDrawable.createWithElevationOverlay(this, appBarElevation)
|
||||
|
@ -345,47 +347,49 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
|||
binding.accountAvatarImageView.background = avatarBackground
|
||||
|
||||
// Add a listener to change the toolbar icon color when it enters/exits its collapsed state.
|
||||
binding.accountAppBarLayout.addOnOffsetChangedListener(object : AppBarLayout.OnOffsetChangedListener {
|
||||
binding.accountAppBarLayout.addOnOffsetChangedListener(
|
||||
object : AppBarLayout.OnOffsetChangedListener {
|
||||
|
||||
override fun onOffsetChanged(appBarLayout: AppBarLayout, verticalOffset: Int) {
|
||||
if (verticalOffset == oldOffset) {
|
||||
return
|
||||
}
|
||||
oldOffset = verticalOffset
|
||||
|
||||
if (titleVisibleHeight + verticalOffset < 0) {
|
||||
supportActionBar?.setDisplayShowTitleEnabled(true)
|
||||
} else {
|
||||
supportActionBar?.setDisplayShowTitleEnabled(false)
|
||||
}
|
||||
|
||||
if (hideFab && !blocking) {
|
||||
if (verticalOffset > oldOffset) {
|
||||
binding.accountFloatingActionButton.show()
|
||||
override fun onOffsetChanged(appBarLayout: AppBarLayout, verticalOffset: Int) {
|
||||
if (verticalOffset == oldOffset) {
|
||||
return
|
||||
}
|
||||
if (verticalOffset < oldOffset) {
|
||||
binding.accountFloatingActionButton.hide()
|
||||
oldOffset = verticalOffset
|
||||
|
||||
if (titleVisibleHeight + verticalOffset < 0) {
|
||||
supportActionBar?.setDisplayShowTitleEnabled(true)
|
||||
} else {
|
||||
supportActionBar?.setDisplayShowTitleEnabled(false)
|
||||
}
|
||||
|
||||
if (hideFab && !blocking) {
|
||||
if (verticalOffset > oldOffset) {
|
||||
binding.accountFloatingActionButton.show()
|
||||
}
|
||||
if (verticalOffset < oldOffset) {
|
||||
binding.accountFloatingActionButton.hide()
|
||||
}
|
||||
}
|
||||
|
||||
val scaledAvatarSize = (avatarSize + verticalOffset) / avatarSize
|
||||
|
||||
binding.accountAvatarImageView.scaleX = scaledAvatarSize
|
||||
binding.accountAvatarImageView.scaleY = scaledAvatarSize
|
||||
|
||||
binding.accountAvatarImageView.visible(scaledAvatarSize > 0)
|
||||
|
||||
val transparencyPercent = (abs(verticalOffset) / titleVisibleHeight.toFloat()).coerceAtMost(1f)
|
||||
|
||||
window.statusBarColor = argbEvaluator.evaluate(transparencyPercent, statusBarColorTransparent, statusBarColorOpaque) as Int
|
||||
|
||||
val evaluatedToolbarColor = argbEvaluator.evaluate(transparencyPercent, Color.TRANSPARENT, toolbarColor) as Int
|
||||
|
||||
toolbarBackground.fillColor = ColorStateList.valueOf(evaluatedToolbarColor)
|
||||
|
||||
binding.swipeToRefreshLayout.isEnabled = verticalOffset == 0
|
||||
}
|
||||
|
||||
val scaledAvatarSize = (avatarSize + verticalOffset) / avatarSize
|
||||
|
||||
binding.accountAvatarImageView.scaleX = scaledAvatarSize
|
||||
binding.accountAvatarImageView.scaleY = scaledAvatarSize
|
||||
|
||||
binding.accountAvatarImageView.visible(scaledAvatarSize > 0)
|
||||
|
||||
val transparencyPercent = (abs(verticalOffset) / titleVisibleHeight.toFloat()).coerceAtMost(1f)
|
||||
|
||||
window.statusBarColor = argbEvaluator.evaluate(transparencyPercent, statusBarColorTransparent, statusBarColorOpaque) as Int
|
||||
|
||||
val evaluatedToolbarColor = argbEvaluator.evaluate(transparencyPercent, Color.TRANSPARENT, toolbarColor) as Int
|
||||
|
||||
toolbarBackground.fillColor = ColorStateList.valueOf(evaluatedToolbarColor)
|
||||
|
||||
binding.swipeToRefreshLayout.isEnabled = verticalOffset == 0
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun makeNotificationBarTransparent() {
|
||||
|
@ -439,7 +443,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
|||
private fun setupRefreshLayout() {
|
||||
binding.swipeToRefreshLayout.setOnRefreshListener { onRefresh() }
|
||||
viewModel.isRefreshing.observe(
|
||||
this
|
||||
this,
|
||||
) { isRefreshing ->
|
||||
binding.swipeToRefreshLayout.isRefreshing = isRefreshing == true
|
||||
}
|
||||
|
@ -496,7 +500,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
|||
try {
|
||||
binding.accountDateJoined.text = resources.getString(
|
||||
R.string.account_date_joined,
|
||||
SimpleDateFormat("MMMM, yyyy", Locale.getDefault()).format(account.createdAt)
|
||||
SimpleDateFormat("MMMM, yyyy", Locale.getDefault()).format(account.createdAt),
|
||||
)
|
||||
binding.accountDateJoined.visibility = View.VISIBLE
|
||||
} catch (e: ParseException) {
|
||||
|
@ -515,7 +519,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
|||
account.avatar,
|
||||
binding.accountAvatarImageView,
|
||||
resources.getDimensionPixelSize(R.dimen.avatar_radius_94dp),
|
||||
animateAvatar
|
||||
animateAvatar,
|
||||
)
|
||||
|
||||
Glide.with(this)
|
||||
|
@ -537,7 +541,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
|||
view.transitionName = uri
|
||||
startActivity(
|
||||
ViewMediaActivity.newSingleImageIntent(view.context, uri),
|
||||
ActivityOptionsCompat.makeSceneTransitionAnimation(this, view, uri).toBundle()
|
||||
ActivityOptionsCompat.makeSceneTransitionAnimation(this, view, uri).toBundle(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -863,7 +867,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
|||
loadedAccount?.let {
|
||||
showMuteAccountDialog(
|
||||
this,
|
||||
it.username
|
||||
it.username,
|
||||
) { notifications, duration ->
|
||||
viewModel.muteAccount(notifications, duration)
|
||||
}
|
||||
|
@ -880,7 +884,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
|||
} else {
|
||||
ComposeActivity.ComposeOptions(
|
||||
mentionedUsernames = setOf(it.username),
|
||||
kind = ComposeActivity.ComposeKind.NEW
|
||||
kind = ComposeActivity.ComposeKind.NEW,
|
||||
)
|
||||
}
|
||||
val intent = ComposeActivity.startIntent(this, options)
|
||||
|
@ -921,7 +925,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
|||
override fun onAccountSelected(account: AccountEntity) {
|
||||
openAsAccount(loadedAccount.url, account)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import com.keylesspalace.tusky.util.setClickableText
|
|||
|
||||
class AccountFieldAdapter(
|
||||
private val linkListener: LinkListener,
|
||||
private val animateEmojis: Boolean
|
||||
private val animateEmojis: Boolean,
|
||||
) : RecyclerView.Adapter<BindingHolder<ItemAccountFieldBinding>>() {
|
||||
|
||||
var emojis: List<Emoji> = emptyList()
|
||||
|
|
|
@ -25,7 +25,7 @@ import com.keylesspalace.tusky.util.CustomFragmentStateAdapter
|
|||
|
||||
class AccountPagerAdapter(
|
||||
activity: FragmentActivity,
|
||||
private val accountId: String
|
||||
private val accountId: String,
|
||||
) : CustomFragmentStateAdapter(activity) {
|
||||
|
||||
override fun getItemCount() = TAB_COUNT
|
||||
|
|
|
@ -28,7 +28,7 @@ import javax.inject.Inject
|
|||
class AccountViewModel @Inject constructor(
|
||||
private val mastodonApi: MastodonApi,
|
||||
private val eventHub: EventHub,
|
||||
accountManager: AccountManager
|
||||
accountManager: AccountManager,
|
||||
) : ViewModel() {
|
||||
|
||||
val accountData = MutableLiveData<Resource<Account>>()
|
||||
|
@ -79,7 +79,7 @@ class AccountViewModel @Inject constructor(
|
|||
accountData.postValue(Error(cause = t))
|
||||
isDataLoading = false
|
||||
isRefreshing.postValue(false)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ class AccountViewModel @Inject constructor(
|
|||
{ t ->
|
||||
Log.w(TAG, "failed obtaining relationships", t)
|
||||
relationshipData.postValue(Error(cause = t))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ class AccountViewModel @Inject constructor(
|
|||
}
|
||||
}, { e ->
|
||||
Log.e(TAG, "Error muting $instance", e)
|
||||
})
|
||||
},)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,7 +163,7 @@ class AccountViewModel @Inject constructor(
|
|||
}
|
||||
}, { e ->
|
||||
Log.e(TAG, "Error unmuting $instance", e)
|
||||
})
|
||||
},)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,7 +181,7 @@ class AccountViewModel @Inject constructor(
|
|||
private fun changeRelationship(
|
||||
relationshipAction: RelationShipAction,
|
||||
parameter: Boolean? = null,
|
||||
duration: Int? = null
|
||||
duration: Int? = null,
|
||||
) = viewModelScope.launch {
|
||||
val relation = relationshipData.value?.data
|
||||
val account = accountData.value?.data
|
||||
|
@ -224,7 +224,7 @@ class AccountViewModel @Inject constructor(
|
|||
val relationshipCall = when (relationshipAction) {
|
||||
RelationShipAction.FOLLOW -> mastodonApi.followAccount(
|
||||
accountId,
|
||||
showReblogs = parameter ?: true
|
||||
showReblogs = parameter ?: true,
|
||||
)
|
||||
RelationShipAction.UNFOLLOW -> mastodonApi.unfollowAccount(accountId)
|
||||
RelationShipAction.BLOCK -> mastodonApi.blockAccount(accountId)
|
||||
|
@ -232,7 +232,7 @@ class AccountViewModel @Inject constructor(
|
|||
RelationShipAction.MUTE -> mastodonApi.muteAccount(
|
||||
accountId,
|
||||
parameter ?: true,
|
||||
duration
|
||||
duration,
|
||||
)
|
||||
RelationShipAction.UNMUTE -> mastodonApi.unmuteAccount(accountId)
|
||||
RelationShipAction.SUBSCRIBE -> {
|
||||
|
@ -265,7 +265,7 @@ class AccountViewModel @Inject constructor(
|
|||
{ t ->
|
||||
Log.w(TAG, "failed loading relationship", t)
|
||||
relationshipData.postValue(Error(relation, cause = t))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -283,7 +283,7 @@ class AccountViewModel @Inject constructor(
|
|||
},
|
||||
{ t ->
|
||||
Log.w(TAG, "Error updating note", t)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ class ListsForAccountFragment : DialogFragment(), Injectable {
|
|||
dialog?.apply {
|
||||
window?.setLayout(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.MATCH_PARENT
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ class ListsForAccountFragment : DialogFragment(), Injectable {
|
|||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
savedInstanceState: Bundle?,
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_lists_for_account, container, false)
|
||||
}
|
||||
|
@ -145,14 +145,14 @@ class ListsForAccountFragment : DialogFragment(), Injectable {
|
|||
private object Differ : DiffUtil.ItemCallback<AccountListState>() {
|
||||
override fun areItemsTheSame(
|
||||
oldItem: AccountListState,
|
||||
newItem: AccountListState
|
||||
newItem: AccountListState,
|
||||
): Boolean {
|
||||
return oldItem.list.id == newItem.list.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: AccountListState,
|
||||
newItem: AccountListState
|
||||
newItem: AccountListState,
|
||||
): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ class ListsForAccountFragment : DialogFragment(), Injectable {
|
|||
ListAdapter<AccountListState, BindingHolder<ItemAddOrRemoveFromListBinding>>(Differ) {
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int
|
||||
viewType: Int,
|
||||
): BindingHolder<ItemAddOrRemoveFromListBinding> {
|
||||
val binding =
|
||||
ItemAddOrRemoveFromListBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
|
|
|
@ -35,23 +35,23 @@ import javax.inject.Inject
|
|||
|
||||
data class AccountListState(
|
||||
val list: MastoList,
|
||||
val includesAccount: Boolean
|
||||
val includesAccount: Boolean,
|
||||
)
|
||||
|
||||
data class ActionError(
|
||||
val error: Throwable,
|
||||
val type: Type,
|
||||
val listId: String
|
||||
val listId: String,
|
||||
) : Throwable(error) {
|
||||
enum class Type {
|
||||
ADD,
|
||||
REMOVE
|
||||
REMOVE,
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class ListsForAccountViewModel @Inject constructor(
|
||||
private val mastodonApi: MastodonApi
|
||||
private val mastodonApi: MastodonApi,
|
||||
) : ViewModel() {
|
||||
|
||||
private lateinit var accountId: String
|
||||
|
@ -75,16 +75,16 @@ class ListsForAccountViewModel @Inject constructor(
|
|||
runCatching {
|
||||
val (all, includes) = listOf(
|
||||
async { mastodonApi.getLists() },
|
||||
async { mastodonApi.getListsIncludesAccount(accountId) }
|
||||
async { mastodonApi.getListsIncludesAccount(accountId) },
|
||||
).awaitAll()
|
||||
|
||||
_states.emit(
|
||||
all.getOrThrow().map { list ->
|
||||
AccountListState(
|
||||
list = list,
|
||||
includesAccount = includes.getOrThrow().any { it.id == list.id }
|
||||
includesAccount = includes.getOrThrow().any { it.id == list.id },
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
.onFailure {
|
||||
|
@ -105,7 +105,7 @@ class ListsForAccountViewModel @Inject constructor(
|
|||
} else {
|
||||
state
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
.onFailure {
|
||||
|
@ -126,7 +126,7 @@ class ListsForAccountViewModel @Inject constructor(
|
|||
} else {
|
||||
state
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
.onFailure {
|
||||
|
|
|
@ -88,7 +88,7 @@ class AccountMediaFragment :
|
|||
adapter = AccountMediaGridAdapter(
|
||||
useBlurhash = useBlurhash,
|
||||
context = view.context,
|
||||
onAttachmentClickListener = ::onAttachmentClick
|
||||
onAttachmentClickListener = ::onAttachmentClick,
|
||||
)
|
||||
|
||||
val columnCount = view.context.resources.getInteger(R.integer.profile_media_column_count)
|
||||
|
@ -174,7 +174,8 @@ class AccountMediaFragment :
|
|||
Attachment.Type.IMAGE,
|
||||
Attachment.Type.GIFV,
|
||||
Attachment.Type.VIDEO,
|
||||
Attachment.Type.AUDIO -> {
|
||||
Attachment.Type.AUDIO,
|
||||
-> {
|
||||
val intent = ViewMediaActivity.newIntent(context, attachmentsFromSameStatus, currentIndex)
|
||||
if (activity != null) {
|
||||
val url = selected.attachment.url
|
||||
|
|
|
@ -26,7 +26,7 @@ import java.util.Random
|
|||
class AccountMediaGridAdapter(
|
||||
private val useBlurhash: Boolean,
|
||||
context: Context,
|
||||
private val onAttachmentClickListener: (AttachmentViewData, View) -> Unit
|
||||
private val onAttachmentClickListener: (AttachmentViewData, View) -> Unit,
|
||||
) : PagingDataAdapter<AttachmentViewData, BindingHolder<ItemAccountMediaBinding>>(
|
||||
object : DiffUtil.ItemCallback<AttachmentViewData>() {
|
||||
override fun areItemsTheSame(oldItem: AttachmentViewData, newItem: AttachmentViewData): Boolean {
|
||||
|
@ -36,7 +36,7 @@ class AccountMediaGridAdapter(
|
|||
override fun areContentsTheSame(oldItem: AttachmentViewData, newItem: AttachmentViewData): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
}
|
||||
},
|
||||
) {
|
||||
|
||||
private val baseItemBackgroundColor = MaterialColors.getColor(context, com.google.android.material.R.attr.colorSurface, Color.BLACK)
|
||||
|
|
|
@ -20,7 +20,7 @@ import androidx.paging.PagingState
|
|||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||
|
||||
class AccountMediaPagingSource(
|
||||
private val viewModel: AccountMediaViewModel
|
||||
private val viewModel: AccountMediaViewModel,
|
||||
) : PagingSource<String, AttachmentViewData>() {
|
||||
|
||||
override fun getRefreshKey(state: PagingState<String, AttachmentViewData>): String? = null
|
||||
|
|
|
@ -29,11 +29,11 @@ import retrofit2.HttpException
|
|||
class AccountMediaRemoteMediator(
|
||||
private val api: MastodonApi,
|
||||
private val activeAccount: AccountEntity,
|
||||
private val viewModel: AccountMediaViewModel
|
||||
private val viewModel: AccountMediaViewModel,
|
||||
) : RemoteMediator<String, AttachmentViewData>() {
|
||||
override suspend fun load(
|
||||
loadType: LoadType,
|
||||
state: PagingState<String, AttachmentViewData>
|
||||
state: PagingState<String, AttachmentViewData>,
|
||||
): MediatorResult {
|
||||
try {
|
||||
val statusResponse = when (loadType) {
|
||||
|
|
|
@ -28,7 +28,7 @@ import javax.inject.Inject
|
|||
|
||||
class AccountMediaViewModel @Inject constructor(
|
||||
private val accountManager: AccountManager,
|
||||
api: MastodonApi
|
||||
api: MastodonApi,
|
||||
) : ViewModel() {
|
||||
|
||||
lateinit var accountId: String
|
||||
|
@ -43,16 +43,16 @@ class AccountMediaViewModel @Inject constructor(
|
|||
val media = Pager(
|
||||
config = PagingConfig(
|
||||
pageSize = LOAD_AT_ONCE,
|
||||
prefetchDistance = LOAD_AT_ONCE * 2
|
||||
prefetchDistance = LOAD_AT_ONCE * 2,
|
||||
),
|
||||
pagingSourceFactory = {
|
||||
AccountMediaPagingSource(
|
||||
viewModel = this
|
||||
viewModel = this,
|
||||
).also { source ->
|
||||
currentSource = source
|
||||
}
|
||||
},
|
||||
remoteMediator = AccountMediaRemoteMediator(api, activeAccount, this)
|
||||
remoteMediator = AccountMediaRemoteMediator(api, activeAccount, this),
|
||||
).flow
|
||||
.cachedIn(viewModelScope)
|
||||
|
||||
|
|
|
@ -23,14 +23,14 @@ import androidx.recyclerview.widget.RecyclerView.ItemDecoration
|
|||
class GridSpacingItemDecoration(
|
||||
private val spanCount: Int,
|
||||
private val spacing: Int,
|
||||
private val topOffset: Int
|
||||
private val topOffset: Int,
|
||||
) : ItemDecoration() {
|
||||
|
||||
override fun getItemOffsets(
|
||||
outRect: Rect,
|
||||
view: View,
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
state: RecyclerView.State,
|
||||
) {
|
||||
val position = parent.getChildAdapterPosition(view) // item position
|
||||
if (position < topOffset) return
|
||||
|
|
|
@ -38,7 +38,7 @@ class AccountListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
MUTES,
|
||||
FOLLOW_REQUESTS,
|
||||
REBLOGGED,
|
||||
FAVOURITED
|
||||
FAVOURITED,
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
|
|
@ -115,7 +115,7 @@ class AccountListFragment :
|
|||
Type.FOLLOW_REQUESTS -> {
|
||||
val headerAdapter = FollowRequestsHeaderAdapter(
|
||||
instanceName = activeAccount.domain,
|
||||
accountLocked = activeAccount.locked
|
||||
accountLocked = activeAccount.locked,
|
||||
)
|
||||
val followRequestsAdapter = FollowRequestsAdapter(this, this, animateAvatar, animateEmojis, showBotOverlay)
|
||||
binding.recyclerView.adapter = ConcatAdapter(headerAdapter, followRequestsAdapter)
|
||||
|
@ -247,7 +247,7 @@ class AccountListFragment :
|
|||
override fun onRespondToFollowRequest(
|
||||
accept: Boolean,
|
||||
accountId: String,
|
||||
position: Int
|
||||
position: Int,
|
||||
) {
|
||||
if (accept) {
|
||||
api.authorizeFollowRequest(accountId)
|
||||
|
@ -266,7 +266,7 @@ class AccountListFragment :
|
|||
"reject"
|
||||
}
|
||||
Log.e(TAG, "Failed to $verb account id $accountId.", throwable)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -365,7 +365,7 @@ class AccountListFragment :
|
|||
binding.messageView.setup(
|
||||
R.drawable.elephant_friend_empty,
|
||||
R.string.message_empty,
|
||||
null
|
||||
null,
|
||||
)
|
||||
} else {
|
||||
binding.messageView.hide()
|
||||
|
|
|
@ -28,7 +28,7 @@ abstract class AccountAdapter<AVH : RecyclerView.ViewHolder> internal constructo
|
|||
protected val accountActionListener: AccountActionListener,
|
||||
protected val animateAvatar: Boolean,
|
||||
protected val animateEmojis: Boolean,
|
||||
protected val showBotOverlay: Boolean
|
||||
protected val showBotOverlay: Boolean,
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder?>() {
|
||||
|
||||
protected var accountList: MutableList<TimelineAccount> = mutableListOf()
|
||||
|
@ -51,7 +51,7 @@ abstract class AccountAdapter<AVH : RecyclerView.ViewHolder> internal constructo
|
|||
|
||||
final override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int
|
||||
viewType: Int,
|
||||
): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
VIEW_TYPE_ACCOUNT -> this.createAccountViewHolder(parent)
|
||||
|
@ -61,7 +61,7 @@ abstract class AccountAdapter<AVH : RecyclerView.ViewHolder> internal constructo
|
|||
}
|
||||
|
||||
private fun createFooterViewHolder(
|
||||
parent: ViewGroup
|
||||
parent: ViewGroup,
|
||||
): RecyclerView.ViewHolder {
|
||||
val binding = ItemFooterBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
return BindingHolder(binding)
|
||||
|
|
|
@ -30,12 +30,12 @@ class BlocksAdapter(
|
|||
accountActionListener: AccountActionListener,
|
||||
animateAvatar: Boolean,
|
||||
animateEmojis: Boolean,
|
||||
showBotOverlay: Boolean
|
||||
showBotOverlay: Boolean,
|
||||
) : AccountAdapter<BindingHolder<ItemBlockedUserBinding>>(
|
||||
accountActionListener = accountActionListener,
|
||||
animateAvatar = animateAvatar,
|
||||
animateEmojis = animateEmojis,
|
||||
showBotOverlay = showBotOverlay
|
||||
showBotOverlay = showBotOverlay,
|
||||
) {
|
||||
|
||||
override fun createAccountViewHolder(parent: ViewGroup): BindingHolder<ItemBlockedUserBinding> {
|
||||
|
|
|
@ -26,12 +26,12 @@ class FollowAdapter(
|
|||
accountActionListener: AccountActionListener,
|
||||
animateAvatar: Boolean,
|
||||
animateEmojis: Boolean,
|
||||
showBotOverlay: Boolean
|
||||
showBotOverlay: Boolean,
|
||||
) : AccountAdapter<AccountViewHolder>(
|
||||
accountActionListener = accountActionListener,
|
||||
animateAvatar = animateAvatar,
|
||||
animateEmojis = animateEmojis,
|
||||
showBotOverlay = showBotOverlay
|
||||
showBotOverlay = showBotOverlay,
|
||||
) {
|
||||
|
||||
override fun createAccountViewHolder(parent: ViewGroup): AccountViewHolder {
|
||||
|
@ -44,7 +44,7 @@ class FollowAdapter(
|
|||
accountList[position],
|
||||
animateAvatar,
|
||||
animateEmojis,
|
||||
showBotOverlay
|
||||
showBotOverlay,
|
||||
)
|
||||
viewHolder.setupActionListener(accountActionListener)
|
||||
}
|
||||
|
|
|
@ -28,25 +28,25 @@ class FollowRequestsAdapter(
|
|||
private val linkListener: LinkListener,
|
||||
animateAvatar: Boolean,
|
||||
animateEmojis: Boolean,
|
||||
showBotOverlay: Boolean
|
||||
showBotOverlay: Boolean,
|
||||
) : AccountAdapter<FollowRequestViewHolder>(
|
||||
accountActionListener = accountActionListener,
|
||||
animateAvatar = animateAvatar,
|
||||
animateEmojis = animateEmojis,
|
||||
showBotOverlay = showBotOverlay
|
||||
showBotOverlay = showBotOverlay,
|
||||
) {
|
||||
|
||||
override fun createAccountViewHolder(parent: ViewGroup): FollowRequestViewHolder {
|
||||
val binding = ItemFollowRequestBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
false,
|
||||
)
|
||||
return FollowRequestViewHolder(
|
||||
binding,
|
||||
accountActionListener,
|
||||
linkListener,
|
||||
showHeader = false
|
||||
showHeader = false,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ class FollowRequestsAdapter(
|
|||
account = accountList[position],
|
||||
animateAvatar = animateAvatar,
|
||||
animateEmojis = animateEmojis,
|
||||
showBotOverlay = showBotOverlay
|
||||
showBotOverlay = showBotOverlay,
|
||||
)
|
||||
viewHolder.setupActionListener(accountActionListener, accountList[position].id)
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import com.keylesspalace.tusky.util.BindingHolder
|
|||
|
||||
class FollowRequestsHeaderAdapter(
|
||||
private val instanceName: String,
|
||||
private val accountLocked: Boolean
|
||||
private val accountLocked: Boolean,
|
||||
) : RecyclerView.Adapter<BindingHolder<ItemFollowRequestsHeaderBinding>>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder<ItemFollowRequestsHeaderBinding> {
|
||||
|
|
|
@ -31,12 +31,12 @@ class MutesAdapter(
|
|||
accountActionListener: AccountActionListener,
|
||||
animateAvatar: Boolean,
|
||||
animateEmojis: Boolean,
|
||||
showBotOverlay: Boolean
|
||||
showBotOverlay: Boolean,
|
||||
) : AccountAdapter<BindingHolder<ItemMutedUserBinding>>(
|
||||
accountActionListener = accountActionListener,
|
||||
animateAvatar = animateAvatar,
|
||||
animateEmojis = animateEmojis,
|
||||
showBotOverlay = showBotOverlay
|
||||
showBotOverlay = showBotOverlay,
|
||||
) {
|
||||
|
||||
private val mutingNotificationsMap = HashMap<String, Boolean>()
|
||||
|
@ -83,7 +83,7 @@ class MutesAdapter(
|
|||
false,
|
||||
account.id,
|
||||
viewHolder.bindingAdapterPosition,
|
||||
false
|
||||
false,
|
||||
)
|
||||
}
|
||||
binding.mutedUserMuteNotifications.setOnCheckedChangeListener { _, isChecked ->
|
||||
|
@ -91,7 +91,7 @@ class MutesAdapter(
|
|||
true,
|
||||
account.id,
|
||||
viewHolder.bindingAdapterPosition,
|
||||
isChecked
|
||||
isChecked,
|
||||
)
|
||||
}
|
||||
binding.root.setOnClickListener { accountActionListener.onViewAccount(account.id) }
|
||||
|
|
|
@ -47,7 +47,7 @@ class AnnouncementAdapter(
|
|||
private var items: List<Announcement> = emptyList(),
|
||||
private val listener: AnnouncementActionListener,
|
||||
private val wellbeingEnabled: Boolean = false,
|
||||
private val animateEmojis: Boolean = false
|
||||
private val animateEmojis: Boolean = false,
|
||||
) : RecyclerView.Adapter<BindingHolder<ItemAnnouncementBinding>>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder<ItemAnnouncementBinding> {
|
||||
|
|
|
@ -37,7 +37,7 @@ import javax.inject.Inject
|
|||
class AnnouncementsViewModel @Inject constructor(
|
||||
private val instanceInfoRepo: InstanceInfoRepository,
|
||||
private val mastodonApi: MastodonApi,
|
||||
private val eventHub: EventHub
|
||||
private val eventHub: EventHub,
|
||||
) : ViewModel() {
|
||||
|
||||
private val announcementsMutable = MutableLiveData<Resource<List<Announcement>>>()
|
||||
|
@ -70,15 +70,15 @@ class AnnouncementsViewModel @Inject constructor(
|
|||
Log.d(
|
||||
TAG,
|
||||
"Failed to mark announcement as read.",
|
||||
throwable
|
||||
throwable,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
announcementsMutable.postValue(Error(cause = it))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ class AnnouncementsViewModel @Inject constructor(
|
|||
if (reaction.name == name) {
|
||||
reaction.copy(
|
||||
count = reaction.count + 1,
|
||||
me = true
|
||||
me = true,
|
||||
)
|
||||
} else {
|
||||
reaction
|
||||
|
@ -113,22 +113,22 @@ class AnnouncementsViewModel @Inject constructor(
|
|||
1,
|
||||
true,
|
||||
url,
|
||||
staticUrl
|
||||
staticUrl,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
announcement
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
},
|
||||
{
|
||||
Log.w(TAG, "Failed to add reaction to the announcement.", it)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ class AnnouncementsViewModel @Inject constructor(
|
|||
if (reaction.count > 1) {
|
||||
reaction.copy(
|
||||
count = reaction.count - 1,
|
||||
me = false
|
||||
me = false,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
@ -156,18 +156,18 @@ class AnnouncementsViewModel @Inject constructor(
|
|||
} else {
|
||||
reaction
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
announcement
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
},
|
||||
{
|
||||
Log.w(TAG, "Failed to remove reaction from the announcement.", it)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -191,7 +191,7 @@ class ComposeActivity :
|
|||
size,
|
||||
itemOld.description,
|
||||
null, // Intentionally reset focus when cropping
|
||||
itemOld
|
||||
itemOld,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -230,7 +230,7 @@ class ComposeActivity :
|
|||
// TODO this is inconsistent to CaptionDialog (device rotation)?
|
||||
},
|
||||
onEditImage = this::editImageInQueue,
|
||||
onRemove = this::removeMediaFromQueue
|
||||
onRemove = this::removeMediaFromQueue,
|
||||
)
|
||||
binding.composeMediaPreviewBar.layoutManager =
|
||||
LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
|
||||
|
@ -248,7 +248,7 @@ class ComposeActivity :
|
|||
if (accountManager.shouldDisplaySelfUsername(this)) {
|
||||
binding.composeUsernameView.text = getString(
|
||||
R.string.compose_active_account_description,
|
||||
activeAccount.fullName
|
||||
activeAccount.fullName,
|
||||
)
|
||||
binding.composeUsernameView.show()
|
||||
} else {
|
||||
|
@ -380,8 +380,8 @@ class ComposeActivity :
|
|||
this,
|
||||
preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false),
|
||||
preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false),
|
||||
preferences.getBoolean(PrefKeys.SHOW_BOT_OVERLAY, true)
|
||||
)
|
||||
preferences.getBoolean(PrefKeys.SHOW_BOT_OVERLAY, true),
|
||||
),
|
||||
)
|
||||
binding.composeEditField.setTokenizer(ComposeTokenizer())
|
||||
|
||||
|
@ -473,8 +473,8 @@ class ComposeActivity :
|
|||
displayTransientMessage(
|
||||
getString(
|
||||
R.string.error_media_upload_sending_fmt,
|
||||
throwable.message
|
||||
)
|
||||
throwable.message,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -542,7 +542,7 @@ class ComposeActivity :
|
|||
|
||||
handleCloseButton()
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -583,11 +583,11 @@ class ComposeActivity :
|
|||
activeAccount.profilePictureUrl,
|
||||
binding.composeAvatar,
|
||||
avatarSize / 8,
|
||||
animateAvatars
|
||||
animateAvatars,
|
||||
)
|
||||
binding.composeAvatar.contentDescription = getString(
|
||||
R.string.compose_active_account_description,
|
||||
activeAccount.fullName
|
||||
activeAccount.fullName,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -821,7 +821,7 @@ class ComposeActivity :
|
|||
ActivityCompat.requestPermissions(
|
||||
this@ComposeActivity,
|
||||
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
|
||||
PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE
|
||||
PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE,
|
||||
)
|
||||
} else {
|
||||
pickMediaFile.launch(true)
|
||||
|
@ -830,7 +830,7 @@ class ComposeActivity :
|
|||
}
|
||||
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {}
|
||||
}
|
||||
},
|
||||
)
|
||||
addMediaBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
|
@ -845,7 +845,7 @@ class ComposeActivity :
|
|||
maxOptionLength = instanceParams.pollMaxLength,
|
||||
minDuration = instanceParams.pollMinDuration,
|
||||
maxDuration = instanceParams.pollMaxDuration,
|
||||
onUpdatePoll = viewModel::updatePoll
|
||||
onUpdatePoll = viewModel::updatePoll,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -889,7 +889,7 @@ class ComposeActivity :
|
|||
return statusLength(
|
||||
binding.composeEditField.text,
|
||||
binding.composeContentWarningField.text,
|
||||
charactersReservedPerUrl
|
||||
charactersReservedPerUrl,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -935,7 +935,7 @@ class ComposeActivity :
|
|||
for (i in 0 until content.clip.itemCount) {
|
||||
pickMedia(
|
||||
content.clip.getItemAt(i).uri,
|
||||
contentInfo.clip.description.label as String?
|
||||
contentInfo.clip.description.label as String?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -976,7 +976,7 @@ class ComposeActivity :
|
|||
Snackbar.make(
|
||||
binding.activityCompose,
|
||||
R.string.error_media_upload_permission,
|
||||
Snackbar.LENGTH_SHORT
|
||||
Snackbar.LENGTH_SHORT,
|
||||
).apply {
|
||||
setAction(R.string.action_retry) { onMediaPick() }
|
||||
// necessary so snackbar is shown over everything
|
||||
|
@ -1001,7 +1001,7 @@ class ComposeActivity :
|
|||
photoUploadUri = FileProvider.getUriForFile(
|
||||
this,
|
||||
BuildConfig.APPLICATION_ID + ".fileprovider",
|
||||
photoFile
|
||||
photoFile,
|
||||
)
|
||||
takePicture.launch(photoUploadUri)
|
||||
}
|
||||
|
@ -1015,7 +1015,7 @@ class ComposeActivity :
|
|||
android.R.attr.textColorTertiary
|
||||
} else {
|
||||
R.attr.textColorDisabled
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1027,7 +1027,7 @@ class ComposeActivity :
|
|||
android.R.attr.textColorTertiary
|
||||
} else {
|
||||
R.attr.textColorDisabled
|
||||
}
|
||||
},
|
||||
)
|
||||
binding.addPollTextActionTextView.setTextColor(textColor)
|
||||
binding.addPollTextActionTextView.compoundDrawablesRelative[0].colorFilter = PorterDuffColorFilter(textColor, PorterDuff.Mode.SRC_IN)
|
||||
|
@ -1049,7 +1049,7 @@ class ComposeActivity :
|
|||
options(uri = item.uri) {
|
||||
setOutputUri(uriNew)
|
||||
setOutputCompressFormat(if (isPng) Bitmap.CompressFormat.PNG else Bitmap.CompressFormat.JPEG)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1227,7 +1227,7 @@ class ComposeActivity :
|
|||
null,
|
||||
getString(R.string.saving_draft),
|
||||
true,
|
||||
false
|
||||
false,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
@ -1263,7 +1263,7 @@ class ComposeActivity :
|
|||
val id: String? = null,
|
||||
val description: String? = null,
|
||||
val focus: Attachment.Focus? = null,
|
||||
val state: State
|
||||
val state: State,
|
||||
) {
|
||||
enum class Type {
|
||||
IMAGE, VIDEO, AUDIO;
|
||||
|
@ -1306,7 +1306,7 @@ class ComposeActivity :
|
|||
EDIT_DRAFT,
|
||||
|
||||
/** Editing an an existing scheduled status */
|
||||
EDIT_SCHEDULED
|
||||
EDIT_SCHEDULED,
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
|
@ -1332,7 +1332,7 @@ class ComposeActivity :
|
|||
var modifiedInitialState: Boolean? = null,
|
||||
var language: String? = null,
|
||||
var statusId: String? = null,
|
||||
var kind: ComposeKind? = null
|
||||
var kind: ComposeKind? = null,
|
||||
) : Parcelable
|
||||
|
||||
companion object {
|
||||
|
@ -1352,7 +1352,7 @@ class ComposeActivity :
|
|||
@JvmStatic
|
||||
fun startIntent(
|
||||
context: Context,
|
||||
options: ComposeOptions
|
||||
options: ComposeOptions,
|
||||
): Intent {
|
||||
return Intent(context, ComposeActivity::class.java).apply {
|
||||
putExtra(COMPOSE_OPTIONS_EXTRA, options)
|
||||
|
|
|
@ -37,7 +37,7 @@ class ComposeAutoCompleteAdapter(
|
|||
private val autocompletionProvider: AutocompletionProvider,
|
||||
private val animateAvatar: Boolean,
|
||||
private val animateEmojis: Boolean,
|
||||
private val showBotBadge: Boolean
|
||||
private val showBotBadge: Boolean,
|
||||
) : BaseAdapter(), Filterable {
|
||||
|
||||
private var resultList: List<AutocompleteResult> = emptyList()
|
||||
|
@ -113,7 +113,7 @@ class ComposeAutoCompleteAdapter(
|
|||
account.avatar,
|
||||
binding.avatar,
|
||||
avatarRadius,
|
||||
animateAvatar
|
||||
animateAvatar,
|
||||
)
|
||||
binding.avatarBadge.visible(showBotBadge && account.bot)
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ class ComposeViewModel @Inject constructor(
|
|||
private val mediaUploader: MediaUploader,
|
||||
private val serviceClient: ServiceClient,
|
||||
private val draftHelper: DraftHelper,
|
||||
instanceInfoRepo: InstanceInfoRepository
|
||||
instanceInfoRepo: InstanceInfoRepository,
|
||||
) : ViewModel() {
|
||||
|
||||
private var replyingStatusAuthor: String? = null
|
||||
|
@ -124,7 +124,7 @@ class ComposeViewModel @Inject constructor(
|
|||
mediaSize: Long,
|
||||
description: String? = null,
|
||||
focus: Attachment.Focus? = null,
|
||||
replaceItem: QueuedMedia? = null
|
||||
replaceItem: QueuedMedia? = null,
|
||||
): QueuedMedia {
|
||||
var stashMediaItem: QueuedMedia? = null
|
||||
|
||||
|
@ -136,7 +136,7 @@ class ComposeViewModel @Inject constructor(
|
|||
mediaSize = mediaSize,
|
||||
description = description,
|
||||
focus = focus,
|
||||
state = QueuedMedia.State.UPLOADING
|
||||
state = QueuedMedia.State.UPLOADING,
|
||||
)
|
||||
stashMediaItem = mediaItem
|
||||
|
||||
|
@ -164,7 +164,7 @@ class ComposeViewModel @Inject constructor(
|
|||
item.copy(
|
||||
id = event.mediaId,
|
||||
uploadPercent = -1,
|
||||
state = if (event.processed) { QueuedMedia.State.PROCESSED } else { QueuedMedia.State.UNPROCESSED }
|
||||
state = if (event.processed) { QueuedMedia.State.PROCESSED } else { QueuedMedia.State.UNPROCESSED },
|
||||
)
|
||||
is UploadEvent.ErrorEvent -> {
|
||||
media.update { mediaList -> mediaList.filter { it.localId != mediaItem.localId } }
|
||||
|
@ -197,7 +197,7 @@ class ComposeViewModel @Inject constructor(
|
|||
id = id,
|
||||
description = description,
|
||||
focus = focus,
|
||||
state = QueuedMedia.State.PUBLISHED
|
||||
state = QueuedMedia.State.PUBLISHED,
|
||||
)
|
||||
mediaList + mediaItem
|
||||
}
|
||||
|
@ -297,7 +297,7 @@ class ComposeViewModel @Inject constructor(
|
|||
failedToSendAlert = false,
|
||||
scheduledAt = scheduledAt.value,
|
||||
language = postLanguage,
|
||||
statusId = originalStatusId
|
||||
statusId = originalStatusId,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -308,7 +308,7 @@ class ComposeViewModel @Inject constructor(
|
|||
suspend fun sendStatus(
|
||||
content: String,
|
||||
spoilerText: String,
|
||||
accountId: Long
|
||||
accountId: Long,
|
||||
) {
|
||||
if (!scheduledTootId.isNullOrEmpty()) {
|
||||
api.deleteScheduledStatus(scheduledTootId!!)
|
||||
|
@ -321,7 +321,7 @@ class ComposeViewModel @Inject constructor(
|
|||
uri = item.uri.toString(),
|
||||
description = item.description,
|
||||
focus = item.focus,
|
||||
processed = item.state == QueuedMedia.State.PROCESSED || item.state == QueuedMedia.State.PUBLISHED
|
||||
processed = item.state == QueuedMedia.State.PROCESSED || item.state == QueuedMedia.State.PUBLISHED,
|
||||
)
|
||||
}
|
||||
val tootToSend = StatusToSend(
|
||||
|
@ -340,7 +340,7 @@ class ComposeViewModel @Inject constructor(
|
|||
idempotencyKey = randomAlphanumericString(16),
|
||||
retries = 0,
|
||||
language = postLanguage,
|
||||
statusId = originalStatusId
|
||||
statusId = originalStatusId,
|
||||
)
|
||||
|
||||
serviceClient.sendToot(tootToSend)
|
||||
|
@ -379,7 +379,7 @@ class ComposeViewModel @Inject constructor(
|
|||
}, { e ->
|
||||
Log.e(TAG, "Autocomplete search for $token failed.", e)
|
||||
emptyList()
|
||||
})
|
||||
},)
|
||||
}
|
||||
'#' -> {
|
||||
return api.searchSync(query = token, type = SearchType.Hashtag.apiParameter, limit = 10)
|
||||
|
@ -388,7 +388,7 @@ class ComposeViewModel @Inject constructor(
|
|||
}, { e ->
|
||||
Log.e(TAG, "Autocomplete search for $token failed.", e)
|
||||
emptyList()
|
||||
})
|
||||
},)
|
||||
}
|
||||
':' -> {
|
||||
val emojiList = emoji.replayCache.firstOrNull() ?: return emptyList()
|
||||
|
@ -420,7 +420,7 @@ class ComposeViewModel @Inject constructor(
|
|||
|
||||
val replyVisibility = composeOptions?.replyVisibility ?: Status.Visibility.UNKNOWN
|
||||
startingVisibility = Status.Visibility.byNum(
|
||||
preferredVisibility.num.coerceAtLeast(replyVisibility.num)
|
||||
preferredVisibility.num.coerceAtLeast(replyVisibility.num),
|
||||
)
|
||||
|
||||
inReplyToId = composeOptions?.inReplyToId
|
||||
|
@ -516,7 +516,7 @@ class ComposeViewModel @Inject constructor(
|
|||
SAVE_OR_DISCARD,
|
||||
UPDATE_OR_DISCARD,
|
||||
CONTINUE_EDITING_OR_DISCARD_CHANGES, // editing post
|
||||
CONTINUE_EDITING_OR_DISCARD_DRAFT // edit draft
|
||||
CONTINUE_EDITING_OR_DISCARD_DRAFT, // edit draft
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ fun downsizeImage(
|
|||
uri: Uri,
|
||||
sizeLimit: Int,
|
||||
contentResolver: ContentResolver,
|
||||
tempFile: File
|
||||
tempFile: File,
|
||||
): Boolean {
|
||||
val decodeBoundsInputStream = try {
|
||||
contentResolver.openInputStream(uri)
|
||||
|
|
|
@ -34,7 +34,7 @@ class MediaPreviewAdapter(
|
|||
private val onAddCaption: (ComposeActivity.QueuedMedia) -> Unit,
|
||||
private val onAddFocus: (ComposeActivity.QueuedMedia) -> Unit,
|
||||
private val onEditImage: (ComposeActivity.QueuedMedia) -> Unit,
|
||||
private val onRemove: (ComposeActivity.QueuedMedia) -> Unit
|
||||
private val onRemove: (ComposeActivity.QueuedMedia) -> Unit,
|
||||
) : RecyclerView.Adapter<MediaPreviewAdapter.PreviewViewHolder>() {
|
||||
|
||||
fun submitList(list: List<ComposeActivity.QueuedMedia>) {
|
||||
|
@ -120,7 +120,7 @@ class MediaPreviewAdapter(
|
|||
override fun areContentsTheSame(oldItem: ComposeActivity.QueuedMedia, newItem: ComposeActivity.QueuedMedia): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
inner class PreviewViewHolder(val progressImageView: ProgressImageView) :
|
||||
|
|
|
@ -71,7 +71,7 @@ sealed class UploadEvent {
|
|||
|
||||
data class UploadData(
|
||||
val flow: Flow<UploadEvent>,
|
||||
val scope: CoroutineScope
|
||||
val scope: CoroutineScope,
|
||||
)
|
||||
|
||||
fun createNewImageFile(context: Context, suffix: String = ".jpg"): File {
|
||||
|
@ -82,7 +82,7 @@ fun createNewImageFile(context: Context, suffix: String = ".jpg"): File {
|
|||
return File.createTempFile(
|
||||
imageFileName, /* prefix */
|
||||
suffix, /* suffix */
|
||||
storageDir /* directory */
|
||||
storageDir, /* directory */
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ class UploadServerError(val errorMessage: String) : Exception()
|
|||
@Singleton
|
||||
class MediaUploader @Inject constructor(
|
||||
private val context: Context,
|
||||
private val mediaUploadApi: MediaUploadApi
|
||||
private val mediaUploadApi: MediaUploadApi,
|
||||
) {
|
||||
|
||||
private val uploads = mutableMapOf<Int, UploadData>()
|
||||
|
@ -175,7 +175,7 @@ class MediaUploader @Inject constructor(
|
|||
uri = FileProvider.getUriForFile(
|
||||
context,
|
||||
BuildConfig.APPLICATION_ID + ".fileprovider",
|
||||
file
|
||||
file,
|
||||
)
|
||||
mediaSize = getMediaSize(contentResolver, uri)
|
||||
}
|
||||
|
@ -198,7 +198,7 @@ class MediaUploader @Inject constructor(
|
|||
uri = FileProvider.getUriForFile(
|
||||
context,
|
||||
BuildConfig.APPLICATION_ID + ".fileprovider",
|
||||
file
|
||||
file,
|
||||
)
|
||||
mediaSize = getMediaSize(contentResolver, uri)
|
||||
}
|
||||
|
@ -268,7 +268,7 @@ class MediaUploader @Inject constructor(
|
|||
context.getString(R.string.app_name),
|
||||
Date().time.toString(),
|
||||
randomAlphanumericString(10),
|
||||
fileExtension
|
||||
fileExtension,
|
||||
)
|
||||
|
||||
val stream = contentResolver.openInputStream(media.uri)
|
||||
|
@ -279,7 +279,7 @@ class MediaUploader @Inject constructor(
|
|||
val fileBody = ProgressRequestBody(
|
||||
stream!!,
|
||||
media.mediaSize,
|
||||
mimeType.toMediaTypeOrNull()!!
|
||||
mimeType.toMediaTypeOrNull()!!,
|
||||
) { percentage ->
|
||||
if (percentage != lastProgress) {
|
||||
trySend(UploadEvent.ProgressEvent(percentage))
|
||||
|
|
|
@ -33,7 +33,7 @@ fun showAddPollDialog(
|
|||
maxOptionLength: Int,
|
||||
minDuration: Int,
|
||||
maxDuration: Int,
|
||||
onUpdatePoll: (NewPoll) -> Unit
|
||||
onUpdatePoll: (NewPoll) -> Unit,
|
||||
) {
|
||||
val binding = DialogAddPollBinding.inflate(LayoutInflater.from(context))
|
||||
|
||||
|
@ -54,7 +54,7 @@ fun showAddPollDialog(
|
|||
},
|
||||
onOptionChanged = { valid ->
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = valid
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
binding.pollChoices.adapter = adapter
|
||||
|
@ -94,8 +94,8 @@ fun showAddPollDialog(
|
|||
NewPoll(
|
||||
options = adapter.pollOptions,
|
||||
expiresIn = durations[selectedPollDurationId],
|
||||
multiple = binding.multipleChoicesCheckBox.isChecked
|
||||
)
|
||||
multiple = binding.multipleChoicesCheckBox.isChecked,
|
||||
),
|
||||
)
|
||||
|
||||
dialog.dismiss()
|
||||
|
|
|
@ -30,7 +30,7 @@ class AddPollOptionsAdapter(
|
|||
private var options: MutableList<String>,
|
||||
private val maxOptionLength: Int,
|
||||
private val onOptionRemoved: (Boolean) -> Unit,
|
||||
private val onOptionChanged: (Boolean) -> Unit
|
||||
private val onOptionChanged: (Boolean) -> Unit,
|
||||
) : RecyclerView.Adapter<BindingHolder<ItemAddPollOptionBinding>>() {
|
||||
|
||||
val pollOptions: List<String>
|
||||
|
|
|
@ -56,7 +56,7 @@ class CaptionDialog : DialogFragment() {
|
|||
input.hint = resources.getQuantityString(
|
||||
R.plurals.hint_describe_for_visually_impaired,
|
||||
MEDIA_DESCRIPTION_CHARACTER_LIMIT,
|
||||
MEDIA_DESCRIPTION_CHARACTER_LIMIT
|
||||
MEDIA_DESCRIPTION_CHARACTER_LIMIT,
|
||||
)
|
||||
input.filters = arrayOf(InputFilter.LengthFilter(MEDIA_DESCRIPTION_CHARACTER_LIMIT))
|
||||
input.setText(arguments?.getString(EXISTING_DESCRIPTION_ARG))
|
||||
|
@ -79,18 +79,20 @@ class CaptionDialog : DialogFragment() {
|
|||
Glide.with(this)
|
||||
.load(previewUri)
|
||||
.downsample(DownsampleStrategy.CENTER_INSIDE)
|
||||
.into(object : CustomTarget<Drawable>(4096, 4096) {
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
imageView.setImageDrawable(placeholder)
|
||||
}
|
||||
.into(
|
||||
object : CustomTarget<Drawable>(4096, 4096) {
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
imageView.setImageDrawable(placeholder)
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Drawable,
|
||||
transition: Transition<in Drawable>?
|
||||
) {
|
||||
imageView.setImageDrawable(resource)
|
||||
}
|
||||
})
|
||||
override fun onResourceReady(
|
||||
resource: Drawable,
|
||||
transition: Transition<in Drawable>?,
|
||||
) {
|
||||
imageView.setImageDrawable(resource)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return dialog
|
||||
}
|
||||
|
@ -103,7 +105,7 @@ class CaptionDialog : DialogFragment() {
|
|||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
savedInstanceState: Bundle?,
|
||||
): View? {
|
||||
savedInstanceState?.getString(DESCRIPTION_KEY)?.let {
|
||||
input.setText(it)
|
||||
|
@ -124,12 +126,12 @@ class CaptionDialog : DialogFragment() {
|
|||
fun newInstance(
|
||||
localId: Int,
|
||||
existingDescription: String?,
|
||||
previewUri: Uri
|
||||
previewUri: Uri,
|
||||
) = CaptionDialog().apply {
|
||||
arguments = bundleOf(
|
||||
LOCAL_ID_ARG to localId,
|
||||
EXISTING_DESCRIPTION_ARG to existingDescription,
|
||||
PREVIEW_URI_ARG to previewUri
|
||||
PREVIEW_URI_ARG to previewUri,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ import kotlinx.coroutines.launch
|
|||
fun <T> T.makeFocusDialog(
|
||||
existingFocus: Focus?,
|
||||
previewUri: Uri,
|
||||
onUpdateFocus: suspend (Focus) -> Unit
|
||||
onUpdateFocus: suspend (Focus) -> Unit,
|
||||
) where T : AppCompatActivity, T : LifecycleOwner {
|
||||
val focus = existingFocus ?: Focus(0.0f, 0.0f) // Default to center
|
||||
|
||||
|
@ -48,31 +48,33 @@ fun <T> T.makeFocusDialog(
|
|||
Glide.with(this)
|
||||
.load(previewUri)
|
||||
.downsample(DownsampleStrategy.CENTER_INSIDE)
|
||||
.listener(object : RequestListener<Drawable> {
|
||||
override fun onLoadFailed(p0: GlideException?, p1: Any?, p2: Target<Drawable?>?, p3: Boolean): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable?>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
|
||||
val width = resource!!.intrinsicWidth
|
||||
val height = resource.intrinsicHeight
|
||||
|
||||
dialogBinding.focusIndicator.setImageSize(width, height)
|
||||
|
||||
// We want the dialog to be a little taller than the image, so you can slide your thumb past the image border,
|
||||
// but if it's *too* much taller that looks weird. See if a threshold has been crossed:
|
||||
if (width > height) {
|
||||
val maxHeight = dialogBinding.focusIndicator.maxAttractiveHeight()
|
||||
|
||||
if (dialogBinding.imageView.height > maxHeight) {
|
||||
val verticalShrinkLayout = FrameLayout.LayoutParams(width, maxHeight)
|
||||
dialogBinding.imageView.layoutParams = verticalShrinkLayout
|
||||
dialogBinding.focusIndicator.layoutParams = verticalShrinkLayout
|
||||
}
|
||||
.listener(
|
||||
object : RequestListener<Drawable> {
|
||||
override fun onLoadFailed(p0: GlideException?, p1: Any?, p2: Target<Drawable?>?, p3: Boolean): Boolean {
|
||||
return false
|
||||
}
|
||||
return false // Pass through
|
||||
}
|
||||
})
|
||||
|
||||
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable?>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
|
||||
val width = resource!!.intrinsicWidth
|
||||
val height = resource.intrinsicHeight
|
||||
|
||||
dialogBinding.focusIndicator.setImageSize(width, height)
|
||||
|
||||
// We want the dialog to be a little taller than the image, so you can slide your thumb past the image border,
|
||||
// but if it's *too* much taller that looks weird. See if a threshold has been crossed:
|
||||
if (width > height) {
|
||||
val maxHeight = dialogBinding.focusIndicator.maxAttractiveHeight()
|
||||
|
||||
if (dialogBinding.imageView.height > maxHeight) {
|
||||
val verticalShrinkLayout = FrameLayout.LayoutParams(width, maxHeight)
|
||||
dialogBinding.imageView.layoutParams = verticalShrinkLayout
|
||||
dialogBinding.focusIndicator.layoutParams = verticalShrinkLayout
|
||||
}
|
||||
}
|
||||
return false // Pass through
|
||||
}
|
||||
},
|
||||
)
|
||||
.into(dialogBinding.imageView)
|
||||
|
||||
val okListener = { dialog: DialogInterface, _: Int ->
|
||||
|
@ -90,7 +92,7 @@ fun <T> T.makeFocusDialog(
|
|||
|
||||
val window = dialog.window
|
||||
window?.setSoftInputMode(
|
||||
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
|
||||
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE,
|
||||
)
|
||||
|
||||
dialog.show()
|
||||
|
|
|
@ -40,7 +40,7 @@ class ComposeScheduleView
|
|||
@JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
defStyleAttr: Int = 0,
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
interface OnTimeSetListener {
|
||||
fun onTimeSet(time: String?)
|
||||
|
@ -48,14 +48,14 @@ class ComposeScheduleView
|
|||
|
||||
private var binding = ViewComposeScheduleBinding.inflate(
|
||||
(context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater),
|
||||
this
|
||||
this,
|
||||
)
|
||||
private var listener: OnTimeSetListener? = null
|
||||
private var dateFormat = SimpleDateFormat.getDateInstance()
|
||||
private var timeFormat = SimpleDateFormat.getTimeInstance()
|
||||
private var iso8601 = SimpleDateFormat(
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
|
||||
Locale.getDefault()
|
||||
Locale.getDefault(),
|
||||
).apply {
|
||||
timeZone = TimeZone.getTimeZone("UTC")
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ class ComposeScheduleView
|
|||
binding.scheduledDateTime.text = String.format(
|
||||
"%s %s",
|
||||
dateFormat.format(scheduled),
|
||||
timeFormat.format(scheduled)
|
||||
timeFormat.format(scheduled),
|
||||
)
|
||||
verifyScheduledTime(scheduled)
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ class ComposeScheduleView
|
|||
val minimumScheduledTime = calendar()
|
||||
minimumScheduledTime.add(
|
||||
Calendar.SECOND,
|
||||
MINIMUM_SCHEDULED_SECONDS
|
||||
MINIMUM_SCHEDULED_SECONDS,
|
||||
)
|
||||
scheduledTime.after(minimumScheduledTime.time)
|
||||
} else {
|
||||
|
|
|
@ -30,7 +30,7 @@ import androidx.emoji2.viewsintegration.EmojiEditTextHelper
|
|||
|
||||
class EditTextTyped @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attributeSet: AttributeSet? = null
|
||||
attributeSet: AttributeSet? = null,
|
||||
) :
|
||||
AppCompatMultiAutoCompleteTextView(context, attributeSet) {
|
||||
|
||||
|
@ -60,7 +60,7 @@ class EditTextTyped @JvmOverloads constructor(
|
|||
EditorInfoCompat.setContentMimeTypes(editorInfo, arrayOf("image/*"))
|
||||
return emojiEditTextHelper.onCreateInputConnection(
|
||||
InputConnectionCompat.createWrapper(this, connection, editorInfo),
|
||||
editorInfo
|
||||
editorInfo,
|
||||
)!!
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ class FocusIndicatorView
|
|||
@JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
defStyleAttr: Int = 0,
|
||||
) : View(context, attrs, defStyleAttr) {
|
||||
private var focus: Attachment.Focus? = null
|
||||
private var imageSize: Point? = null
|
||||
|
|
|
@ -27,7 +27,7 @@ import com.keylesspalace.tusky.entity.NewPoll
|
|||
class PollPreviewView @JvmOverloads constructor(
|
||||
context: Context?,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
defStyleAttr: Int = 0,
|
||||
) :
|
||||
LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ class ProgressImageView
|
|||
@JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
defStyleAttr: Int = 0,
|
||||
) : MediaPreviewImageView(context, attrs, defStyleAttr) {
|
||||
private var progress = -1
|
||||
private val progressRect = RectF()
|
||||
|
@ -52,7 +52,7 @@ class ProgressImageView
|
|||
}
|
||||
private val captionDrawable = AppCompatResources.getDrawable(
|
||||
context,
|
||||
R.drawable.spellcheck
|
||||
R.drawable.spellcheck,
|
||||
)!!.apply {
|
||||
setTint(Color.WHITE)
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ class ProgressImageView
|
|||
width - circleMargin - circleRadius,
|
||||
height - circleMargin - circleRadius,
|
||||
width - circleMargin,
|
||||
height - circleMargin
|
||||
height - circleMargin,
|
||||
)
|
||||
captionDrawable.draw(canvas)
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ class TootButton
|
|||
@JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
defStyleAttr: Int = 0,
|
||||
) : MaterialButton(context, attrs, defStyleAttr) {
|
||||
|
||||
private val smallStyle: Boolean = context.resources.getBoolean(R.bool.show_small_toot_button)
|
||||
|
@ -58,7 +58,8 @@ class TootButton
|
|||
null
|
||||
}
|
||||
Status.Visibility.PRIVATE,
|
||||
Status.Visibility.DIRECT -> {
|
||||
Status.Visibility.DIRECT,
|
||||
-> {
|
||||
setText(R.string.action_send)
|
||||
IconicsDrawable(context, GoogleMaterial.Icon.gmd_lock).apply { sizeDp = 18; colorInt = Color.WHITE }
|
||||
}
|
||||
|
|
|
@ -26,14 +26,14 @@ import com.keylesspalace.tusky.util.StatusDisplayOptions
|
|||
|
||||
class ConversationAdapter(
|
||||
private var statusDisplayOptions: StatusDisplayOptions,
|
||||
private val listener: StatusActionListener
|
||||
private val listener: StatusActionListener,
|
||||
) : PagingDataAdapter<ConversationViewData, ConversationViewHolder>(CONVERSATION_COMPARATOR) {
|
||||
|
||||
var mediaPreviewEnabled: Boolean
|
||||
get() = statusDisplayOptions.mediaPreviewEnabled
|
||||
set(mediaPreviewEnabled) {
|
||||
statusDisplayOptions = statusDisplayOptions.copy(
|
||||
mediaPreviewEnabled = mediaPreviewEnabled
|
||||
mediaPreviewEnabled = mediaPreviewEnabled,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ class ConversationAdapter(
|
|||
override fun onBindViewHolder(
|
||||
holder: ConversationViewHolder,
|
||||
position: Int,
|
||||
payloads: List<Any>
|
||||
payloads: List<Any>,
|
||||
) {
|
||||
getItem(position)?.let { conversationViewData ->
|
||||
holder.setupWithConversation(conversationViewData, payloads.firstOrNull())
|
||||
|
|
|
@ -37,7 +37,7 @@ data class ConversationEntity(
|
|||
val order: Int,
|
||||
val accounts: List<ConversationAccountEntity>,
|
||||
val unread: Boolean,
|
||||
@Embedded(prefix = "s_") val lastStatus: ConversationStatusEntity
|
||||
@Embedded(prefix = "s_") val lastStatus: ConversationStatusEntity,
|
||||
) {
|
||||
fun toViewData(): ConversationViewData {
|
||||
return ConversationViewData(
|
||||
|
@ -45,7 +45,7 @@ data class ConversationEntity(
|
|||
order = order,
|
||||
accounts = accounts,
|
||||
unread = unread,
|
||||
lastStatus = lastStatus.toViewData()
|
||||
lastStatus = lastStatus.toViewData(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ data class ConversationAccountEntity(
|
|||
val username: String,
|
||||
val displayName: String,
|
||||
val avatar: String,
|
||||
val emojis: List<Emoji>
|
||||
val emojis: List<Emoji>,
|
||||
) {
|
||||
fun toAccount(): TimelineAccount {
|
||||
return TimelineAccount(
|
||||
|
@ -67,7 +67,7 @@ data class ConversationAccountEntity(
|
|||
note = "",
|
||||
url = "",
|
||||
avatar = avatar,
|
||||
emojis = emojis
|
||||
emojis = emojis,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ data class ConversationStatusEntity(
|
|||
val collapsed: Boolean,
|
||||
val muted: Boolean,
|
||||
val poll: Poll?,
|
||||
val language: String?
|
||||
val language: String?,
|
||||
) {
|
||||
|
||||
fun toViewData(): StatusViewData {
|
||||
|
@ -131,11 +131,11 @@ data class ConversationStatusEntity(
|
|||
poll = poll,
|
||||
card = null,
|
||||
language = language,
|
||||
filtered = null
|
||||
filtered = null,
|
||||
),
|
||||
isExpanded = expanded,
|
||||
isShowingContent = showingHiddenContent,
|
||||
isCollapsed = collapsed
|
||||
isCollapsed = collapsed,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -147,13 +147,13 @@ fun TimelineAccount.toEntity() =
|
|||
username = username,
|
||||
displayName = name,
|
||||
avatar = avatar,
|
||||
emojis = emojis.orEmpty()
|
||||
emojis = emojis.orEmpty(),
|
||||
)
|
||||
|
||||
fun Status.toEntity(
|
||||
expanded: Boolean,
|
||||
contentShowing: Boolean,
|
||||
contentCollapsed: Boolean
|
||||
contentCollapsed: Boolean,
|
||||
) =
|
||||
ConversationStatusEntity(
|
||||
id = id,
|
||||
|
@ -179,7 +179,7 @@ fun Status.toEntity(
|
|||
collapsed = contentCollapsed,
|
||||
muted = muted ?: false,
|
||||
poll = poll,
|
||||
language = language
|
||||
language = language,
|
||||
)
|
||||
|
||||
fun Conversation.toEntity(
|
||||
|
@ -187,7 +187,7 @@ fun Conversation.toEntity(
|
|||
order: Int,
|
||||
expanded: Boolean,
|
||||
contentShowing: Boolean,
|
||||
contentCollapsed: Boolean
|
||||
contentCollapsed: Boolean,
|
||||
) =
|
||||
ConversationEntity(
|
||||
accountId = accountId,
|
||||
|
@ -198,6 +198,6 @@ fun Conversation.toEntity(
|
|||
lastStatus = lastStatus!!.toEntity(
|
||||
expanded = expanded,
|
||||
contentShowing = contentShowing,
|
||||
contentCollapsed = contentCollapsed
|
||||
)
|
||||
contentCollapsed = contentCollapsed,
|
||||
),
|
||||
)
|
||||
|
|
|
@ -24,7 +24,7 @@ import com.keylesspalace.tusky.util.BindingHolder
|
|||
import com.keylesspalace.tusky.util.visible
|
||||
|
||||
class ConversationLoadStateAdapter(
|
||||
private val retryCallback: () -> Unit
|
||||
private val retryCallback: () -> Unit,
|
||||
) : LoadStateAdapter<BindingHolder<ItemNetworkStateBinding>>() {
|
||||
|
||||
override fun onBindViewHolder(holder: BindingHolder<ItemNetworkStateBinding>, loadState: LoadState) {
|
||||
|
@ -45,7 +45,7 @@ class ConversationLoadStateAdapter(
|
|||
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
loadState: LoadState
|
||||
loadState: LoadState,
|
||||
): BindingHolder<ItemNetworkStateBinding> {
|
||||
val binding = ItemNetworkStateBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
return BindingHolder(binding)
|
||||
|
|
|
@ -23,7 +23,7 @@ data class ConversationViewData(
|
|||
val order: Int,
|
||||
val accounts: List<ConversationAccountEntity>,
|
||||
val unread: Boolean,
|
||||
val lastStatus: StatusViewData
|
||||
val lastStatus: StatusViewData,
|
||||
) {
|
||||
fun toEntity(
|
||||
accountId: Long,
|
||||
|
@ -33,7 +33,7 @@ data class ConversationViewData(
|
|||
poll: Poll? = lastStatus.status.poll,
|
||||
expanded: Boolean = lastStatus.isExpanded,
|
||||
collapsed: Boolean = lastStatus.isCollapsed,
|
||||
showingHiddenContent: Boolean = lastStatus.isShowingContent
|
||||
showingHiddenContent: Boolean = lastStatus.isShowingContent,
|
||||
): ConversationEntity {
|
||||
return ConversationEntity(
|
||||
accountId = accountId,
|
||||
|
@ -48,8 +48,8 @@ data class ConversationViewData(
|
|||
poll = poll,
|
||||
expanded = expanded,
|
||||
collapsed = collapsed,
|
||||
showingHiddenContent = showingHiddenContent
|
||||
)
|
||||
showingHiddenContent = showingHiddenContent,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ fun StatusViewData.toConversationStatusEntity(
|
|||
poll: Poll? = status.poll,
|
||||
expanded: Boolean = isExpanded,
|
||||
collapsed: Boolean = isCollapsed,
|
||||
showingHiddenContent: Boolean = isShowingContent
|
||||
showingHiddenContent: Boolean = isShowingContent,
|
||||
): ConversationStatusEntity {
|
||||
return ConversationStatusEntity(
|
||||
id = id,
|
||||
|
@ -87,6 +87,6 @@ fun StatusViewData.toConversationStatusEntity(
|
|||
collapsed = collapsed,
|
||||
muted = muted,
|
||||
poll = poll,
|
||||
language = status.language
|
||||
language = status.language,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ class ConversationsFragment :
|
|||
animateEmojis = preferences.getBoolean(PrefKeys.ANIMATE_CUSTOM_EMOJIS, false),
|
||||
showStatsInline = preferences.getBoolean(PrefKeys.SHOW_STATS_INLINE, false),
|
||||
showSensitiveMedia = accountManager.activeAccount!!.alwaysShowSensitiveMedia,
|
||||
openSpoiler = accountManager.activeAccount!!.alwaysOpenSpoiler
|
||||
openSpoiler = accountManager.activeAccount!!.alwaysOpenSpoiler,
|
||||
)
|
||||
|
||||
adapter = ConversationAdapter(statusDisplayOptions, this)
|
||||
|
@ -147,35 +147,39 @@ class ConversationsFragment :
|
|||
}
|
||||
}
|
||||
|
||||
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
||||
if (positionStart == 0 && adapter.itemCount != itemCount) {
|
||||
binding.recyclerView.post {
|
||||
if (getView() != null) {
|
||||
binding.recyclerView.scrollBy(0, Utils.dpToPx(requireContext(), -30))
|
||||
adapter.registerAdapterDataObserver(
|
||||
object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
||||
if (positionStart == 0 && adapter.itemCount != itemCount) {
|
||||
binding.recyclerView.post {
|
||||
if (getView() != null) {
|
||||
binding.recyclerView.scrollBy(0, Utils.dpToPx(requireContext(), -30))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
hideFab = preferences.getBoolean(PrefKeys.FAB_HIDE, false)
|
||||
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(view: RecyclerView, dx: Int, dy: Int) {
|
||||
val composeButton = (activity as ActionButtonActivity).actionButton
|
||||
if (composeButton != null) {
|
||||
if (hideFab) {
|
||||
if (dy > 0 && composeButton.isShown) {
|
||||
composeButton.hide() // hides the button if we're scrolling down
|
||||
} else if (dy < 0 && !composeButton.isShown) {
|
||||
composeButton.show() // shows it if we are scrolling up
|
||||
binding.recyclerView.addOnScrollListener(
|
||||
object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(view: RecyclerView, dx: Int, dy: Int) {
|
||||
val composeButton = (activity as ActionButtonActivity).actionButton
|
||||
if (composeButton != null) {
|
||||
if (hideFab) {
|
||||
if (dy > 0 && composeButton.isShown) {
|
||||
composeButton.hide() // hides the button if we're scrolling down
|
||||
} else if (dy < 0 && !composeButton.isShown) {
|
||||
composeButton.show() // shows it if we are scrolling up
|
||||
}
|
||||
} else if (!composeButton.isShown) {
|
||||
composeButton.show()
|
||||
}
|
||||
} else if (!composeButton.isShown) {
|
||||
composeButton.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.conversationFlow.collectLatest { pagingData ->
|
||||
|
@ -190,7 +194,7 @@ class ConversationsFragment :
|
|||
adapter.notifyItemRangeChanged(
|
||||
0,
|
||||
adapter.itemCount,
|
||||
listOf(StatusBaseViewHolder.Key.KEY_CREATED)
|
||||
listOf(StatusBaseViewHolder.Key.KEY_CREATED),
|
||||
)
|
||||
delay(1.toDuration(DurationUnit.MINUTES))
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import retrofit2.HttpException
|
|||
class ConversationsRemoteMediator(
|
||||
private val api: MastodonApi,
|
||||
private val db: AppDatabase,
|
||||
accountManager: AccountManager
|
||||
accountManager: AccountManager,
|
||||
) : RemoteMediator<Int, ConversationEntity>() {
|
||||
|
||||
private var nextKey: String? = null
|
||||
|
@ -26,7 +26,7 @@ class ConversationsRemoteMediator(
|
|||
|
||||
override suspend fun load(
|
||||
loadType: LoadType,
|
||||
state: PagingState<Int, ConversationEntity>
|
||||
state: PagingState<Int, ConversationEntity>,
|
||||
): MediatorResult {
|
||||
if (loadType == LoadType.PREPEND) {
|
||||
return MediatorResult.Success(endOfPaginationReached = true)
|
||||
|
@ -68,9 +68,9 @@ class ConversationsRemoteMediator(
|
|||
order = order++,
|
||||
expanded = expanded,
|
||||
contentShowing = contentShowing,
|
||||
contentCollapsed = contentCollapsed
|
||||
contentCollapsed = contentCollapsed,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
return MediatorResult.Success(endOfPaginationReached = nextKey == null)
|
||||
|
|
|
@ -37,7 +37,7 @@ class ConversationsViewModel @Inject constructor(
|
|||
private val timelineCases: TimelineCases,
|
||||
private val database: AppDatabase,
|
||||
private val accountManager: AccountManager,
|
||||
private val api: MastodonApi
|
||||
private val api: MastodonApi,
|
||||
) : ViewModel() {
|
||||
|
||||
@OptIn(ExperimentalPagingApi::class)
|
||||
|
@ -51,7 +51,7 @@ class ConversationsViewModel @Inject constructor(
|
|||
} else {
|
||||
database.conversationDao().conversationsForAccount(activeAccount.id)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
.flow
|
||||
.map { pagingData ->
|
||||
|
@ -64,13 +64,13 @@ class ConversationsViewModel @Inject constructor(
|
|||
timelineCases.favourite(conversation.lastStatus.id, favourite).fold({
|
||||
val newConversation = conversation.toEntity(
|
||||
accountId = accountManager.activeAccount!!.id,
|
||||
favourited = favourite
|
||||
favourited = favourite,
|
||||
)
|
||||
|
||||
saveConversationToDb(newConversation)
|
||||
}, { e ->
|
||||
Log.w(TAG, "failed to favourite status", e)
|
||||
})
|
||||
},)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,13 +79,13 @@ class ConversationsViewModel @Inject constructor(
|
|||
timelineCases.bookmark(conversation.lastStatus.id, bookmark).fold({
|
||||
val newConversation = conversation.toEntity(
|
||||
accountId = accountManager.activeAccount!!.id,
|
||||
bookmarked = bookmark
|
||||
bookmarked = bookmark,
|
||||
)
|
||||
|
||||
saveConversationToDb(newConversation)
|
||||
}, { e ->
|
||||
Log.w(TAG, "failed to bookmark status", e)
|
||||
})
|
||||
},)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,13 +95,13 @@ class ConversationsViewModel @Inject constructor(
|
|||
.fold({ poll ->
|
||||
val newConversation = conversation.toEntity(
|
||||
accountId = accountManager.activeAccount!!.id,
|
||||
poll = poll
|
||||
poll = poll,
|
||||
)
|
||||
|
||||
saveConversationToDb(newConversation)
|
||||
}, { e ->
|
||||
Log.w(TAG, "failed to vote in poll", e)
|
||||
})
|
||||
},)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ class ConversationsViewModel @Inject constructor(
|
|||
viewModelScope.launch {
|
||||
val newConversation = conversation.toEntity(
|
||||
accountId = accountManager.activeAccount!!.id,
|
||||
expanded = expanded
|
||||
expanded = expanded,
|
||||
)
|
||||
saveConversationToDb(newConversation)
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ class ConversationsViewModel @Inject constructor(
|
|||
viewModelScope.launch {
|
||||
val newConversation = conversation.toEntity(
|
||||
accountId = accountManager.activeAccount!!.id,
|
||||
collapsed = collapsed
|
||||
collapsed = collapsed,
|
||||
)
|
||||
saveConversationToDb(newConversation)
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ class ConversationsViewModel @Inject constructor(
|
|||
viewModelScope.launch {
|
||||
val newConversation = conversation.toEntity(
|
||||
accountId = accountManager.activeAccount!!.id,
|
||||
showingHiddenContent = showing
|
||||
showingHiddenContent = showing,
|
||||
)
|
||||
saveConversationToDb(newConversation)
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ class ConversationsViewModel @Inject constructor(
|
|||
|
||||
database.conversationDao().delete(
|
||||
id = conversation.id,
|
||||
accountId = accountManager.activeAccount!!.id
|
||||
accountId = accountManager.activeAccount!!.id,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "failed to delete conversation", e)
|
||||
|
@ -155,12 +155,12 @@ class ConversationsViewModel @Inject constructor(
|
|||
try {
|
||||
timelineCases.muteConversation(
|
||||
conversation.lastStatus.id,
|
||||
!(conversation.lastStatus.status.muted ?: false)
|
||||
!(conversation.lastStatus.status.muted ?: false),
|
||||
)
|
||||
|
||||
val newConversation = conversation.toEntity(
|
||||
accountId = accountManager.activeAccount!!.id,
|
||||
muted = !(conversation.lastStatus.status.muted ?: false)
|
||||
muted = !(conversation.lastStatus.status.muted ?: false),
|
||||
)
|
||||
|
||||
database.conversationDao().insert(newConversation)
|
||||
|
|
|
@ -45,7 +45,7 @@ import javax.inject.Inject
|
|||
class DraftHelper @Inject constructor(
|
||||
val context: Context,
|
||||
private val okHttpClient: OkHttpClient,
|
||||
db: AppDatabase
|
||||
db: AppDatabase,
|
||||
) {
|
||||
|
||||
private val draftDao = db.draftDao()
|
||||
|
@ -66,7 +66,7 @@ class DraftHelper @Inject constructor(
|
|||
failedToSendAlert: Boolean,
|
||||
scheduledAt: String?,
|
||||
language: String?,
|
||||
statusId: String?
|
||||
statusId: String?,
|
||||
) = withContext(Dispatchers.IO) {
|
||||
val externalFilesDir = context.getExternalFilesDir("Tusky")
|
||||
|
||||
|
@ -108,8 +108,8 @@ class DraftHelper @Inject constructor(
|
|||
uriString = uris[i].toString(),
|
||||
description = mediaDescriptions[i],
|
||||
focus = mediaFocus[i],
|
||||
type = types[i]
|
||||
)
|
||||
type = types[i],
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,7 @@ class DraftHelper @Inject constructor(
|
|||
failedToSendNew = failedToSendAlert,
|
||||
scheduledAt = scheduledAt,
|
||||
language = language,
|
||||
statusId = statusId
|
||||
statusId = statusId,
|
||||
)
|
||||
|
||||
draftDao.insertOrReplace(draft)
|
||||
|
|
|
@ -28,7 +28,7 @@ import com.keylesspalace.tusky.db.DraftAttachment
|
|||
import com.keylesspalace.tusky.view.MediaPreviewImageView
|
||||
|
||||
class DraftMediaAdapter(
|
||||
private val attachmentClick: () -> Unit
|
||||
private val attachmentClick: () -> Unit,
|
||||
) : ListAdapter<DraftAttachment, DraftMediaAdapter.DraftMediaViewHolder>(
|
||||
object : DiffUtil.ItemCallback<DraftAttachment>() {
|
||||
override fun areItemsTheSame(oldItem: DraftAttachment, newItem: DraftAttachment): Boolean {
|
||||
|
@ -38,7 +38,7 @@ class DraftMediaAdapter(
|
|||
override fun areContentsTheSame(oldItem: DraftAttachment, newItem: DraftAttachment): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
}
|
||||
},
|
||||
) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DraftMediaViewHolder {
|
||||
|
|
|
@ -119,7 +119,7 @@ class DraftsActivity : BaseActivity(), DraftActionListener {
|
|||
scheduledAt = draft.scheduledAt,
|
||||
language = draft.language,
|
||||
statusId = draft.statusId,
|
||||
kind = ComposeActivity.ComposeKind.EDIT_DRAFT
|
||||
kind = ComposeActivity.ComposeKind.EDIT_DRAFT,
|
||||
)
|
||||
|
||||
bottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
|
@ -140,7 +140,7 @@ class DraftsActivity : BaseActivity(), DraftActionListener {
|
|||
Snackbar.make(binding.root, getString(R.string.drafts_failed_loading_reply), Snackbar.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ class DraftsActivity : BaseActivity(), DraftActionListener {
|
|||
scheduledAt = draft.scheduledAt,
|
||||
language = draft.language,
|
||||
statusId = draft.statusId,
|
||||
kind = ComposeActivity.ComposeKind.EDIT_DRAFT
|
||||
kind = ComposeActivity.ComposeKind.EDIT_DRAFT,
|
||||
)
|
||||
|
||||
startActivity(ComposeActivity.startIntent(this, composeOptions))
|
||||
|
|
|
@ -34,7 +34,7 @@ interface DraftActionListener {
|
|||
}
|
||||
|
||||
class DraftsAdapter(
|
||||
private val listener: DraftActionListener
|
||||
private val listener: DraftActionListener,
|
||||
) : PagingDataAdapter<DraftEntity, BindingHolder<ItemDraftBinding>>(
|
||||
object : DiffUtil.ItemCallback<DraftEntity>() {
|
||||
override fun areItemsTheSame(oldItem: DraftEntity, newItem: DraftEntity): Boolean {
|
||||
|
@ -44,7 +44,7 @@ class DraftsAdapter(
|
|||
override fun areContentsTheSame(oldItem: DraftEntity, newItem: DraftEntity): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
}
|
||||
},
|
||||
) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder<ItemDraftBinding> {
|
||||
|
|
|
@ -33,12 +33,12 @@ class DraftsViewModel @Inject constructor(
|
|||
val database: AppDatabase,
|
||||
val accountManager: AccountManager,
|
||||
val api: MastodonApi,
|
||||
private val draftHelper: DraftHelper
|
||||
private val draftHelper: DraftHelper,
|
||||
) : ViewModel() {
|
||||
|
||||
val drafts = Pager(
|
||||
config = PagingConfig(pageSize = 20),
|
||||
pagingSourceFactory = { database.draftDao().draftsPagingSource(accountManager.activeAccount?.id!!) }
|
||||
pagingSourceFactory = { database.draftDao().draftsPagingSource(accountManager.activeAccount?.id!!) },
|
||||
).flow
|
||||
.cachedIn(viewModelScope)
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ class EditFilterActivity : BaseActivity() {
|
|||
filterContextNotifications to Filter.Kind.NOTIFICATIONS,
|
||||
filterContextPublic to Filter.Kind.PUBLIC,
|
||||
filterContextThread to Filter.Kind.THREAD,
|
||||
filterContextAccount to Filter.Kind.ACCOUNT
|
||||
filterContextAccount to Filter.Kind.ACCOUNT,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ class EditFilterActivity : BaseActivity() {
|
|||
R.string.filter_addition_title
|
||||
} else {
|
||||
R.string.filter_edit_title
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
binding.actionChip.setOnClickListener { showAddKeywordDialog() }
|
||||
|
@ -110,7 +110,7 @@ class EditFilterActivity : BaseActivity() {
|
|||
Filter.Action.WARN
|
||||
} else {
|
||||
Filter.Action.HIDE
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
binding.filterDurationSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
|
@ -120,7 +120,7 @@ class EditFilterActivity : BaseActivity() {
|
|||
position
|
||||
} else {
|
||||
position - 1
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -192,7 +192,7 @@ class EditFilterActivity : BaseActivity() {
|
|||
chip.text = if (filterKeyword.wholeWord) {
|
||||
binding.root.context.getString(
|
||||
R.string.filter_keyword_display_format,
|
||||
filterKeyword.keyword
|
||||
filterKeyword.keyword,
|
||||
)
|
||||
} else {
|
||||
filterKeyword.keyword
|
||||
|
@ -225,8 +225,8 @@ class EditFilterActivity : BaseActivity() {
|
|||
FilterKeyword(
|
||||
"",
|
||||
binding.phraseEditText.text.toString(),
|
||||
binding.phraseWholeWord.isChecked
|
||||
)
|
||||
binding.phraseWholeWord.isChecked,
|
||||
),
|
||||
)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
|
@ -246,8 +246,8 @@ class EditFilterActivity : BaseActivity() {
|
|||
keyword,
|
||||
keyword.copy(
|
||||
keyword = binding.phraseEditText.text.toString(),
|
||||
wholeWord = binding.phraseWholeWord.isChecked
|
||||
)
|
||||
wholeWord = binding.phraseWholeWord.isChecked,
|
||||
),
|
||||
)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
|
@ -285,12 +285,12 @@ class EditFilterActivity : BaseActivity() {
|
|||
},
|
||||
{
|
||||
Snackbar.make(binding.root, "Error deleting filter '${filter.title}'", Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Snackbar.make(binding.root, "Error deleting filter '${filter.title}'", Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi, val eventHub
|
|||
title = title,
|
||||
context = contexts,
|
||||
filterAction = action,
|
||||
expiresInSeconds = expiresInSeconds
|
||||
expiresInSeconds = expiresInSeconds,
|
||||
).fold(
|
||||
{ newFilter ->
|
||||
// This is _terrible_, but the all-in-one update filter api Just Doesn't Work
|
||||
|
@ -112,7 +112,7 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi, val eventHub
|
|||
// Endpoint not found, fall back to v1 api
|
||||
createFilterV1(contexts, expiresInSeconds)
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -123,7 +123,7 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi, val eventHub
|
|||
title = title,
|
||||
context = contexts,
|
||||
filterAction = action,
|
||||
expiresInSeconds = expiresInSeconds
|
||||
expiresInSeconds = expiresInSeconds,
|
||||
).fold(
|
||||
{
|
||||
// This is _terrible_, but the all-in-one update filter api Just Doesn't Work
|
||||
|
@ -148,7 +148,7 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi, val eventHub
|
|||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,7 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi, val eventHub
|
|||
context = context,
|
||||
irreversible = false,
|
||||
wholeWord = keyword.wholeWord,
|
||||
expiresInSeconds = expiresInSeconds
|
||||
expiresInSeconds = expiresInSeconds,
|
||||
)
|
||||
} else {
|
||||
api.updateFilterV1(
|
||||
|
@ -175,7 +175,7 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi, val eventHub
|
|||
context = context,
|
||||
irreversible = false,
|
||||
wholeWord = keyword.wholeWord,
|
||||
expiresInSeconds = expiresInSeconds
|
||||
expiresInSeconds = expiresInSeconds,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ class FiltersActivity : BaseActivity(), FiltersListener {
|
|||
binding.messageView.setup(
|
||||
R.drawable.elephant_friend_empty,
|
||||
R.string.message_empty,
|
||||
null
|
||||
null,
|
||||
)
|
||||
binding.messageView.show()
|
||||
} else {
|
||||
|
|
|
@ -32,13 +32,13 @@ class FiltersAdapter(val listener: FiltersListener, val filters: List<Filter>) :
|
|||
context.getString(
|
||||
R.string.filter_expiration_format,
|
||||
filter.title,
|
||||
getRelativeTimeSpanString(binding.root.context, filter.expiresAt.time, System.currentTimeMillis())
|
||||
getRelativeTimeSpanString(binding.root.context, filter.expiresAt.time, System.currentTimeMillis()),
|
||||
)
|
||||
}
|
||||
binding.textSecondary.text = context.getString(
|
||||
R.string.filter_description_format,
|
||||
actions.getOrNull(filter.action.ordinal - 1),
|
||||
filter.context.map { contexts.getOrNull(Filter.Kind.from(it).ordinal) }.joinToString("/")
|
||||
filter.context.map { contexts.getOrNull(Filter.Kind.from(it).ordinal) }.joinToString("/"),
|
||||
)
|
||||
|
||||
binding.delete.setOnClickListener {
|
||||
|
|
|
@ -18,7 +18,7 @@ import javax.inject.Inject
|
|||
|
||||
class FiltersViewModel @Inject constructor(
|
||||
private val api: MastodonApi,
|
||||
private val eventHub: EventHub
|
||||
private val eventHub: EventHub,
|
||||
) : ViewModel() {
|
||||
|
||||
enum class LoadingState {
|
||||
|
@ -49,13 +49,13 @@ class FiltersViewModel @Inject constructor(
|
|||
// TODO log errors (also below)
|
||||
|
||||
this@FiltersViewModel._state.value = _state.value.copy(loadingState = LoadingState.ERROR_OTHER)
|
||||
}
|
||||
},
|
||||
)
|
||||
this@FiltersViewModel._state.value = _state.value.copy(loadingState = LoadingState.ERROR_OTHER)
|
||||
} else {
|
||||
this@FiltersViewModel._state.value = _state.value.copy(loadingState = LoadingState.ERROR_NETWORK)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -77,12 +77,12 @@ class FiltersViewModel @Inject constructor(
|
|||
},
|
||||
{
|
||||
Snackbar.make(parent, "Error deleting filter '${filter.title}'", Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Snackbar.make(parent, "Error deleting filter '${filter.title}'", Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ class FiltersViewModel @Inject constructor(
|
|||
FilterV1.NOTIFICATIONS,
|
||||
FilterV1.THREAD,
|
||||
FilterV1.PUBLIC,
|
||||
FilterV1.ACCOUNT
|
||||
FilterV1.ACCOUNT,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,15 +86,17 @@ class FollowedTagsActivity :
|
|||
|
||||
val hideFab = sharedPreferences.getBoolean(PrefKeys.FAB_HIDE, false)
|
||||
if (hideFab) {
|
||||
binding.followedTagsView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
if (dy > 0 && binding.fab.isShown) {
|
||||
binding.fab.hide()
|
||||
} else if (dy < 0 && !binding.fab.isShown) {
|
||||
binding.fab.show()
|
||||
binding.followedTagsView.addOnScrollListener(
|
||||
object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
if (dy > 0 && binding.fab.isShown) {
|
||||
binding.fab.hide()
|
||||
} else if (dy < 0 && !binding.fab.isShown) {
|
||||
binding.fab.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,10 +135,10 @@ class FollowedTagsActivity :
|
|||
this@FollowedTagsActivity,
|
||||
binding.followedTagsView,
|
||||
getString(R.string.error_following_hashtag_format, tagName),
|
||||
Snackbar.LENGTH_SHORT
|
||||
Snackbar.LENGTH_SHORT,
|
||||
)
|
||||
.show()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +152,7 @@ class FollowedTagsActivity :
|
|||
this@FollowedTagsActivity,
|
||||
binding.followedTagsView,
|
||||
getString(R.string.confirmation_hashtag_unfollowed, tagName),
|
||||
Snackbar.LENGTH_LONG
|
||||
Snackbar.LENGTH_LONG,
|
||||
)
|
||||
.setAction(R.string.action_undo) {
|
||||
follow(tagName, position)
|
||||
|
@ -164,12 +166,12 @@ class FollowedTagsActivity :
|
|||
binding.followedTagsView,
|
||||
getString(
|
||||
R.string.error_unfollowing_hashtag_format,
|
||||
tagName
|
||||
tagName,
|
||||
),
|
||||
Snackbar.LENGTH_SHORT
|
||||
Snackbar.LENGTH_SHORT,
|
||||
)
|
||||
.show()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -191,8 +193,8 @@ class FollowedTagsActivity :
|
|||
requireActivity() as FollowedTagsActivity,
|
||||
animateAvatar = false,
|
||||
animateEmojis = false,
|
||||
showBotBadge = false
|
||||
)
|
||||
showBotBadge = false,
|
||||
),
|
||||
)
|
||||
|
||||
return AlertDialog.Builder(requireActivity())
|
||||
|
@ -200,7 +202,7 @@ class FollowedTagsActivity :
|
|||
.setView(layout)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
(requireActivity() as FollowedTagsActivity).follow(
|
||||
autoCompleteTextView.text.toString().removePrefix("#")
|
||||
autoCompleteTextView.text.toString().removePrefix("#"),
|
||||
)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _: DialogInterface, _: Int -> }
|
||||
|
|
|
@ -13,7 +13,7 @@ import com.keylesspalace.tusky.util.BindingHolder
|
|||
|
||||
class FollowedTagsAdapter(
|
||||
private val actionListener: HashtagActionListener,
|
||||
private val viewModel: FollowedTagsViewModel
|
||||
private val viewModel: FollowedTagsViewModel,
|
||||
) : PagingDataAdapter<String, BindingHolder<ItemFollowedHashtagBinding>>(STRING_COMPARATOR) {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder<ItemFollowedHashtagBinding> =
|
||||
BindingHolder(ItemFollowedHashtagBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||
|
|
|
@ -13,11 +13,11 @@ import retrofit2.Response
|
|||
@OptIn(ExperimentalPagingApi::class)
|
||||
class FollowedTagsRemoteMediator(
|
||||
private val api: MastodonApi,
|
||||
private val viewModel: FollowedTagsViewModel
|
||||
private val viewModel: FollowedTagsViewModel,
|
||||
) : RemoteMediator<String, String>() {
|
||||
override suspend fun load(
|
||||
loadType: LoadType,
|
||||
state: PagingState<String, String>
|
||||
state: PagingState<String, String>,
|
||||
): MediatorResult {
|
||||
return try {
|
||||
val response = request(loadType)
|
||||
|
|
|
@ -16,7 +16,7 @@ import com.keylesspalace.tusky.network.MastodonApi
|
|||
import javax.inject.Inject
|
||||
|
||||
class FollowedTagsViewModel @Inject constructor(
|
||||
private val api: MastodonApi
|
||||
private val api: MastodonApi,
|
||||
) : ViewModel(), Injectable {
|
||||
val tags: MutableList<HashTag> = mutableListOf()
|
||||
var nextKey: String? = null
|
||||
|
@ -28,11 +28,11 @@ class FollowedTagsViewModel @Inject constructor(
|
|||
remoteMediator = FollowedTagsRemoteMediator(api, this),
|
||||
pagingSourceFactory = {
|
||||
FollowedTagsPagingSource(
|
||||
viewModel = this
|
||||
viewModel = this,
|
||||
).also { source ->
|
||||
currentSource = source
|
||||
}
|
||||
}
|
||||
},
|
||||
).flow.cachedIn(viewModelScope)
|
||||
|
||||
fun searchAutocompleteSuggestions(token: String): List<ComposeAutoCompleteAdapter.AutocompleteResult> {
|
||||
|
@ -42,7 +42,7 @@ class FollowedTagsViewModel @Inject constructor(
|
|||
}, { e ->
|
||||
Log.e(TAG, "Autocomplete search for $token failed.", e)
|
||||
emptyList()
|
||||
})
|
||||
},)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -29,5 +29,5 @@ data class InstanceInfo(
|
|||
val maxFields: Int,
|
||||
val maxFieldNameLength: Int?,
|
||||
val maxFieldValueLength: Int?,
|
||||
val version: String?
|
||||
val version: String?,
|
||||
)
|
||||
|
|
|
@ -32,7 +32,7 @@ import javax.inject.Inject
|
|||
class InstanceInfoRepository @Inject constructor(
|
||||
private val api: MastodonApi,
|
||||
db: AppDatabase,
|
||||
accountManager: AccountManager
|
||||
accountManager: AccountManager,
|
||||
) {
|
||||
|
||||
private val dao = db.instanceDao()
|
||||
|
@ -76,7 +76,7 @@ class InstanceInfoRepository @Inject constructor(
|
|||
maxMediaAttachments = instance.configuration?.statuses?.maxMediaAttachments ?: instance.maxMediaAttachments,
|
||||
maxFields = instance.pleroma?.metadata?.fieldLimits?.maxFields,
|
||||
maxFieldNameLength = instance.pleroma?.metadata?.fieldLimits?.nameLength,
|
||||
maxFieldValueLength = instance.pleroma?.metadata?.fieldLimits?.valueLength
|
||||
maxFieldValueLength = instance.pleroma?.metadata?.fieldLimits?.valueLength,
|
||||
)
|
||||
dao.upsert(instanceEntity)
|
||||
instanceEntity
|
||||
|
@ -84,7 +84,7 @@ class InstanceInfoRepository @Inject constructor(
|
|||
{ throwable ->
|
||||
Log.w(TAG, "failed to instance, falling back to cache and default values", throwable)
|
||||
dao.getInstanceInfo(instanceName)
|
||||
}
|
||||
},
|
||||
).let { instanceInfo: InstanceInfoEntity? ->
|
||||
InstanceInfo(
|
||||
maxChars = instanceInfo?.maximumTootCharacters ?: DEFAULT_CHARACTER_LIMIT,
|
||||
|
@ -100,7 +100,7 @@ class InstanceInfoRepository @Inject constructor(
|
|||
maxFields = instanceInfo?.maxFields ?: DEFAULT_MAX_ACCOUNT_FIELDS,
|
||||
maxFieldNameLength = instanceInfo?.maxFieldNameLength,
|
||||
maxFieldValueLength = instanceInfo?.maxFieldValueLength,
|
||||
version = instanceInfo?.version
|
||||
version = instanceInfo?.version,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import com.keylesspalace.tusky.databinding.ItemMutedDomainBinding
|
|||
import com.keylesspalace.tusky.util.BindingHolder
|
||||
|
||||
class DomainMutesAdapter(
|
||||
private val actionListener: InstanceActionListener
|
||||
private val actionListener: InstanceActionListener,
|
||||
) : RecyclerView.Adapter<BindingHolder<ItemMutedDomainBinding>>() {
|
||||
|
||||
var instances: MutableList<String> = mutableListOf()
|
||||
|
|
|
@ -69,7 +69,7 @@ class InstanceListFragment : Fragment(R.layout.fragment_instance_list), Injectab
|
|||
adapter.addItem(instance)
|
||||
}, { e ->
|
||||
Log.e(TAG, "Error muting domain $instance", e)
|
||||
})
|
||||
},)
|
||||
} else {
|
||||
api.unblockDomain(instance).fold({
|
||||
adapter.removeItem(position)
|
||||
|
@ -80,7 +80,7 @@ class InstanceListFragment : Fragment(R.layout.fragment_instance_list), Injectab
|
|||
.show()
|
||||
}, { e ->
|
||||
Log.e(TAG, "Error unmuting domain $instance", e)
|
||||
})
|
||||
},)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ class InstanceListFragment : Fragment(R.layout.fragment_instance_list), Injectab
|
|||
},
|
||||
{ throwable ->
|
||||
onFetchInstancesFailure(throwable)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ class InstanceListFragment : Fragment(R.layout.fragment_instance_list), Injectab
|
|||
binding.messageView.setup(
|
||||
R.drawable.elephant_friend_empty,
|
||||
R.string.message_empty,
|
||||
null
|
||||
null,
|
||||
)
|
||||
} else {
|
||||
binding.messageView.hide()
|
||||
|
|
|
@ -100,7 +100,7 @@ class LoginActivity : BaseActivity(), Injectable {
|
|||
|
||||
preferences = getSharedPreferences(
|
||||
getString(R.string.preferences_file_key),
|
||||
Context.MODE_PRIVATE
|
||||
Context.MODE_PRIVATE,
|
||||
)
|
||||
|
||||
binding.loginButton.setOnClickListener { onLoginClick(true) }
|
||||
|
@ -173,7 +173,7 @@ class LoginActivity : BaseActivity(), Injectable {
|
|||
getString(R.string.app_name),
|
||||
oauthRedirectUri,
|
||||
OAUTH_SCOPES,
|
||||
getString(R.string.tusky_website)
|
||||
getString(R.string.tusky_website),
|
||||
).fold(
|
||||
{ credentials ->
|
||||
// Before we open browser page we save the data.
|
||||
|
@ -196,7 +196,7 @@ class LoginActivity : BaseActivity(), Injectable {
|
|||
setLoading(false)
|
||||
Log.e(TAG, Log.getStackTraceString(e))
|
||||
return@launch
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -282,7 +282,7 @@ class LoginActivity : BaseActivity(), Injectable {
|
|||
clientSecret,
|
||||
oauthRedirectUri,
|
||||
code,
|
||||
"authorization_code"
|
||||
"authorization_code",
|
||||
).fold(
|
||||
{ accessToken ->
|
||||
fetchAccountDetails(accessToken, domain, clientId, clientSecret)
|
||||
|
@ -292,7 +292,7 @@ class LoginActivity : BaseActivity(), Injectable {
|
|||
binding.domainTextInputLayout.error =
|
||||
getString(R.string.error_retrieving_oauth_token)
|
||||
Log.e(TAG, getString(R.string.error_retrieving_oauth_token), e)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -300,11 +300,11 @@ class LoginActivity : BaseActivity(), Injectable {
|
|||
accessToken: AccessToken,
|
||||
domain: String,
|
||||
clientId: String,
|
||||
clientSecret: String
|
||||
clientSecret: String,
|
||||
) {
|
||||
mastodonApi.accountVerifyCredentials(
|
||||
domain = domain,
|
||||
auth = "Bearer ${accessToken.accessToken}"
|
||||
auth = "Bearer ${accessToken.accessToken}",
|
||||
).fold({ newAccount ->
|
||||
accountManager.addAccount(
|
||||
accessToken = accessToken.accessToken,
|
||||
|
@ -312,7 +312,7 @@ class LoginActivity : BaseActivity(), Injectable {
|
|||
clientId = clientId,
|
||||
clientSecret = clientSecret,
|
||||
oauthScopes = OAUTH_SCOPES,
|
||||
newAccount = newAccount
|
||||
newAccount = newAccount,
|
||||
)
|
||||
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
|
@ -325,7 +325,7 @@ class LoginActivity : BaseActivity(), Injectable {
|
|||
binding.domainTextInputLayout.error =
|
||||
getString(R.string.error_loading_account_details)
|
||||
Log.e(TAG, getString(R.string.error_loading_account_details), e)
|
||||
})
|
||||
},)
|
||||
}
|
||||
|
||||
private fun setLoading(loadingState: Boolean) {
|
||||
|
|
|
@ -88,7 +88,7 @@ class OauthLogin : ActivityResultContract<LoginData, LoginResult>() {
|
|||
data class LoginData(
|
||||
val domain: String,
|
||||
val url: Uri,
|
||||
val oauthRedirectUrl: Uri
|
||||
val oauthRedirectUrl: Uri,
|
||||
) : Parcelable
|
||||
|
||||
sealed class LoginResult : Parcelable {
|
||||
|
@ -149,7 +149,7 @@ class LoginWebViewActivity : BaseActivity(), Injectable {
|
|||
override fun onReceivedError(
|
||||
view: WebView,
|
||||
request: WebResourceRequest,
|
||||
error: WebResourceError
|
||||
error: WebResourceError,
|
||||
) {
|
||||
Log.d("LoginWeb", "Failed to load ${data.url}: $error")
|
||||
sendResult(LoginResult.Err(getString(R.string.error_could_not_load_login_page)))
|
||||
|
@ -157,7 +157,7 @@ class LoginWebViewActivity : BaseActivity(), Injectable {
|
|||
|
||||
override fun shouldOverrideUrlLoading(
|
||||
view: WebView,
|
||||
request: WebResourceRequest
|
||||
request: WebResourceRequest,
|
||||
): Boolean {
|
||||
return shouldOverrideUrlLoading(request.url)
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ class LoginWebViewActivity : BaseActivity(), Injectable {
|
|||
AlertDialog.Builder(this@LoginWebViewActivity)
|
||||
.setTitle(getString(R.string.instance_rule_title, data.domain))
|
||||
.setMessage(
|
||||
instanceRules.joinToString(separator = "\n\n") { "• $it" }
|
||||
instanceRules.joinToString(separator = "\n\n") { "• $it" },
|
||||
)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
|
|
|
@ -25,7 +25,7 @@ import kotlinx.coroutines.launch
|
|||
import javax.inject.Inject
|
||||
|
||||
class LoginWebViewViewModel @Inject constructor(
|
||||
private val api: MastodonApi
|
||||
private val api: MastodonApi,
|
||||
) : ViewModel() {
|
||||
|
||||
val instanceRules: MutableStateFlow<List<String>> = MutableStateFlow(emptyList())
|
||||
|
@ -40,7 +40,7 @@ class LoginWebViewViewModel @Inject constructor(
|
|||
instanceRules.value = instance.rules?.map { rule -> rule.text }.orEmpty()
|
||||
}, { throwable ->
|
||||
Log.w("LoginWebViewViewModel", "failed to load instance info", throwable)
|
||||
})
|
||||
},)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,16 +34,16 @@ import com.keylesspalace.tusky.viewdata.NotificationViewData
|
|||
class FollowViewHolder(
|
||||
private val binding: ItemFollowBinding,
|
||||
private val notificationActionListener: NotificationActionListener,
|
||||
private val linkListener: LinkListener
|
||||
private val linkListener: LinkListener,
|
||||
) : NotificationsPagingAdapter.ViewHolder, RecyclerView.ViewHolder(binding.root) {
|
||||
private val avatarRadius42dp = itemView.context.resources.getDimensionPixelSize(
|
||||
R.dimen.avatar_radius_42dp
|
||||
R.dimen.avatar_radius_42dp,
|
||||
)
|
||||
|
||||
override fun bind(
|
||||
viewData: NotificationViewData,
|
||||
payloads: List<*>?,
|
||||
statusDisplayOptions: StatusDisplayOptions
|
||||
statusDisplayOptions: StatusDisplayOptions,
|
||||
) {
|
||||
// Skip updates with payloads. That indicates a timestamp update, and
|
||||
// this view does not have timestamps.
|
||||
|
@ -53,7 +53,7 @@ class FollowViewHolder(
|
|||
viewData.account,
|
||||
viewData.type === Notification.Type.SIGN_UP,
|
||||
statusDisplayOptions.animateAvatars,
|
||||
statusDisplayOptions.animateEmojis
|
||||
statusDisplayOptions.animateEmojis,
|
||||
)
|
||||
setupButtons(notificationActionListener, viewData.account.id)
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ class FollowViewHolder(
|
|||
account: TimelineAccount,
|
||||
isSignUp: Boolean,
|
||||
animateAvatars: Boolean,
|
||||
animateEmojis: Boolean
|
||||
animateEmojis: Boolean,
|
||||
) {
|
||||
val context = binding.notificationText.context
|
||||
val format =
|
||||
|
@ -71,7 +71,7 @@ class FollowViewHolder(
|
|||
R.string.notification_sign_up_format
|
||||
} else {
|
||||
R.string.notification_follow_format
|
||||
}
|
||||
},
|
||||
)
|
||||
val wrappedDisplayName = account.name.unicodeWrap()
|
||||
val wholeMessage = String.format(format, wrappedDisplayName)
|
||||
|
@ -79,7 +79,7 @@ class FollowViewHolder(
|
|||
wholeMessage.emojify(
|
||||
account.emojis,
|
||||
binding.notificationText,
|
||||
animateEmojis
|
||||
animateEmojis,
|
||||
)
|
||||
binding.notificationText.text = emojifiedMessage
|
||||
val username = context.getString(R.string.post_username_format, account.username)
|
||||
|
@ -87,20 +87,20 @@ class FollowViewHolder(
|
|||
val emojifiedDisplayName = wrappedDisplayName.emojify(
|
||||
account.emojis,
|
||||
binding.notificationUsername,
|
||||
animateEmojis
|
||||
animateEmojis,
|
||||
)
|
||||
binding.notificationDisplayName.text = emojifiedDisplayName
|
||||
loadAvatar(
|
||||
account.avatar,
|
||||
binding.notificationAvatar,
|
||||
avatarRadius42dp,
|
||||
animateAvatars
|
||||
animateAvatars,
|
||||
)
|
||||
|
||||
val emojifiedNote = account.note.parseAsMastodonHtml().emojify(
|
||||
account.emojis,
|
||||
binding.notificationAccountNote,
|
||||
animateEmojis
|
||||
animateEmojis,
|
||||
)
|
||||
setClickableText(binding.notificationAccountNote, emojifiedNote, emptyList(), null, linkListener)
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ import kotlin.time.Duration.Companion.milliseconds
|
|||
class NotificationFetcher @Inject constructor(
|
||||
private val mastodonApi: MastodonApi,
|
||||
private val accountManager: AccountManager,
|
||||
private val context: Context
|
||||
private val context: Context,
|
||||
) {
|
||||
suspend fun fetchAndShow() {
|
||||
for (account in accountManager.getAllAccountsOrderedByActive()) {
|
||||
|
@ -93,7 +93,7 @@ class NotificationFetcher @Inject constructor(
|
|||
notificationManager,
|
||||
notification,
|
||||
account,
|
||||
index == 0
|
||||
index == 0,
|
||||
)
|
||||
notificationManager.notify(notification.id, account.id.toInt(), androidNotification)
|
||||
// Android will rate limit / drop notifications if they're posted too
|
||||
|
@ -105,7 +105,7 @@ class NotificationFetcher @Inject constructor(
|
|||
NotificationHelper.updateSummaryNotifications(
|
||||
context,
|
||||
notificationManager,
|
||||
account
|
||||
account,
|
||||
)
|
||||
|
||||
accountManager.saveAccount(account)
|
||||
|
@ -161,7 +161,7 @@ class NotificationFetcher @Inject constructor(
|
|||
val response = mastodonApi.notificationsWithAuth(
|
||||
authHeader,
|
||||
account.domain,
|
||||
minId = minId
|
||||
minId = minId,
|
||||
)
|
||||
if (!response.isSuccessful) break
|
||||
|
||||
|
@ -185,7 +185,7 @@ class NotificationFetcher @Inject constructor(
|
|||
mastodonApi.updateMarkersWithAuth(
|
||||
auth = authHeader,
|
||||
domain = account.domain,
|
||||
notificationsLastReadId = newMarkerId
|
||||
notificationsLastReadId = newMarkerId,
|
||||
)
|
||||
account.notificationMarkerId = newMarkerId
|
||||
accountManager.saveAccount(account)
|
||||
|
@ -199,7 +199,7 @@ class NotificationFetcher @Inject constructor(
|
|||
val allMarkers = mastodonApi.markersWithAuth(
|
||||
authHeader,
|
||||
account.domain,
|
||||
listOf("notifications")
|
||||
listOf("notifications"),
|
||||
)
|
||||
val notificationMarker = allMarkers["notifications"]
|
||||
Log.d(TAG, "Fetched marker for ${account.fullName}: $notificationMarker")
|
||||
|
|
|
@ -114,14 +114,14 @@ class NotificationsFragment :
|
|||
statusActionListener = this,
|
||||
notificationActionListener = this,
|
||||
accountActionListener = this,
|
||||
statusDisplayOptions = viewModel.statusDisplayOptions.value
|
||||
statusDisplayOptions = viewModel.statusDisplayOptions.value,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
savedInstanceState: Bundle?,
|
||||
): View {
|
||||
return inflater.inflate(R.layout.fragment_timeline_notifications, container, false)
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ class NotificationsFragment :
|
|||
binding.recyclerView.setAccessibilityDelegateCompat(
|
||||
ListStatusAccessibilityDelegate(
|
||||
binding.recyclerView,
|
||||
this
|
||||
this,
|
||||
) { pos: Int ->
|
||||
val notification = adapter.snapshot().getOrNull(pos)
|
||||
// We support replies only for now
|
||||
|
@ -158,49 +158,51 @@ class NotificationsFragment :
|
|||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
binding.recyclerView.addItemDecoration(
|
||||
DividerItemDecoration(
|
||||
context,
|
||||
DividerItemDecoration.VERTICAL
|
||||
)
|
||||
DividerItemDecoration.VERTICAL,
|
||||
),
|
||||
)
|
||||
|
||||
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
val actionButton = (activity as ActionButtonActivity).actionButton
|
||||
binding.recyclerView.addOnScrollListener(
|
||||
object : RecyclerView.OnScrollListener() {
|
||||
val actionButton = (activity as ActionButtonActivity).actionButton
|
||||
|
||||
override fun onScrolled(view: RecyclerView, dx: Int, dy: Int) {
|
||||
actionButton?.let { fab ->
|
||||
if (!viewModel.uiState.value.showFabWhileScrolling) {
|
||||
if (dy > 0 && fab.isShown) {
|
||||
fab.hide() // Hide when scrolling down
|
||||
} else if (dy < 0 && !fab.isShown) {
|
||||
fab.show() // Show when scrolling up
|
||||
override fun onScrolled(view: RecyclerView, dx: Int, dy: Int) {
|
||||
actionButton?.let { fab ->
|
||||
if (!viewModel.uiState.value.showFabWhileScrolling) {
|
||||
if (dy > 0 && fab.isShown) {
|
||||
fab.hide() // Hide when scrolling down
|
||||
} else if (dy < 0 && !fab.isShown) {
|
||||
fab.show() // Show when scrolling up
|
||||
}
|
||||
} else if (!fab.isShown) {
|
||||
fab.show()
|
||||
}
|
||||
} else if (!fab.isShown) {
|
||||
fab.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("SyntheticAccessor")
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
newState != SCROLL_STATE_IDLE && return
|
||||
@Suppress("SyntheticAccessor")
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
newState != SCROLL_STATE_IDLE && return
|
||||
|
||||
// Save the ID of the first notification visible in the list, so the user's
|
||||
// reading position is always restorable.
|
||||
layoutManager.findFirstVisibleItemPosition().takeIf { it != NO_POSITION }?.let { position ->
|
||||
adapter.snapshot().getOrNull(position)?.id?.let { id ->
|
||||
viewModel.accept(InfallibleUiAction.SaveVisibleId(visibleId = id))
|
||||
// Save the ID of the first notification visible in the list, so the user's
|
||||
// reading position is always restorable.
|
||||
layoutManager.findFirstVisibleItemPosition().takeIf { it != NO_POSITION }?.let { position ->
|
||||
adapter.snapshot().getOrNull(position)?.id?.let { id ->
|
||||
viewModel.accept(InfallibleUiAction.SaveVisibleId(visibleId = id))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
binding.recyclerView.adapter = adapter.withLoadStateHeaderAndFooter(
|
||||
header = TimelineLoadStateAdapter { adapter.retry() },
|
||||
footer = TimelineLoadStateAdapter { adapter.retry() }
|
||||
footer = TimelineLoadStateAdapter { adapter.retry() },
|
||||
)
|
||||
|
||||
(binding.recyclerView.itemAnimator as SimpleItemAnimator?)!!.supportsChangeAnimations =
|
||||
|
@ -241,13 +243,13 @@ class NotificationsFragment :
|
|||
val message = getString(
|
||||
error.message,
|
||||
error.throwable.localizedMessage
|
||||
?: getString(R.string.ui_error_unknown)
|
||||
?: getString(R.string.ui_error_unknown),
|
||||
)
|
||||
val snackbar = Snackbar.make(
|
||||
// Without this the FAB will not move out of the way
|
||||
(activity as ActionButtonActivity).actionButton ?: binding.root,
|
||||
message,
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
Snackbar.LENGTH_INDEFINITE,
|
||||
).setTextMaxLines(5)
|
||||
error.action?.let { action ->
|
||||
snackbar.setAction(R.string.action_retry) {
|
||||
|
@ -281,14 +283,15 @@ class NotificationsFragment :
|
|||
Snackbar.make(
|
||||
(activity as ActionButtonActivity).actionButton ?: binding.root,
|
||||
getString(it.msg),
|
||||
Snackbar.LENGTH_SHORT
|
||||
Snackbar.LENGTH_SHORT,
|
||||
).show()
|
||||
|
||||
when (it) {
|
||||
// The follow request is no longer valid, refresh the adapter to
|
||||
// remove it.
|
||||
is NotificationActionSuccess.AcceptFollowRequest,
|
||||
is NotificationActionSuccess.RejectFollowRequest -> adapter.refresh()
|
||||
is NotificationActionSuccess.RejectFollowRequest,
|
||||
-> adapter.refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -318,11 +321,11 @@ class NotificationsFragment :
|
|||
statusViewData.status.copy(reblogged = it.action.state)
|
||||
is StatusActionSuccess.VoteInPoll ->
|
||||
statusViewData.status.copy(
|
||||
poll = it.action.poll.votedCopy(it.action.choices)
|
||||
poll = it.action.poll.votedCopy(it.action.choices),
|
||||
)
|
||||
}
|
||||
indexedViewData.value?.statusViewData = statusViewData.copy(
|
||||
status = status
|
||||
status = status,
|
||||
)
|
||||
|
||||
adapter.notifyItemChanged(indexedViewData.index)
|
||||
|
@ -402,7 +405,7 @@ class NotificationsFragment :
|
|||
getView() ?: return@post
|
||||
binding.recyclerView.smoothScrollBy(
|
||||
0,
|
||||
Utils.dpToPx(requireContext(), -30)
|
||||
Utils.dpToPx(requireContext(), -30),
|
||||
)
|
||||
}
|
||||
peeked = true
|
||||
|
@ -441,7 +444,7 @@ class NotificationsFragment :
|
|||
if (adapter.itemCount == 0) {
|
||||
binding.statusView.setup(
|
||||
R.drawable.elephant_friend_empty,
|
||||
R.string.message_empty
|
||||
R.string.message_empty,
|
||||
)
|
||||
binding.recyclerView.hide()
|
||||
binding.statusView.show()
|
||||
|
@ -559,7 +562,7 @@ class NotificationsFragment :
|
|||
super.viewMedia(
|
||||
attachmentIndex,
|
||||
list(status, viewModel.statusDisplayOptions.value.showSensitiveMedia),
|
||||
view
|
||||
view,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -576,7 +579,7 @@ class NotificationsFragment :
|
|||
override fun onExpandedChange(expanded: Boolean, position: Int) {
|
||||
val notificationViewData = adapter.snapshot()[position] ?: return
|
||||
notificationViewData.statusViewData = notificationViewData.statusViewData?.copy(
|
||||
isExpanded = expanded
|
||||
isExpanded = expanded,
|
||||
)
|
||||
adapter.notifyItemChanged(position)
|
||||
}
|
||||
|
@ -584,7 +587,7 @@ class NotificationsFragment :
|
|||
override fun onContentHiddenChange(isShowing: Boolean, position: Int) {
|
||||
val notificationViewData = adapter.snapshot()[position] ?: return
|
||||
notificationViewData.statusViewData = notificationViewData.statusViewData?.copy(
|
||||
isShowingContent = isShowing
|
||||
isShowingContent = isShowing,
|
||||
)
|
||||
adapter.notifyItemChanged(position)
|
||||
}
|
||||
|
@ -592,7 +595,7 @@ class NotificationsFragment :
|
|||
override fun onContentCollapsedChange(isCollapsed: Boolean, position: Int) {
|
||||
val notificationViewData = adapter.snapshot()[position] ?: return
|
||||
notificationViewData.statusViewData = notificationViewData.statusViewData?.copy(
|
||||
isCollapsed = isCollapsed
|
||||
isCollapsed = isCollapsed,
|
||||
)
|
||||
adapter.notifyItemChanged(position)
|
||||
}
|
||||
|
@ -604,7 +607,7 @@ class NotificationsFragment :
|
|||
override fun clearWarningAction(position: Int) {
|
||||
val notificationViewData = adapter.snapshot()[position] ?: return
|
||||
notificationViewData.statusViewData = notificationViewData.statusViewData?.copy(
|
||||
filterAction = Filter.Action.NONE
|
||||
filterAction = Filter.Action.NONE,
|
||||
)
|
||||
adapter.notifyItemChanged(position)
|
||||
}
|
||||
|
@ -654,7 +657,7 @@ class NotificationsFragment :
|
|||
|
||||
override fun onViewReport(reportId: String) {
|
||||
requireContext().openLink(
|
||||
"https://${viewModel.account.domain}/admin/reports/$reportId"
|
||||
"https://${viewModel.account.domain}/admin/reports/$reportId",
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -676,21 +679,21 @@ class NotificationsFragment :
|
|||
object : DiffUtil.ItemCallback<NotificationViewData>() {
|
||||
override fun areItemsTheSame(
|
||||
oldItem: NotificationViewData,
|
||||
newItem: NotificationViewData
|
||||
newItem: NotificationViewData,
|
||||
): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: NotificationViewData,
|
||||
newItem: NotificationViewData
|
||||
newItem: NotificationViewData,
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getChangePayload(
|
||||
oldItem: NotificationViewData,
|
||||
newItem: NotificationViewData
|
||||
newItem: NotificationViewData,
|
||||
): Any? {
|
||||
return if (oldItem == newItem) {
|
||||
// If items are equal - update timestamp only
|
||||
|
@ -706,7 +709,7 @@ class NotificationsFragment :
|
|||
|
||||
class FilterDialogFragment(
|
||||
private val activeFilter: Set<Notification.Type>,
|
||||
private val listener: ((filter: Set<Notification.Type>) -> Unit)
|
||||
private val listener: ((filter: Set<Notification.Type>) -> Unit),
|
||||
) : DialogFragment() {
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val context = requireContext()
|
||||
|
|
|
@ -52,20 +52,24 @@ enum class NotificationViewKind {
|
|||
FOLLOW,
|
||||
FOLLOW_REQUEST,
|
||||
REPORT,
|
||||
UNKNOWN;
|
||||
UNKNOWN,
|
||||
;
|
||||
|
||||
companion object {
|
||||
fun from(kind: Notification.Type?): NotificationViewKind {
|
||||
return when (kind) {
|
||||
Notification.Type.MENTION,
|
||||
Notification.Type.POLL,
|
||||
Notification.Type.UNKNOWN -> STATUS
|
||||
Notification.Type.UNKNOWN,
|
||||
-> STATUS
|
||||
Notification.Type.FAVOURITE,
|
||||
Notification.Type.REBLOG,
|
||||
Notification.Type.STATUS,
|
||||
Notification.Type.UPDATE -> NOTIFICATION
|
||||
Notification.Type.UPDATE,
|
||||
-> NOTIFICATION
|
||||
Notification.Type.FOLLOW,
|
||||
Notification.Type.SIGN_UP -> FOLLOW
|
||||
Notification.Type.SIGN_UP,
|
||||
-> FOLLOW
|
||||
Notification.Type.FOLLOW_REQUEST -> FOLLOW_REQUEST
|
||||
Notification.Type.REPORT -> REPORT
|
||||
null -> UNKNOWN
|
||||
|
@ -106,7 +110,7 @@ class NotificationsPagingAdapter(
|
|||
private val statusActionListener: StatusActionListener,
|
||||
private val notificationActionListener: NotificationActionListener,
|
||||
private val accountActionListener: AccountActionListener,
|
||||
var statusDisplayOptions: StatusDisplayOptions
|
||||
var statusDisplayOptions: StatusDisplayOptions,
|
||||
) : PagingDataAdapter<NotificationViewData, RecyclerView.ViewHolder>(diffCallback) {
|
||||
|
||||
private val absoluteTimeFormatter = AbsoluteTimeFormatter()
|
||||
|
@ -117,7 +121,7 @@ class NotificationsPagingAdapter(
|
|||
fun bind(
|
||||
viewData: NotificationViewData,
|
||||
payloads: List<*>?,
|
||||
statusDisplayOptions: StatusDisplayOptions
|
||||
statusDisplayOptions: StatusDisplayOptions,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -138,14 +142,14 @@ class NotificationsPagingAdapter(
|
|||
StatusViewHolder(
|
||||
ItemStatusBinding.inflate(inflater, parent, false),
|
||||
statusActionListener,
|
||||
accountId
|
||||
accountId,
|
||||
)
|
||||
}
|
||||
NotificationViewKind.STATUS_FILTERED -> {
|
||||
StatusViewHolder(
|
||||
ItemStatusWrapperBinding.inflate(inflater, parent, false),
|
||||
statusActionListener,
|
||||
accountId
|
||||
accountId,
|
||||
)
|
||||
}
|
||||
NotificationViewKind.NOTIFICATION -> {
|
||||
|
@ -153,14 +157,14 @@ class NotificationsPagingAdapter(
|
|||
ItemStatusNotificationBinding.inflate(inflater, parent, false),
|
||||
statusActionListener,
|
||||
notificationActionListener,
|
||||
absoluteTimeFormatter
|
||||
absoluteTimeFormatter,
|
||||
)
|
||||
}
|
||||
NotificationViewKind.FOLLOW -> {
|
||||
FollowViewHolder(
|
||||
ItemFollowBinding.inflate(inflater, parent, false),
|
||||
notificationActionListener,
|
||||
statusActionListener
|
||||
statusActionListener,
|
||||
)
|
||||
}
|
||||
NotificationViewKind.FOLLOW_REQUEST -> {
|
||||
|
@ -168,18 +172,18 @@ class NotificationsPagingAdapter(
|
|||
ItemFollowRequestBinding.inflate(inflater, parent, false),
|
||||
accountActionListener,
|
||||
statusActionListener,
|
||||
showHeader = true
|
||||
showHeader = true,
|
||||
)
|
||||
}
|
||||
NotificationViewKind.REPORT -> {
|
||||
ReportNotificationViewHolder(
|
||||
ItemReportNotificationBinding.inflate(inflater, parent, false),
|
||||
notificationActionListener
|
||||
notificationActionListener,
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
FallbackNotificationViewHolder(
|
||||
SimpleListItem1Binding.inflate(inflater, parent, false)
|
||||
SimpleListItem1Binding.inflate(inflater, parent, false),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -192,7 +196,7 @@ class NotificationsPagingAdapter(
|
|||
override fun onBindViewHolder(
|
||||
holder: RecyclerView.ViewHolder,
|
||||
position: Int,
|
||||
payloads: MutableList<Any>
|
||||
payloads: MutableList<Any>,
|
||||
) {
|
||||
bindViewHolder(holder, position, payloads)
|
||||
}
|
||||
|
@ -200,7 +204,7 @@ class NotificationsPagingAdapter(
|
|||
private fun bindViewHolder(
|
||||
holder: RecyclerView.ViewHolder,
|
||||
position: Int,
|
||||
payloads: List<*>?
|
||||
payloads: List<*>?,
|
||||
) {
|
||||
getItem(position)?.let { (holder as ViewHolder).bind(it, payloads, statusDisplayOptions) }
|
||||
}
|
||||
|
@ -210,12 +214,12 @@ class NotificationsPagingAdapter(
|
|||
* be used, but is useful when migrating code.
|
||||
*/
|
||||
private class FallbackNotificationViewHolder(
|
||||
val binding: SimpleListItem1Binding
|
||||
val binding: SimpleListItem1Binding,
|
||||
) : ViewHolder, RecyclerView.ViewHolder(binding.root) {
|
||||
override fun bind(
|
||||
viewData: NotificationViewData,
|
||||
payloads: List<*>?,
|
||||
statusDisplayOptions: StatusDisplayOptions
|
||||
statusDisplayOptions: StatusDisplayOptions,
|
||||
) {
|
||||
binding.text1.text = viewData.statusViewData?.content
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ private val INVALID = LoadResult.Invalid<String, Notification>()
|
|||
class NotificationsPagingSource @Inject constructor(
|
||||
private val mastodonApi: MastodonApi,
|
||||
private val gson: Gson,
|
||||
private val notificationFilter: Set<Notification.Type>
|
||||
private val notificationFilter: Set<Notification.Type>,
|
||||
) : PagingSource<String, Notification>() {
|
||||
override suspend fun load(params: LoadParams<String>): LoadResult<String, Notification> {
|
||||
Log.d(TAG, "load() with ${params.javaClass.simpleName} for key: ${params.key}")
|
||||
|
@ -50,12 +50,12 @@ class NotificationsPagingSource @Inject constructor(
|
|||
is LoadParams.Append -> mastodonApi.notifications(
|
||||
maxId = params.key,
|
||||
limit = params.loadSize,
|
||||
excludes = notificationFilter
|
||||
excludes = notificationFilter,
|
||||
)
|
||||
is LoadParams.Prepend -> mastodonApi.notifications(
|
||||
minId = params.key,
|
||||
limit = params.loadSize,
|
||||
excludes = notificationFilter
|
||||
excludes = notificationFilter,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ class NotificationsPagingSource @Inject constructor(
|
|||
return LoadResult.Page(
|
||||
data = response.body()!!,
|
||||
nextKey = links.next,
|
||||
prevKey = links.prev
|
||||
prevKey = links.prev,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
return LoadResult.Error(e)
|
||||
|
@ -116,7 +116,7 @@ class NotificationsPagingSource @Inject constructor(
|
|||
val key = params.key
|
||||
?: return@coroutineScope mastodonApi.notifications(
|
||||
limit = params.loadSize,
|
||||
excludes = notificationFilter
|
||||
excludes = notificationFilter,
|
||||
)
|
||||
|
||||
// It's important to return *something* from this state. If an empty page is returned
|
||||
|
@ -193,7 +193,7 @@ class NotificationsPagingSource @Inject constructor(
|
|||
// Everything failed -- fallback to fetching the most recent notifications
|
||||
return@coroutineScope mastodonApi.notifications(
|
||||
limit = params.loadSize,
|
||||
excludes = notificationFilter
|
||||
excludes = notificationFilter,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ import javax.inject.Inject
|
|||
class NotificationsRepository @Inject constructor(
|
||||
private val mastodonApi: MastodonApi,
|
||||
private val gson: Gson,
|
||||
@ApplicationScope private val externalScope: CoroutineScope
|
||||
@ApplicationScope private val externalScope: CoroutineScope,
|
||||
) {
|
||||
private var factory: InvalidatingPagingSourceFactory<String, Notification>? = null
|
||||
|
||||
|
@ -48,7 +48,7 @@ class NotificationsRepository @Inject constructor(
|
|||
fun getNotificationsStream(
|
||||
filter: Set<Notification.Type>,
|
||||
pageSize: Int = PAGE_SIZE,
|
||||
initialKey: String? = null
|
||||
initialKey: String? = null,
|
||||
): Flow<PagingData<Notification>> {
|
||||
Log.d(TAG, "getNotificationsStream(), filtering: $filter")
|
||||
|
||||
|
@ -59,7 +59,7 @@ class NotificationsRepository @Inject constructor(
|
|||
return Pager(
|
||||
config = PagingConfig(pageSize = pageSize, initialLoadSize = pageSize),
|
||||
initialKey = initialKey,
|
||||
pagingSourceFactory = factory!!
|
||||
pagingSourceFactory = factory!!,
|
||||
).flow
|
||||
}
|
||||
|
||||
|
|
|
@ -82,17 +82,17 @@ data class UiState(
|
|||
val activeFilter: Set<Notification.Type> = emptySet(),
|
||||
|
||||
/** True if the FAB should be shown while scrolling */
|
||||
val showFabWhileScrolling: Boolean = true
|
||||
val showFabWhileScrolling: Boolean = true,
|
||||
)
|
||||
|
||||
/** Preferences the UI reacts to */
|
||||
data class UiPrefs(
|
||||
val showFabWhileScrolling: Boolean
|
||||
val showFabWhileScrolling: Boolean,
|
||||
) {
|
||||
companion object {
|
||||
/** Relevant preference keys. Changes to any of these trigger a display update */
|
||||
val prefKeys = setOf(
|
||||
PrefKeys.FAB_HIDE
|
||||
PrefKeys.FAB_HIDE,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ sealed class NotificationActionSuccess(
|
|||
* The original action, in case additional information is required from it to display the
|
||||
* message.
|
||||
*/
|
||||
open val action: NotificationAction
|
||||
open val action: NotificationAction,
|
||||
) : UiSuccess() {
|
||||
data class AcceptFollowRequest(override val action: NotificationAction) :
|
||||
NotificationActionSuccess(R.string.ui_success_accepted_follow_request, action)
|
||||
|
@ -181,7 +181,7 @@ sealed class NotificationActionSuccess(
|
|||
|
||||
/** Actions the user can trigger on an individual status */
|
||||
sealed class StatusAction(
|
||||
open val statusViewData: StatusViewData
|
||||
open val statusViewData: StatusViewData,
|
||||
) : FallibleUiAction() {
|
||||
/** Set the bookmark state for a status */
|
||||
data class Bookmark(val state: Boolean, override val statusViewData: StatusViewData) :
|
||||
|
@ -199,7 +199,7 @@ sealed class StatusAction(
|
|||
data class VoteInPoll(
|
||||
val poll: Poll,
|
||||
val choices: List<Int>,
|
||||
override val statusViewData: StatusViewData
|
||||
override val statusViewData: StatusViewData,
|
||||
) : StatusAction(statusViewData)
|
||||
}
|
||||
|
||||
|
@ -236,45 +236,45 @@ sealed class UiError(
|
|||
@StringRes val message: Int,
|
||||
|
||||
/** The action that failed. Can be resent to retry the action */
|
||||
open val action: UiAction? = null
|
||||
open val action: UiAction? = null,
|
||||
) {
|
||||
data class ClearNotifications(override val throwable: Throwable) : UiError(
|
||||
throwable,
|
||||
R.string.ui_error_clear_notifications
|
||||
R.string.ui_error_clear_notifications,
|
||||
)
|
||||
|
||||
data class Bookmark(
|
||||
override val throwable: Throwable,
|
||||
override val action: StatusAction.Bookmark
|
||||
override val action: StatusAction.Bookmark,
|
||||
) : UiError(throwable, R.string.ui_error_bookmark, action)
|
||||
|
||||
data class Favourite(
|
||||
override val throwable: Throwable,
|
||||
override val action: StatusAction.Favourite
|
||||
override val action: StatusAction.Favourite,
|
||||
) : UiError(throwable, R.string.ui_error_favourite, action)
|
||||
|
||||
data class Reblog(
|
||||
override val throwable: Throwable,
|
||||
override val action: StatusAction.Reblog
|
||||
override val action: StatusAction.Reblog,
|
||||
) : UiError(throwable, R.string.ui_error_reblog, action)
|
||||
|
||||
data class VoteInPoll(
|
||||
override val throwable: Throwable,
|
||||
override val action: StatusAction.VoteInPoll
|
||||
override val action: StatusAction.VoteInPoll,
|
||||
) : UiError(throwable, R.string.ui_error_vote, action)
|
||||
|
||||
data class AcceptFollowRequest(
|
||||
override val throwable: Throwable,
|
||||
override val action: NotificationAction.AcceptFollowRequest
|
||||
override val action: NotificationAction.AcceptFollowRequest,
|
||||
) : UiError(throwable, R.string.ui_error_accept_follow_request, action)
|
||||
|
||||
data class RejectFollowRequest(
|
||||
override val throwable: Throwable,
|
||||
override val action: NotificationAction.RejectFollowRequest
|
||||
override val action: NotificationAction.RejectFollowRequest,
|
||||
) : UiError(throwable, R.string.ui_error_reject_follow_request, action)
|
||||
|
||||
data class GetFilters(
|
||||
override val throwable: Throwable
|
||||
override val throwable: Throwable,
|
||||
) : UiError(throwable, R.string.ui_error_filter_v1_load, null)
|
||||
|
||||
companion object {
|
||||
|
@ -298,7 +298,7 @@ class NotificationsViewModel @Inject constructor(
|
|||
private val timelineCases: TimelineCases,
|
||||
private val eventHub: EventHub,
|
||||
private val filtersRepository: FiltersRepository,
|
||||
private val filterModel: FilterModel
|
||||
private val filterModel: FilterModel,
|
||||
) : ViewModel() {
|
||||
/** The account to display notifications for */
|
||||
val account = accountManager.activeAccount!!
|
||||
|
@ -355,8 +355,8 @@ class NotificationsViewModel @Inject constructor(
|
|||
.onStart {
|
||||
emit(
|
||||
InfallibleUiAction.ApplyFilter(
|
||||
filter = deserialize(account.notificationsFilter)
|
||||
)
|
||||
filter = deserialize(account.notificationsFilter),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -391,8 +391,8 @@ class NotificationsViewModel @Inject constructor(
|
|||
statusDisplayOptions = MutableStateFlow(
|
||||
StatusDisplayOptions.from(
|
||||
preferences,
|
||||
account
|
||||
)
|
||||
account,
|
||||
),
|
||||
)
|
||||
|
||||
viewModelScope.launch {
|
||||
|
@ -403,7 +403,7 @@ class NotificationsViewModel @Inject constructor(
|
|||
statusDisplayOptions.value.make(
|
||||
preferences,
|
||||
it.preferenceKey,
|
||||
account
|
||||
account,
|
||||
)
|
||||
}
|
||||
.collect {
|
||||
|
@ -458,23 +458,23 @@ class NotificationsViewModel @Inject constructor(
|
|||
is StatusAction.Bookmark ->
|
||||
timelineCases.bookmark(
|
||||
action.statusViewData.actionableId,
|
||||
action.state
|
||||
action.state,
|
||||
)
|
||||
is StatusAction.Favourite ->
|
||||
timelineCases.favourite(
|
||||
action.statusViewData.actionableId,
|
||||
action.state
|
||||
action.state,
|
||||
)
|
||||
is StatusAction.Reblog ->
|
||||
timelineCases.reblog(
|
||||
action.statusViewData.actionableId,
|
||||
action.state
|
||||
action.state,
|
||||
)
|
||||
is StatusAction.VoteInPoll ->
|
||||
timelineCases.voteInPoll(
|
||||
action.statusViewData.actionableId,
|
||||
action.poll.id,
|
||||
action.choices
|
||||
action.choices,
|
||||
)
|
||||
}.getOrThrow()
|
||||
uiSuccess.emit(StatusActionSuccess.from(action))
|
||||
|
@ -516,18 +516,18 @@ class NotificationsViewModel @Inject constructor(
|
|||
uiState = combine(notificationFilter, getUiPrefs()) { filter, prefs ->
|
||||
UiState(
|
||||
activeFilter = filter.filter,
|
||||
showFabWhileScrolling = prefs.showFabWhileScrolling
|
||||
showFabWhileScrolling = prefs.showFabWhileScrolling,
|
||||
)
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5000),
|
||||
initialValue = UiState()
|
||||
initialValue = UiState(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getNotifications(
|
||||
filters: Set<Notification.Type>,
|
||||
initialKey: String? = null
|
||||
initialKey: String? = null,
|
||||
): Flow<PagingData<NotificationViewData>> {
|
||||
Log.d(TAG, "getNotifications: $initialKey")
|
||||
return repository.getNotificationsStream(filter = filters, initialKey = initialKey)
|
||||
|
@ -539,7 +539,7 @@ class NotificationsViewModel @Inject constructor(
|
|||
!(notification.status?.actionableStatus?.sensitive ?: false),
|
||||
isExpanded = statusDisplayOptions.value.openSpoiler,
|
||||
isCollapsed = true,
|
||||
filterAction = filterAction
|
||||
filterAction = filterAction,
|
||||
)
|
||||
}.filter {
|
||||
it.statusViewData?.filterAction != Filter.Action.HIDE
|
||||
|
@ -561,7 +561,7 @@ class NotificationsViewModel @Inject constructor(
|
|||
filterModel.initWithFilters(
|
||||
filters.filters.filter {
|
||||
it.context.contains("notifications")
|
||||
}
|
||||
},
|
||||
)
|
||||
repository.invalidate()
|
||||
}
|
||||
|
@ -594,7 +594,7 @@ class NotificationsViewModel @Inject constructor(
|
|||
.onStart { emit(toPrefs()) }
|
||||
|
||||
private fun toPrefs() = UiPrefs(
|
||||
showFabWhileScrolling = !preferences.getBoolean(PrefKeys.FAB_HIDE, false)
|
||||
showFabWhileScrolling = !preferences.getBoolean(PrefKeys.FAB_HIDE, false),
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -55,7 +55,7 @@ fun showMigrationNoticeIfNecessary(
|
|||
context: Context,
|
||||
parent: View,
|
||||
anchorView: View?,
|
||||
accountManager: AccountManager
|
||||
accountManager: AccountManager,
|
||||
) {
|
||||
// No point showing anything if we cannot enable it
|
||||
if (!isUnifiedPushAvailable(context)) return
|
||||
|
@ -163,7 +163,7 @@ suspend fun registerUnifiedPushEndpoint(
|
|||
api: MastodonApi,
|
||||
accountManager: AccountManager,
|
||||
account: AccountEntity,
|
||||
endpoint: String
|
||||
endpoint: String,
|
||||
) = withContext(Dispatchers.IO) {
|
||||
// Generate a prime256v1 key pair for WebPush
|
||||
// Decryption is unimplemented for now, since Mastodon uses an old WebPush
|
||||
|
@ -179,7 +179,7 @@ suspend fun registerUnifiedPushEndpoint(
|
|||
endpoint,
|
||||
keyPair.pubkey,
|
||||
auth,
|
||||
buildSubscriptionData(context, account)
|
||||
buildSubscriptionData(context, account),
|
||||
).onFailure { throwable ->
|
||||
Log.w(TAG, "Error setting push endpoint for account ${account.id}", throwable)
|
||||
disableUnifiedPushNotificationsForAccount(context, account)
|
||||
|
@ -201,7 +201,7 @@ suspend fun updateUnifiedPushSubscription(context: Context, api: MastodonApi, ac
|
|||
api.updatePushNotificationSubscription(
|
||||
"Bearer ${account.accessToken}",
|
||||
account.domain,
|
||||
buildSubscriptionData(context, account)
|
||||
buildSubscriptionData(context, account),
|
||||
).onSuccess {
|
||||
Log.d(TAG, "UnifiedPush subscription updated for account ${account.id}")
|
||||
|
||||
|
|
|
@ -67,22 +67,22 @@ internal class StatusNotificationViewHolder(
|
|||
private val binding: ItemStatusNotificationBinding,
|
||||
private val statusActionListener: StatusActionListener,
|
||||
private val notificationActionListener: NotificationActionListener,
|
||||
private val absoluteTimeFormatter: AbsoluteTimeFormatter
|
||||
private val absoluteTimeFormatter: AbsoluteTimeFormatter,
|
||||
) : NotificationsPagingAdapter.ViewHolder, RecyclerView.ViewHolder(binding.root) {
|
||||
private val avatarRadius48dp = itemView.context.resources.getDimensionPixelSize(
|
||||
R.dimen.avatar_radius_48dp
|
||||
R.dimen.avatar_radius_48dp,
|
||||
)
|
||||
private val avatarRadius36dp = itemView.context.resources.getDimensionPixelSize(
|
||||
R.dimen.avatar_radius_36dp
|
||||
R.dimen.avatar_radius_36dp,
|
||||
)
|
||||
private val avatarRadius24dp = itemView.context.resources.getDimensionPixelSize(
|
||||
R.dimen.avatar_radius_24dp
|
||||
R.dimen.avatar_radius_24dp,
|
||||
)
|
||||
|
||||
override fun bind(
|
||||
viewData: NotificationViewData,
|
||||
payloads: List<*>?,
|
||||
statusDisplayOptions: StatusDisplayOptions
|
||||
statusDisplayOptions: StatusDisplayOptions,
|
||||
) {
|
||||
val statusViewData = viewData.statusViewData
|
||||
if (payloads.isNullOrEmpty()) {
|
||||
|
@ -103,13 +103,13 @@ internal class StatusNotificationViewHolder(
|
|||
account.avatar,
|
||||
account.bot,
|
||||
statusDisplayOptions.animateAvatars,
|
||||
statusDisplayOptions.showBotOverlay
|
||||
statusDisplayOptions.showBotOverlay,
|
||||
)
|
||||
} else {
|
||||
setAvatars(
|
||||
account.avatar,
|
||||
viewData.account.avatar,
|
||||
statusDisplayOptions.animateAvatars
|
||||
statusDisplayOptions.animateAvatars,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -129,7 +129,7 @@ internal class StatusNotificationViewHolder(
|
|||
if (StatusBaseViewHolder.Key.KEY_CREATED == item && statusViewData != null) {
|
||||
setCreatedAt(
|
||||
statusViewData.status.actionableStatus.createdAt,
|
||||
statusDisplayOptions.useAbsoluteTime
|
||||
statusDisplayOptions.useAbsoluteTime,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ internal class StatusNotificationViewHolder(
|
|||
then,
|
||||
now,
|
||||
DateUtils.SECOND_IN_MILLIS,
|
||||
DateUtils.FORMAT_ABBREV_RELATIVE
|
||||
DateUtils.FORMAT_ABBREV_RELATIVE,
|
||||
)
|
||||
} else {
|
||||
// unknown minutes~
|
||||
|
@ -193,7 +193,7 @@ internal class StatusNotificationViewHolder(
|
|||
private fun getIconWithColor(
|
||||
context: Context,
|
||||
@DrawableRes drawable: Int,
|
||||
@ColorRes color: Int
|
||||
@ColorRes color: Int,
|
||||
): Drawable? {
|
||||
val icon = ContextCompat.getDrawable(context, drawable)
|
||||
icon?.setColorFilter(context.getColor(color), PorterDuff.Mode.SRC_ATOP)
|
||||
|
@ -206,7 +206,7 @@ internal class StatusNotificationViewHolder(
|
|||
statusAvatarUrl,
|
||||
binding.notificationStatusAvatar,
|
||||
avatarRadius48dp,
|
||||
animateAvatars
|
||||
animateAvatars,
|
||||
)
|
||||
if (showBotOverlay && isBot) {
|
||||
binding.notificationNotificationAvatar.visibility = View.VISIBLE
|
||||
|
@ -225,21 +225,21 @@ internal class StatusNotificationViewHolder(
|
|||
statusAvatarUrl,
|
||||
binding.notificationStatusAvatar,
|
||||
avatarRadius36dp,
|
||||
animateAvatars
|
||||
animateAvatars,
|
||||
)
|
||||
binding.notificationNotificationAvatar.visibility = View.VISIBLE
|
||||
loadAvatar(
|
||||
notificationAvatarUrl,
|
||||
binding.notificationNotificationAvatar,
|
||||
avatarRadius24dp,
|
||||
animateAvatars
|
||||
animateAvatars,
|
||||
)
|
||||
}
|
||||
|
||||
fun setMessage(
|
||||
notificationViewData: NotificationViewData,
|
||||
listener: LinkListener,
|
||||
animateEmojis: Boolean
|
||||
animateEmojis: Boolean,
|
||||
) {
|
||||
val statusViewData = notificationViewData.statusViewData
|
||||
val displayName = notificationViewData.account.name.unicodeWrap()
|
||||
|
@ -273,7 +273,7 @@ internal class StatusNotificationViewHolder(
|
|||
icon,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
null,
|
||||
)
|
||||
val wholeMessage = String.format(format, displayName)
|
||||
val str = SpannableStringBuilder(wholeMessage)
|
||||
|
@ -282,12 +282,12 @@ internal class StatusNotificationViewHolder(
|
|||
StyleSpan(Typeface.BOLD),
|
||||
displayNameIndex,
|
||||
displayNameIndex + displayName.length,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
|
||||
)
|
||||
val emojifiedText = str.emojify(
|
||||
notificationViewData.account.emojis,
|
||||
binding.notificationTopText,
|
||||
animateEmojis
|
||||
animateEmojis,
|
||||
)
|
||||
binding.notificationTopText.text = emojifiedText
|
||||
if (statusViewData != null) {
|
||||
|
@ -298,18 +298,18 @@ internal class StatusNotificationViewHolder(
|
|||
if (hasSpoiler) View.VISIBLE else View.GONE
|
||||
if (statusViewData.isExpanded) {
|
||||
binding.notificationContentWarningButton.setText(
|
||||
R.string.post_content_warning_show_less
|
||||
R.string.post_content_warning_show_less,
|
||||
)
|
||||
} else {
|
||||
binding.notificationContentWarningButton.setText(
|
||||
R.string.post_content_warning_show_more
|
||||
R.string.post_content_warning_show_more,
|
||||
)
|
||||
}
|
||||
binding.notificationContentWarningButton.setOnClickListener {
|
||||
if (bindingAdapterPosition != RecyclerView.NO_POSITION) {
|
||||
notificationActionListener.onExpandedChange(
|
||||
!statusViewData.isExpanded,
|
||||
bindingAdapterPosition
|
||||
bindingAdapterPosition,
|
||||
)
|
||||
}
|
||||
binding.notificationContent.visibility =
|
||||
|
@ -322,7 +322,7 @@ internal class StatusNotificationViewHolder(
|
|||
private fun setupContentAndSpoiler(
|
||||
listener: LinkListener,
|
||||
statusViewData: StatusViewData,
|
||||
animateEmojis: Boolean
|
||||
animateEmojis: Boolean,
|
||||
) {
|
||||
val shouldShowContentIfSpoiler = statusViewData.isExpanded
|
||||
val hasSpoiler = !TextUtils.isEmpty(statusViewData.status.spoilerText)
|
||||
|
@ -339,19 +339,19 @@ internal class StatusNotificationViewHolder(
|
|||
if (position != RecyclerView.NO_POSITION) {
|
||||
notificationActionListener.onNotificationContentCollapsedChange(
|
||||
!statusViewData.isCollapsed,
|
||||
position
|
||||
position,
|
||||
)
|
||||
}
|
||||
}
|
||||
binding.buttonToggleNotificationContent.visibility = View.VISIBLE
|
||||
if (statusViewData.isCollapsed) {
|
||||
binding.buttonToggleNotificationContent.setText(
|
||||
R.string.post_content_warning_show_more
|
||||
R.string.post_content_warning_show_more,
|
||||
)
|
||||
binding.notificationContent.filters = COLLAPSE_INPUT_FILTER
|
||||
} else {
|
||||
binding.buttonToggleNotificationContent.setText(
|
||||
R.string.post_content_warning_show_less
|
||||
R.string.post_content_warning_show_less,
|
||||
)
|
||||
binding.notificationContent.filters = NO_INPUT_FILTER
|
||||
}
|
||||
|
@ -363,19 +363,19 @@ internal class StatusNotificationViewHolder(
|
|||
content.emojify(
|
||||
emojis,
|
||||
binding.notificationContent,
|
||||
animateEmojis
|
||||
animateEmojis,
|
||||
)
|
||||
setClickableText(
|
||||
binding.notificationContent,
|
||||
emojifiedText,
|
||||
statusViewData.actionable.mentions,
|
||||
statusViewData.actionable.tags,
|
||||
listener
|
||||
listener,
|
||||
)
|
||||
val emojifiedContentWarning: CharSequence = statusViewData.spoilerText.emojify(
|
||||
statusViewData.actionable.emojis,
|
||||
binding.notificationContentWarningDescription,
|
||||
animateEmojis
|
||||
animateEmojis,
|
||||
)
|
||||
binding.notificationContentWarningDescription.text = emojifiedContentWarning
|
||||
}
|
||||
|
|
|
@ -27,13 +27,13 @@ import com.keylesspalace.tusky.viewdata.NotificationViewData
|
|||
internal class StatusViewHolder(
|
||||
binding: ViewBinding,
|
||||
private val statusActionListener: StatusActionListener,
|
||||
private val accountId: String
|
||||
private val accountId: String,
|
||||
) : NotificationsPagingAdapter.ViewHolder, StatusViewHolder(binding.root) {
|
||||
|
||||
override fun bind(
|
||||
viewData: NotificationViewData,
|
||||
payloads: List<*>?,
|
||||
statusDisplayOptions: StatusDisplayOptions
|
||||
statusDisplayOptions: StatusDisplayOptions,
|
||||
) {
|
||||
val statusViewData = viewData.statusViewData
|
||||
if (statusViewData == null) {
|
||||
|
@ -48,7 +48,7 @@ internal class StatusViewHolder(
|
|||
statusViewData,
|
||||
statusActionListener,
|
||||
statusDisplayOptions,
|
||||
payloads?.firstOrNull()
|
||||
payloads?.firstOrNull(),
|
||||
)
|
||||
}
|
||||
if (viewData.type == Notification.Type.POLL) {
|
||||
|
|
|
@ -99,7 +99,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
|||
activity?.startActivity(intent)
|
||||
activity?.overridePendingTransition(
|
||||
R.anim.slide_from_right,
|
||||
R.anim.slide_to_left
|
||||
R.anim.slide_to_left,
|
||||
)
|
||||
true
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
|||
activity?.startActivity(intent)
|
||||
activity?.overridePendingTransition(
|
||||
R.anim.slide_from_right,
|
||||
R.anim.slide_to_left
|
||||
R.anim.slide_to_left,
|
||||
)
|
||||
true
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
|||
activity?.startActivity(intent)
|
||||
activity?.overridePendingTransition(
|
||||
R.anim.slide_from_right,
|
||||
R.anim.slide_to_left
|
||||
R.anim.slide_to_left,
|
||||
)
|
||||
true
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
|||
activity?.startActivity(intent)
|
||||
activity?.overridePendingTransition(
|
||||
R.anim.slide_from_right,
|
||||
R.anim.slide_to_left
|
||||
R.anim.slide_to_left,
|
||||
)
|
||||
true
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
|||
activity?.startActivity(intent)
|
||||
activity?.overridePendingTransition(
|
||||
R.anim.slide_from_right,
|
||||
R.anim.slide_to_left
|
||||
R.anim.slide_to_left,
|
||||
)
|
||||
true
|
||||
}
|
||||
|
@ -294,28 +294,30 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
|||
// TODO these could also be "datastore backed" preferences (a ServerPreferenceDataStore); follow-up of issue #3204
|
||||
|
||||
mastodonApi.accountUpdateSource(visibility, sensitive, language)
|
||||
.enqueue(object : Callback<Account> {
|
||||
override fun onResponse(call: Call<Account>, response: Response<Account>) {
|
||||
val account = response.body()
|
||||
if (response.isSuccessful && account != null) {
|
||||
accountManager.activeAccount?.let {
|
||||
it.defaultPostPrivacy = account.source?.privacy
|
||||
?: Status.Visibility.PUBLIC
|
||||
it.defaultMediaSensitivity = account.source?.sensitive ?: false
|
||||
it.defaultPostLanguage = language.orEmpty()
|
||||
accountManager.saveAccount(it)
|
||||
.enqueue(
|
||||
object : Callback<Account> {
|
||||
override fun onResponse(call: Call<Account>, response: Response<Account>) {
|
||||
val account = response.body()
|
||||
if (response.isSuccessful && account != null) {
|
||||
accountManager.activeAccount?.let {
|
||||
it.defaultPostPrivacy = account.source?.privacy
|
||||
?: Status.Visibility.PUBLIC
|
||||
it.defaultMediaSensitivity = account.source?.sensitive ?: false
|
||||
it.defaultPostLanguage = language.orEmpty()
|
||||
accountManager.saveAccount(it)
|
||||
}
|
||||
} else {
|
||||
Log.e("AccountPreferences", "failed updating settings on server")
|
||||
showErrorSnackbar(visibility, sensitive)
|
||||
}
|
||||
} else {
|
||||
Log.e("AccountPreferences", "failed updating settings on server")
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Account>, t: Throwable) {
|
||||
Log.e("AccountPreferences", "failed updating settings on server", t)
|
||||
showErrorSnackbar(visibility, sensitive)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Account>, t: Throwable) {
|
||||
Log.e("AccountPreferences", "failed updating settings on server", t)
|
||||
showErrorSnackbar(visibility, sensitive)
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun showErrorSnackbar(visibility: String?, sensitive: Boolean?) {
|
||||
|
|
|
@ -97,18 +97,18 @@ class PreferencesActivity :
|
|||
|
||||
onBackPressedDispatcher.addCallback(this, restartActivitiesOnBackPressedCallback)
|
||||
restartActivitiesOnBackPressedCallback.isEnabled = intent.extras?.getBoolean(
|
||||
EXTRA_RESTART_ON_BACK
|
||||
EXTRA_RESTART_ON_BACK,
|
||||
) ?: savedInstanceState?.getBoolean(EXTRA_RESTART_ON_BACK, false) ?: false
|
||||
}
|
||||
|
||||
override fun onPreferenceStartFragment(
|
||||
caller: PreferenceFragmentCompat,
|
||||
pref: Preference
|
||||
pref: Preference,
|
||||
): Boolean {
|
||||
val args = pref.extras
|
||||
val fragment = supportFragmentManager.fragmentFactory.instantiate(
|
||||
classLoader,
|
||||
pref.fragment!!
|
||||
pref.fragment!!,
|
||||
)
|
||||
fragment.arguments = args
|
||||
fragment.setTargetFragment(caller, 0)
|
||||
|
@ -117,7 +117,7 @@ class PreferencesActivity :
|
|||
R.anim.slide_from_right,
|
||||
R.anim.slide_to_left,
|
||||
R.anim.slide_from_left,
|
||||
R.anim.slide_to_right
|
||||
R.anim.slide_to_right,
|
||||
)
|
||||
replace(R.id.fragment_container, fragment)
|
||||
addToBackStack(null)
|
||||
|
@ -160,7 +160,8 @@ class PreferencesActivity :
|
|||
}
|
||||
"statusTextSize", "absoluteTimeView", "showBotOverlay", "animateGifAvatars", "useBlurhash",
|
||||
"showSelfUsername", "showCardsInTimelines", "confirmReblogs", "confirmFavourites",
|
||||
"enableSwipeForTabs", "mainNavPosition", PrefKeys.HIDE_TOP_TOOLBAR, PrefKeys.SHOW_STATS_INLINE -> {
|
||||
"enableSwipeForTabs", "mainNavPosition", PrefKeys.HIDE_TOP_TOOLBAR, PrefKeys.SHOW_STATS_INLINE,
|
||||
-> {
|
||||
restartActivitiesOnBackPressedCallback.isEnabled = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ class ProxyPreferencesFragment : PreferenceFragmentCompat() {
|
|||
val portErrorMessage = getString(
|
||||
R.string.pref_title_http_proxy_port_message,
|
||||
MIN_PROXY_PORT,
|
||||
MAX_PROXY_PORT
|
||||
MAX_PROXY_PORT,
|
||||
)
|
||||
|
||||
validatedEditTextPreference(portErrorMessage, ProxyConfiguration::isValidProxyPort) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue