Merge tag 'v28.0' into develop

# Conflicts:
#	README.md
#	app/build.gradle
#	app/src/main/java/com/keylesspalace/tusky/ListsActivity.kt
#	app/src/main/java/com/keylesspalace/tusky/adapter/NotificationsAdapter.java
#	app/src/main/java/com/keylesspalace/tusky/components/account/AccountActivity.kt
#	app/src/main/java/com/keylesspalace/tusky/components/account/media/AccountMediaFragment.kt
#	app/src/main/java/com/keylesspalace/tusky/components/accountlist/AccountListFragment.kt
#	app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementsActivity.kt
#	app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt
#	app/src/main/java/com/keylesspalace/tusky/components/compose/view/ProgressImageView.kt
#	app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt
#	app/src/main/java/com/keylesspalace/tusky/components/filters/FiltersActivity.kt
#	app/src/main/java/com/keylesspalace/tusky/components/login/LoginActivity.kt
#	app/src/main/java/com/keylesspalace/tusky/components/report/fragments/ReportStatusesFragment.kt
#	app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusActivity.kt
#	app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchFragment.kt
#	app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt
#	app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagsFragment.kt
#	app/src/main/java/com/keylesspalace/tusky/components/viewthread/ViewThreadFragment.kt
#	app/src/main/java/com/keylesspalace/tusky/components/viewthread/edits/ViewEditsFragment.kt
#	app/src/main/java/com/keylesspalace/tusky/view/GraphView.kt
#	app/src/main/res/color/compound_button_color.xml
#	app/src/main/res/color/text_input_layout_box_stroke_color.xml
#	app/src/main/res/drawable/ic_check_circle.xml
#	app/src/main/res/drawable/ic_flag_24dp.xml
#	app/src/main/res/drawable/ic_person_add_24dp.xml
#	app/src/main/res/drawable/ic_play_indicator.xml
#	app/src/main/res/drawable/ic_poll_24dp.xml
#	app/src/main/res/drawable/ic_reblog_active_24dp.xml
#	app/src/main/res/drawable/ic_reblog_private_active_24dp.xml
#	app/src/main/res/drawable/report_success_background.xml
#	app/src/main/res/layout-land/item_trending_cell.xml
#	app/src/main/res/layout/activity_account.xml
#	app/src/main/res/layout/activity_edit_filter.xml
#	app/src/main/res/layout/card_license.xml
#	app/src/main/res/layout/item_announcement.xml
#	app/src/main/res/layout/item_status.xml
#	app/src/main/res/layout/item_status_detailed.xml
#	app/src/main/res/layout/item_tab_preference.xml
#	app/src/main/res/layout/item_trending_cell.xml
#	app/src/main/res/values-cs/strings.xml
#	app/src/main/res/values-de/strings.xml
#	app/src/main/res/values-es/strings.xml
#	app/src/main/res/values-eu/strings.xml
#	app/src/main/res/values-fr/strings.xml
#	app/src/main/res/values-kab/strings.xml
#	app/src/main/res/values-lv/strings.xml
#	app/src/main/res/values-nb-rNO/strings.xml
#	app/src/main/res/values-night/theme_colors.xml
#	app/src/main/res/values/colors.xml
#	app/src/main/res/values/strings.xml
#	app/src/main/res/values/styles.xml
#	app/src/main/res/values/theme_colors.xml
This commit is contained in:
Mike Barnes 2026-01-03 09:57:39 +11:00
commit a66f7bb515
614 changed files with 52429 additions and 19916 deletions

View file

