Skip to content
This repository was archived by the owner on May 23, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 82 additions & 95 deletions app/src/main/java/com/keylesspalace/tusky/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.keylesspalace.tusky

import android.Manifest
import android.annotation.SuppressLint
import android.app.NotificationManager
import android.content.Context
import android.content.DialogInterface
Expand All @@ -34,6 +35,7 @@ import android.view.KeyEvent
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.MenuItem.SHOW_AS_ACTION_NEVER
import android.view.View
import android.widget.ImageView
import androidx.activity.OnBackPressedCallback
Expand All @@ -46,6 +48,8 @@ import androidx.core.content.IntentCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.view.GravityCompat
import androidx.core.view.MenuProvider
import androidx.core.view.forEach
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import androidx.viewpager2.widget.MarginPageTransformer
Expand Down Expand Up @@ -102,7 +106,6 @@ import com.keylesspalace.tusky.util.show
import com.keylesspalace.tusky.util.unsafeLazy
import com.keylesspalace.tusky.util.updateShortcut
import com.keylesspalace.tusky.util.viewBinding
import com.keylesspalace.tusky.util.visible
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.colorInt
Expand Down Expand Up @@ -177,6 +180,7 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
/** Adapter for the different timeline tabs */
private lateinit var tabAdapter: MainPagerAdapter

@SuppressLint("RestrictedApi")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

Expand Down Expand Up @@ -253,7 +257,6 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
}
window.statusBarColor = Color.TRANSPARENT // don't draw a status bar, the DrawerLayout and the MaterialDrawerLayout have their own
setContentView(binding.root)
setSupportActionBar(binding.mainToolbar)

glide = Glide.with(this)

Expand All @@ -262,8 +265,21 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
startActivity(composeIntent)
}

// Determine which of the three toolbars should be the supportActionBar (which hosts
// the options menu).
val hideTopToolbar = preferences.getBoolean(PrefKeys.HIDE_TOP_TOOLBAR, false)
binding.mainToolbar.visible(!hideTopToolbar)
if (hideTopToolbar) {
when (preferences.getString(PrefKeys.MAIN_NAV_POSITION, "top")) {
"top" -> setSupportActionBar(binding.topNav)
"bottom" -> setSupportActionBar(binding.bottomNav)
}
binding.mainToolbar.hide()
// There's not enough space in the top/bottom bars to show the title as well.
supportActionBar?.setDisplayShowTitleEnabled(false)
} else {
setSupportActionBar(binding.mainToolbar)
binding.mainToolbar.show()
}

loadDrawerAvatar(activeAccount.profilePictureUrl, true)

Expand Down Expand Up @@ -361,6 +377,14 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
}
}

override fun onPrepareMenu(menu: Menu) {
super.onPrepareMenu(menu)

// If the main toolbar is hidden then there's no space in the top/bottomNav to show
// the menu items as icons, so forceably disable them
if (!binding.mainToolbar.isVisible) menu.forEach { it.setShowAsAction(SHOW_AS_ACTION_NEVER) }
}

