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