@ -18,41 +18,37 @@ package com.keylesspalace.tusky
import android.graphics.Color
import android.os.Bundle
import android.view.View
import android.widget.FrameLayout
import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.AppCompatEditText
import androidx.core.view.updatePadding
import androidx.core.widget.doOnTextChanged
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat.Type.systemBars
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.transition.TransitionManager
import at.connyduck.sparkbutton.helpers.Utils
import com.google.android.material.transition.MaterialArcMotion
import com.google.android.material.transition.MaterialContainerTransform
import com.keylesspalace.tusky.adapter.ItemInteractionListener
import com.keylesspalace.tusky.adapter.TabAdapter
import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.appstore.MainTabsChangedEvent
import com.keylesspalace.tusky.components.account.list.ListSelectionFragment
import com.keylesspalace.tusky.databinding.ActivityTabPreferenceBinding
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.entity.MastoList
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.ensureBottomPadding
import com.keylesspalace.tusky.util.unsafeLazy
import com.keylesspalace.tusky.util.viewBinding
import com.keylesspalace.tusky.util.visible
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector
import java.util.regex.Pattern
import com.keylesspalace.tusky.view.showHashtagPickerDialog
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class TabPreferenceActivity : BaseActivity(), Injectable, HasAndroidInjector, ItemInteractionListener, ListSelectionFragment.ListSelectionListener {
@AndroidEntryPoint
class TabPreferenceActivity : BaseActivity(), ItemInteractionListener, ListSelectionFragment.ListSelectionListener {
@Inject
lateinit var mastodonApi: MastodonApi
@ -60,9 +56,6 @@ class TabPreferenceActivity : BaseActivity(), Injectable, HasAndroidInjector, It
@Inject
lateinit var eventHub: EventHub
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
private val binding by viewBinding(ActivityTabPreferenceBinding::inflate)
private lateinit var currentTabs: MutableList<TabData>
@ -70,16 +63,10 @@ class TabPreferenceActivity : BaseActivity(), Injectable, HasAndroidInjector, It
private lateinit var touchHelper: ItemTouchHelper
private lateinit var addTabAdapter: TabAdapter
private var tabsChanged = false
private val selectedItemElevation by unsafeLazy {
resources.getDimension(R.dimen.selected_drag_item_elevation)
}
private val hashtagRegex by unsafeLazy {
Pattern.compile("([\\w_]*[\\p{Alpha}_][\\w_]*)", Pattern.CASE_INSENSITIVE)
}
private val onFabDismissedCallback = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
toggleFab(false)
@ -99,6 +86,19 @@ class TabPreferenceActivity : BaseActivity(), Injectable, HasAndroidInjector, It
setDisplayShowHomeEnabled(true)
}
binding.currentTabsRecyclerView.ensureBottomPadding(fab = true)
ViewCompat.setOnApplyWindowInsetsListener(binding.actionButton) { _, insets ->
val bottomInset = insets.getInsets(systemBars()).bottom
val actionButtonMargin = resources.getDimensionPixelSize(R.dimen.fabMargin)
binding.actionButton.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = bottomInset + actionButtonMargin
}
binding.sheet.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = bottomInset + actionButtonMargin
}
insets.inset(0, 0, 0, bottomInset)
}
currentTabs = accountManager.activeAccount?.tabPreferences.orEmpty().toMutableList()
currentTabsAdapter = TabAdapter(currentTabs, false, this, currentTabs.size <= MIN_TAB_COUNT)
binding.currentTabsRecyclerView.adapter = currentTabsAdapter
@ -233,44 +233,21 @@ class TabPreferenceActivity : BaseActivity(), Injectable, HasAndroidInjector, It
}
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)
showHashtagPickerDialog(mastodonApi, R.string.add_hashtag_title) { hashtag ->
if (tab == null) {
val newTab = createTabDataFromId(HASHTAG, listOf(hashtag))
currentTabs.add(newTab)
currentTabsAdapter.notifyItemInserted(currentTabs.size - 1)
} else {
val newTab = tab.copy(arguments = tab.arguments + hashtag)
currentTabs[tabPosition] = newTab
val editText = AppCompatEditText(this)
editText.setHint(R.string.edit_hashtag_hint)
editText.setText("")
frameLayout.addView(editText)
val dialog = AlertDialog.Builder(this)
.setTitle(R.string.add_hashtag_title)
.setView(frameLayout)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.action_save) { _, _ ->
val input = editText.text.toString().trim()
if (tab == null) {
val newTab = createTabDataFromId(HASHTAG, listOf(input))
currentTabs.add(newTab)
currentTabsAdapter.notifyItemInserted(currentTabs.size - 1)
} else {
val newTab = tab.copy(arguments = tab.arguments + input)
currentTabs[tabPosition] = newTab
currentTabsAdapter.notifyItemChanged(tabPosition)
}
updateAvailableTabs()
saveTabs()
currentTabsAdapter.notifyItemChanged(tabPosition)
}
.create()
editText.doOnTextChanged { s, _, _, _ ->
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = validateHashtag(s)
updateAvailableTabs()
saveTabs()
}
dialog.show()
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = validateHashtag(editText.text)
editText.requestFocus()
}
private var listSelectDialog: ListSelectionFragment? = null
@ -293,11 +270,6 @@ class TabPreferenceActivity : BaseActivity(), Injectable, HasAndroidInjector, It
saveTabs()
}
private fun validateHashtag(input: CharSequence?): Boolean {
val trimmedInput = input?.trim() ?: ""
return trimmedInput.isNotEmpty() && hashtagRegex.matcher(trimmedInput).matches()
}
private fun updateAvailableTabs() {
val addableTabs: MutableList<TabData> = mutableListOf()
@ -351,25 +323,12 @@ class TabPreferenceActivity : BaseActivity(), Injectable, HasAndroidInjector, It
private fun saveTabs() {
accountManager.activeAccount?.let {
lifecycleScope.launch(Dispatchers.IO) {
it.tabPreferences = currentTabs
accountManager.saveAccount(it)
}
}
tabsChanged = true
}
override fun onPause() {
super.onPause()
if (tabsChanged) {
lifecycleScope.launch {
eventHub.dispatch(MainTabsChangedEvent(currentTabs))
accountManager.updateAccount(it) { copy(tabPreferences = currentTabs) }
}
}
}
override fun androidInjector() = dispatchingAndroidInjector
companion object {
private const val MIN_TAB_COUNT = 2
}