override fun onMenuItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_search -> {
Expand Down Expand Up @@ -458,8 +482,8 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
val drawerOpenClickListener = View.OnClickListener { binding.mainDrawerLayout.open() }

binding.mainToolbar.setNavigationOnClickListener(drawerOpenClickListener)
binding.topNavAvatar.setOnClickListener(drawerOpenClickListener)
binding.bottomNavAvatar.setOnClickListener(drawerOpenClickListener)
binding.topNav.setNavigationOnClickListener(drawerOpenClickListener)
binding.bottomNav.setNavigationOnClickListener(drawerOpenClickListener)

header = AccountHeaderView(this).apply {
headerBackgroundScaleType = ImageView.ScaleType.CENTER_CROP
Expand Down Expand Up @@ -894,112 +918,75 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidInje
val hideTopToolbar = preferences.getBoolean(PrefKeys.HIDE_TOP_TOOLBAR, false)
val animateAvatars = preferences.getBoolean("animateGifAvatars", false)

if (hideTopToolbar) {
val activeToolbar = if (hideTopToolbar) {
val navOnBottom = preferences.getString("mainNavPosition", "top") == "bottom"

val avatarView = if (navOnBottom) {
binding.bottomNavAvatar.show()
binding.bottomNavAvatar
if (navOnBottom) {
binding.bottomNav
} else {
binding.topNavAvatar.show()
binding.topNavAvatar
}

if (animateAvatars) {
Glide.with(this)
.load(avatarUrl)
.placeholder(R.drawable.avatar_default)
.into(avatarView)
} else {
Glide.with(this)
.asBitmap()
.load(avatarUrl)
.placeholder(R.drawable.avatar_default)
.into(avatarView)
binding.topNav
}
} else {
binding.bottomNavAvatar.hide()
binding.topNavAvatar.hide()
binding.mainToolbar
}

val navIconSize = resources.getDimensionPixelSize(R.dimen.avatar_toolbar_nav_icon_size)
val navIconSize = resources.getDimensionPixelSize(R.dimen.avatar_toolbar_nav_icon_size)

if (animateAvatars) {
glide.asDrawable()
.load(avatarUrl)
.transform(
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp))
)
.apply {
if (showPlaceholder) {
placeholder(R.drawable.avatar_default)
}
}
.into(object : CustomTarget<Drawable>(navIconSize, navIconSize) {
if (animateAvatars) {
glide.asDrawable().load(avatarUrl).transform(RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp)))
.apply {
if (showPlaceholder) placeholder(R.drawable.avatar_default)
}
.into(object : CustomTarget<Drawable>(navIconSize, navIconSize) {

override fun onLoadStarted(placeholder: Drawable?) {
if (placeholder != null) {
binding.mainToolbar.navigationIcon =
FixedSizeDrawable(placeholder, navIconSize, navIconSize)
}
override fun onLoadStarted(placeholder: Drawable?) {
placeholder?.let {
activeToolbar.navigationIcon = FixedSizeDrawable(it, navIconSize, navIconSize)
}
}

override fun onResourceReady(
resource: Drawable,
transition: Transition<in Drawable>?
) {
if (resource is Animatable) {
resource.start()
}
binding.mainToolbar.navigationIcon =
FixedSizeDrawable(resource, navIconSize, navIconSize)
}
override fun onResourceReady(
resource: Drawable,
transition: Transition<in Drawable>?
) {
if (resource is Animatable) resource.start()
activeToolbar.navigationIcon = FixedSizeDrawable(resource, navIconSize, navIconSize)
}

override fun onLoadCleared(placeholder: Drawable?) {
if (placeholder != null) {
binding.mainToolbar.navigationIcon =
FixedSizeDrawable(placeholder, navIconSize, navIconSize)
}
}
})
} else {
glide.asBitmap()
.load(avatarUrl)
.transform(
RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp))
)
.apply {
if (showPlaceholder) {
placeholder(R.drawable.avatar_default)
override fun onLoadCleared(placeholder: Drawable?) {
placeholder?.let {
activeToolbar.navigationIcon = FixedSizeDrawable(it, navIconSize, navIconSize)
}
}
.into(object : CustomTarget<Bitmap>(navIconSize, navIconSize) {

override fun onLoadStarted(placeholder: Drawable?) {
if (placeholder != null) {
binding.mainToolbar.navigationIcon =
FixedSizeDrawable(placeholder, navIconSize, navIconSize)
}
})
} else {
glide.asBitmap().load(avatarUrl).transform(RoundedCorners(resources.getDimensionPixelSize(R.dimen.avatar_radius_36dp)))
.apply {
if (showPlaceholder) placeholder(R.drawable.avatar_default)
}
.into(object : CustomTarget<Bitmap>(navIconSize, navIconSize) {
override fun onLoadStarted(placeholder: Drawable?) {
placeholder?.let {
activeToolbar.navigationIcon = FixedSizeDrawable(it, navIconSize, navIconSize)
}
}

override fun onResourceReady(
resource: Bitmap,
transition: Transition<in Bitmap>?
) {
binding.mainToolbar.navigationIcon = FixedSizeDrawable(
BitmapDrawable(resources, resource),
navIconSize,
navIconSize
)
}
override fun onResourceReady(
resource: Bitmap,
transition: Transition<in Bitmap>?
) {
activeToolbar.navigationIcon = FixedSizeDrawable(
BitmapDrawable(resources, resource),
navIconSize,
navIconSize
)
}

override fun onLoadCleared(placeholder: Drawable?) {
if (placeholder != null) {
binding.mainToolbar.navigationIcon =
FixedSizeDrawable(placeholder, navIconSize, navIconSize)
}
override fun onLoadCleared(placeholder: Drawable?) {
placeholder?.let {
activeToolbar.navigationIcon = FixedSizeDrawable(it, navIconSize, navIconSize)
}
})
}
}
})
}
}

Expand Down
6 changes: 6 additions & 0 deletions app/src/main/java/com/keylesspalace/tusky/TuskyApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ class TuskyApplication : Application(), HasAndroidInjector {
editor.remove(PrefKeys.MEDIA_PREVIEW_ENABLED)
}

if (oldVersion < 2023072401) {
// The notifications filter / clear options are shown on a menu, not a separate bar,
// the preference to display them is not needed.
editor.remove(PrefKeys.Deprecated.SHOW_NOTIFICATIONS_FILTER)
}

editor.putInt(PrefKeys.SCHEMA_VERSION, newVersion)
editor.apply()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.MenuProvider
import androidx.core.view.isVisible
import androidx.fragment.app.DialogFragment
Expand All @@ -46,7 +45,6 @@ import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE
import androidx.recyclerview.widget.SimpleItemAnimator
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import at.connyduck.sparkbutton.helpers.Utils
import com.google.android.material.appbar.AppBarLayout.ScrollingViewBehavior
import com.google.android.material.color.MaterialColors
import com.google.android.material.snackbar.Snackbar
import com.keylesspalace.tusky.R
Expand Down Expand Up @@ -123,21 +121,6 @@ class NotificationsFragment :
return inflater.inflate(R.layout.fragment_timeline_notifications, container, false)
}

private fun updateFilterVisibility(showFilter: Boolean) {
val params = binding.swipeRefreshLayout.layoutParams as CoordinatorLayout.LayoutParams
if (showFilter) {
binding.appBarOptions.setExpanded(true, false)
binding.appBarOptions.visibility = View.VISIBLE
// Set content behaviour to hide filter on scroll
params.behavior = ScrollingViewBehavior()
} else {
binding.appBarOptions.setExpanded(false, false)
binding.appBarOptions.visibility = View.GONE
// Clear behaviour to hide app bar
params.behavior = null
}
}

private fun confirmClearNotifications() {
AlertDialog.Builder(requireContext())
.setMessage(R.string.notification_clear_text)
Expand Down Expand Up @@ -215,8 +198,6 @@ class NotificationsFragment :
footer = NotificationsLoadStateAdapter { adapter.retry() }
)

binding.buttonClear.setOnClickListener { confirmClearNotifications() }
binding.buttonFilter.setOnClickListener { showFilterDialog() }
(binding.recyclerView.itemAnimator as SimpleItemAnimator?)!!.supportsChangeAnimations =
false

Expand Down Expand Up @@ -369,10 +350,10 @@ class NotificationsFragment :
}
}

// Update filter option visibility from uiState
launch {
viewModel.uiState.collectLatest { updateFilterVisibility(it.showFilterOptions) }
}
// Collect the uiState. Nothing is done with it, but if you don't collect it then
// accessing viewModel.uiState.value (e.g., when the filter dialog is created)
// returns an empty object.
launch { viewModel.uiState.collect() }

// Update status display from statusDisplayOptions. If the new options request
// relative time display collect the flow to periodically update the timestamp in the list gui elements.
Expand Down Expand Up @@ -439,10 +420,17 @@ class NotificationsFragment :

override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.fragment_notifications, menu)
val iconColor = MaterialColors.getColor(binding.root, android.R.attr.textColorPrimary)
menu.findItem(R.id.action_refresh)?.apply {
icon = IconicsDrawable(requireContext(), GoogleMaterial.Icon.gmd_refresh).apply {
sizeDp = 20
colorInt = MaterialColors.getColor(binding.root, android.R.attr.textColorPrimary)
colorInt = iconColor
}
}
menu.findItem(R.id.action_edit_notification_filter)?.apply {
icon = IconicsDrawable(requireContext(), GoogleMaterial.Icon.gmd_tune).apply {
sizeDp = 20
colorInt = iconColor
}
}
}
Expand All @@ -458,6 +446,14 @@ class NotificationsFragment :
viewModel.accept(InfallibleUiAction.LoadNewest)
true
}
R.id.action_edit_notification_filter -> {
showFilterDialog()
true
}
R.id.action_clear_notifications -> {
confirmClearNotifications()
true
}
else -> false
}
}
Expand Down Expand Up @@ -625,7 +621,6 @@ class NotificationsFragment :

override fun onReselect() {
if (isAdded) {
binding.appBarOptions.setExpanded(true, false)
layoutManager.scrollToPosition(0)
}
}
Expand Down
Loading