update ktlint plugin to 11.3.1, format code (#3442)
This commit is contained in:
parent
774d79d666
commit
d839f18267
141 changed files with 589 additions and 428 deletions
|
@ -51,7 +51,6 @@ class AboutActivity : BottomSheetActivity(), Injectable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TextView.setClickableTextWithoutUnderlines(@StringRes textId: Int) {
|
private fun TextView.setClickableTextWithoutUnderlines(@StringRes textId: Int) {
|
||||||
|
|
||||||
val text = SpannableString(context.getText(textId))
|
val text = SpannableString(context.getText(textId))
|
||||||
|
|
||||||
Linkify.addLinks(text, Linkify.WEB_URLS)
|
Linkify.addLinks(text, Linkify.WEB_URLS)
|
||||||
|
|
|
@ -153,12 +153,14 @@ class AccountsInListFragment : DialogFragment(), Injectable {
|
||||||
if (error is IOException) {
|
if (error is IOException) {
|
||||||
binding.messageView.setup(
|
binding.messageView.setup(
|
||||||
R.drawable.elephant_offline,
|
R.drawable.elephant_offline,
|
||||||
R.string.error_network, retryAction
|
R.string.error_network,
|
||||||
|
retryAction
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
binding.messageView.setup(
|
binding.messageView.setup(
|
||||||
R.drawable.elephant_error,
|
R.drawable.elephant_error,
|
||||||
R.string.error_generic, retryAction
|
R.string.error_generic,
|
||||||
|
retryAction
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,5 +177,5 @@ abstract class BottomSheetActivity : BaseActivity() {
|
||||||
|
|
||||||
enum class PostLookupFallbackBehavior {
|
enum class PostLookupFallbackBehavior {
|
||||||
OPEN_IN_BROWSER,
|
OPEN_IN_BROWSER,
|
||||||
DISPLAY_ERROR,
|
DISPLAY_ERROR
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,7 +136,6 @@ class EditProfileActivity : BaseActivity(), Injectable {
|
||||||
is Success -> {
|
is Success -> {
|
||||||
val me = profileRes.data
|
val me = profileRes.data
|
||||||
if (me != null) {
|
if (me != null) {
|
||||||
|
|
||||||
binding.displayNameEditText.setText(me.displayName)
|
binding.displayNameEditText.setText(me.displayName)
|
||||||
binding.noteEditText.setText(me.source?.note)
|
binding.noteEditText.setText(me.source?.note)
|
||||||
binding.lockedCheckBox.isChecked = me.locked
|
binding.lockedCheckBox.isChecked = me.locked
|
||||||
|
|
|
@ -44,7 +44,6 @@ class LicenseActivity : BaseActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadFileIntoTextView(@RawRes fileId: Int, textView: TextView) {
|
private fun loadFileIntoTextView(@RawRes fileId: Int, textView: TextView) {
|
||||||
|
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
|
|
||||||
val br = BufferedReader(InputStreamReader(resources.openRawResource(fileId)))
|
val br = BufferedReader(InputStreamReader(resources.openRawResource(fileId)))
|
||||||
|
|
|
@ -134,8 +134,11 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
|
||||||
val dialog = AlertDialog.Builder(this)
|
val dialog = AlertDialog.Builder(this)
|
||||||
.setView(layout)
|
.setView(layout)
|
||||||
.setPositiveButton(
|
.setPositiveButton(
|
||||||
if (list == null) R.string.action_create_list
|
if (list == null) {
|
||||||
else R.string.action_rename_list
|
R.string.action_create_list
|
||||||
|
} else {
|
||||||
|
R.string.action_rename_list
|
||||||
|
}
|
||||||
) { _, _ ->
|
) { _, _ ->
|
||||||
onPickedDialogName(editText.text, list?.id)
|
onPickedDialogName(editText.text, list?.id)
|
||||||
}
|
}
|
||||||
|
@ -181,7 +184,8 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
|
||||||
if (state.lists.isEmpty()) {
|
if (state.lists.isEmpty()) {
|
||||||
binding.messageView.show()
|
binding.messageView.show()
|
||||||
binding.messageView.setup(
|
binding.messageView.setup(
|
||||||
R.drawable.elephant_friend_empty, R.string.message_empty,
|
R.drawable.elephant_friend_empty,
|
||||||
|
R.string.message_empty,
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -192,7 +196,9 @@ class ListsActivity : BaseActivity(), Injectable, HasAndroidInjector {
|
||||||
|
|
||||||
private fun showMessage(@StringRes messageId: Int) {
|
private fun showMessage(@StringRes messageId: Int) {
|
||||||
Snackbar.make(
|
Snackbar.make(
|
||||||
binding.listsRecycler, messageId, Snackbar.LENGTH_SHORT
|
binding.listsRecycler,
|
||||||
|
messageId,
|
||||||
|
Snackbar.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -215,7 +215,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
} else {
|
} else {
|
||||||
// No account was provided, show the chooser
|
// No account was provided, show the chooser
|
||||||
showAccountChooserDialog(
|
showAccountChooserDialog(
|
||||||
getString(R.string.action_share_as), true,
|
getString(R.string.action_share_as),
|
||||||
|
true,
|
||||||
object : AccountSelectionListener {
|
object : AccountSelectionListener {
|
||||||
override fun onAccountSelected(account: AccountEntity) {
|
override fun onAccountSelected(account: AccountEntity) {
|
||||||
val requestedId = account.id
|
val requestedId = account.id
|
||||||
|
@ -295,7 +296,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
is MainTabsChangedEvent -> {
|
is MainTabsChangedEvent -> {
|
||||||
refreshMainDrawerItems(
|
refreshMainDrawerItems(
|
||||||
addSearchButton = hideTopToolbar,
|
addSearchButton = hideTopToolbar,
|
||||||
addTrendingButton = !event.newTabs.hasTab(TRENDING),
|
addTrendingButton = !event.newTabs.hasTab(TRENDING)
|
||||||
)
|
)
|
||||||
|
|
||||||
setupTabs(false)
|
setupTabs(false)
|
||||||
|
@ -407,7 +408,6 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
// FIXME: blackberry keyONE raises SHIFT key event even CTRL IS PRESSED
|
// FIXME: blackberry keyONE raises SHIFT key event even CTRL IS PRESSED
|
||||||
when (keyCode) {
|
when (keyCode) {
|
||||||
KeyEvent.KEYCODE_N -> {
|
KeyEvent.KEYCODE_N -> {
|
||||||
|
|
||||||
// open compose activity by pressing SHIFT + N (or CTRL + N)
|
// open compose activity by pressing SHIFT + N (or CTRL + N)
|
||||||
val composeIntent = Intent(applicationContext, ComposeActivity::class.java)
|
val composeIntent = Intent(applicationContext, ComposeActivity::class.java)
|
||||||
startActivity(composeIntent)
|
startActivity(composeIntent)
|
||||||
|
@ -444,7 +444,6 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
addSearchButton: Boolean,
|
addSearchButton: Boolean,
|
||||||
addTrendingButton: Boolean
|
addTrendingButton: Boolean
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val drawerOpenClickListener = View.OnClickListener { binding.mainDrawerLayout.open() }
|
val drawerOpenClickListener = View.OnClickListener { binding.mainDrawerLayout.open() }
|
||||||
|
|
||||||
binding.mainToolbar.setNavigationOnClickListener(drawerOpenClickListener)
|
binding.mainToolbar.setNavigationOnClickListener(drawerOpenClickListener)
|
||||||
|
@ -708,7 +707,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
tabAdapter.notifyItemRangeChanged(0, tabs.size)
|
tabAdapter.notifyItemRangeChanged(0, tabs.size)
|
||||||
|
|
||||||
tabLayoutMediator = TabLayoutMediator(activeTabLayout, binding.viewPager, true) {
|
tabLayoutMediator = TabLayoutMediator(activeTabLayout, binding.viewPager, true) {
|
||||||
tab: TabLayout.Tab, position: Int ->
|
tab: TabLayout.Tab, position: Int ->
|
||||||
tab.icon = AppCompatResources.getDrawable(this@MainActivity, tabs[position].icon)
|
tab.icon = AppCompatResources.getDrawable(this@MainActivity, tabs[position].icon)
|
||||||
tab.contentDescription = when (tabs[position].id) {
|
tab.contentDescription = when (tabs[position].id) {
|
||||||
LIST -> tabs[position].arguments[1]
|
LIST -> tabs[position].arguments[1]
|
||||||
|
@ -881,7 +880,6 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadDrawerAvatar(avatarUrl: String, showPlaceholder: Boolean) {
|
private fun loadDrawerAvatar(avatarUrl: String, showPlaceholder: Boolean) {
|
||||||
|
|
||||||
val hideTopToolbar = preferences.getBoolean(PrefKeys.HIDE_TOP_TOOLBAR, false)
|
val hideTopToolbar = preferences.getBoolean(PrefKeys.HIDE_TOP_TOOLBAR, false)
|
||||||
val animateAvatars = preferences.getBoolean("animateGifAvatars", false)
|
val animateAvatars = preferences.getBoolean("animateGifAvatars", false)
|
||||||
|
|
||||||
|
@ -909,7 +907,6 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
.into(avatarView)
|
.into(avatarView)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
binding.bottomNavAvatar.hide()
|
binding.bottomNavAvatar.hide()
|
||||||
binding.topNavAvatar.hide()
|
binding.topNavAvatar.hide()
|
||||||
|
|
||||||
|
@ -1040,7 +1037,9 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
|
||||||
header.setActiveProfile(accountManager.activeAccount!!.id)
|
header.setActiveProfile(accountManager.activeAccount!!.id)
|
||||||
binding.mainToolbar.subtitle = if (accountManager.shouldDisplaySelfUsername(this)) {
|
binding.mainToolbar.subtitle = if (accountManager.shouldDisplaySelfUsername(this)) {
|
||||||
accountManager.activeAccount!!.fullName
|
accountManager.activeAccount!!.fullName
|
||||||
} else null
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getActionButton() = binding.composeButton
|
override fun getActionButton() = binding.composeButton
|
||||||
|
|
|
@ -225,7 +225,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
||||||
title = "#$tag",
|
title = "#$tag",
|
||||||
context = listOf(FilterV1.HOME),
|
context = listOf(FilterV1.HOME),
|
||||||
filterAction = Filter.Action.WARN.action,
|
filterAction = Filter.Action.WARN.action,
|
||||||
expiresInSeconds = null,
|
expiresInSeconds = null
|
||||||
).fold(
|
).fold(
|
||||||
{ filter ->
|
{ filter ->
|
||||||
if (mastodonApi.addFilterKeyword(filterId = filter.id, keyword = tag, wholeWord = true).isSuccess) {
|
if (mastodonApi.addFilterKeyword(filterId = filter.id, keyword = tag, wholeWord = true).isSuccess) {
|
||||||
|
@ -276,7 +276,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
||||||
// This filter exists in multiple contexts, just remove the home context
|
// This filter exists in multiple contexts, just remove the home context
|
||||||
mastodonApi.updateFilter(
|
mastodonApi.updateFilter(
|
||||||
id = filter.id,
|
id = filter.id,
|
||||||
context = filter.context.filter { it != Filter.Kind.HOME.kind },
|
context = filter.context.filter { it != Filter.Kind.HOME.kind }
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
mastodonApi.deleteFilter(filter.id)
|
mastodonApi.deleteFilter(filter.id)
|
||||||
|
@ -291,7 +291,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
||||||
context = filter.context.filter { it != FilterV1.HOME },
|
context = filter.context.filter { it != FilterV1.HOME },
|
||||||
irreversible = null,
|
irreversible = null,
|
||||||
wholeWord = null,
|
wholeWord = null,
|
||||||
expiresInSeconds = null,
|
expiresInSeconds = null
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
mastodonApi.deleteFilterV1(filter.id)
|
mastodonApi.deleteFilterV1(filter.id)
|
||||||
|
|
|
@ -59,6 +59,7 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var mastodonApi: MastodonApi
|
lateinit var mastodonApi: MastodonApi
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var eventHub: EventHub
|
lateinit var eventHub: EventHub
|
||||||
|
|
||||||
|
@ -161,7 +162,6 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTabAdded(tab: TabData) {
|
override fun onTabAdded(tab: TabData) {
|
||||||
|
|
||||||
if (currentTabs.size >= MAX_TAB_COUNT) {
|
if (currentTabs.size >= MAX_TAB_COUNT) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -223,7 +223,6 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showAddHashtagDialog(tab: TabData? = null, tabPosition: Int = 0) {
|
private fun showAddHashtagDialog(tab: TabData? = null, tabPosition: Int = 0) {
|
||||||
|
|
||||||
val frameLayout = FrameLayout(this)
|
val frameLayout = FrameLayout(this)
|
||||||
val padding = Utils.dpToPx(this, 8)
|
val padding = Utils.dpToPx(this, 8)
|
||||||
frameLayout.updatePadding(left = padding, right = padding)
|
frameLayout.updatePadding(left = padding, right = padding)
|
||||||
|
|
|
@ -306,8 +306,9 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
|
||||||
isCreating = false
|
isCreating = false
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
binding.progressBarShare.visibility = View.GONE
|
binding.progressBarShare.visibility = View.GONE
|
||||||
if (result)
|
if (result) {
|
||||||
shareFile(file, "image/png")
|
shareFile(file, "image/png")
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ error ->
|
{ error ->
|
||||||
isCreating = false
|
isCreating = false
|
||||||
|
|
|
@ -26,7 +26,6 @@ import com.keylesspalace.tusky.entity.MastoList
|
||||||
|
|
||||||
class ListSelectionAdapter(context: Context) : ArrayAdapter<MastoList>(context, R.layout.item_picker_list) {
|
class ListSelectionAdapter(context: Context) : ArrayAdapter<MastoList>(context, R.layout.item_picker_list) {
|
||||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||||
|
|
||||||
val binding = if (convertView == null) {
|
val binding = if (convertView == null) {
|
||||||
ItemPickerListBinding.inflate(LayoutInflater.from(context), parent, false)
|
ItemPickerListBinding.inflate(LayoutInflater.from(context), parent, false)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -75,7 +75,6 @@ class PollAdapter : RecyclerView.Adapter<BindingHolder<ItemPollBinding>>() {
|
||||||
override fun getItemCount() = pollOptions.size
|
override fun getItemCount() = pollOptions.size
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: BindingHolder<ItemPollBinding>, position: Int) {
|
override fun onBindViewHolder(holder: BindingHolder<ItemPollBinding>, position: Int) {
|
||||||
|
|
||||||
val option = pollOptions[position]
|
val option = pollOptions[position]
|
||||||
|
|
||||||
val resultTextView = holder.binding.statusPollOptionResult
|
val resultTextView = holder.binding.statusPollOptionResult
|
||||||
|
|
|
@ -35,7 +35,7 @@ import java.util.Date
|
||||||
|
|
||||||
class ReportNotificationViewHolder(
|
class ReportNotificationViewHolder(
|
||||||
private val binding: ItemReportNotificationBinding,
|
private val binding: ItemReportNotificationBinding,
|
||||||
private val notificationActionListener: NotificationActionListener,
|
private val notificationActionListener: NotificationActionListener
|
||||||
) : NotificationsPagingAdapter.ViewHolder, RecyclerView.ViewHolder(binding.root) {
|
) : NotificationsPagingAdapter.ViewHolder, RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
override fun bind(
|
override fun bind(
|
||||||
|
|
|
@ -24,7 +24,7 @@ import java.util.Locale
|
||||||
import java.util.TimeZone
|
import java.util.TimeZone
|
||||||
|
|
||||||
class TrendingDateViewHolder(
|
class TrendingDateViewHolder(
|
||||||
private val binding: ItemTrendingDateBinding,
|
private val binding: ItemTrendingDateBinding
|
||||||
) : RecyclerView.ViewHolder(binding.root) {
|
) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
private val dateFormat = SimpleDateFormat("EEE dd MMM yyyy", Locale.getDefault()).apply {
|
private val dateFormat = SimpleDateFormat("EEE dd MMM yyyy", Locale.getDefault()).apply {
|
||||||
|
|
|
@ -32,7 +32,7 @@ class TrendingTagViewHolder(
|
||||||
fun setup(
|
fun setup(
|
||||||
tagViewData: TrendingViewData.Tag,
|
tagViewData: TrendingViewData.Tag,
|
||||||
maxTrendingValue: Long,
|
maxTrendingValue: Long,
|
||||||
trendingListener: LinkListener,
|
trendingListener: LinkListener
|
||||||
) {
|
) {
|
||||||
val reversedHistory = tagViewData.tag.history.reversed()
|
val reversedHistory = tagViewData.tag.history.reversed()
|
||||||
setGraph(reversedHistory, maxTrendingValue)
|
setGraph(reversedHistory, maxTrendingValue)
|
||||||
|
|
|
@ -107,8 +107,10 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
|
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var viewModelFactory: ViewModelFactory
|
lateinit var viewModelFactory: ViewModelFactory
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var draftsAlert: DraftsAlert
|
lateinit var draftsAlert: DraftsAlert
|
||||||
|
|
||||||
|
@ -134,14 +136,18 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
||||||
// fields for scroll animation
|
// fields for scroll animation
|
||||||
private var hideFab: Boolean = false
|
private var hideFab: Boolean = false
|
||||||
private var oldOffset: Int = 0
|
private var oldOffset: Int = 0
|
||||||
|
|
||||||
@ColorInt
|
@ColorInt
|
||||||
private var toolbarColor: Int = 0
|
private var toolbarColor: Int = 0
|
||||||
|
|
||||||
@ColorInt
|
@ColorInt
|
||||||
private var statusBarColorTransparent: Int = 0
|
private var statusBarColorTransparent: Int = 0
|
||||||
|
|
||||||
@ColorInt
|
@ColorInt
|
||||||
private var statusBarColorOpaque: Int = 0
|
private var statusBarColorOpaque: Int = 0
|
||||||
|
|
||||||
private var avatarSize: Float = 0f
|
private var avatarSize: Float = 0f
|
||||||
|
|
||||||
@Px
|
@Px
|
||||||
private var titleVisibleHeight: Int = 0
|
private var titleVisibleHeight: Int = 0
|
||||||
private lateinit var domain: String
|
private lateinit var domain: String
|
||||||
|
@ -342,7 +348,6 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
||||||
binding.accountAppBarLayout.addOnOffsetChangedListener(object : AppBarLayout.OnOffsetChangedListener {
|
binding.accountAppBarLayout.addOnOffsetChangedListener(object : AppBarLayout.OnOffsetChangedListener {
|
||||||
|
|
||||||
override fun onOffsetChanged(appBarLayout: AppBarLayout, verticalOffset: Int) {
|
override fun onOffsetChanged(appBarLayout: AppBarLayout, verticalOffset: Int) {
|
||||||
|
|
||||||
if (verticalOffset == oldOffset) {
|
if (verticalOffset == oldOffset) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -650,10 +655,11 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
||||||
binding.accountSubscribeButton.setOnClickListener {
|
binding.accountSubscribeButton.setOnClickListener {
|
||||||
viewModel.changeSubscribingState()
|
viewModel.changeSubscribingState()
|
||||||
}
|
}
|
||||||
if (relation.notifying != null)
|
if (relation.notifying != null) {
|
||||||
subscribing = relation.notifying
|
subscribing = relation.notifying
|
||||||
else if (relation.subscribing != null)
|
} else if (relation.subscribing != null) {
|
||||||
subscribing = relation.subscribing
|
subscribing = relation.subscribing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove the listener so it doesn't fire on non-user changes
|
// remove the listener so it doesn't fire on non-user changes
|
||||||
|
@ -717,7 +723,6 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
|
|
||||||
if (loadedAccount?.moved == null) {
|
if (loadedAccount?.moved == null) {
|
||||||
|
|
||||||
binding.accountFollowButton.show()
|
binding.accountFollowButton.show()
|
||||||
updateFollowButton()
|
updateFollowButton()
|
||||||
updateSubscribeButton()
|
updateSubscribeButton()
|
||||||
|
@ -750,7 +755,6 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!viewModel.isSelf) {
|
if (!viewModel.isSelf) {
|
||||||
|
|
||||||
val block = menu.findItem(R.id.action_block)
|
val block = menu.findItem(R.id.action_block)
|
||||||
block.title = if (blocking) {
|
block.title = if (blocking) {
|
||||||
getString(R.string.action_unblock)
|
getString(R.string.action_unblock)
|
||||||
|
@ -908,7 +912,8 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
||||||
R.id.action_open_as -> {
|
R.id.action_open_as -> {
|
||||||
loadedAccount?.let { loadedAccount ->
|
loadedAccount?.let { loadedAccount ->
|
||||||
showAccountChooserDialog(
|
showAccountChooserDialog(
|
||||||
item.title, false,
|
item.title,
|
||||||
|
false,
|
||||||
object : AccountSelectionListener {
|
object : AccountSelectionListener {
|
||||||
override fun onAccountSelected(account: AccountEntity) {
|
override fun onAccountSelected(account: AccountEntity) {
|
||||||
openAsAccount(loadedAccount.url, account)
|
openAsAccount(loadedAccount.url, account)
|
||||||
|
@ -979,7 +984,9 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
|
||||||
override fun getActionButton(): FloatingActionButton? {
|
override fun getActionButton(): FloatingActionButton? {
|
||||||
return if (!blocking) {
|
return if (!blocking) {
|
||||||
binding.accountFloatingActionButton
|
binding.accountFloatingActionButton
|
||||||
} else null
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFullUsername(account: Account): String {
|
private fun getFullUsername(account: Account): String {
|
||||||
|
|
|
@ -80,7 +80,6 @@ class AccountViewModel @Inject constructor(
|
||||||
|
|
||||||
private fun obtainRelationship(reload: Boolean = false) {
|
private fun obtainRelationship(reload: Boolean = false) {
|
||||||
if (relationshipData.value == null || reload) {
|
if (relationshipData.value == null || reload) {
|
||||||
|
|
||||||
relationshipData.postValue(Loading())
|
relationshipData.postValue(Loading())
|
||||||
|
|
||||||
mastodonApi.relationships(listOf(accountId))
|
mastodonApi.relationships(listOf(accountId))
|
||||||
|
@ -209,14 +208,18 @@ class AccountViewModel @Inject constructor(
|
||||||
RelationShipAction.MUTE -> relation.copy(muting = true)
|
RelationShipAction.MUTE -> relation.copy(muting = true)
|
||||||
RelationShipAction.UNMUTE -> relation.copy(muting = false)
|
RelationShipAction.UNMUTE -> relation.copy(muting = false)
|
||||||
RelationShipAction.SUBSCRIBE -> {
|
RelationShipAction.SUBSCRIBE -> {
|
||||||
if (isMastodon)
|
if (isMastodon) {
|
||||||
relation.copy(notifying = true)
|
relation.copy(notifying = true)
|
||||||
else relation.copy(subscribing = true)
|
} else {
|
||||||
|
relation.copy(subscribing = true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
RelationShipAction.UNSUBSCRIBE -> {
|
RelationShipAction.UNSUBSCRIBE -> {
|
||||||
if (isMastodon)
|
if (isMastodon) {
|
||||||
relation.copy(notifying = false)
|
relation.copy(notifying = false)
|
||||||
else relation.copy(subscribing = false)
|
} else {
|
||||||
|
relation.copy(subscribing = false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
relationshipData.postValue(Loading(newRelation))
|
relationshipData.postValue(Loading(newRelation))
|
||||||
|
@ -238,14 +241,18 @@ class AccountViewModel @Inject constructor(
|
||||||
)
|
)
|
||||||
RelationShipAction.UNMUTE -> mastodonApi.unmuteAccount(accountId)
|
RelationShipAction.UNMUTE -> mastodonApi.unmuteAccount(accountId)
|
||||||
RelationShipAction.SUBSCRIBE -> {
|
RelationShipAction.SUBSCRIBE -> {
|
||||||
if (isMastodon)
|
if (isMastodon) {
|
||||||
mastodonApi.followAccount(accountId, notify = true)
|
mastodonApi.followAccount(accountId, notify = true)
|
||||||
else mastodonApi.subscribeAccount(accountId)
|
} else {
|
||||||
|
mastodonApi.subscribeAccount(accountId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
RelationShipAction.UNSUBSCRIBE -> {
|
RelationShipAction.UNSUBSCRIBE -> {
|
||||||
if (isMastodon)
|
if (isMastodon) {
|
||||||
mastodonApi.followAccount(accountId, notify = false)
|
mastodonApi.followAccount(accountId, notify = false)
|
||||||
else mastodonApi.unsubscribeAccount(accountId)
|
} else {
|
||||||
|
mastodonApi.unsubscribeAccount(accountId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,12 +301,14 @@ class AccountViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reload(isReload: Boolean = false) {
|
private fun reload(isReload: Boolean = false) {
|
||||||
if (isDataLoading)
|
if (isDataLoading) {
|
||||||
return
|
return
|
||||||
|
}
|
||||||
accountId.let {
|
accountId.let {
|
||||||
obtainAccount(isReload)
|
obtainAccount(isReload)
|
||||||
if (!isSelf)
|
if (!isSelf) {
|
||||||
obtainRelationship(isReload)
|
obtainRelationship(isReload)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ class ListsForAccountFragment : DialogFragment(), Injectable {
|
||||||
dialog?.apply {
|
dialog?.apply {
|
||||||
window?.setLayout(
|
window?.setLayout(
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
LinearLayout.LayoutParams.MATCH_PARENT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,7 +172,7 @@ class ListsForAccountFragment : DialogFragment(), Injectable {
|
||||||
ListAdapter<AccountListState, BindingHolder<ItemAddOrRemoveFromListBinding>>(Differ) {
|
ListAdapter<AccountListState, BindingHolder<ItemAddOrRemoveFromListBinding>>(Differ) {
|
||||||
override fun onCreateViewHolder(
|
override fun onCreateViewHolder(
|
||||||
parent: ViewGroup,
|
parent: ViewGroup,
|
||||||
viewType: Int,
|
viewType: Int
|
||||||
): BindingHolder<ItemAddOrRemoveFromListBinding> {
|
): BindingHolder<ItemAddOrRemoveFromListBinding> {
|
||||||
val binding =
|
val binding =
|
||||||
ItemAddOrRemoveFromListBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
ItemAddOrRemoveFromListBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
|
|
@ -35,23 +35,23 @@ import javax.inject.Inject
|
||||||
|
|
||||||
data class AccountListState(
|
data class AccountListState(
|
||||||
val list: MastoList,
|
val list: MastoList,
|
||||||
val includesAccount: Boolean,
|
val includesAccount: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
data class ActionError(
|
data class ActionError(
|
||||||
val error: Throwable,
|
val error: Throwable,
|
||||||
val type: Type,
|
val type: Type,
|
||||||
val listId: String,
|
val listId: String
|
||||||
) : Throwable(error) {
|
) : Throwable(error) {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
ADD,
|
ADD,
|
||||||
REMOVE,
|
REMOVE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
class ListsForAccountViewModel @Inject constructor(
|
class ListsForAccountViewModel @Inject constructor(
|
||||||
private val mastodonApi: MastodonApi,
|
private val mastodonApi: MastodonApi
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private lateinit var accountId: String
|
private lateinit var accountId: String
|
||||||
|
@ -75,14 +75,14 @@ class ListsForAccountViewModel @Inject constructor(
|
||||||
runCatching {
|
runCatching {
|
||||||
val (all, includes) = listOf(
|
val (all, includes) = listOf(
|
||||||
async { mastodonApi.getLists() },
|
async { mastodonApi.getLists() },
|
||||||
async { mastodonApi.getListsIncludesAccount(accountId) },
|
async { mastodonApi.getListsIncludesAccount(accountId) }
|
||||||
).awaitAll()
|
).awaitAll()
|
||||||
|
|
||||||
_states.emit(
|
_states.emit(
|
||||||
all.getOrThrow().map { list ->
|
all.getOrThrow().map { list ->
|
||||||
AccountListState(
|
AccountListState(
|
||||||
list = list,
|
list = list,
|
||||||
includesAccount = includes.getOrThrow().any { it.id == list.id },
|
includesAccount = includes.getOrThrow().any { it.id == list.id }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,7 +26,6 @@ class AccountMediaPagingSource(
|
||||||
override fun getRefreshKey(state: PagingState<String, AttachmentViewData>): String? = null
|
override fun getRefreshKey(state: PagingState<String, AttachmentViewData>): String? = null
|
||||||
|
|
||||||
override suspend fun load(params: LoadParams<String>): LoadResult<String, AttachmentViewData> {
|
override suspend fun load(params: LoadParams<String>): LoadResult<String, AttachmentViewData> {
|
||||||
|
|
||||||
return if (params is LoadParams.Refresh) {
|
return if (params is LoadParams.Refresh) {
|
||||||
val list = viewModel.attachmentData.toList()
|
val list = viewModel.attachmentData.toList()
|
||||||
LoadResult.Page(list, null, list.lastOrNull()?.statusId)
|
LoadResult.Page(list, null, list.lastOrNull()?.statusId)
|
||||||
|
|
|
@ -34,7 +34,6 @@ class AccountMediaRemoteMediator(
|
||||||
loadType: LoadType,
|
loadType: LoadType,
|
||||||
state: PagingState<String, AttachmentViewData>
|
state: PagingState<String, AttachmentViewData>
|
||||||
): MediatorResult {
|
): MediatorResult {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val statusResponse = when (loadType) {
|
val statusResponse = when (loadType) {
|
||||||
LoadType.REFRESH -> {
|
LoadType.REFRESH -> {
|
||||||
|
|
|
@ -63,6 +63,7 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var api: MastodonApi
|
lateinit var api: MastodonApi
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var accountManager: AccountManager
|
lateinit var accountManager: AccountManager
|
||||||
|
|
||||||
|
@ -83,7 +84,6 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
|
||||||
binding.recyclerView.setHasFixedSize(true)
|
binding.recyclerView.setHasFixedSize(true)
|
||||||
val layoutManager = LinearLayoutManager(view.context)
|
val layoutManager = LinearLayoutManager(view.context)
|
||||||
binding.recyclerView.layoutManager = layoutManager
|
binding.recyclerView.layoutManager = layoutManager
|
||||||
|
@ -227,7 +227,6 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct
|
||||||
accountId: String,
|
accountId: String,
|
||||||
position: Int
|
position: Int
|
||||||
) {
|
) {
|
||||||
|
|
||||||
if (accept) {
|
if (accept) {
|
||||||
api.authorizeFollowRequest(accountId)
|
api.authorizeFollowRequest(accountId)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -61,7 +61,7 @@ abstract class AccountAdapter<AVH : RecyclerView.ViewHolder> internal constructo
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createFooterViewHolder(
|
private fun createFooterViewHolder(
|
||||||
parent: ViewGroup,
|
parent: ViewGroup
|
||||||
): RecyclerView.ViewHolder {
|
): RecyclerView.ViewHolder {
|
||||||
val binding = ItemFooterBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
val binding = ItemFooterBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
return BindingHolder(binding)
|
return BindingHolder(binding)
|
||||||
|
|
|
@ -30,7 +30,7 @@ class BlocksAdapter(
|
||||||
accountActionListener: AccountActionListener,
|
accountActionListener: AccountActionListener,
|
||||||
animateAvatar: Boolean,
|
animateAvatar: Boolean,
|
||||||
animateEmojis: Boolean,
|
animateEmojis: Boolean,
|
||||||
showBotOverlay: Boolean,
|
showBotOverlay: Boolean
|
||||||
) : AccountAdapter<BindingHolder<ItemBlockedUserBinding>>(
|
) : AccountAdapter<BindingHolder<ItemBlockedUserBinding>>(
|
||||||
accountActionListener = accountActionListener,
|
accountActionListener = accountActionListener,
|
||||||
animateAvatar = animateAvatar,
|
animateAvatar = animateAvatar,
|
||||||
|
|
|
@ -36,7 +36,9 @@ class FollowRequestsAdapter(
|
||||||
|
|
||||||
override fun createAccountViewHolder(parent: ViewGroup): FollowRequestViewHolder {
|
override fun createAccountViewHolder(parent: ViewGroup): FollowRequestViewHolder {
|
||||||
val binding = ItemFollowRequestBinding.inflate(
|
val binding = ItemFollowRequestBinding.inflate(
|
||||||
LayoutInflater.from(parent.context), parent, false
|
LayoutInflater.from(parent.context),
|
||||||
|
parent,
|
||||||
|
false
|
||||||
)
|
)
|
||||||
return FollowRequestViewHolder(
|
return FollowRequestViewHolder(
|
||||||
binding,
|
binding,
|
||||||
|
|
|
@ -107,8 +107,7 @@ class AnnouncementsViewModel @Inject constructor(
|
||||||
} else {
|
} else {
|
||||||
listOf(
|
listOf(
|
||||||
*announcement.reactions.toTypedArray(),
|
*announcement.reactions.toTypedArray(),
|
||||||
emojis.value!!.find { emoji -> emoji.shortcode == name }
|
emojis.value!!.find { emoji -> emoji.shortcode == name }!!.run {
|
||||||
!!.run {
|
|
||||||
Announcement.Reaction(
|
Announcement.Reaction(
|
||||||
name,
|
name,
|
||||||
1,
|
1,
|
||||||
|
|
|
@ -812,25 +812,26 @@ class ComposeActivity :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onMediaPick() {
|
private fun onMediaPick() {
|
||||||
addMediaBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
|
addMediaBehavior.addBottomSheetCallback(
|
||||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
object : BottomSheetBehavior.BottomSheetCallback() {
|
||||||
// Wait until bottom sheet is not collapsed and show next screen after
|
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||||
if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
|
// Wait until bottom sheet is not collapsed and show next screen after
|
||||||
addMediaBehavior.removeBottomSheetCallback(this)
|
if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && ContextCompat.checkSelfPermission(this@ComposeActivity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
addMediaBehavior.removeBottomSheetCallback(this)
|
||||||
ActivityCompat.requestPermissions(
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && ContextCompat.checkSelfPermission(this@ComposeActivity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||||
this@ComposeActivity,
|
ActivityCompat.requestPermissions(
|
||||||
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
|
this@ComposeActivity,
|
||||||
PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE
|
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
|
||||||
)
|
PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE
|
||||||
} else {
|
)
|
||||||
pickMediaFile.launch(true)
|
} else {
|
||||||
|
pickMediaFile.launch(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {}
|
override fun onSlide(bottomSheet: View, slideOffset: Float) {}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
addMediaBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
addMediaBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||||
}
|
}
|
||||||
|
@ -953,7 +954,6 @@ class ComposeActivity :
|
||||||
binding.composeEditField.error = getString(R.string.error_empty)
|
binding.composeEditField.error = getString(R.string.error_empty)
|
||||||
enableButtons(true, viewModel.editing)
|
enableButtons(true, viewModel.editing)
|
||||||
} else if (characterCount <= maximumTootCharacters) {
|
} else if (characterCount <= maximumTootCharacters) {
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
viewModel.sendStatus(contentText, spoilerText)
|
viewModel.sendStatus(contentText, spoilerText)
|
||||||
deleteDraftAndFinish()
|
deleteDraftAndFinish()
|
||||||
|
@ -972,7 +972,8 @@ class ComposeActivity :
|
||||||
pickMediaFile.launch(true)
|
pickMediaFile.launch(true)
|
||||||
} else {
|
} else {
|
||||||
Snackbar.make(
|
Snackbar.make(
|
||||||
binding.activityCompose, R.string.error_media_upload_permission,
|
binding.activityCompose,
|
||||||
|
R.string.error_media_upload_permission,
|
||||||
Snackbar.LENGTH_SHORT
|
Snackbar.LENGTH_SHORT
|
||||||
).apply {
|
).apply {
|
||||||
setAction(R.string.action_retry) { onMediaPick() }
|
setAction(R.string.action_retry) { onMediaPick() }
|
||||||
|
@ -1006,9 +1007,13 @@ class ComposeActivity :
|
||||||
private fun enableButton(button: ImageButton, clickable: Boolean, colorActive: Boolean) {
|
private fun enableButton(button: ImageButton, clickable: Boolean, colorActive: Boolean) {
|
||||||
button.isEnabled = clickable
|
button.isEnabled = clickable
|
||||||
setDrawableTint(
|
setDrawableTint(
|
||||||
this, button.drawable,
|
this,
|
||||||
if (colorActive) android.R.attr.textColorTertiary
|
button.drawable,
|
||||||
else R.attr.textColorDisabled
|
if (colorActive) {
|
||||||
|
android.R.attr.textColorTertiary
|
||||||
|
} else {
|
||||||
|
R.attr.textColorDisabled
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1016,8 +1021,11 @@ class ComposeActivity :
|
||||||
binding.addPollTextActionTextView.isEnabled = enable
|
binding.addPollTextActionTextView.isEnabled = enable
|
||||||
val textColor = MaterialColors.getColor(
|
val textColor = MaterialColors.getColor(
|
||||||
binding.addPollTextActionTextView,
|
binding.addPollTextActionTextView,
|
||||||
if (enable) android.R.attr.textColorTertiary
|
if (enable) {
|
||||||
else R.attr.textColorDisabled
|
android.R.attr.textColorTertiary
|
||||||
|
} else {
|
||||||
|
R.attr.textColorDisabled
|
||||||
|
}
|
||||||
)
|
)
|
||||||
binding.addPollTextActionTextView.setTextColor(textColor)
|
binding.addPollTextActionTextView.setTextColor(textColor)
|
||||||
binding.addPollTextActionTextView.compoundDrawablesRelative[0].colorFilter = PorterDuffColorFilter(textColor, PorterDuff.Mode.SRC_IN)
|
binding.addPollTextActionTextView.compoundDrawablesRelative[0].colorFilter = PorterDuffColorFilter(textColor, PorterDuff.Mode.SRC_IN)
|
||||||
|
@ -1193,8 +1201,11 @@ class ComposeActivity :
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val dialog = if (viewModel.shouldShowSaveDraftDialog()) {
|
val dialog = if (viewModel.shouldShowSaveDraftDialog()) {
|
||||||
ProgressDialog.show(
|
ProgressDialog.show(
|
||||||
this@ComposeActivity, null,
|
this@ComposeActivity,
|
||||||
getString(R.string.saving_draft), true, false
|
null,
|
||||||
|
getString(R.string.saving_draft),
|
||||||
|
true,
|
||||||
|
false
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
|
|
@ -274,7 +274,7 @@ class ComposeViewModel @Inject constructor(
|
||||||
failedToSendAlert = false,
|
failedToSendAlert = false,
|
||||||
scheduledAt = scheduledAt.value,
|
scheduledAt = scheduledAt.value,
|
||||||
language = postLanguage,
|
language = postLanguage,
|
||||||
statusId = originalStatusId,
|
statusId = originalStatusId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,7 +286,6 @@ class ComposeViewModel @Inject constructor(
|
||||||
content: String,
|
content: String,
|
||||||
spoilerText: String
|
spoilerText: String
|
||||||
) {
|
) {
|
||||||
|
|
||||||
if (!scheduledTootId.isNullOrEmpty()) {
|
if (!scheduledTootId.isNullOrEmpty()) {
|
||||||
api.deleteScheduledStatus(scheduledTootId!!)
|
api.deleteScheduledStatus(scheduledTootId!!)
|
||||||
}
|
}
|
||||||
|
@ -405,7 +404,6 @@ class ComposeViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setup(composeOptions: ComposeActivity.ComposeOptions?) {
|
fun setup(composeOptions: ComposeActivity.ComposeOptions?) {
|
||||||
|
|
||||||
if (setupComplete) {
|
if (setupComplete) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -440,14 +438,16 @@ class ComposeViewModel @Inject constructor(
|
||||||
pickMedia(attachment.uri, attachment.description, attachment.focus)
|
pickMedia(attachment.uri, attachment.description, attachment.focus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else composeOptions?.mediaAttachments?.forEach { a ->
|
} else {
|
||||||
// when coming from redraft or ScheduledTootActivity
|
composeOptions?.mediaAttachments?.forEach { a ->
|
||||||
val mediaType = when (a.type) {
|
// when coming from redraft or ScheduledTootActivity
|
||||||
Attachment.Type.VIDEO, Attachment.Type.GIFV -> QueuedMedia.Type.VIDEO
|
val mediaType = when (a.type) {
|
||||||
Attachment.Type.UNKNOWN, Attachment.Type.IMAGE -> QueuedMedia.Type.IMAGE
|
Attachment.Type.VIDEO, Attachment.Type.GIFV -> QueuedMedia.Type.VIDEO
|
||||||
Attachment.Type.AUDIO -> QueuedMedia.Type.AUDIO
|
Attachment.Type.UNKNOWN, Attachment.Type.IMAGE -> QueuedMedia.Type.IMAGE
|
||||||
|
Attachment.Type.AUDIO -> QueuedMedia.Type.AUDIO
|
||||||
|
}
|
||||||
|
addUploadedMedia(a.id, mediaType, a.url.toUri(), a.description, a.meta?.focus)
|
||||||
}
|
}
|
||||||
addUploadedMedia(a.id, mediaType, a.url.toUri(), a.description, a.meta?.focus)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
draftId = composeOptions?.draftId ?: 0
|
draftId = composeOptions?.draftId ?: 0
|
||||||
|
|
|
@ -41,7 +41,6 @@ fun downsizeImage(
|
||||||
contentResolver: ContentResolver,
|
contentResolver: ContentResolver,
|
||||||
tempFile: File
|
tempFile: File
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
|
||||||
val decodeBoundsInputStream = try {
|
val decodeBoundsInputStream = try {
|
||||||
contentResolver.openInputStream(uri)
|
contentResolver.openInputStream(uri)
|
||||||
} catch (e: FileNotFoundException) {
|
} catch (e: FileNotFoundException) {
|
||||||
|
|
|
@ -90,10 +90,11 @@ class MediaPreviewAdapter(
|
||||||
val imageView = holder.progressImageView
|
val imageView = holder.progressImageView
|
||||||
val focus = item.focus
|
val focus = item.focus
|
||||||
|
|
||||||
if (focus != null)
|
if (focus != null) {
|
||||||
imageView.setFocalPoint(focus)
|
imageView.setFocalPoint(focus)
|
||||||
else
|
} else {
|
||||||
imageView.removeFocalPoint() // Probably unnecessary since we have no UI for removal once added.
|
imageView.removeFocalPoint() // Probably unnecessary since we have no UI for removal once added.
|
||||||
|
}
|
||||||
|
|
||||||
var glide = Glide.with(holder.itemView.context)
|
var glide = Glide.with(holder.itemView.context)
|
||||||
.load(item.uri)
|
.load(item.uri)
|
||||||
|
@ -101,8 +102,9 @@ class MediaPreviewAdapter(
|
||||||
.dontAnimate()
|
.dontAnimate()
|
||||||
.centerInside()
|
.centerInside()
|
||||||
|
|
||||||
if (focus != null)
|
if (focus != null) {
|
||||||
glide = glide.addListener(imageView)
|
glide = glide.addListener(imageView)
|
||||||
|
}
|
||||||
|
|
||||||
glide.into(imageView)
|
glide.into(imageView)
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,7 +159,6 @@ class MediaUploader @Inject constructor(
|
||||||
try {
|
try {
|
||||||
when (inUri.scheme) {
|
when (inUri.scheme) {
|
||||||
ContentResolver.SCHEME_CONTENT -> {
|
ContentResolver.SCHEME_CONTENT -> {
|
||||||
|
|
||||||
mimeType = contentResolver.getType(uri)
|
mimeType = contentResolver.getType(uri)
|
||||||
|
|
||||||
val suffix = "." + MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType ?: "tmp")
|
val suffix = "." + MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType ?: "tmp")
|
||||||
|
@ -278,7 +277,8 @@ class MediaUploader @Inject constructor(
|
||||||
|
|
||||||
var lastProgress = -1
|
var lastProgress = -1
|
||||||
val fileBody = ProgressRequestBody(
|
val fileBody = ProgressRequestBody(
|
||||||
stream!!, media.mediaSize,
|
stream!!,
|
||||||
|
media.mediaSize,
|
||||||
mimeType.toMediaTypeOrNull()!!
|
mimeType.toMediaTypeOrNull()!!
|
||||||
) { percentage ->
|
) { percentage ->
|
||||||
if (percentage != lastProgress) {
|
if (percentage != lastProgress) {
|
||||||
|
|
|
@ -35,7 +35,6 @@ fun showAddPollDialog(
|
||||||
maxDuration: Int,
|
maxDuration: Int,
|
||||||
onUpdatePoll: (NewPoll) -> Unit
|
onUpdatePoll: (NewPoll) -> Unit
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val binding = DialogAddPollBinding.inflate(LayoutInflater.from(context))
|
val binding = DialogAddPollBinding.inflate(LayoutInflater.from(context))
|
||||||
|
|
||||||
val dialog = AlertDialog.Builder(context)
|
val dialog = AlertDialog.Builder(context)
|
||||||
|
|
|
@ -67,7 +67,8 @@ class CaptionDialog : DialogFragment() {
|
||||||
input = EditText(context)
|
input = EditText(context)
|
||||||
input.hint = resources.getQuantityString(
|
input.hint = resources.getQuantityString(
|
||||||
R.plurals.hint_describe_for_visually_impaired,
|
R.plurals.hint_describe_for_visually_impaired,
|
||||||
MEDIA_DESCRIPTION_CHARACTER_LIMIT, MEDIA_DESCRIPTION_CHARACTER_LIMIT
|
MEDIA_DESCRIPTION_CHARACTER_LIMIT,
|
||||||
|
MEDIA_DESCRIPTION_CHARACTER_LIMIT
|
||||||
)
|
)
|
||||||
dialogLayout.addView(input)
|
dialogLayout.addView(input)
|
||||||
(input.layoutParams as LinearLayout.LayoutParams).setMargins(margin, margin, margin, margin)
|
(input.layoutParams as LinearLayout.LayoutParams).setMargins(margin, margin, margin, margin)
|
||||||
|
@ -105,7 +106,7 @@ class CaptionDialog : DialogFragment() {
|
||||||
|
|
||||||
override fun onResourceReady(
|
override fun onResourceReady(
|
||||||
resource: Drawable,
|
resource: Drawable,
|
||||||
transition: Transition<in Drawable>?,
|
transition: Transition<in Drawable>?
|
||||||
) {
|
) {
|
||||||
imageView.setImageDrawable(resource)
|
imageView.setImageDrawable(resource)
|
||||||
}
|
}
|
||||||
|
@ -122,7 +123,7 @@ class CaptionDialog : DialogFragment() {
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?,
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
savedInstanceState?.getString(DESCRIPTION_KEY)?.let {
|
savedInstanceState?.getString(DESCRIPTION_KEY)?.let {
|
||||||
input.setText(it)
|
input.setText(it)
|
||||||
|
@ -143,12 +144,12 @@ class CaptionDialog : DialogFragment() {
|
||||||
fun newInstance(
|
fun newInstance(
|
||||||
localId: Int,
|
localId: Int,
|
||||||
existingDescription: String?,
|
existingDescription: String?,
|
||||||
previewUri: Uri,
|
previewUri: Uri
|
||||||
) = CaptionDialog().apply {
|
) = CaptionDialog().apply {
|
||||||
arguments = bundleOf(
|
arguments = bundleOf(
|
||||||
LOCAL_ID_ARG to localId,
|
LOCAL_ID_ARG to localId,
|
||||||
EXISTING_DESCRIPTION_ARG to existingDescription,
|
EXISTING_DESCRIPTION_ARG to existingDescription,
|
||||||
PREVIEW_URI_ARG to previewUri,
|
PREVIEW_URI_ARG to previewUri
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,14 +27,16 @@ class FocusIndicatorView
|
||||||
|
|
||||||
fun setImageSize(width: Int, height: Int) {
|
fun setImageSize(width: Int, height: Int) {
|
||||||
this.imageSize = Point(width, height)
|
this.imageSize = Point(width, height)
|
||||||
if (focus != null)
|
if (focus != null) {
|
||||||
invalidate()
|
invalidate()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setFocus(focus: Attachment.Focus) {
|
fun setFocus(focus: Attachment.Focus) {
|
||||||
this.focus = focus
|
this.focus = focus
|
||||||
if (imageSize != null)
|
if (imageSize != null) {
|
||||||
invalidate()
|
invalidate()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assumes setFocus called first
|
// Assumes setFocus called first
|
||||||
|
@ -46,8 +48,9 @@ class FocusIndicatorView
|
||||||
// so base it on the view width/height whenever the first access occurs.
|
// so base it on the view width/height whenever the first access occurs.
|
||||||
private fun getCircleRadius(): Float {
|
private fun getCircleRadius(): Float {
|
||||||
val circleRadius = this.circleRadius
|
val circleRadius = this.circleRadius
|
||||||
if (circleRadius != null)
|
if (circleRadius != null) {
|
||||||
return circleRadius
|
return circleRadius
|
||||||
|
}
|
||||||
val newCircleRadius = min(this.width, this.height).toFloat() / 4.0f
|
val newCircleRadius = min(this.width, this.height).toFloat() / 4.0f
|
||||||
this.circleRadius = newCircleRadius
|
this.circleRadius = newCircleRadius
|
||||||
return newCircleRadius
|
return newCircleRadius
|
||||||
|
@ -67,8 +70,9 @@ class FocusIndicatorView
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility") // Android Studio wants us to implement PerformClick for accessibility, but that unfortunately cannot be made meaningful for this widget.
|
@SuppressLint("ClickableViewAccessibility") // Android Studio wants us to implement PerformClick for accessibility, but that unfortunately cannot be made meaningful for this widget.
|
||||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||||
if (event.actionMasked == MotionEvent.ACTION_CANCEL)
|
if (event.actionMasked == MotionEvent.ACTION_CANCEL) {
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
val imageSize = this.imageSize ?: return false
|
val imageSize = this.imageSize ?: return false
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,6 @@ class TootButton
|
||||||
|
|
||||||
fun setStatusVisibility(visibility: Status.Visibility) {
|
fun setStatusVisibility(visibility: Status.Visibility) {
|
||||||
if (!smallStyle) {
|
if (!smallStyle) {
|
||||||
|
|
||||||
icon = when (visibility) {
|
icon = when (visibility) {
|
||||||
Status.Visibility.PUBLIC -> {
|
Status.Visibility.PUBLIC -> {
|
||||||
setText(R.string.action_send_public)
|
setText(R.string.action_send_public)
|
||||||
|
|
|
@ -66,7 +66,7 @@ data class ConversationAccountEntity(
|
||||||
displayName = displayName,
|
displayName = displayName,
|
||||||
url = "",
|
url = "",
|
||||||
avatar = avatar,
|
avatar = avatar,
|
||||||
emojis = emojis,
|
emojis = emojis
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ data class ConversationStatusEntity(
|
||||||
val collapsed: Boolean,
|
val collapsed: Boolean,
|
||||||
val muted: Boolean,
|
val muted: Boolean,
|
||||||
val poll: Poll?,
|
val poll: Poll?,
|
||||||
val language: String?,
|
val language: String?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun toViewData(): StatusViewData.Concrete {
|
fun toViewData(): StatusViewData.Concrete {
|
||||||
|
@ -130,11 +130,11 @@ data class ConversationStatusEntity(
|
||||||
poll = poll,
|
poll = poll,
|
||||||
card = null,
|
card = null,
|
||||||
language = language,
|
language = language,
|
||||||
filtered = null,
|
filtered = null
|
||||||
),
|
),
|
||||||
isExpanded = expanded,
|
isExpanded = expanded,
|
||||||
isShowingContent = showingHiddenContent,
|
isShowingContent = showingHiddenContent,
|
||||||
isCollapsed = collapsed,
|
isCollapsed = collapsed
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ fun Status.toEntity(
|
||||||
collapsed = contentCollapsed,
|
collapsed = contentCollapsed,
|
||||||
muted = muted ?: false,
|
muted = muted ?: false,
|
||||||
poll = poll,
|
poll = poll,
|
||||||
language = language,
|
language = language
|
||||||
)
|
)
|
||||||
|
|
||||||
fun Conversation.toEntity(
|
fun Conversation.toEntity(
|
||||||
|
|
|
@ -87,6 +87,6 @@ fun StatusViewData.Concrete.toConversationStatusEntity(
|
||||||
collapsed = collapsed,
|
collapsed = collapsed,
|
||||||
muted = muted,
|
muted = muted,
|
||||||
poll = poll,
|
poll = poll,
|
||||||
language = status.language,
|
language = status.language
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import retrofit2.HttpException
|
||||||
class ConversationsRemoteMediator(
|
class ConversationsRemoteMediator(
|
||||||
private val api: MastodonApi,
|
private val api: MastodonApi,
|
||||||
private val db: AppDatabase,
|
private val db: AppDatabase,
|
||||||
accountManager: AccountManager,
|
accountManager: AccountManager
|
||||||
) : RemoteMediator<Int, ConversationEntity>() {
|
) : RemoteMediator<Int, ConversationEntity>() {
|
||||||
|
|
||||||
private var nextKey: String? = null
|
private var nextKey: String? = null
|
||||||
|
@ -28,7 +28,6 @@ class ConversationsRemoteMediator(
|
||||||
loadType: LoadType,
|
loadType: LoadType,
|
||||||
state: PagingState<Int, ConversationEntity>
|
state: PagingState<Int, ConversationEntity>
|
||||||
): MediatorResult {
|
): MediatorResult {
|
||||||
|
|
||||||
if (loadType == LoadType.PREPEND) {
|
if (loadType == LoadType.PREPEND) {
|
||||||
return MediatorResult.Success(endOfPaginationReached = true)
|
return MediatorResult.Success(endOfPaginationReached = true)
|
||||||
}
|
}
|
||||||
|
@ -47,7 +46,6 @@ class ConversationsRemoteMediator(
|
||||||
}
|
}
|
||||||
|
|
||||||
db.withTransaction {
|
db.withTransaction {
|
||||||
|
|
||||||
if (loadType == LoadType.REFRESH) {
|
if (loadType == LoadType.REFRESH) {
|
||||||
db.conversationDao().deleteForAccount(activeAccount.id)
|
db.conversationDao().deleteForAccount(activeAccount.id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ class DraftHelper @Inject constructor(
|
||||||
failedToSendAlert: Boolean,
|
failedToSendAlert: Boolean,
|
||||||
scheduledAt: String?,
|
scheduledAt: String?,
|
||||||
language: String?,
|
language: String?,
|
||||||
statusId: String?,
|
statusId: String?
|
||||||
) = withContext(Dispatchers.IO) {
|
) = withContext(Dispatchers.IO) {
|
||||||
val externalFilesDir = context.getExternalFilesDir("Tusky")
|
val externalFilesDir = context.getExternalFilesDir("Tusky")
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ class DraftHelper @Inject constructor(
|
||||||
failedToSendNew = failedToSendAlert,
|
failedToSendNew = failedToSendAlert,
|
||||||
scheduledAt = scheduledAt,
|
scheduledAt = scheduledAt,
|
||||||
language = language,
|
language = language,
|
||||||
statusId = statusId,
|
statusId = statusId
|
||||||
)
|
)
|
||||||
|
|
||||||
draftDao.insertOrReplace(draft)
|
draftDao.insertOrReplace(draft)
|
||||||
|
|
|
@ -51,18 +51,20 @@ class DraftMediaAdapter(
|
||||||
holder.imageView.clearFocus()
|
holder.imageView.clearFocus()
|
||||||
holder.imageView.setImageResource(R.drawable.ic_music_box_preview_24dp)
|
holder.imageView.setImageResource(R.drawable.ic_music_box_preview_24dp)
|
||||||
} else {
|
} else {
|
||||||
if (attachment.focus != null)
|
if (attachment.focus != null) {
|
||||||
holder.imageView.setFocalPoint(attachment.focus)
|
holder.imageView.setFocalPoint(attachment.focus)
|
||||||
else
|
} else {
|
||||||
holder.imageView.clearFocus()
|
holder.imageView.clearFocus()
|
||||||
|
}
|
||||||
var glide = Glide.with(holder.itemView.context)
|
var glide = Glide.with(holder.itemView.context)
|
||||||
.load(attachment.uri)
|
.load(attachment.uri)
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
.dontAnimate()
|
.dontAnimate()
|
||||||
.centerInside()
|
.centerInside()
|
||||||
|
|
||||||
if (attachment.focus != null)
|
if (attachment.focus != null) {
|
||||||
glide = glide.addListener(holder.imageView)
|
glide = glide.addListener(holder.imageView)
|
||||||
|
}
|
||||||
|
|
||||||
glide.into(holder.imageView)
|
glide.into(holder.imageView)
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,6 @@ class DraftsAdapter(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder<ItemDraftBinding> {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder<ItemDraftBinding> {
|
||||||
|
|
||||||
val binding = ItemDraftBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
val binding = ItemDraftBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
|
||||||
val viewHolder = BindingHolder(binding)
|
val viewHolder = BindingHolder(binding)
|
||||||
|
|
|
@ -55,7 +55,7 @@ class EditFilterActivity : BaseActivity() {
|
||||||
filterContextNotifications to Filter.Kind.NOTIFICATIONS,
|
filterContextNotifications to Filter.Kind.NOTIFICATIONS,
|
||||||
filterContextPublic to Filter.Kind.PUBLIC,
|
filterContextPublic to Filter.Kind.PUBLIC,
|
||||||
filterContextThread to Filter.Kind.THREAD,
|
filterContextThread to Filter.Kind.THREAD,
|
||||||
filterContextAccount to Filter.Kind.ACCOUNT,
|
filterContextAccount to Filter.Kind.ACCOUNT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +213,7 @@ class EditFilterActivity : BaseActivity() {
|
||||||
FilterKeyword(
|
FilterKeyword(
|
||||||
"",
|
"",
|
||||||
binding.phraseEditText.text.toString(),
|
binding.phraseEditText.text.toString(),
|
||||||
binding.phraseWholeWord.isChecked,
|
binding.phraseWholeWord.isChecked
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -234,7 +234,7 @@ class EditFilterActivity : BaseActivity() {
|
||||||
keyword,
|
keyword,
|
||||||
keyword.copy(
|
keyword.copy(
|
||||||
keyword = binding.phraseEditText.text.toString(),
|
keyword = binding.phraseEditText.text.toString(),
|
||||||
wholeWord = binding.phraseWholeWord.isChecked,
|
wholeWord = binding.phraseWholeWord.isChecked
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi, val eventHub
|
||||||
title = title,
|
title = title,
|
||||||
context = contexts,
|
context = contexts,
|
||||||
filterAction = action,
|
filterAction = action,
|
||||||
expiresInSeconds = expiresInSeconds,
|
expiresInSeconds = expiresInSeconds
|
||||||
).fold(
|
).fold(
|
||||||
{ newFilter ->
|
{ newFilter ->
|
||||||
// This is _terrible_, but the all-in-one update filter api Just Doesn't Work
|
// This is _terrible_, but the all-in-one update filter api Just Doesn't Work
|
||||||
|
@ -123,7 +123,7 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi, val eventHub
|
||||||
title = title,
|
title = title,
|
||||||
context = contexts,
|
context = contexts,
|
||||||
filterAction = action,
|
filterAction = action,
|
||||||
expiresInSeconds = expiresInSeconds,
|
expiresInSeconds = expiresInSeconds
|
||||||
).fold(
|
).fold(
|
||||||
{
|
{
|
||||||
// This is _terrible_, but the all-in-one update filter api Just Doesn't Work
|
// This is _terrible_, but the all-in-one update filter api Just Doesn't Work
|
||||||
|
@ -175,7 +175,7 @@ class EditFilterViewModel @Inject constructor(val api: MastodonApi, val eventHub
|
||||||
context = context,
|
context = context,
|
||||||
irreversible = false,
|
irreversible = false,
|
||||||
wholeWord = keyword.wholeWord,
|
wholeWord = keyword.wholeWord,
|
||||||
expiresInSeconds = expiresInSeconds,
|
expiresInSeconds = expiresInSeconds
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ class FiltersViewModel @Inject constructor(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Snackbar.make(parent, "Error deleting filter '${filter.title}'", Snackbar.LENGTH_SHORT).show()
|
Snackbar.make(parent, "Error deleting filter '${filter.title}'", Snackbar.LENGTH_SHORT).show()
|
||||||
},
|
}
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Snackbar.make(parent, "Error deleting filter '${filter.title}'", Snackbar.LENGTH_SHORT).show()
|
Snackbar.make(parent, "Error deleting filter '${filter.title}'", Snackbar.LENGTH_SHORT).show()
|
||||||
|
|
|
@ -13,7 +13,7 @@ import com.keylesspalace.tusky.util.BindingHolder
|
||||||
|
|
||||||
class FollowedTagsAdapter(
|
class FollowedTagsAdapter(
|
||||||
private val actionListener: HashtagActionListener,
|
private val actionListener: HashtagActionListener,
|
||||||
private val viewModel: FollowedTagsViewModel,
|
private val viewModel: FollowedTagsViewModel
|
||||||
) : PagingDataAdapter<String, BindingHolder<ItemFollowedHashtagBinding>>(STRING_COMPARATOR) {
|
) : PagingDataAdapter<String, BindingHolder<ItemFollowedHashtagBinding>>(STRING_COMPARATOR) {
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder<ItemFollowedHashtagBinding> =
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder<ItemFollowedHashtagBinding> =
|
||||||
BindingHolder(ItemFollowedHashtagBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
BindingHolder(ItemFollowedHashtagBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
||||||
|
|
|
@ -13,7 +13,7 @@ import retrofit2.Response
|
||||||
@OptIn(ExperimentalPagingApi::class)
|
@OptIn(ExperimentalPagingApi::class)
|
||||||
class FollowedTagsRemoteMediator(
|
class FollowedTagsRemoteMediator(
|
||||||
private val api: MastodonApi,
|
private val api: MastodonApi,
|
||||||
private val viewModel: FollowedTagsViewModel,
|
private val viewModel: FollowedTagsViewModel
|
||||||
) : RemoteMediator<String, String>() {
|
) : RemoteMediator<String, String>() {
|
||||||
override suspend fun load(
|
override suspend fun load(
|
||||||
loadType: LoadType,
|
loadType: LoadType,
|
||||||
|
|
|
@ -30,8 +30,9 @@ class DomainMutesAdapter(
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
var count = instances.size
|
var count = instances.size
|
||||||
if (bottomLoading)
|
if (bottomLoading) {
|
||||||
++count
|
++count
|
||||||
|
}
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,8 @@ class LoginActivity : BaseActivity(), Injectable {
|
||||||
}
|
}
|
||||||
|
|
||||||
preferences = getSharedPreferences(
|
preferences = getSharedPreferences(
|
||||||
getString(R.string.preferences_file_key), Context.MODE_PRIVATE
|
getString(R.string.preferences_file_key),
|
||||||
|
Context.MODE_PRIVATE
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.loginButton.setOnClickListener { onLoginClick(true) }
|
binding.loginButton.setOnClickListener { onLoginClick(true) }
|
||||||
|
@ -168,8 +169,11 @@ class LoginActivity : BaseActivity(), Injectable {
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
mastodonApi.authenticateApp(
|
mastodonApi.authenticateApp(
|
||||||
domain, getString(R.string.app_name), oauthRedirectUri,
|
domain,
|
||||||
OAUTH_SCOPES, getString(R.string.tusky_website)
|
getString(R.string.app_name),
|
||||||
|
oauthRedirectUri,
|
||||||
|
OAUTH_SCOPES,
|
||||||
|
getString(R.string.tusky_website)
|
||||||
).fold(
|
).fold(
|
||||||
{ credentials ->
|
{ credentials ->
|
||||||
// Before we open browser page we save the data.
|
// Before we open browser page we save the data.
|
||||||
|
@ -273,7 +277,12 @@ class LoginActivity : BaseActivity(), Injectable {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
|
||||||
mastodonApi.fetchOAuthToken(
|
mastodonApi.fetchOAuthToken(
|
||||||
domain, clientId, clientSecret, oauthRedirectUri, code, "authorization_code"
|
domain,
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
oauthRedirectUri,
|
||||||
|
code,
|
||||||
|
"authorization_code"
|
||||||
).fold(
|
).fold(
|
||||||
{ accessToken ->
|
{ accessToken ->
|
||||||
fetchAccountDetails(accessToken, domain, clientId, clientSecret)
|
fetchAccountDetails(accessToken, domain, clientId, clientSecret)
|
||||||
|
@ -293,7 +302,6 @@ class LoginActivity : BaseActivity(), Injectable {
|
||||||
clientId: String,
|
clientId: String,
|
||||||
clientSecret: String
|
clientSecret: String
|
||||||
) {
|
) {
|
||||||
|
|
||||||
mastodonApi.accountVerifyCredentials(
|
mastodonApi.accountVerifyCredentials(
|
||||||
domain = domain,
|
domain = domain,
|
||||||
auth = "Bearer ${accessToken.accessToken}"
|
auth = "Bearer ${accessToken.accessToken}"
|
||||||
|
@ -349,6 +357,7 @@ class LoginActivity : BaseActivity(), Injectable {
|
||||||
|
|
||||||
const val MODE_DEFAULT = 0
|
const val MODE_DEFAULT = 0
|
||||||
const val MODE_ADDITIONAL_LOGIN = 1
|
const val MODE_ADDITIONAL_LOGIN = 1
|
||||||
|
|
||||||
// "Migration" is used to update the OAuth scope granted to the client
|
// "Migration" is used to update the OAuth scope granted to the client
|
||||||
const val MODE_MIGRATION = 2
|
const val MODE_MIGRATION = 2
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ class OauthLogin : ActivityResultContract<LoginData, LoginResult>() {
|
||||||
data class LoginData(
|
data class LoginData(
|
||||||
val domain: String,
|
val domain: String,
|
||||||
val url: Uri,
|
val url: Uri,
|
||||||
val oauthRedirectUrl: Uri,
|
val oauthRedirectUrl: Uri
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
sealed class LoginResult : Parcelable {
|
sealed class LoginResult : Parcelable {
|
||||||
|
|
|
@ -30,7 +30,7 @@ import com.keylesspalace.tusky.viewdata.NotificationViewData
|
||||||
|
|
||||||
class FollowViewHolder(
|
class FollowViewHolder(
|
||||||
private val binding: ItemFollowBinding,
|
private val binding: ItemFollowBinding,
|
||||||
private val notificationActionListener: NotificationActionListener,
|
private val notificationActionListener: NotificationActionListener
|
||||||
) : NotificationsPagingAdapter.ViewHolder, RecyclerView.ViewHolder(binding.root) {
|
) : NotificationsPagingAdapter.ViewHolder, RecyclerView.ViewHolder(binding.root) {
|
||||||
private val avatarRadius42dp = itemView.context.resources.getDimensionPixelSize(
|
private val avatarRadius42dp = itemView.context.resources.getDimensionPixelSize(
|
||||||
R.dimen.avatar_radius_42dp
|
R.dimen.avatar_radius_42dp
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
* see <http://www.gnu.org/licenses>. */
|
* see <http://www.gnu.org/licenses>. */
|
||||||
|
|
||||||
@file:JvmName("PushNotificationHelper")
|
@file:JvmName("PushNotificationHelper")
|
||||||
|
|
||||||
package com.keylesspalace.tusky.components.notifications
|
package com.keylesspalace.tusky.components.notifications
|
||||||
|
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
|
@ -163,7 +164,6 @@ suspend fun registerUnifiedPushEndpoint(
|
||||||
account: AccountEntity,
|
account: AccountEntity,
|
||||||
endpoint: String
|
endpoint: String
|
||||||
) = withContext(Dispatchers.IO) {
|
) = withContext(Dispatchers.IO) {
|
||||||
|
|
||||||
// Generate a prime256v1 key pair for WebPush
|
// Generate a prime256v1 key pair for WebPush
|
||||||
// Decryption is unimplemented for now, since Mastodon uses an old WebPush
|
// Decryption is unimplemented for now, since Mastodon uses an old WebPush
|
||||||
// standard which does not send needed information for decryption in the payload
|
// standard which does not send needed information for decryption in the payload
|
||||||
|
@ -173,8 +173,11 @@ suspend fun registerUnifiedPushEndpoint(
|
||||||
val auth = CryptoUtil.secureRandomBytesEncoded(16)
|
val auth = CryptoUtil.secureRandomBytesEncoded(16)
|
||||||
|
|
||||||
api.subscribePushNotifications(
|
api.subscribePushNotifications(
|
||||||
"Bearer ${account.accessToken}", account.domain,
|
"Bearer ${account.accessToken}",
|
||||||
endpoint, keyPair.pubkey, auth,
|
account.domain,
|
||||||
|
endpoint,
|
||||||
|
keyPair.pubkey,
|
||||||
|
auth,
|
||||||
buildSubscriptionData(context, account)
|
buildSubscriptionData(context, account)
|
||||||
).onFailure { throwable ->
|
).onFailure { throwable ->
|
||||||
Log.w(TAG, "Error setting push endpoint for account ${account.id}", throwable)
|
Log.w(TAG, "Error setting push endpoint for account ${account.id}", throwable)
|
||||||
|
@ -195,7 +198,8 @@ suspend fun registerUnifiedPushEndpoint(
|
||||||
suspend fun updateUnifiedPushSubscription(context: Context, api: MastodonApi, accountManager: AccountManager, account: AccountEntity) {
|
suspend fun updateUnifiedPushSubscription(context: Context, api: MastodonApi, accountManager: AccountManager, account: AccountEntity) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
api.updatePushNotificationSubscription(
|
api.updatePushNotificationSubscription(
|
||||||
"Bearer ${account.accessToken}", account.domain,
|
"Bearer ${account.accessToken}",
|
||||||
|
account.domain,
|
||||||
buildSubscriptionData(context, account)
|
buildSubscriptionData(context, account)
|
||||||
).onSuccess {
|
).onSuccess {
|
||||||
Log.d(TAG, "UnifiedPush subscription updated for account ${account.id}")
|
Log.d(TAG, "UnifiedPush subscription updated for account ${account.id}")
|
||||||
|
|
|
@ -300,7 +300,6 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable {
|
||||||
override fun onResponse(call: Call<Account>, response: Response<Account>) {
|
override fun onResponse(call: Call<Account>, response: Response<Account>) {
|
||||||
val account = response.body()
|
val account = response.body()
|
||||||
if (response.isSuccessful && account != null) {
|
if (response.isSuccessful && account != null) {
|
||||||
|
|
||||||
accountManager.activeAccount?.let {
|
accountManager.activeAccount?.let {
|
||||||
it.defaultPostPrivacy = account.source?.privacy
|
it.defaultPostPrivacy = account.source?.privacy
|
||||||
?: Status.Visibility.PUBLIC
|
?: Status.Visibility.PUBLIC
|
||||||
|
|
|
@ -85,8 +85,11 @@ class StatusViewHolder(
|
||||||
val sensitive = viewData.status.sensitive
|
val sensitive = viewData.status.sensitive
|
||||||
|
|
||||||
statusViewHelper.setMediasPreview(
|
statusViewHelper.setMediasPreview(
|
||||||
statusDisplayOptions, viewData.status.attachments,
|
statusDisplayOptions,
|
||||||
sensitive, previewListener, viewState.isMediaShow(viewData.id, viewData.status.sensitive),
|
viewData.status.attachments,
|
||||||
|
sensitive,
|
||||||
|
previewListener,
|
||||||
|
viewState.isMediaShow(viewData.id, viewData.status.sensitive),
|
||||||
mediaViewHeight
|
mediaViewHeight
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -97,8 +100,10 @@ class StatusViewHolder(
|
||||||
private fun updateTextView() {
|
private fun updateTextView() {
|
||||||
viewdata()?.let { viewdata ->
|
viewdata()?.let { viewdata ->
|
||||||
setupCollapsedState(
|
setupCollapsedState(
|
||||||
shouldTrimStatus(viewdata.content), viewState.isCollapsed(viewdata.id, true),
|
shouldTrimStatus(viewdata.content),
|
||||||
viewState.isContentShow(viewdata.id, viewdata.status.sensitive), viewdata.spoilerText
|
viewState.isCollapsed(viewdata.id, true),
|
||||||
|
viewState.isContentShow(viewdata.id, viewdata.status.sensitive),
|
||||||
|
viewdata.spoilerText
|
||||||
)
|
)
|
||||||
|
|
||||||
if (viewdata.spoilerText.isBlank()) {
|
if (viewdata.spoilerText.isBlank()) {
|
||||||
|
|
|
@ -38,7 +38,10 @@ class StatusesAdapter(
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StatusViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StatusViewHolder {
|
||||||
val binding = ItemReportStatusBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
val binding = ItemReportStatusBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
return StatusViewHolder(
|
return StatusViewHolder(
|
||||||
binding, statusDisplayOptions, statusViewState, adapterHandler,
|
binding,
|
||||||
|
statusDisplayOptions,
|
||||||
|
statusViewState,
|
||||||
|
adapterHandler,
|
||||||
statusForPosition
|
statusForPosition
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,8 +72,9 @@ class ReportNoteFragment : Fragment(R.layout.fragment_report_note), Injectable {
|
||||||
binding.reportDescriptionRemoteInstance.hide()
|
binding.reportDescriptionRemoteInstance.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viewModel.isRemoteAccount)
|
if (viewModel.isRemoteAccount) {
|
||||||
binding.checkIsNotifyRemote.text = getString(R.string.report_remote_instance, viewModel.remoteServer)
|
binding.checkIsNotifyRemote.text = getString(R.string.report_remote_instance, viewModel.remoteServer)
|
||||||
|
}
|
||||||
binding.checkIsNotifyRemote.isChecked = viewModel.isRemoteNotify
|
binding.checkIsNotifyRemote.isChecked = viewModel.isRemoteNotify
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ class SearchActivity : BottomSheetActivity(), HasAndroidInjector, MenuProvider {
|
||||||
binding.pages.isUserInputEnabled = enableSwipeForTabs
|
binding.pages.isUserInputEnabled = enableSwipeForTabs
|
||||||
|
|
||||||
TabLayoutMediator(binding.tabs, binding.pages) {
|
TabLayoutMediator(binding.tabs, binding.pages) {
|
||||||
tab, position ->
|
tab, position ->
|
||||||
tab.text = getPageTitle(position)
|
tab.text = getPageTitle(position)
|
||||||
}.attach()
|
}.attach()
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,6 @@ class SearchPagingSource<T : Any>(
|
||||||
val currentKey = params.key ?: 0
|
val currentKey = params.key ?: 0
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
val data = mastodonApi.searchObservable(
|
val data = mastodonApi.searchObservable(
|
||||||
query = searchRequest,
|
query = searchRequest,
|
||||||
type = searchType.apiParameter,
|
type = searchType.apiParameter,
|
||||||
|
|
|
@ -133,7 +133,8 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
||||||
Attachment.Type.GIFV, Attachment.Type.VIDEO, Attachment.Type.IMAGE, Attachment.Type.AUDIO -> {
|
Attachment.Type.GIFV, Attachment.Type.VIDEO, Attachment.Type.IMAGE, Attachment.Type.AUDIO -> {
|
||||||
val attachments = AttachmentViewData.list(actionable)
|
val attachments = AttachmentViewData.list(actionable)
|
||||||
val intent = ViewMediaActivity.newIntent(
|
val intent = ViewMediaActivity.newIntent(
|
||||||
context, attachments,
|
context,
|
||||||
|
attachments,
|
||||||
attachmentIndex
|
attachmentIndex
|
||||||
)
|
)
|
||||||
if (view != null) {
|
if (view != null) {
|
||||||
|
@ -141,7 +142,8 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
||||||
ViewCompat.setTransitionName(view, url)
|
ViewCompat.setTransitionName(view, url)
|
||||||
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
|
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
view, url
|
view,
|
||||||
|
url
|
||||||
)
|
)
|
||||||
startActivity(intent, options.toBundle())
|
startActivity(intent, options.toBundle())
|
||||||
} else {
|
} else {
|
||||||
|
@ -399,7 +401,8 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
||||||
|
|
||||||
private fun showOpenAsDialog(statusUrl: String, dialogTitle: CharSequence?) {
|
private fun showOpenAsDialog(statusUrl: String, dialogTitle: CharSequence?) {
|
||||||
bottomSheetActivity?.showAccountChooserDialog(
|
bottomSheetActivity?.showAccountChooserDialog(
|
||||||
dialogTitle, false,
|
dialogTitle,
|
||||||
|
false,
|
||||||
object : AccountSelectionListener {
|
object : AccountSelectionListener {
|
||||||
override fun onAccountSelected(account: AccountEntity) {
|
override fun onAccountSelected(account: AccountEntity) {
|
||||||
bottomSheetActivity?.openAsAccount(statusUrl, account)
|
bottomSheetActivity?.openAsAccount(statusUrl, account)
|
||||||
|
@ -515,7 +518,7 @@ class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), Status
|
||||||
language = status.language,
|
language = status.language,
|
||||||
statusId = source.id,
|
statusId = source.id,
|
||||||
poll = status.poll?.toNewPoll(status.createdAt),
|
poll = status.poll?.toNewPoll(status.createdAt),
|
||||||
kind = ComposeActivity.ComposeKind.EDIT_POSTED,
|
kind = ComposeActivity.ComposeKind.EDIT_POSTED
|
||||||
)
|
)
|
||||||
startActivity(ComposeActivity.startIntent(requireContext(), composeOptions))
|
startActivity(ComposeActivity.startIntent(requireContext(), composeOptions))
|
||||||
},
|
},
|
||||||
|
|
|
@ -170,7 +170,7 @@ class TimelineFragment :
|
||||||
viewModel.init(
|
viewModel.init(
|
||||||
kind,
|
kind,
|
||||||
id,
|
id,
|
||||||
tags,
|
tags
|
||||||
)
|
)
|
||||||
|
|
||||||
isSwipeToRefreshEnabled = arguments.getBoolean(ARG_ENABLE_SWIPE_TO_REFRESH, true)
|
isSwipeToRefreshEnabled = arguments.getBoolean(ARG_ENABLE_SWIPE_TO_REFRESH, true)
|
||||||
|
@ -188,7 +188,11 @@ class TimelineFragment :
|
||||||
PrefKeys.SHOW_CARDS_IN_TIMELINES,
|
PrefKeys.SHOW_CARDS_IN_TIMELINES,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
) CardViewMode.INDENTED else CardViewMode.NONE,
|
) {
|
||||||
|
CardViewMode.INDENTED
|
||||||
|
} else {
|
||||||
|
CardViewMode.NONE
|
||||||
|
},
|
||||||
confirmReblogs = preferences.getBoolean(PrefKeys.CONFIRM_REBLOGS, true),
|
confirmReblogs = preferences.getBoolean(PrefKeys.CONFIRM_REBLOGS, true),
|
||||||
confirmFavourites = preferences.getBoolean(PrefKeys.CONFIRM_FAVOURITES, false),
|
confirmFavourites = preferences.getBoolean(PrefKeys.CONFIRM_FAVOURITES, false),
|
||||||
hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false),
|
hideStats = preferences.getBoolean(PrefKeys.WELLBEING_HIDE_STATS_POSTS, false),
|
||||||
|
@ -255,7 +259,9 @@ class TimelineFragment :
|
||||||
if (getView() != null) {
|
if (getView() != null) {
|
||||||
if (isSwipeToRefreshEnabled) {
|
if (isSwipeToRefreshEnabled) {
|
||||||
binding.recyclerView.scrollBy(0, Utils.dpToPx(requireContext(), -30))
|
binding.recyclerView.scrollBy(0, Utils.dpToPx(requireContext(), -30))
|
||||||
} else binding.recyclerView.scrollToPosition(0)
|
} else {
|
||||||
|
binding.recyclerView.scrollToPosition(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,8 +131,10 @@ class TimelinePagingAdapter(
|
||||||
return if (oldItem == newItem) {
|
return if (oldItem == newItem) {
|
||||||
// If items are equal - update timestamp only
|
// If items are equal - update timestamp only
|
||||||
listOf(StatusBaseViewHolder.Key.KEY_CREATED)
|
listOf(StatusBaseViewHolder.Key.KEY_CREATED)
|
||||||
} else // If items are different - update the whole view holder
|
} else {
|
||||||
|
// If items are different - update the whole view holder
|
||||||
null
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,7 @@ fun Placeholder.toEntity(timelineUserId: Long): TimelineStatusEntity {
|
||||||
card = null,
|
card = null,
|
||||||
repliesCount = 0,
|
repliesCount = 0,
|
||||||
language = null,
|
language = null,
|
||||||
filtered = null,
|
filtered = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +150,7 @@ fun Status.toEntity(
|
||||||
card = actionableStatus.card?.let(gson::toJson),
|
card = actionableStatus.card?.let(gson::toJson),
|
||||||
repliesCount = actionableStatus.repliesCount,
|
repliesCount = actionableStatus.repliesCount,
|
||||||
language = actionableStatus.language,
|
language = actionableStatus.language,
|
||||||
filtered = actionableStatus.filtered,
|
filtered = actionableStatus.filtered
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +198,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson, isDetailed: Boolean = false
|
||||||
card = card,
|
card = card,
|
||||||
repliesCount = status.repliesCount,
|
repliesCount = status.repliesCount,
|
||||||
language = status.language,
|
language = status.language,
|
||||||
filtered = status.filtered,
|
filtered = status.filtered
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val status = if (reblog != null) {
|
val status = if (reblog != null) {
|
||||||
|
@ -231,7 +231,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson, isDetailed: Boolean = false
|
||||||
card = null,
|
card = null,
|
||||||
repliesCount = status.repliesCount,
|
repliesCount = status.repliesCount,
|
||||||
language = status.language,
|
language = status.language,
|
||||||
filtered = status.filtered,
|
filtered = status.filtered
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Status(
|
Status(
|
||||||
|
@ -263,7 +263,7 @@ fun TimelineStatusWithAccount.toViewData(gson: Gson, isDetailed: Boolean = false
|
||||||
card = card,
|
card = card,
|
||||||
repliesCount = status.repliesCount,
|
repliesCount = status.repliesCount,
|
||||||
language = status.language,
|
language = status.language,
|
||||||
filtered = status.filtered,
|
filtered = status.filtered
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return StatusViewData.Concrete(
|
return StatusViewData.Concrete(
|
||||||
|
|
|
@ -49,7 +49,6 @@ class CachedTimelineRemoteMediator(
|
||||||
loadType: LoadType,
|
loadType: LoadType,
|
||||||
state: PagingState<Int, TimelineStatusWithAccount>
|
state: PagingState<Int, TimelineStatusWithAccount>
|
||||||
): MediatorResult {
|
): MediatorResult {
|
||||||
|
|
||||||
if (!activeAccount.isLoggedIn()) {
|
if (!activeAccount.isLoggedIn()) {
|
||||||
return MediatorResult.Success(endOfPaginationReached = true)
|
return MediatorResult.Success(endOfPaginationReached = true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,7 +204,6 @@ class CachedTimelineViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
db.withTransaction {
|
db.withTransaction {
|
||||||
|
|
||||||
timelineDao.delete(activeAccount.id, placeholderId)
|
timelineDao.delete(activeAccount.id, placeholderId)
|
||||||
|
|
||||||
val overlappedStatuses = if (statuses.isNotEmpty()) {
|
val overlappedStatuses = if (statuses.isNotEmpty()) {
|
||||||
|
|
|
@ -26,7 +26,6 @@ class NetworkTimelinePagingSource(
|
||||||
override fun getRefreshKey(state: PagingState<String, StatusViewData>): String? = null
|
override fun getRefreshKey(state: PagingState<String, StatusViewData>): String? = null
|
||||||
|
|
||||||
override suspend fun load(params: LoadParams<String>): LoadResult<String, StatusViewData> {
|
override suspend fun load(params: LoadParams<String>): LoadResult<String, StatusViewData> {
|
||||||
|
|
||||||
return if (params is LoadParams.Refresh) {
|
return if (params is LoadParams.Refresh) {
|
||||||
val list = viewModel.statusData.toList()
|
val list = viewModel.statusData.toList()
|
||||||
LoadResult.Page(list, null, viewModel.nextKey)
|
LoadResult.Page(list, null, viewModel.nextKey)
|
||||||
|
|
|
@ -36,7 +36,6 @@ class NetworkTimelineRemoteMediator(
|
||||||
loadType: LoadType,
|
loadType: LoadType,
|
||||||
state: PagingState<String, StatusViewData>
|
state: PagingState<String, StatusViewData>
|
||||||
): MediatorResult {
|
): MediatorResult {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val statusResponse = when (loadType) {
|
val statusResponse = when (loadType) {
|
||||||
LoadType.REFRESH -> {
|
LoadType.REFRESH -> {
|
||||||
|
@ -80,7 +79,6 @@ class NetworkTimelineRemoteMediator(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loadType == LoadType.REFRESH && viewModel.statusData.isNotEmpty()) {
|
if (loadType == LoadType.REFRESH && viewModel.statusData.isNotEmpty()) {
|
||||||
|
|
||||||
val insertPlaceholder = if (statuses.isNotEmpty()) {
|
val insertPlaceholder = if (statuses.isNotEmpty()) {
|
||||||
!viewModel.statusData.removeAll { statusViewData ->
|
!viewModel.statusData.removeAll { statusViewData ->
|
||||||
statuses.any { status -> status.id == statusViewData.asStatusOrNull()?.id }
|
statuses.any { status -> status.id == statusViewData.asStatusOrNull()?.id }
|
||||||
|
|
|
@ -183,7 +183,7 @@ class NetworkTimelineViewModel @Inject constructor(
|
||||||
.copy(
|
.copy(
|
||||||
isShowingContent = oldStatus!!.isShowingContent,
|
isShowingContent = oldStatus!!.isShowingContent,
|
||||||
isExpanded = oldStatus.isExpanded,
|
isExpanded = oldStatus.isExpanded,
|
||||||
isCollapsed = oldStatus.isCollapsed,
|
isCollapsed = oldStatus.isCollapsed
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -302,7 +302,7 @@ abstract class TimelineViewModel(
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "Error getting filters", throwable)
|
Log.e(TAG, "Error getting filters", throwable)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ import com.keylesspalace.tusky.interfaces.LinkListener
|
||||||
import com.keylesspalace.tusky.viewdata.TrendingViewData
|
import com.keylesspalace.tusky.viewdata.TrendingViewData
|
||||||
|
|
||||||
class TrendingAdapter(
|
class TrendingAdapter(
|
||||||
private val trendingListener: LinkListener,
|
private val trendingListener: LinkListener
|
||||||
) : ListAdapter<TrendingViewData, RecyclerView.ViewHolder>(TrendingDifferCallback) {
|
) : ListAdapter<TrendingViewData, RecyclerView.ViewHolder>(TrendingDifferCallback) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|
|
@ -95,7 +95,7 @@ class TrendingFragment :
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
adapter = TrendingAdapter(
|
adapter = TrendingAdapter(
|
||||||
this,
|
this
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +208,8 @@ class TrendingFragment :
|
||||||
binding.recyclerView.hide()
|
binding.recyclerView.hide()
|
||||||
binding.messageView.show()
|
binding.messageView.show()
|
||||||
binding.messageView.setup(
|
binding.messageView.setup(
|
||||||
R.drawable.elephant_friend_empty, R.string.message_empty,
|
R.drawable.elephant_friend_empty,
|
||||||
|
R.string.message_empty,
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -242,7 +243,7 @@ class TrendingFragment :
|
||||||
binding.swipeRefreshLayout.isRefreshing = false
|
binding.swipeRefreshLayout.isRefreshing = false
|
||||||
binding.messageView.setup(
|
binding.messageView.setup(
|
||||||
R.drawable.elephant_offline,
|
R.drawable.elephant_offline,
|
||||||
R.string.error_network,
|
R.string.error_network
|
||||||
) { refreshContent() }
|
) { refreshContent() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +255,7 @@ class TrendingFragment :
|
||||||
binding.swipeRefreshLayout.isRefreshing = false
|
binding.swipeRefreshLayout.isRefreshing = false
|
||||||
binding.messageView.setup(
|
binding.messageView.setup(
|
||||||
R.drawable.elephant_error,
|
R.drawable.elephant_error,
|
||||||
R.string.error_generic,
|
R.string.error_generic
|
||||||
) { refreshContent() }
|
) { refreshContent() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,8 +93,10 @@ class ThreadAdapter(
|
||||||
return if (oldItem == newItem) {
|
return if (oldItem == newItem) {
|
||||||
// If items are equal - update timestamp only
|
// If items are equal - update timestamp only
|
||||||
listOf(StatusBaseViewHolder.Key.KEY_CREATED)
|
listOf(StatusBaseViewHolder.Key.KEY_CREATED)
|
||||||
} else // If items are different - update the whole view holder
|
} else {
|
||||||
|
// If items are different - update the whole view holder
|
||||||
null
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,7 +167,7 @@ class ViewThreadViewModel @Inject constructor(
|
||||||
_uiState.value = ThreadUiState.Success(
|
_uiState.value = ThreadUiState.Success(
|
||||||
statusViewData = listOf(detailedStatus),
|
statusViewData = listOf(detailedStatus),
|
||||||
detailedStatusPosition = 0,
|
detailedStatusPosition = 0,
|
||||||
revealButton = RevealButtonState.NO_BUTTON,
|
revealButton = RevealButtonState.NO_BUTTON
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@ data class AccountEntity(
|
||||||
var pushPubKey: String = "",
|
var pushPubKey: String = "",
|
||||||
var pushPrivKey: String = "",
|
var pushPrivKey: String = "",
|
||||||
var pushAuth: String = "",
|
var pushAuth: String = "",
|
||||||
var pushServerKey: String = "",
|
var pushServerKey: String = ""
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val identifier: String
|
val identifier: String
|
||||||
|
|
|
@ -66,7 +66,6 @@ class AccountManager @Inject constructor(db: AppDatabase) {
|
||||||
oauthScopes: String,
|
oauthScopes: String,
|
||||||
newAccount: Account
|
newAccount: Account
|
||||||
) {
|
) {
|
||||||
|
|
||||||
activeAccount?.let {
|
activeAccount?.let {
|
||||||
it.isActive = false
|
it.isActive = false
|
||||||
Log.d(TAG, "addAccount: saving account with id " + it.id)
|
Log.d(TAG, "addAccount: saving account with id " + it.id)
|
||||||
|
@ -121,7 +120,6 @@ class AccountManager @Inject constructor(db: AppDatabase) {
|
||||||
* @return the new active account, or null if no other account was found
|
* @return the new active account, or null if no other account was found
|
||||||
*/
|
*/
|
||||||
fun logActiveAccountOut(): AccountEntity? {
|
fun logActiveAccountOut(): AccountEntity? {
|
||||||
|
|
||||||
return activeAccount?.let { account ->
|
return activeAccount?.let { account ->
|
||||||
|
|
||||||
account.logout()
|
account.logout()
|
||||||
|
@ -167,7 +165,6 @@ class AccountManager @Inject constructor(db: AppDatabase) {
|
||||||
* @param accountId the database id of the new active account
|
* @param accountId the database id of the new active account
|
||||||
*/
|
*/
|
||||||
fun setActiveAccount(accountId: Long) {
|
fun setActiveAccount(accountId: Long) {
|
||||||
|
|
||||||
val newActiveAccount = accounts.find { (id) ->
|
val newActiveAccount = accounts.find { (id) ->
|
||||||
id == accountId
|
id == accountId
|
||||||
} ?: return // invalid accountId passed, do nothing
|
} ?: return // invalid accountId passed, do nothing
|
||||||
|
@ -237,10 +234,12 @@ class AccountManager @Inject constructor(db: AppDatabase) {
|
||||||
fun shouldDisplaySelfUsername(context: Context): Boolean {
|
fun shouldDisplaySelfUsername(context: Context): Boolean {
|
||||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
val showUsernamePreference = sharedPreferences.getString(PrefKeys.SHOW_SELF_USERNAME, "disambiguate")
|
val showUsernamePreference = sharedPreferences.getString(PrefKeys.SHOW_SELF_USERNAME, "disambiguate")
|
||||||
if (showUsernamePreference == "always")
|
if (showUsernamePreference == "always") {
|
||||||
return true
|
return true
|
||||||
if (showUsernamePreference == "never")
|
}
|
||||||
|
if (showUsernamePreference == "never") {
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return accounts.size > 1 // "disambiguate"
|
return accounts.size > 1 // "disambiguate"
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ data class DraftEntity(
|
||||||
val failedToSendNew: Boolean,
|
val failedToSendNew: Boolean,
|
||||||
val scheduledAt: String?,
|
val scheduledAt: String?,
|
||||||
val language: String?,
|
val language: String?,
|
||||||
val statusId: String?,
|
val statusId: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -85,7 +85,7 @@ data class TimelineStatusEntity(
|
||||||
val pinned: Boolean,
|
val pinned: Boolean,
|
||||||
val card: String?,
|
val card: String?,
|
||||||
val language: String?,
|
val language: String?,
|
||||||
val filtered: List<FilterResult>?,
|
val filtered: List<FilterResult>?
|
||||||
) {
|
) {
|
||||||
val isPlaceholder: Boolean
|
val isPlaceholder: Boolean
|
||||||
get() = this.authorServerId == null
|
get() = this.authorServerId == null
|
||||||
|
|
|
@ -68,7 +68,7 @@ class AppModule {
|
||||||
AppDatabase.MIGRATION_38_39, AppDatabase.MIGRATION_39_40, AppDatabase.MIGRATION_40_41,
|
AppDatabase.MIGRATION_38_39, AppDatabase.MIGRATION_39_40, AppDatabase.MIGRATION_40_41,
|
||||||
AppDatabase.MIGRATION_41_42, AppDatabase.MIGRATION_42_43, AppDatabase.MIGRATION_43_44,
|
AppDatabase.MIGRATION_41_42, AppDatabase.MIGRATION_42_43, AppDatabase.MIGRATION_43_44,
|
||||||
AppDatabase.MIGRATION_44_45, AppDatabase.MIGRATION_45_46, AppDatabase.MIGRATION_46_47,
|
AppDatabase.MIGRATION_44_45, AppDatabase.MIGRATION_45_46, AppDatabase.MIGRATION_46_47,
|
||||||
AppDatabase.MIGRATION_47_48,
|
AppDatabase.MIGRATION_47_48
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,9 @@ data class Account(
|
||||||
val name: String
|
val name: String
|
||||||
get() = if (displayName.isNullOrEmpty()) {
|
get() = if (displayName.isNullOrEmpty()) {
|
||||||
localUsername
|
localUsername
|
||||||
} else displayName
|
} else {
|
||||||
|
displayName
|
||||||
|
}
|
||||||
|
|
||||||
fun isRemote(): Boolean = this.username != this.localUsername
|
fun isRemote(): Boolean = this.username != this.localUsername
|
||||||
}
|
}
|
||||||
|
@ -53,7 +55,7 @@ data class AccountSource(
|
||||||
val sensitive: Boolean?,
|
val sensitive: Boolean?,
|
||||||
val note: String?,
|
val note: String?,
|
||||||
val fields: List<StringField>?,
|
val fields: List<StringField>?,
|
||||||
val language: String?,
|
val language: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Field(
|
data class Field(
|
||||||
|
|
|
@ -39,12 +39,16 @@ data class Attachment(
|
||||||
enum class Type {
|
enum class Type {
|
||||||
@SerializedName("image")
|
@SerializedName("image")
|
||||||
IMAGE,
|
IMAGE,
|
||||||
|
|
||||||
@SerializedName("gifv")
|
@SerializedName("gifv")
|
||||||
GIFV,
|
GIFV,
|
||||||
|
|
||||||
@SerializedName("video")
|
@SerializedName("video")
|
||||||
VIDEO,
|
VIDEO,
|
||||||
|
|
||||||
@SerializedName("audio")
|
@SerializedName("audio")
|
||||||
AUDIO,
|
AUDIO,
|
||||||
|
|
||||||
@SerializedName("unknown")
|
@SerializedName("unknown")
|
||||||
UNKNOWN
|
UNKNOWN
|
||||||
}
|
}
|
||||||
|
@ -70,7 +74,7 @@ data class Attachment(
|
||||||
val focus: Focus?,
|
val focus: Focus?,
|
||||||
val duration: Float?,
|
val duration: Float?,
|
||||||
val original: Size?,
|
val original: Size?,
|
||||||
val small: Size?,
|
val small: Size?
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -28,7 +28,7 @@ data class DeletedStatus(
|
||||||
@SerializedName("media_attachments") val attachments: ArrayList<Attachment>?,
|
@SerializedName("media_attachments") val attachments: ArrayList<Attachment>?,
|
||||||
val poll: Poll?,
|
val poll: Poll?,
|
||||||
@SerializedName("created_at") val createdAt: Date,
|
@SerializedName("created_at") val createdAt: Date,
|
||||||
val language: String?,
|
val language: String?
|
||||||
) {
|
) {
|
||||||
fun isEmpty(): Boolean {
|
fun isEmpty(): Boolean {
|
||||||
return text == null && attachments == null
|
return text == null && attachments == null
|
||||||
|
|
|
@ -12,7 +12,7 @@ data class Filter(
|
||||||
val context: List<String>,
|
val context: List<String>,
|
||||||
@SerializedName("expires_at") val expiresAt: Date?,
|
@SerializedName("expires_at") val expiresAt: Date?,
|
||||||
@SerializedName("filter_action") private val filterAction: String,
|
@SerializedName("filter_action") private val filterAction: String,
|
||||||
val keywords: List<FilterKeyword>,
|
val keywords: List<FilterKeyword>
|
||||||
// val statuses: List<FilterStatus>,
|
// val statuses: List<FilterStatus>,
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
enum class Action(val action: String) {
|
enum class Action(val action: String) {
|
||||||
|
|
|
@ -8,5 +8,5 @@ import kotlinx.parcelize.Parcelize
|
||||||
data class FilterKeyword(
|
data class FilterKeyword(
|
||||||
val id: String,
|
val id: String,
|
||||||
val keyword: String,
|
val keyword: String,
|
||||||
@SerializedName("whole_word") val wholeWord: Boolean,
|
@SerializedName("whole_word") val wholeWord: Boolean
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
|
@ -5,5 +5,5 @@ import com.google.gson.annotations.SerializedName
|
||||||
data class FilterResult(
|
data class FilterResult(
|
||||||
val filter: Filter,
|
val filter: Filter,
|
||||||
@SerializedName("keyword_matches") val keywordMatches: List<String>?,
|
@SerializedName("keyword_matches") val keywordMatches: List<String>?,
|
||||||
@SerializedName("status_matches") val statusMatches: String?,
|
@SerializedName("status_matches") val statusMatches: String?
|
||||||
)
|
)
|
||||||
|
|
|
@ -57,7 +57,7 @@ data class FilterV1(
|
||||||
FilterKeyword(
|
FilterKeyword(
|
||||||
id = id,
|
id = id,
|
||||||
keyword = phrase,
|
keyword = phrase,
|
||||||
wholeWord = wholeWord,
|
wholeWord = wholeWord
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -54,19 +54,19 @@ data class PollConfiguration(
|
||||||
@SerializedName("max_option_chars") val maxOptionChars: Int?,
|
@SerializedName("max_option_chars") val maxOptionChars: Int?,
|
||||||
@SerializedName("max_characters_per_option") val maxCharactersPerOption: Int?,
|
@SerializedName("max_characters_per_option") val maxCharactersPerOption: Int?,
|
||||||
@SerializedName("min_expiration") val minExpiration: Int?,
|
@SerializedName("min_expiration") val minExpiration: Int?,
|
||||||
@SerializedName("max_expiration") val maxExpiration: Int?,
|
@SerializedName("max_expiration") val maxExpiration: Int?
|
||||||
)
|
)
|
||||||
|
|
||||||
data class InstanceConfiguration(
|
data class InstanceConfiguration(
|
||||||
val statuses: StatusConfiguration?,
|
val statuses: StatusConfiguration?,
|
||||||
@SerializedName("media_attachments") val mediaAttachments: MediaAttachmentConfiguration?,
|
@SerializedName("media_attachments") val mediaAttachments: MediaAttachmentConfiguration?,
|
||||||
val polls: PollConfiguration?,
|
val polls: PollConfiguration?
|
||||||
)
|
)
|
||||||
|
|
||||||
data class StatusConfiguration(
|
data class StatusConfiguration(
|
||||||
@SerializedName("max_characters") val maxCharacters: Int?,
|
@SerializedName("max_characters") val maxCharacters: Int?,
|
||||||
@SerializedName("max_media_attachments") val maxMediaAttachments: Int?,
|
@SerializedName("max_media_attachments") val maxMediaAttachments: Int?,
|
||||||
@SerializedName("characters_reserved_per_url") val charactersReservedPerUrl: Int?,
|
@SerializedName("characters_reserved_per_url") val charactersReservedPerUrl: Int?
|
||||||
)
|
)
|
||||||
|
|
||||||
data class MediaAttachmentConfiguration(
|
data class MediaAttachmentConfiguration(
|
||||||
|
@ -75,7 +75,7 @@ data class MediaAttachmentConfiguration(
|
||||||
@SerializedName("image_matrix_limit") val imageMatrixLimit: Int?,
|
@SerializedName("image_matrix_limit") val imageMatrixLimit: Int?,
|
||||||
@SerializedName("video_size_limit") val videoSizeLimit: Int?,
|
@SerializedName("video_size_limit") val videoSizeLimit: Int?,
|
||||||
@SerializedName("video_frame_rate_limit") val videoFrameRateLimit: Int?,
|
@SerializedName("video_frame_rate_limit") val videoFrameRateLimit: Int?,
|
||||||
@SerializedName("video_matrix_limit") val videoMatrixLimit: Int?,
|
@SerializedName("video_matrix_limit") val videoMatrixLimit: Int?
|
||||||
)
|
)
|
||||||
|
|
||||||
data class PleromaConfiguration(
|
data class PleromaConfiguration(
|
||||||
|
|
|
@ -29,7 +29,7 @@ data class NewStatus(
|
||||||
@SerializedName("media_attributes") val mediaAttributes: List<MediaAttribute>?,
|
@SerializedName("media_attributes") val mediaAttributes: List<MediaAttribute>?,
|
||||||
@SerializedName("scheduled_at") val scheduledAt: String?,
|
@SerializedName("scheduled_at") val scheduledAt: String?,
|
||||||
val poll: NewPoll?,
|
val poll: NewPoll?,
|
||||||
val language: String?,
|
val language: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
|
@ -46,5 +46,5 @@ data class MediaAttribute(
|
||||||
val id: String,
|
val id: String,
|
||||||
val description: String?,
|
val description: String?,
|
||||||
val focus: String?,
|
val focus: String?,
|
||||||
val thumbnail: String?,
|
val thumbnail: String?
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
|
@ -28,7 +28,7 @@ data class Notification(
|
||||||
val id: String,
|
val id: String,
|
||||||
val account: TimelineAccount,
|
val account: TimelineAccount,
|
||||||
val status: Status?,
|
val status: Status?,
|
||||||
val report: Report?,
|
val report: Report?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/** From https://docs.joinmastodon.org/entities/Notification/#type */
|
/** From https://docs.joinmastodon.org/entities/Notification/#type */
|
||||||
|
@ -70,8 +70,9 @@ data class Notification(
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun byString(s: String): Type {
|
fun byString(s: String): Type {
|
||||||
values().forEach {
|
values().forEach {
|
||||||
if (s == it.presentation)
|
if (s == it.presentation) {
|
||||||
return it
|
return it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return UNKNOWN
|
return UNKNOWN
|
||||||
}
|
}
|
||||||
|
@ -115,7 +116,11 @@ data class Notification(
|
||||||
return if (status.mentions.any {
|
return if (status.mentions.any {
|
||||||
it.id == accountId
|
it.id == accountId
|
||||||
}
|
}
|
||||||
) this else copy(type = Type.STATUS)
|
) {
|
||||||
|
this
|
||||||
|
} else {
|
||||||
|
copy(type = Type.STATUS)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,5 +20,5 @@ import com.google.gson.annotations.SerializedName
|
||||||
data class NotificationSubscribeResult(
|
data class NotificationSubscribeResult(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val endpoint: String,
|
val endpoint: String,
|
||||||
@SerializedName("server_key") val serverKey: String,
|
@SerializedName("server_key") val serverKey: String
|
||||||
)
|
)
|
||||||
|
|
|
@ -8,5 +8,5 @@ data class Report(
|
||||||
val category: String,
|
val category: String,
|
||||||
val status_ids: List<String>?,
|
val status_ids: List<String>?,
|
||||||
@SerializedName("created_at") val createdAt: Date,
|
@SerializedName("created_at") val createdAt: Date,
|
||||||
@SerializedName("target_account") val targetAccount: TimelineAccount,
|
@SerializedName("target_account") val targetAccount: TimelineAccount
|
||||||
)
|
)
|
||||||
|
|
|
@ -51,7 +51,7 @@ data class Status(
|
||||||
val poll: Poll?,
|
val poll: Poll?,
|
||||||
val card: Card?,
|
val card: Card?,
|
||||||
val language: String?,
|
val language: String?,
|
||||||
val filtered: List<FilterResult>?,
|
val filtered: List<FilterResult>?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val actionableId: String
|
val actionableId: String
|
||||||
|
@ -69,12 +69,16 @@ data class Status(
|
||||||
|
|
||||||
enum class Visibility(val num: Int) {
|
enum class Visibility(val num: Int) {
|
||||||
UNKNOWN(0),
|
UNKNOWN(0),
|
||||||
|
|
||||||
@SerializedName("public")
|
@SerializedName("public")
|
||||||
PUBLIC(1),
|
PUBLIC(1),
|
||||||
|
|
||||||
@SerializedName("unlisted")
|
@SerializedName("unlisted")
|
||||||
UNLISTED(2),
|
UNLISTED(2),
|
||||||
|
|
||||||
@SerializedName("private")
|
@SerializedName("private")
|
||||||
PRIVATE(3),
|
PRIVATE(3),
|
||||||
|
|
||||||
@SerializedName("direct")
|
@SerializedName("direct")
|
||||||
DIRECT(4);
|
DIRECT(4);
|
||||||
|
|
||||||
|
@ -134,7 +138,7 @@ data class Status(
|
||||||
attachments = attachments,
|
attachments = attachments,
|
||||||
poll = poll,
|
poll = poll,
|
||||||
createdAt = createdAt,
|
createdAt = createdAt,
|
||||||
language = language,
|
language = language
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,5 +20,5 @@ import com.google.gson.annotations.SerializedName
|
||||||
data class StatusSource(
|
data class StatusSource(
|
||||||
val id: String,
|
val id: String,
|
||||||
val text: String,
|
val text: String,
|
||||||
@SerializedName("spoiler_text") val spoilerText: String,
|
@SerializedName("spoiler_text") val spoilerText: String
|
||||||
)
|
)
|
||||||
|
|
|
@ -29,11 +29,13 @@ data class TimelineAccount(
|
||||||
val url: String,
|
val url: String,
|
||||||
val avatar: String,
|
val avatar: String,
|
||||||
val bot: Boolean = false,
|
val bot: Boolean = false,
|
||||||
val emojis: List<Emoji>? = emptyList(), // nullable for backward compatibility
|
val emojis: List<Emoji>? = emptyList() // nullable for backward compatibility
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val name: String
|
val name: String
|
||||||
get() = if (displayName.isNullOrEmpty()) {
|
get() = if (displayName.isNullOrEmpty()) {
|
||||||
localUsername
|
localUsername
|
||||||
} else displayName
|
} else {
|
||||||
|
displayName
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ data class TrendingTag(
|
||||||
val name: String,
|
val name: String,
|
||||||
val url: String,
|
val url: String,
|
||||||
val history: List<TrendingTagHistory>,
|
val history: List<TrendingTagHistory>,
|
||||||
val following: Boolean,
|
val following: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,7 +42,7 @@ data class TrendingTag(
|
||||||
data class TrendingTagHistory(
|
data class TrendingTagHistory(
|
||||||
val day: String,
|
val day: String,
|
||||||
val accounts: String,
|
val accounts: String,
|
||||||
val uses: String,
|
val uses: String
|
||||||
)
|
)
|
||||||
|
|
||||||
fun TrendingTag.start() = Date(history.last().day.toLong() * 1000L)
|
fun TrendingTag.start() = Date(history.last().day.toLong() * 1000L)
|
||||||
|
|
|
@ -309,7 +309,6 @@ abstract class SFragment : Fragment(), Injectable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onMute(accountId: String, accountUsername: String) {
|
private fun onMute(accountId: String, accountUsername: String) {
|
||||||
|
|
||||||
showMuteAccountDialog(this.requireActivity(), accountUsername) { notifications: Boolean?, duration: Int? ->
|
showMuteAccountDialog(this.requireActivity(), accountUsername) { notifications: Boolean?, duration: Int? ->
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
timelineCases.mute(accountId, notifications == true, duration)
|
timelineCases.mute(accountId, notifications == true, duration)
|
||||||
|
@ -339,7 +338,8 @@ abstract class SFragment : Fragment(), Injectable {
|
||||||
view.transitionName = url
|
view.transitionName = url
|
||||||
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
|
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
view, url
|
view,
|
||||||
|
url
|
||||||
)
|
)
|
||||||
startActivity(intent, options.toBundle())
|
startActivity(intent, options.toBundle())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -207,7 +207,7 @@ class ViewImageFragment : ViewMediaFragment() {
|
||||||
.dontAnimate()
|
.dontAnimate()
|
||||||
.onlyRetrieveFromCache(true)
|
.onlyRetrieveFromCache(true)
|
||||||
.let {
|
.let {
|
||||||
if (previewUrl != null)
|
if (previewUrl != null) {
|
||||||
it.thumbnail(
|
it.thumbnail(
|
||||||
glide
|
glide
|
||||||
.load(previewUrl)
|
.load(previewUrl)
|
||||||
|
@ -216,7 +216,9 @@ class ViewImageFragment : ViewMediaFragment() {
|
||||||
.centerInside()
|
.centerInside()
|
||||||
.addListener(ImageRequestListener(true, isThumbnailRequest = true))
|
.addListener(ImageRequestListener(true, isThumbnailRequest = true))
|
||||||
)
|
)
|
||||||
else it
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Request image from the network on fail load image from cache
|
// Request image from the network on fail load image from cache
|
||||||
.error(
|
.error(
|
||||||
|
|
|
@ -42,6 +42,7 @@ abstract class ViewMediaFragment : Fragment() {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
protected val ARG_ATTACHMENT = "attach"
|
protected val ARG_ATTACHMENT = "attach"
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
protected val ARG_SINGLE_IMAGE_URL = "singleImageUrl"
|
protected val ARG_SINGLE_IMAGE_URL = "singleImageUrl"
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,6 @@ internal fun String.parseIsoDate(): Date {
|
||||||
return GregorianCalendar(year, month - 1, day).time
|
return GregorianCalendar(year, month - 1, day).time
|
||||||
}
|
}
|
||||||
if (hasT) {
|
if (hasT) {
|
||||||
|
|
||||||
// extract hours, minutes, seconds and milliseconds
|
// extract hours, minutes, seconds and milliseconds
|
||||||
hour = parseInt(this, 1.let { offset += it; offset }, 2.let { offset += it; offset })
|
hour = parseInt(this, 1.let { offset += it; offset }, 2.let { offset += it; offset })
|
||||||
if (checkOffset(this, offset, ':')) {
|
if (checkOffset(this, offset, ':')) {
|
||||||
|
|
|
@ -29,8 +29,9 @@ class FilterModel @Inject constructor() {
|
||||||
// Patterns are expensive and thread-safe, matchers are neither.
|
// Patterns are expensive and thread-safe, matchers are neither.
|
||||||
val matcher = pattern?.matcher("") ?: return Filter.Action.NONE
|
val matcher = pattern?.matcher("") ?: return Filter.Action.NONE
|
||||||
|
|
||||||
if (status.poll?.options?.any { matcher.reset(it.title).find() } == true)
|
if (status.poll?.options?.any { matcher.reset(it.title).find() } == true) {
|
||||||
return Filter.Action.HIDE
|
return Filter.Action.HIDE
|
||||||
|
}
|
||||||
|
|
||||||
val spoilerText = status.actionableStatus.spoilerText
|
val spoilerText = status.actionableStatus.spoilerText
|
||||||
val attachmentsDescriptions = status.attachments.mapNotNull { it.description }
|
val attachmentsDescriptions = status.attachments.mapNotNull { it.description }
|
||||||
|
|
|
@ -34,7 +34,6 @@ class InstanceSwitchAuthInterceptor(private val accountManager: AccountManager)
|
||||||
|
|
||||||
// only switch domains if the request comes from retrofit
|
// only switch domains if the request comes from retrofit
|
||||||
return if (originalRequest.url.host == MastodonApi.PLACEHOLDER_DOMAIN) {
|
return if (originalRequest.url.host == MastodonApi.PLACEHOLDER_DOMAIN) {
|
||||||
|
|
||||||
val builder: Request.Builder = originalRequest.newBuilder()
|
val builder: Request.Builder = originalRequest.newBuilder()
|
||||||
val instanceHeader = originalRequest.header(MastodonApi.DOMAIN_HEADER)
|
val instanceHeader = originalRequest.header(MastodonApi.DOMAIN_HEADER)
|
||||||
|
|
||||||
|
|
|
@ -194,7 +194,7 @@ interface MastodonApi {
|
||||||
@Header("Authorization") auth: String,
|
@Header("Authorization") auth: String,
|
||||||
@Header(DOMAIN_HEADER) domain: String,
|
@Header(DOMAIN_HEADER) domain: String,
|
||||||
@Header("Idempotency-Key") idempotencyKey: String,
|
@Header("Idempotency-Key") idempotencyKey: String,
|
||||||
@Body editedStatus: NewStatus,
|
@Body editedStatus: NewStatus
|
||||||
): NetworkResult<Status>
|
): NetworkResult<Status>
|
||||||
|
|
||||||
@GET("api/v1/statuses/{id}")
|
@GET("api/v1/statuses/{id}")
|
||||||
|
@ -298,7 +298,7 @@ interface MastodonApi {
|
||||||
@GET("api/v1/accounts/verify_credentials")
|
@GET("api/v1/accounts/verify_credentials")
|
||||||
suspend fun accountVerifyCredentials(
|
suspend fun accountVerifyCredentials(
|
||||||
@Header(DOMAIN_HEADER) domain: String? = null,
|
@Header(DOMAIN_HEADER) domain: String? = null,
|
||||||
@Header("Authorization") auth: String? = null,
|
@Header("Authorization") auth: String? = null
|
||||||
): NetworkResult<Account>
|
): NetworkResult<Account>
|
||||||
|
|
||||||
@FormUrlEncoded
|
@FormUrlEncoded
|
||||||
|
@ -306,7 +306,7 @@ interface MastodonApi {
|
||||||
fun accountUpdateSource(
|
fun accountUpdateSource(
|
||||||
@Field("source[privacy]") privacy: String?,
|
@Field("source[privacy]") privacy: String?,
|
||||||
@Field("source[sensitive]") sensitive: Boolean?,
|
@Field("source[sensitive]") sensitive: Boolean?,
|
||||||
@Field("source[language]") language: String?,
|
@Field("source[language]") language: String?
|
||||||
): Call<Account>
|
): Call<Account>
|
||||||
|
|
||||||
@Multipart
|
@Multipart
|
||||||
|
@ -607,7 +607,7 @@ interface MastodonApi {
|
||||||
@Field("title") title: String,
|
@Field("title") title: String,
|
||||||
@Field("context[]") context: List<String>,
|
@Field("context[]") context: List<String>,
|
||||||
@Field("filter_action") filterAction: String,
|
@Field("filter_action") filterAction: String,
|
||||||
@Field("expires_in") expiresInSeconds: Int?,
|
@Field("expires_in") expiresInSeconds: Int?
|
||||||
): NetworkResult<Filter>
|
): NetworkResult<Filter>
|
||||||
|
|
||||||
@FormUrlEncoded
|
@FormUrlEncoded
|
||||||
|
@ -617,7 +617,7 @@ interface MastodonApi {
|
||||||
@Field("title") title: String? = null,
|
@Field("title") title: String? = null,
|
||||||
@Field("context[]") context: List<String>? = null,
|
@Field("context[]") context: List<String>? = null,
|
||||||
@Field("filter_action") filterAction: String? = null,
|
@Field("filter_action") filterAction: String? = null,
|
||||||
@Field("expires_in") expiresInSeconds: Int? = null,
|
@Field("expires_in") expiresInSeconds: Int? = null
|
||||||
): NetworkResult<Filter>
|
): NetworkResult<Filter>
|
||||||
|
|
||||||
@DELETE("api/v2/filters/{id}")
|
@DELETE("api/v2/filters/{id}")
|
||||||
|
@ -630,7 +630,7 @@ interface MastodonApi {
|
||||||
suspend fun addFilterKeyword(
|
suspend fun addFilterKeyword(
|
||||||
@Path("filterId") filterId: String,
|
@Path("filterId") filterId: String,
|
||||||
@Field("keyword") keyword: String,
|
@Field("keyword") keyword: String,
|
||||||
@Field("whole_word") wholeWord: Boolean,
|
@Field("whole_word") wholeWord: Boolean
|
||||||
): NetworkResult<FilterKeyword>
|
): NetworkResult<FilterKeyword>
|
||||||
|
|
||||||
@FormUrlEncoded
|
@FormUrlEncoded
|
||||||
|
@ -638,12 +638,12 @@ interface MastodonApi {
|
||||||
suspend fun updateFilterKeyword(
|
suspend fun updateFilterKeyword(
|
||||||
@Path("keywordId") keywordId: String,
|
@Path("keywordId") keywordId: String,
|
||||||
@Field("keyword") keyword: String,
|
@Field("keyword") keyword: String,
|
||||||
@Field("whole_word") wholeWord: Boolean,
|
@Field("whole_word") wholeWord: Boolean
|
||||||
): NetworkResult<FilterKeyword>
|
): NetworkResult<FilterKeyword>
|
||||||
|
|
||||||
@DELETE("api/v2/filters/keywords/{keywordId}")
|
@DELETE("api/v2/filters/keywords/{keywordId}")
|
||||||
suspend fun deleteFilterKeyword(
|
suspend fun deleteFilterKeyword(
|
||||||
@Path("keywordId") keywordId: String,
|
@Path("keywordId") keywordId: String
|
||||||
): NetworkResult<ResponseBody>
|
): NetworkResult<ResponseBody>
|
||||||
|
|
||||||
@FormUrlEncoded
|
@FormUrlEncoded
|
||||||
|
@ -751,7 +751,7 @@ interface MastodonApi {
|
||||||
@DELETE("api/v1/push/subscription")
|
@DELETE("api/v1/push/subscription")
|
||||||
suspend fun unsubscribePushNotifications(
|
suspend fun unsubscribePushNotifications(
|
||||||
@Header("Authorization") auth: String,
|
@Header("Authorization") auth: String,
|
||||||
@Header(DOMAIN_HEADER) domain: String,
|
@Header(DOMAIN_HEADER) domain: String
|
||||||
): NetworkResult<ResponseBody>
|
): NetworkResult<ResponseBody>
|
||||||
|
|
||||||
@GET("api/v1/tags/{name}")
|
@GET("api/v1/tags/{name}")
|
||||||
|
@ -762,7 +762,7 @@ interface MastodonApi {
|
||||||
@Query("min_id") minId: String? = null,
|
@Query("min_id") minId: String? = null,
|
||||||
@Query("since_id") sinceId: String? = null,
|
@Query("since_id") sinceId: String? = null,
|
||||||
@Query("max_id") maxId: String? = null,
|
@Query("max_id") maxId: String? = null,
|
||||||
@Query("limit") limit: Int? = null,
|
@Query("limit") limit: Int? = null
|
||||||
): Response<List<HashTag>>
|
): Response<List<HashTag>>
|
||||||
|
|
||||||
@POST("api/v1/tags/{name}/follow")
|
@POST("api/v1/tags/{name}/follow")
|
||||||
|
|
|
@ -97,7 +97,7 @@ class SendStatusBroadcastReceiver : BroadcastReceiver() {
|
||||||
idempotencyKey = randomAlphanumericString(16),
|
idempotencyKey = randomAlphanumericString(16),
|
||||||
retries = 0,
|
retries = 0,
|
||||||
language = null,
|
language = null,
|
||||||
statusId = null,
|
statusId = null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue