Browse Source

Full support for Android 15, edge-to-edge mode on Android 15 (#4897)

- Update to Api 35
- Update all dependencies that were blocked because they require Api 35
- fix some deprecation warnings
- implement the now required edge-to-edge mode

Edge-to-edge mode means that we now draw under the status bar and the
navigation bar and need to make sure we don't overlap with them.
Previously the system would do that for us, and we would only provide
the color we want the bars in.

For the edge-to-edge mode there are two Apis that are important:
- `fitsSystemWindows` - some Widgets, mostly from the Material library,
automatically handle system insets if you set this attribute on them to
true
- `ViewCompat.setOnApplyWindowInsetsListener` - this allows you to
manually handle the various insets. By returning new insets from the
callback it is possible to tell the system which ones are handled and
which ones should be taken care of by another view.

In most places edge-to-edge was straightforward to implement, except in
`ComposeActivity`, `AccountActivity` and `MainActivity` which required a
more custom approach, and a hacky solution to make landscape mode work
in `BaseActivity`.

There is also the `ViewCompat.setWindowInsetsAnimationCallback` Api
which allows animating with moving insets. I used that in
`LoginActivity` and `ComposeActivity` to animate the Views together with
the keyboard.

On Android Versions below 15 (Api <= 34) Tusky will look almost the same
as before. I think the only exception is the main bottom bar, which is
now slighty larger. We customized it to be smaller than the default, but
in edge-to-edge mode the height needs to be `wrap_content` or it won't
handle insets correctly.

Screenshots: 

<img
src="https://github.com/user-attachments/assets/2a1bf5d9-79fb-48eb-affc-1cbb1164d5f0"
width="280"/>
<img
src="https://github.com/user-attachments/assets/9edccdf2-c0e9-4881-a6df-bd0872934f28"
width="280"/>
<img
src="https://github.com/user-attachments/assets/2916a271-f53e-4d38-a83a-69083eb3053f"
width="280"/>
pull/4910/head
Konrad Pozniak 1 year ago committed by GitHub
parent
commit
78152f0449
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      app/build.gradle
  2. 5
      app/src/main/AndroidManifest.xml
  3. 10
      app/src/main/java/com/keylesspalace/tusky/AboutActivity.kt
  4. 56
      app/src/main/java/com/keylesspalace/tusky/BaseActivity.kt
  5. 25
      app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt
  6. 10
      app/src/main/java/com/keylesspalace/tusky/LicenseActivity.kt
  7. 5
      app/src/main/java/com/keylesspalace/tusky/ListsActivity.kt
  8. 54
      app/src/main/java/com/keylesspalace/tusky/MainActivity.kt
  9. 24
      app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt
  10. 6
      app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt
  11. 3
      app/src/main/java/com/keylesspalace/tusky/adapter/PreviewPollOptionsAdapter.kt
  12. 37
      app/src/main/java/com/keylesspalace/tusky/components/account/AccountActivity.kt
  13. 2
      app/src/main/java/com/keylesspalace/tusky/components/accountlist/AccountListFragment.kt
  14. 2
      app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementsActivity.kt
  15. 14
      app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt
  16. 2
      app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt
  17. 3
      app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksFragment.kt
  18. 3
      app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsActivity.kt
  19. 9
      app/src/main/java/com/keylesspalace/tusky/components/filters/EditFilterActivity.kt
  20. 5
      app/src/main/java/com/keylesspalace/tusky/components/filters/FiltersActivity.kt
  21. 5
      app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsActivity.kt
  22. 9
      app/src/main/java/com/keylesspalace/tusky/components/login/LoginActivity.kt
  23. 12
      app/src/main/java/com/keylesspalace/tusky/components/login/LoginWebViewActivity.kt
  24. 3
      app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsFragment.kt
  25. 3
      app/src/main/java/com/keylesspalace/tusky/components/preference/AccountPreferencesFragment.kt
  26. 20
      app/src/main/java/com/keylesspalace/tusky/components/preference/BasePreferencesFragment.kt
  27. 3
      app/src/main/java/com/keylesspalace/tusky/components/preference/NotificationPreferencesFragment.kt
  28. 8
      app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesActivity.kt
  29. 3
      app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesFragment.kt
  30. 3
      app/src/main/java/com/keylesspalace/tusky/components/preference/ProxyPreferencesFragment.kt
  31. 3
      app/src/main/java/com/keylesspalace/tusky/components/preference/TabFilterPreferencesFragment.kt
  32. 10
      app/src/main/java/com/keylesspalace/tusky/components/report/ReportActivity.kt
  33. 3
      app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusActivity.kt
  34. 2
      app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchFragment.kt
  35. 15
      app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt
  36. 3
      app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagsFragment.kt
  37. 11
      app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt
  38. 15
      app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt
  39. 107
      app/src/main/java/com/keylesspalace/tusky/util/ViewExtensions.kt
  40. 37
      app/src/main/res/layout-sw640dp/fragment_timeline.xml
  41. 32
      app/src/main/res/layout-sw640dp/fragment_timeline_notifications.xml
  42. 2
      app/src/main/res/layout/activity_about.xml
  43. 10
      app/src/main/res/layout/activity_account.xml
  44. 135
      app/src/main/res/layout/activity_compose.xml
  45. 2
      app/src/main/res/layout/activity_edit_filter.xml
  46. 1
      app/src/main/res/layout/activity_edit_profile.xml
  47. 46
      app/src/main/res/layout/activity_license.xml
  48. 42
      app/src/main/res/layout/activity_login.xml
  49. 1
      app/src/main/res/layout/activity_login_webview.xml
  50. 13
      app/src/main/res/layout/activity_main.xml
  51. 4
      app/src/main/res/layout/activity_search.xml
  52. 4
      app/src/main/res/layout/activity_tab_preference.xml
  53. 35
      app/src/main/res/layout/activity_view_media.xml
  54. 47
      app/src/main/res/layout/fragment_timeline.xml
  55. 29
      app/src/main/res/layout/fragment_timeline_notifications.xml
  56. 28
      app/src/main/res/layout/fragment_trending_tags.xml
  57. 2
      app/src/main/res/layout/fragment_view_image.xml
  58. 1
      app/src/main/res/layout/toolbar_basic.xml
  59. 2
      app/src/main/res/values-v27/styles.xml
  60. 6
      app/src/main/res/values-v35/values.xml
  61. 5
      app/src/main/res/values/dimens.xml
  62. 3
      app/src/main/res/values/values.xml
  63. 13
      gradle/libs.versions.toml
  64. 279
      gradle/verification-metadata.xml

4
app/build.gradle

@ -22,13 +22,13 @@ final def CUSTOM_INSTANCE = ""
final def SUPPORT_ACCOUNT_URL = "https://mastodon.social/@Tusky" final def SUPPORT_ACCOUNT_URL = "https://mastodon.social/@Tusky"
android { android {
compileSdk 34 compileSdk 35
namespace "com.keylesspalace.tusky" namespace "com.keylesspalace.tusky"
defaultConfig { defaultConfig {
applicationId APP_ID applicationId APP_ID
namespace "com.keylesspalace.tusky" namespace "com.keylesspalace.tusky"
minSdk 24 minSdk 24
targetSdk 34 targetSdk 35
versionCode 129 versionCode 129
versionName "27.2" versionName "27.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

5
app/src/main/AndroidManifest.xml

@ -107,8 +107,7 @@
<activity <activity
android:name=".components.compose.ComposeActivity" android:name=".components.compose.ComposeActivity"
android:theme="@style/TuskyDialogActivityTheme" android:theme="@style/TuskyDialogActivityTheme"
android:alwaysRetainTaskState="true" android:alwaysRetainTaskState="true" />
android:windowSoftInputMode="stateVisible|adjustResize" />
<activity <activity
android:name=".components.viewthread.ViewThreadActivity" android:name=".components.viewthread.ViewThreadActivity"
android:configChanges="orientation|screenSize" /> android:configChanges="orientation|screenSize" />
@ -117,7 +116,7 @@
android:theme="@style/TuskyBaseTheme" android:theme="@style/TuskyBaseTheme"
android:configChanges="orientation|screenSize|keyboardHidden|screenLayout|smallestScreenSize" /> android:configChanges="orientation|screenSize|keyboardHidden|screenLayout|smallestScreenSize" />
<activity android:name=".components.account.AccountActivity" /> <activity android:name=".components.account.AccountActivity" />
<activity android:name=".EditProfileActivity" /> <activity android:name=".EditProfileActivity"/>
<activity android:name=".components.preference.PreferencesActivity" /> <activity android:name=".components.preference.PreferencesActivity" />
<activity android:name=".StatusListActivity" /> <activity android:name=".StatusListActivity" />
<activity android:name=".components.accountlist.AccountListActivity" /> <activity android:name=".components.accountlist.AccountListActivity" />

10
app/src/main/java/com/keylesspalace/tusky/AboutActivity.kt

@ -10,6 +10,9 @@ import android.text.style.URLSpan
import android.text.util.Linkify import android.text.util.Linkify
import android.widget.TextView import android.widget.TextView
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat.Type.systemBars
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.keylesspalace.tusky.components.instanceinfo.InstanceInfoRepository import com.keylesspalace.tusky.components.instanceinfo.InstanceInfoRepository
import com.keylesspalace.tusky.databinding.ActivityAboutBinding import com.keylesspalace.tusky.databinding.ActivityAboutBinding
@ -41,6 +44,13 @@ class AboutActivity : BottomSheetActivity() {
setTitle(R.string.about_title_activity) setTitle(R.string.about_title_activity)
ViewCompat.setOnApplyWindowInsetsListener(binding.scrollView) { scrollView, insets ->
val systemBarInsets = insets.getInsets(systemBars())
scrollView.updatePadding(bottom = systemBarInsets.bottom)
insets.inset(0, 0, 0, systemBarInsets.bottom)
}
binding.versionTextView.text = getString(R.string.about_app_version, getString(R.string.app_name), BuildConfig.VERSION_NAME) binding.versionTextView.text = getString(R.string.about_app_version, getString(R.string.app_name), BuildConfig.VERSION_NAME)
binding.deviceInfo.text = getString( binding.deviceInfo.text = getString(

56
app/src/main/java/com/keylesspalace/tusky/BaseActivity.kt

@ -21,12 +21,23 @@ import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.Color import android.graphics.Color
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.annotation.StyleRes import androidx.annotation.StyleRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsCompat.Type.displayCutout
import androidx.core.view.WindowInsetsCompat.Type.systemBars
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.lifecycle.ViewModelProvider.Factory import androidx.lifecycle.ViewModelProvider.Factory
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.google.android.material.R as materialR
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.keylesspalace.tusky.MainActivity.Companion.redirectIntent import com.keylesspalace.tusky.MainActivity.Companion.redirectIntent
@ -88,16 +99,28 @@ abstract class BaseActivity : AppCompatActivity() {
setTheme(R.style.TuskyTheme) setTheme(R.style.TuskyTheme)
} }
/* set the taskdescription programmatically, the theme would turn it blue */ /* Set the taskdescription programmatically - by default the primary color is used.
* On newer Android versions (or launchers?) this doesn't seem to have an effect. */
val appName = getString(R.string.app_name) val appName = getString(R.string.app_name)
val appIcon = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
val recentsBackgroundColor = MaterialColors.getColor( val recentsBackgroundColor = MaterialColors.getColor(
this, this,
com.google.android.material.R.attr.colorSurface, materialR.attr.colorSurface,
Color.BLACK Color.BLACK
) )
setTaskDescription(TaskDescription(appName, appIcon, recentsBackgroundColor)) val taskDescription = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
TaskDescription.Builder()
.setLabel(appName)
.setIcon(R.mipmap.ic_launcher)
.setPrimaryColor(recentsBackgroundColor)
.build()
} else {
val appIcon = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
@Suppress("DEPRECATION")
TaskDescription(appName, appIcon, recentsBackgroundColor)
}
setTaskDescription(taskDescription)
val style = textStyle(preferences.getString(PrefKeys.STATUS_TEXT_SIZE, "medium")) val style = textStyle(preferences.getString(PrefKeys.STATUS_TEXT_SIZE, "medium"))
getTheme().applyStyle(style, true) getTheme().applyStyle(style, true)
@ -107,6 +130,31 @@ abstract class BaseActivity : AppCompatActivity() {
} }
} }
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
window.decorView.setBackgroundColor(Color.BLACK)
val contentView: View = findViewById(android.R.id.content)
contentView.setBackgroundColor(MaterialColors.getColor(contentView, android.R.attr.colorBackground))
// handle left/right insets. This is relevant for edge-to-edge mode in landscape orientation
ViewCompat.setOnApplyWindowInsetsListener(contentView) { _, insets ->
val systemBarInsets = insets.getInsets(systemBars())
val displayCutoutInsets = insets.getInsets(displayCutout())
// use padding for system bar insets so they get our background color and margin for cutout insets to turn them black
contentView.updatePadding(left = systemBarInsets.left, right = systemBarInsets.right)
contentView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
leftMargin = displayCutoutInsets.left
rightMargin = displayCutoutInsets.right
}
WindowInsetsCompat.Builder(insets)
.setInsets(systemBars(), Insets.of(0, systemBarInsets.top, 0, systemBarInsets.bottom))
.setInsets(displayCutout(), Insets.of(0, displayCutoutInsets.top, 0, displayCutoutInsets.bottom))
.build()
}
}
private fun activityTransitionWasRequested(): Boolean { private fun activityTransitionWasRequested(): Boolean {
return intent.getBooleanExtra(OPEN_WITH_SLIDE_IN, false) return intent.getBooleanExtra(OPEN_WITH_SLIDE_IN, false)
} }

25
app/src/main/java/com/keylesspalace/tusky/EditProfileActivity.kt

@ -27,7 +27,13 @@ import android.widget.ImageView
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsCompat.Type.ime
import androidx.core.view.WindowInsetsCompat.Type.systemBars
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.core.widget.doAfterTextChanged import androidx.core.widget.doAfterTextChanged
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -119,6 +125,25 @@ class EditProfileActivity : BaseActivity() {
setDisplayShowHomeEnabled(true) setDisplayShowHomeEnabled(true)
} }
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { scrollView, insets ->
// if keyboard visible -> set inset on the root to push the scrollview up
// if keyboard hidden -> set inset on the scrollview so last element does not get obscured by navigation bar
// scrollview has clipToPadding set to false so it draws behind the navigation bar in edge-to-edge mode
val imeInsets = insets.getInsets(ime())
val systemBarsInsets = insets.getInsets(systemBars())
binding.root.updatePadding(bottom = imeInsets.bottom)
val scrollViewPadding = if (imeInsets.bottom == 0) {
systemBarsInsets.bottom
} else {
0
}
binding.scrollView.updatePadding(bottom = scrollViewPadding)
WindowInsetsCompat.Builder(insets)
.setInsets(ime(), Insets.of(imeInsets.left, imeInsets.top, imeInsets.right, 0))
.setInsets(systemBars(), Insets.of(systemBarsInsets.left, systemBarsInsets.top, imeInsets.right, 0))
.build()
}
binding.avatarButton.setOnClickListener { pickMedia(PickType.AVATAR) } binding.avatarButton.setOnClickListener { pickMedia(PickType.AVATAR) }
binding.headerButton.setOnClickListener { pickMedia(PickType.HEADER) } binding.headerButton.setOnClickListener { pickMedia(PickType.HEADER) }

10
app/src/main/java/com/keylesspalace/tusky/LicenseActivity.kt

@ -19,6 +19,9 @@ import android.os.Bundle
import android.util.Log import android.util.Log
import android.widget.TextView import android.widget.TextView
import androidx.annotation.RawRes import androidx.annotation.RawRes
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat.Type.systemBars
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.keylesspalace.tusky.databinding.ActivityLicenseBinding import com.keylesspalace.tusky.databinding.ActivityLicenseBinding
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@ -45,6 +48,13 @@ class LicenseActivity : BaseActivity() {
setTitle(R.string.title_licenses) setTitle(R.string.title_licenses)
ViewCompat.setOnApplyWindowInsetsListener(binding.scrollView) { scrollView, insets ->
val systemBarInsets = insets.getInsets(systemBars())
scrollView.updatePadding(bottom = systemBarInsets.bottom)
insets.inset(0, 0, 0, systemBarInsets.bottom)
}
loadFileIntoTextView(R.raw.apache, binding.licenseApacheTextView) loadFileIntoTextView(R.raw.apache, binding.licenseApacheTextView)
} }

5
app/src/main/java/com/keylesspalace/tusky/ListsActivity.kt

@ -40,6 +40,8 @@ import com.keylesspalace.tusky.databinding.DialogListBinding
import com.keylesspalace.tusky.databinding.ItemListBinding import com.keylesspalace.tusky.databinding.ItemListBinding
import com.keylesspalace.tusky.entity.MastoList import com.keylesspalace.tusky.entity.MastoList
import com.keylesspalace.tusky.util.BindingHolder import com.keylesspalace.tusky.util.BindingHolder
import com.keylesspalace.tusky.util.ensureBottomMargin
import com.keylesspalace.tusky.util.ensureBottomPadding
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.show
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
@ -78,6 +80,9 @@ class ListsActivity : BaseActivity() {
setDisplayShowHomeEnabled(true) setDisplayShowHomeEnabled(true)
} }
binding.addListButton.ensureBottomMargin()
binding.listsRecycler.ensureBottomPadding(fab = true)
binding.listsRecycler.adapter = adapter binding.listsRecycler.adapter = adapter
binding.listsRecycler.layoutManager = LinearLayoutManager(this) binding.listsRecycler.layoutManager = LinearLayoutManager(this)
binding.listsRecycler.addItemDecoration( binding.listsRecycler.addItemDecoration(

54
app/src/main/java/com/keylesspalace/tusky/MainActivity.kt

@ -38,6 +38,7 @@ import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.MenuItem.SHOW_AS_ACTION_NEVER import android.view.MenuItem.SHOW_AS_ACTION_NEVER
import android.view.View import android.view.View
import android.view.ViewGroup.LayoutParams
import android.widget.ImageView import android.widget.ImageView
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
@ -49,8 +50,12 @@ import androidx.core.app.ActivityCompat
import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.MenuProvider import androidx.core.view.MenuProvider
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat.Type.systemBars
import androidx.core.view.forEach import androidx.core.view.forEach
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.widget.MarginPageTransformer import androidx.viewpager2.widget.MarginPageTransformer
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
@ -91,7 +96,6 @@ import com.keylesspalace.tusky.usecase.DeveloperToolsUseCase
import com.keylesspalace.tusky.usecase.LogoutUsecase import com.keylesspalace.tusky.usecase.LogoutUsecase
import com.keylesspalace.tusky.util.ActivityConstants import com.keylesspalace.tusky.util.ActivityConstants
import com.keylesspalace.tusky.util.emojify import com.keylesspalace.tusky.util.emojify
import com.keylesspalace.tusky.util.getDimension
import com.keylesspalace.tusky.util.getParcelableExtraCompat import com.keylesspalace.tusky.util.getParcelableExtraCompat
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.overrideActivityTransitionCompat import com.keylesspalace.tusky.util.overrideActivityTransitionCompat
@ -236,9 +240,45 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
} }
} }
window.statusBarColor = Color.TRANSPARENT // don't draw a status bar, the DrawerLayout and the MaterialDrawerLayout have their own
setContentView(binding.root) setContentView(binding.root)
val bottomBarHeight = if (preferences.getString(PrefKeys.MAIN_NAV_POSITION, "top") == "bottom") {
resources.getDimensionPixelSize(R.dimen.bottomAppBarHeight)
} else {
0
}
val fabMargin = resources.getDimensionPixelSize(R.dimen.fabMargin)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
ViewCompat.setOnApplyWindowInsetsListener(binding.viewPager) { _, insets ->
val systemBarsInsets = insets.getInsets(systemBars())
val bottomInsets = systemBarsInsets.bottom
binding.composeButton.updateLayoutParams<CoordinatorLayout.LayoutParams> {
bottomMargin = bottomBarHeight + fabMargin + bottomInsets
}
binding.mainDrawer.recyclerView.updatePadding(bottom = bottomInsets)
if (preferences.getString(PrefKeys.MAIN_NAV_POSITION, "top") == "top") {
insets
} else {
binding.viewPager.updatePadding(bottom = bottomBarHeight + bottomInsets)
insets.inset(0, 0, 0, bottomInsets)
}
}
} else {
// don't draw a status bar, the DrawerLayout and the MaterialDrawerLayout have their own
// on Vanilla Ice Cream (API 35) and up there is no status bar color because of edge-to-edge mode
@Suppress("DEPRECATION")
window.statusBarColor = Color.TRANSPARENT
binding.composeButton.updateLayoutParams<CoordinatorLayout.LayoutParams> {
bottomMargin = bottomBarHeight + fabMargin
}
binding.viewPager.updatePadding(bottom = bottomBarHeight)
}
binding.composeButton.setOnClickListener { binding.composeButton.setOnClickListener {
val composeIntent = Intent(applicationContext, ComposeActivity::class.java) val composeIntent = Intent(applicationContext, ComposeActivity::class.java)
startActivity(composeIntent) startActivity(composeIntent)
@ -252,11 +292,14 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
"top" -> setSupportActionBar(binding.topNav) "top" -> setSupportActionBar(binding.topNav)
"bottom" -> setSupportActionBar(binding.bottomNav) "bottom" -> setSupportActionBar(binding.bottomNav)
} }
binding.mainToolbar.hide() // this is a bit hacky, but when the mainToolbar is GONE, the toolbar size gets messed up for some reason
binding.mainToolbar.layoutParams.height = 0
binding.mainToolbar.visibility = View.INVISIBLE
// There's not enough space in the top/bottom bars to show the title as well. // There's not enough space in the top/bottom bars to show the title as well.
supportActionBar?.setDisplayShowTitleEnabled(false) supportActionBar?.setDisplayShowTitleEnabled(false)
} else { } else {
setSupportActionBar(binding.mainToolbar) setSupportActionBar(binding.mainToolbar)
binding.mainToolbar.layoutParams.height = LayoutParams.WRAP_CONTENT
binding.mainToolbar.show() binding.mainToolbar.show()
} }
@ -793,15 +836,10 @@ class MainActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvider {
private fun setupTabs(tabs: List<TabData>) { private fun setupTabs(tabs: List<TabData>) {
val activeTabLayout = if (preferences.getString(PrefKeys.MAIN_NAV_POSITION, "top") == "bottom") { val activeTabLayout = if (preferences.getString(PrefKeys.MAIN_NAV_POSITION, "top") == "bottom") {
val actionBarSize = getDimension(this, androidx.appcompat.R.attr.actionBarSize)
val fabMargin = resources.getDimensionPixelSize(R.dimen.fabMargin)
(binding.composeButton.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = actionBarSize + fabMargin
binding.topNav.hide() binding.topNav.hide()
binding.bottomTabLayout binding.bottomTabLayout
} else { } else {
binding.bottomNav.hide() binding.bottomNav.hide()
(binding.viewPager.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = 0
(binding.composeButton.layoutParams as CoordinatorLayout.LayoutParams).anchorId = R.id.viewPager
binding.tabLayout binding.tabLayout
} }

24
app/src/main/java/com/keylesspalace/tusky/TabPreferenceActivity.kt

@ -18,7 +18,11 @@ package com.keylesspalace.tusky
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat.Type.systemBars
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
@ -34,7 +38,7 @@ import com.keylesspalace.tusky.components.account.list.ListSelectionFragment
import com.keylesspalace.tusky.databinding.ActivityTabPreferenceBinding import com.keylesspalace.tusky.databinding.ActivityTabPreferenceBinding
import com.keylesspalace.tusky.entity.MastoList import com.keylesspalace.tusky.entity.MastoList
import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.hashtagPattern import com.keylesspalace.tusky.util.ensureBottomPadding
import com.keylesspalace.tusky.util.unsafeLazy import com.keylesspalace.tusky.util.unsafeLazy
import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.util.viewBinding
import com.keylesspalace.tusky.util.visible import com.keylesspalace.tusky.util.visible
@ -82,6 +86,19 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener, ListSelec
setDisplayShowHomeEnabled(true) 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() currentTabs = accountManager.activeAccount?.tabPreferences.orEmpty().toMutableList()
currentTabsAdapter = TabAdapter(currentTabs, false, this, currentTabs.size <= MIN_TAB_COUNT) currentTabsAdapter = TabAdapter(currentTabs, false, this, currentTabs.size <= MIN_TAB_COUNT)
binding.currentTabsRecyclerView.adapter = currentTabsAdapter binding.currentTabsRecyclerView.adapter = currentTabsAdapter
@ -253,11 +270,6 @@ class TabPreferenceActivity : BaseActivity(), ItemInteractionListener, ListSelec
saveTabs() saveTabs()
} }
private fun validateHashtag(input: CharSequence?): Boolean {
val trimmedInput = input?.trim() ?: ""
return trimmedInput.isNotEmpty() && hashtagPattern.matcher(trimmedInput).matches()
}
private fun updateAvailableTabs() { private fun updateAvailableTabs() {
val addableTabs: MutableList<TabData> = mutableListOf() val addableTabs: MutableList<TabData> = mutableListOf()

6
app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt

@ -156,9 +156,13 @@ class ViewMediaActivity :
true true
} }
// yes it is deprecated, but it looks cool so it stays for now
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE
window.statusBarColor = Color.BLACK if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
@Suppress("DEPRECATION")
window.statusBarColor = Color.BLACK
}
window.sharedElementEnterTransition.addListener(object : NoopTransitionListener { window.sharedElementEnterTransition.addListener(object : NoopTransitionListener {
override fun onTransitionEnd(transition: Transition) { override fun onTransitionEnd(transition: Transition) {
adapter.onTransitionEnd(binding.viewPager.currentItem) adapter.onTransitionEnd(binding.viewPager.currentItem)

3
app/src/main/java/com/keylesspalace/tusky/adapter/PreviewPollOptionsAdapter.kt

@ -19,7 +19,6 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import androidx.core.widget.TextViewCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
@ -58,7 +57,7 @@ class PreviewPollOptionsAdapter : RecyclerView.Adapter<PreviewViewHolder>() {
R.drawable.ic_radio_button_unchecked_18dp R.drawable.ic_radio_button_unchecked_18dp
} }
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(textView, iconId, 0, 0, 0) textView.setCompoundDrawablesRelativeWithIntrinsicBounds(iconId, 0, 0, 0)
textView.text = options[position] textView.text = options[position]

37
app/src/main/java/com/keylesspalace/tusky/components/account/AccountActivity.kt

@ -21,6 +21,7 @@ import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color import android.graphics.Color
import android.graphics.Typeface import android.graphics.Typeface
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.text.TextWatcher import android.text.TextWatcher
@ -40,8 +41,8 @@ import androidx.core.graphics.ColorUtils
import androidx.core.view.MenuProvider import androidx.core.view.MenuProvider
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsCompat.Type.systemBars import androidx.core.view.WindowInsetsCompat.Type.systemBars
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.core.widget.doAfterTextChanged import androidx.core.widget.doAfterTextChanged
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -83,6 +84,7 @@ import com.keylesspalace.tusky.util.Loading
import com.keylesspalace.tusky.util.Success import com.keylesspalace.tusky.util.Success
import com.keylesspalace.tusky.util.copyToClipboard import com.keylesspalace.tusky.util.copyToClipboard
import com.keylesspalace.tusky.util.emojify import com.keylesspalace.tusky.util.emojify
import com.keylesspalace.tusky.util.ensureBottomMargin
import com.keylesspalace.tusky.util.getDomain import com.keylesspalace.tusky.util.getDomain
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.loadAvatar import com.keylesspalace.tusky.util.loadAvatar
@ -285,25 +287,21 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
} }
private fun handleWindowInsets() { private fun handleWindowInsets() {
binding.accountFloatingActionButton.ensureBottomMargin()
ViewCompat.setOnApplyWindowInsetsListener(binding.accountCoordinatorLayout) { _, insets -> ViewCompat.setOnApplyWindowInsetsListener(binding.accountCoordinatorLayout) { _, insets ->
val top = insets.getInsets(systemBars()).top val systemBarInsets = insets.getInsets(systemBars())
val toolbarParams = binding.accountToolbar.layoutParams as ViewGroup.MarginLayoutParams val top = systemBarInsets.top
toolbarParams.topMargin = top
binding.accountToolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
val right = insets.getInsets(systemBars()).right topMargin = top
val bottom = insets.getInsets(systemBars()).bottom }
val left = insets.getInsets(systemBars()).left
binding.accountCoordinatorLayout.updatePadding(
right = right,
bottom = bottom,
left = left
)
binding.swipeToRefreshLayout.setProgressViewEndTarget( binding.swipeToRefreshLayout.setProgressViewEndTarget(
false, false,
top + resources.getDimensionPixelSize(R.dimen.account_swiperefresh_distance) top + resources.getDimensionPixelSize(R.dimen.account_swiperefresh_distance)
) )
WindowInsetsCompat.CONSUMED insets.inset(0, top, 0, 0)
} }
} }
@ -355,7 +353,10 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
1f 1f
) )
window.statusBarColor = argbEvaluator.evaluate(transparencyPercent, statusBarColorTransparent, statusBarColorOpaque) as Int if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
@Suppress("DEPRECATION")
window.statusBarColor = argbEvaluator.evaluate(transparencyPercent, statusBarColorTransparent, statusBarColorOpaque) as Int
}
val evaluatedToolbarColor = argbEvaluator.evaluate( val evaluatedToolbarColor = argbEvaluator.evaluate(
transparencyPercent, transparencyPercent,
@ -364,6 +365,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
) as Int ) as Int
binding.accountToolbar.setBackgroundColor(evaluatedToolbarColor) binding.accountToolbar.setBackgroundColor(evaluatedToolbarColor)
binding.accountStatusBarScrim.setBackgroundColor(evaluatedToolbarColor)
binding.swipeToRefreshLayout.isEnabled = verticalOffset == 0 binding.swipeToRefreshLayout.isEnabled = verticalOffset == 0
} }
@ -372,7 +374,10 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, MenuProvide
private fun makeNotificationBarTransparent() { private fun makeNotificationBarTransparent() {
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
window.statusBarColor = statusBarColorTransparent if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
@Suppress("DEPRECATION")
window.statusBarColor = statusBarColorTransparent
}
} }
/** /**

2
app/src/main/java/com/keylesspalace/tusky/components/accountlist/AccountListFragment.kt

@ -49,6 +49,7 @@ import com.keylesspalace.tusky.interfaces.LinkListener
import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.settings.PrefKeys import com.keylesspalace.tusky.settings.PrefKeys
import com.keylesspalace.tusky.util.HttpHeaderLink import com.keylesspalace.tusky.util.HttpHeaderLink
import com.keylesspalace.tusky.util.ensureBottomPadding
import com.keylesspalace.tusky.util.getSerializableCompat import com.keylesspalace.tusky.util.getSerializableCompat
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.show
@ -92,6 +93,7 @@ class AccountListFragment :
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.recyclerView.ensureBottomPadding()
binding.recyclerView.setHasFixedSize(true) binding.recyclerView.setHasFixedSize(true)
val layoutManager = LinearLayoutManager(view.context) val layoutManager = LinearLayoutManager(view.context)
binding.recyclerView.layoutManager = layoutManager binding.recyclerView.layoutManager = layoutManager

2
app/src/main/java/com/keylesspalace/tusky/components/announcements/AnnouncementsActivity.kt

@ -38,6 +38,7 @@ import com.keylesspalace.tusky.settings.PrefKeys
import com.keylesspalace.tusky.util.Error import com.keylesspalace.tusky.util.Error
import com.keylesspalace.tusky.util.Loading import com.keylesspalace.tusky.util.Loading
import com.keylesspalace.tusky.util.Success import com.keylesspalace.tusky.util.Success
import com.keylesspalace.tusky.util.ensureBottomPadding
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.show
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
@ -87,6 +88,7 @@ class AnnouncementsActivity :
binding.swipeRefreshLayout.setOnRefreshListener(this::refreshAnnouncements) binding.swipeRefreshLayout.setOnRefreshListener(this::refreshAnnouncements)
binding.announcementsList.ensureBottomPadding()
binding.announcementsList.setHasFixedSize(true) binding.announcementsList.setHasFixedSize(true)
binding.announcementsList.layoutManager = LinearLayoutManager(this) binding.announcementsList.layoutManager = LinearLayoutManager(this)
val divider = DividerItemDecoration(this, DividerItemDecoration.VERTICAL) val divider = DividerItemDecoration(this, DividerItemDecoration.VERTICAL)

14
app/src/main/java/com/keylesspalace/tusky/components/compose/ComposeActivity.kt

@ -50,8 +50,11 @@ import androidx.core.content.FileProvider
import androidx.core.content.res.use import androidx.core.content.res.use
import androidx.core.view.ContentInfoCompat import androidx.core.view.ContentInfoCompat
import androidx.core.view.OnReceiveContentListener import androidx.core.view.OnReceiveContentListener
import androidx.core.view.WindowInsetsCompat.Type.ime
import androidx.core.view.WindowInsetsCompat.Type.systemBars
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.core.widget.doAfterTextChanged import androidx.core.widget.doAfterTextChanged
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -106,6 +109,7 @@ import com.keylesspalace.tusky.util.loadAvatar
import com.keylesspalace.tusky.util.map import com.keylesspalace.tusky.util.map
import com.keylesspalace.tusky.util.modernLanguageCode import com.keylesspalace.tusky.util.modernLanguageCode
import com.keylesspalace.tusky.util.setDrawableTint import com.keylesspalace.tusky.util.setDrawableTint
import com.keylesspalace.tusky.util.setOnWindowInsetsChangeListener
import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.show
import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.util.viewBinding
import com.keylesspalace.tusky.util.visible import com.keylesspalace.tusky.util.visible
@ -259,6 +263,16 @@ class ComposeActivity :
} }
setContentView(binding.root) setContentView(binding.root)
binding.composeBottomBar.setOnWindowInsetsChangeListener { windowInsets ->
val insets = windowInsets.getInsets(systemBars() or ime())
binding.composeBottomBar.updatePadding(bottom = insets.bottom + at.connyduck.sparkbutton.helpers.Utils.dpToPx(this@ComposeActivity, 4))
binding.addMediaBottomSheet.updatePadding(bottom = insets.bottom + at.connyduck.sparkbutton.helpers.Utils.dpToPx(this@ComposeActivity, 50))
binding.emojiView.updatePadding(bottom = insets.bottom + at.connyduck.sparkbutton.helpers.Utils.dpToPx(this@ComposeActivity, 50))
binding.composeOptionsBottomSheet.updatePadding(bottom = insets.bottom + at.connyduck.sparkbutton.helpers.Utils.dpToPx(this@ComposeActivity, 50))
binding.composeScheduleView.updatePadding(bottom = insets.bottom + at.connyduck.sparkbutton.helpers.Utils.dpToPx(this@ComposeActivity, 50))
(binding.composeMainScrollView.layoutParams as ViewGroup.MarginLayoutParams).bottomMargin = insets.bottom + at.connyduck.sparkbutton.helpers.Utils.dpToPx(this@ComposeActivity, 58)
}
setupActionBar() setupActionBar()
setupAvatar(activeAccount) setupAvatar(activeAccount)

2
app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsFragment.kt

@ -49,6 +49,7 @@ import com.keylesspalace.tusky.interfaces.StatusActionListener
import com.keylesspalace.tusky.settings.PrefKeys import com.keylesspalace.tusky.settings.PrefKeys
import com.keylesspalace.tusky.util.CardViewMode import com.keylesspalace.tusky.util.CardViewMode
import com.keylesspalace.tusky.util.StatusDisplayOptions import com.keylesspalace.tusky.util.StatusDisplayOptions
import com.keylesspalace.tusky.util.ensureBottomPadding
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.isAnyLoading import com.keylesspalace.tusky.util.isAnyLoading
import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.show
@ -228,6 +229,7 @@ class ConversationsFragment :
} }
private fun setupRecyclerView(adapter: ConversationAdapter) { private fun setupRecyclerView(adapter: ConversationAdapter) {
binding.recyclerView.ensureBottomPadding(fab = true)
binding.recyclerView.setHasFixedSize(true) binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = LinearLayoutManager(context) binding.recyclerView.layoutManager = LinearLayoutManager(context)

3
app/src/main/java/com/keylesspalace/tusky/components/domainblocks/DomainBlocksFragment.kt

@ -12,6 +12,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.databinding.FragmentDomainBlocksBinding import com.keylesspalace.tusky.databinding.FragmentDomainBlocksBinding
import com.keylesspalace.tusky.util.ensureBottomPadding
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.show
import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.util.viewBinding
@ -30,6 +31,8 @@ class DomainBlocksFragment : Fragment(R.layout.fragment_domain_blocks) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val adapter = DomainBlocksAdapter(viewModel::unblock) val adapter = DomainBlocksAdapter(viewModel::unblock)
binding.recyclerView.ensureBottomPadding()
binding.recyclerView.setHasFixedSize(true) binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.addItemDecoration( binding.recyclerView.addItemDecoration(
DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL) DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL)

3
app/src/main/java/com/keylesspalace/tusky/components/drafts/DraftsActivity.kt

@ -34,6 +34,7 @@ import com.keylesspalace.tusky.components.compose.ComposeActivity
import com.keylesspalace.tusky.databinding.ActivityDraftsBinding import com.keylesspalace.tusky.databinding.ActivityDraftsBinding
import com.keylesspalace.tusky.db.DraftsAlert import com.keylesspalace.tusky.db.DraftsAlert
import com.keylesspalace.tusky.db.entity.DraftEntity import com.keylesspalace.tusky.db.entity.DraftEntity
import com.keylesspalace.tusky.util.ensureBottomPadding
import com.keylesspalace.tusky.util.isHttpNotFound import com.keylesspalace.tusky.util.isHttpNotFound
import com.keylesspalace.tusky.util.parseAsMastodonHtml import com.keylesspalace.tusky.util.parseAsMastodonHtml
import com.keylesspalace.tusky.util.visible import com.keylesspalace.tusky.util.visible
@ -66,6 +67,8 @@ class DraftsActivity : BaseActivity(), DraftActionListener {
setDisplayShowHomeEnabled(true) setDisplayShowHomeEnabled(true)
} }
binding.draftsRecyclerView.ensureBottomPadding()
binding.draftsErrorMessageView.setup(R.drawable.elephant_friend_empty, R.string.no_drafts) binding.draftsErrorMessageView.setup(R.drawable.elephant_friend_empty, R.string.no_drafts)
val adapter = DraftsAdapter(this) val adapter = DraftsAdapter(this)

9
app/src/main/java/com/keylesspalace/tusky/components/filters/EditFilterActivity.kt

@ -20,7 +20,10 @@ import android.os.Bundle
import android.view.WindowManager import android.view.WindowManager
import android.widget.AdapterView import android.widget.AdapterView
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat.Type.systemBars
import androidx.core.view.size import androidx.core.view.size
import androidx.core.view.updatePadding
import androidx.core.widget.doAfterTextChanged import androidx.core.widget.doAfterTextChanged
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import at.connyduck.calladapter.networkresult.fold import at.connyduck.calladapter.networkresult.fold
@ -91,6 +94,12 @@ class EditFilterActivity : BaseActivity() {
} }
) )
ViewCompat.setOnApplyWindowInsetsListener(binding.scrollView) { scrollView, insets ->
val systemBarsInsets = insets.getInsets(systemBars())
scrollView.updatePadding(bottom = systemBarsInsets.bottom)
insets.inset(0, 0, 0, systemBarsInsets.bottom)
}
binding.actionChip.setOnClickListener { showAddKeywordDialog() } binding.actionChip.setOnClickListener { showAddKeywordDialog() }
binding.filterSaveButton.setOnClickListener { saveChanges() } binding.filterSaveButton.setOnClickListener { saveChanges() }
binding.filterDeleteButton.setOnClickListener { binding.filterDeleteButton.setOnClickListener {

5
app/src/main/java/com/keylesspalace/tusky/components/filters/FiltersActivity.kt

@ -11,6 +11,8 @@ import com.keylesspalace.tusky.BaseActivity
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.databinding.ActivityFiltersBinding import com.keylesspalace.tusky.databinding.ActivityFiltersBinding
import com.keylesspalace.tusky.entity.Filter import com.keylesspalace.tusky.entity.Filter
import com.keylesspalace.tusky.util.ensureBottomMargin
import com.keylesspalace.tusky.util.ensureBottomPadding
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.launchAndRepeatOnLifecycle import com.keylesspalace.tusky.util.launchAndRepeatOnLifecycle
import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.show
@ -43,6 +45,9 @@ class FiltersActivity : BaseActivity(), FiltersListener {
setDisplayShowHomeEnabled(true) setDisplayShowHomeEnabled(true)
} }
binding.filtersList.ensureBottomPadding(fab = true)
binding.addFilterButton.ensureBottomMargin()
binding.addFilterButton.setOnClickListener { binding.addFilterButton.setOnClickListener {
launchEditFilterActivity() launchEditFilterActivity()
} }

5
app/src/main/java/com/keylesspalace/tusky/components/followedtags/FollowedTagsActivity.kt

@ -18,6 +18,8 @@ import com.keylesspalace.tusky.databinding.ActivityFollowedTagsBinding
import com.keylesspalace.tusky.interfaces.HashtagActionListener import com.keylesspalace.tusky.interfaces.HashtagActionListener
import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.copyToClipboard import com.keylesspalace.tusky.util.copyToClipboard
import com.keylesspalace.tusky.util.ensureBottomMargin
import com.keylesspalace.tusky.util.ensureBottomPadding
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.show
import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.util.viewBinding
@ -54,6 +56,9 @@ class FollowedTagsActivity :
setDisplayShowHomeEnabled(true) setDisplayShowHomeEnabled(true)
} }
binding.fab.ensureBottomMargin()
binding.followedTagsView.ensureBottomPadding(fab = true)
binding.fab.setOnClickListener { binding.fab.setOnClickListener {
showDialog() showDialog()
} }

9
app/src/main/java/com/keylesspalace/tusky/components/login/LoginActivity.kt

@ -25,6 +25,9 @@ import android.view.Menu
import android.view.View import android.view.View
import android.widget.TextView import android.widget.TextView
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.view.WindowInsetsCompat.Type.ime
import androidx.core.view.WindowInsetsCompat.Type.systemBars
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import at.connyduck.calladapter.networkresult.fold import at.connyduck.calladapter.networkresult.fold
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
@ -39,6 +42,7 @@ import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.getNonNullString import com.keylesspalace.tusky.util.getNonNullString
import com.keylesspalace.tusky.util.openLinkInCustomTab import com.keylesspalace.tusky.util.openLinkInCustomTab
import com.keylesspalace.tusky.util.rickRoll import com.keylesspalace.tusky.util.rickRoll
import com.keylesspalace.tusky.util.setOnWindowInsetsChangeListener
import com.keylesspalace.tusky.util.shouldRickRoll import com.keylesspalace.tusky.util.shouldRickRoll
import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.util.viewBinding
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@ -75,6 +79,11 @@ class LoginActivity : BaseActivity() {
setContentView(binding.root) setContentView(binding.root)
binding.loginScrollView.setOnWindowInsetsChangeListener { windowInsets ->
val insets = windowInsets.getInsets(systemBars() or ime())
binding.loginScrollView.updatePadding(bottom = insets.bottom)
}
if (savedInstanceState == null && if (savedInstanceState == null &&
BuildConfig.CUSTOM_INSTANCE.isNotBlank() && BuildConfig.CUSTOM_INSTANCE.isNotBlank() &&
!isAdditionalLogin() !isAdditionalLogin()

12
app/src/main/java/com/keylesspalace/tusky/components/login/LoginWebViewActivity.kt

@ -33,6 +33,11 @@ import android.webkit.WebViewClient
import androidx.activity.result.contract.ActivityResultContract import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsCompat.Type.ime
import androidx.core.view.WindowInsetsCompat.Type.systemBars
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.keylesspalace.tusky.BaseActivity import com.keylesspalace.tusky.BaseActivity
@ -122,10 +127,15 @@ class LoginWebViewActivity : BaseActivity() {
setTitle(R.string.title_login) setTitle(R.string.title_login)
ViewCompat.setOnApplyWindowInsetsListener(binding.loginWebView) { _, insets ->
val bottomInsets = insets.getInsets(systemBars() or ime()).bottom
binding.root.updatePadding(bottom = bottomInsets)
WindowInsetsCompat.CONSUMED
}
val webView = binding.loginWebView val webView = binding.loginWebView
webView.settings.allowContentAccess = false webView.settings.allowContentAccess = false
webView.settings.allowFileAccess = false webView.settings.allowFileAccess = false
webView.settings.databaseEnabled = false
webView.settings.displayZoomControls = false webView.settings.displayZoomControls = false
webView.settings.javaScriptCanOpenWindowsAutomatically = false webView.settings.javaScriptCanOpenWindowsAutomatically = false
// JavaScript needs to be enabled because otherwise 2FA does not work in some instances // JavaScript needs to be enabled because otherwise 2FA does not work in some instances

3
app/src/main/java/com/keylesspalace/tusky/components/notifications/NotificationsFragment.kt

@ -65,6 +65,7 @@ import com.keylesspalace.tusky.util.CardViewMode
import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate
import com.keylesspalace.tusky.util.StatusDisplayOptions import com.keylesspalace.tusky.util.StatusDisplayOptions
import com.keylesspalace.tusky.util.StatusProvider import com.keylesspalace.tusky.util.StatusProvider
import com.keylesspalace.tusky.util.ensureBottomPadding
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.openLink import com.keylesspalace.tusky.util.openLink
import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.show
@ -137,6 +138,8 @@ class NotificationsFragment :
openSpoiler = accountManager.activeAccount!!.alwaysOpenSpoiler openSpoiler = accountManager.activeAccount!!.alwaysOpenSpoiler
) )
binding.recyclerView.ensureBottomPadding(fab = true)
// setup the notifications filter bar // setup the notifications filter bar
showNotificationsFilterBar = preferences.getBoolean(PrefKeys.SHOW_NOTIFICATIONS_FILTER, true) showNotificationsFilterBar = preferences.getBoolean(PrefKeys.SHOW_NOTIFICATIONS_FILTER, true)
updateFilterBarVisibility() updateFilterBarVisibility()

3
app/src/main/java/com/keylesspalace/tusky/components/preference/AccountPreferencesFragment.kt

@ -23,7 +23,6 @@ import android.os.Bundle
import android.util.Log import android.util.Log
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.PreferenceFragmentCompat
import at.connyduck.calladapter.networkresult.fold import at.connyduck.calladapter.networkresult.fold
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
@ -64,7 +63,7 @@ import javax.inject.Inject
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@AndroidEntryPoint @AndroidEntryPoint
class AccountPreferencesFragment : PreferenceFragmentCompat() { class AccountPreferencesFragment : BasePreferencesFragment() {
@Inject @Inject
lateinit var accountManager: AccountManager lateinit var accountManager: AccountManager

20
app/src/main/java/com/keylesspalace/tusky/components/preference/BasePreferencesFragment.kt

@ -0,0 +1,20 @@
package com.keylesspalace.tusky.components.preference
import android.os.Bundle
import android.view.View
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat.Type.systemBars
import androidx.core.view.updatePadding
import androidx.preference.PreferenceFragmentCompat
abstract class BasePreferencesFragment : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
ViewCompat.setOnApplyWindowInsetsListener(listView) { listView, insets ->
val systemBarsInsets = insets.getInsets(systemBars())
listView.updatePadding(bottom = systemBarsInsets.bottom)
insets.inset(0, 0, 0, systemBarsInsets.bottom)
}
}
}

3
app/src/main/java/com/keylesspalace/tusky/components/preference/NotificationPreferencesFragment.kt

@ -17,7 +17,6 @@ package com.keylesspalace.tusky.components.preference
import android.os.Bundle import android.os.Bundle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceFragmentCompat
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.components.systemnotifications.NotificationService import com.keylesspalace.tusky.components.systemnotifications.NotificationService
import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.db.AccountManager
@ -31,7 +30,7 @@ import javax.inject.Inject
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@AndroidEntryPoint @AndroidEntryPoint
class NotificationPreferencesFragment : PreferenceFragmentCompat() { class NotificationPreferencesFragment : BasePreferencesFragment() {
@Inject @Inject
lateinit var accountManager: AccountManager lateinit var accountManager: AccountManager

8
app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesActivity.kt

@ -18,9 +18,11 @@ package com.keylesspalace.tusky.components.preference
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.core.view.WindowCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.commit import androidx.fragment.app.commit
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -66,6 +68,12 @@ class PreferencesActivity :
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// Workaround for edge-to-edge mode not working when an activity is recreated
// https://stackoverflow.com/questions/79319740/edge-to-edge-doesnt-work-when-activity-recreated-or-appcompatdelegate-setdefaul
if (savedInstanceState != null && Build.VERSION.SDK_INT >= 35) {
WindowCompat.setDecorFitsSystemWindows(window, false)
}
val binding = ActivityPreferencesBinding.inflate(layoutInflater) val binding = ActivityPreferencesBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)

3
app/src/main/java/com/keylesspalace/tusky/components/preference/PreferencesFragment.kt

@ -18,7 +18,6 @@ package com.keylesspalace.tusky.components.preference
import android.os.Bundle import android.os.Bundle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.entity.Notification import com.keylesspalace.tusky.entity.Notification
@ -40,7 +39,7 @@ import javax.inject.Inject
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@AndroidEntryPoint @AndroidEntryPoint
class PreferencesFragment : PreferenceFragmentCompat() { class PreferencesFragment : BasePreferencesFragment() {
@Inject @Inject
lateinit var accountManager: AccountManager lateinit var accountManager: AccountManager

3
app/src/main/java/com/keylesspalace/tusky/components/preference/ProxyPreferencesFragment.kt

@ -17,7 +17,6 @@ package com.keylesspalace.tusky.components.preference
import android.os.Bundle import android.os.Bundle
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.settings.PrefKeys import com.keylesspalace.tusky.settings.PrefKeys
import com.keylesspalace.tusky.settings.ProxyConfiguration import com.keylesspalace.tusky.settings.ProxyConfiguration
@ -30,7 +29,7 @@ import com.keylesspalace.tusky.settings.validatedEditTextPreference
import com.keylesspalace.tusky.util.getNonNullString import com.keylesspalace.tusky.util.getNonNullString
import kotlin.system.exitProcess import kotlin.system.exitProcess
class ProxyPreferencesFragment : PreferenceFragmentCompat() { class ProxyPreferencesFragment : BasePreferencesFragment() {
private var pendingRestart = false private var pendingRestart = false
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {

3
app/src/main/java/com/keylesspalace/tusky/components/preference/TabFilterPreferencesFragment.kt

@ -19,7 +19,6 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.preference.PreferenceFragmentCompat
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.settings.AccountPreferenceDataStore import com.keylesspalace.tusky.settings.AccountPreferenceDataStore
@ -31,7 +30,7 @@ import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class TabFilterPreferencesFragment : PreferenceFragmentCompat() { class TabFilterPreferencesFragment : BasePreferencesFragment() {
@Inject @Inject
lateinit var accountPreferenceDataStore: AccountPreferenceDataStore lateinit var accountPreferenceDataStore: AccountPreferenceDataStore

10
app/src/main/java/com/keylesspalace/tusky/components/report/ReportActivity.kt

@ -19,6 +19,9 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat.Type.systemBars
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.keylesspalace.tusky.BottomSheetActivity import com.keylesspalace.tusky.BottomSheetActivity
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
@ -58,6 +61,13 @@ class ReportActivity : BottomSheetActivity() {
setHomeAsUpIndicator(R.drawable.ic_close_24dp) setHomeAsUpIndicator(R.drawable.ic_close_24dp)
} }
ViewCompat.setOnApplyWindowInsetsListener(binding.wizard) { wizard, insets ->
val systemBarInsets = insets.getInsets(systemBars())
wizard.updatePadding(bottom = systemBarInsets.bottom)
insets.inset(0, 0, 0, systemBarInsets.bottom)
}
initViewPager() initViewPager()
if (savedInstanceState == null) { if (savedInstanceState == null) {
viewModel.navigateTo(Screen.Statuses) viewModel.navigateTo(Screen.Statuses)

3
app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledStatusActivity.kt

@ -36,6 +36,7 @@ import com.keylesspalace.tusky.appstore.StatusScheduledEvent
import com.keylesspalace.tusky.components.compose.ComposeActivity import com.keylesspalace.tusky.components.compose.ComposeActivity
import com.keylesspalace.tusky.databinding.ActivityScheduledStatusBinding import com.keylesspalace.tusky.databinding.ActivityScheduledStatusBinding
import com.keylesspalace.tusky.entity.ScheduledStatus import com.keylesspalace.tusky.entity.ScheduledStatus
import com.keylesspalace.tusky.util.ensureBottomPadding
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.show
import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.util.viewBinding
@ -76,6 +77,8 @@ class ScheduledStatusActivity :
setDisplayShowHomeEnabled(true) setDisplayShowHomeEnabled(true)
} }
binding.scheduledTootList.ensureBottomPadding()
binding.swipeRefreshLayout.setOnRefreshListener(this::refreshStatuses) binding.swipeRefreshLayout.setOnRefreshListener(this::refreshStatuses)
binding.scheduledTootList.setHasFixedSize(true) binding.scheduledTootList.setHasFixedSize(true)

2
app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchFragment.kt

@ -26,6 +26,7 @@ import com.keylesspalace.tusky.components.search.SearchViewModel
import com.keylesspalace.tusky.databinding.FragmentSearchBinding import com.keylesspalace.tusky.databinding.FragmentSearchBinding
import com.keylesspalace.tusky.interfaces.LinkListener import com.keylesspalace.tusky.interfaces.LinkListener
import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.ensureBottomPadding
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
import com.keylesspalace.tusky.util.viewBinding import com.keylesspalace.tusky.util.viewBinding
import com.keylesspalace.tusky.util.visible import com.keylesspalace.tusky.util.visible
@ -63,6 +64,7 @@ abstract class SearchFragment<T : Any> :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val adapter = initAdapter() val adapter = initAdapter()
binding.swipeRefreshLayout.setOnRefreshListener(this) binding.swipeRefreshLayout.setOnRefreshListener(this)
binding.searchRecyclerView.ensureBottomPadding()
requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED) requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
subscribeObservables(adapter) subscribeObservables(adapter)
} }

15
app/src/main/java/com/keylesspalace/tusky/components/timeline/TimelineFragment.kt

@ -25,7 +25,6 @@ import android.view.View
import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.view.MenuProvider import androidx.core.view.MenuProvider
import androidx.core.view.updatePadding
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -61,6 +60,7 @@ import com.keylesspalace.tusky.settings.PrefKeys
import com.keylesspalace.tusky.util.CardViewMode import com.keylesspalace.tusky.util.CardViewMode
import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate
import com.keylesspalace.tusky.util.StatusDisplayOptions import com.keylesspalace.tusky.util.StatusDisplayOptions
import com.keylesspalace.tusky.util.ensureBottomPadding
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.show
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
@ -378,6 +378,9 @@ class TimelineFragment :
} }
private fun setupRecyclerView(adapter: TimelinePagingAdapter) { private fun setupRecyclerView(adapter: TimelinePagingAdapter) {
val hasFab = (activity as? ActionButtonActivity?)?.actionButton != null
binding.recyclerView.ensureBottomPadding(fab = hasFab)
binding.recyclerView.setAccessibilityDelegateCompat( binding.recyclerView.setAccessibilityDelegateCompat(
ListStatusAccessibilityDelegate(binding.recyclerView, this) { pos -> ListStatusAccessibilityDelegate(binding.recyclerView, this) { pos ->
if (pos in 0 until adapter.itemCount) { if (pos in 0 until adapter.itemCount) {
@ -387,19 +390,11 @@ class TimelineFragment :
} }
} }
) )
binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = LinearLayoutManager(context) binding.recyclerView.layoutManager = LinearLayoutManager(context)
val divider = DividerItemDecoration(context, RecyclerView.VERTICAL) val divider = DividerItemDecoration(context, RecyclerView.VERTICAL)
binding.recyclerView.addItemDecoration(divider) binding.recyclerView.addItemDecoration(divider)
val recyclerViewBottomPadding = if ((activity as? ActionButtonActivity?)?.actionButton != null) {
resources.getDimensionPixelSize(R.dimen.recyclerview_bottom_padding_actionbutton)
} else {
resources.getDimensionPixelSize(R.dimen.recyclerview_bottom_padding_no_actionbutton)
}
binding.recyclerView.updatePadding(bottom = recyclerViewBottomPadding)
// CWs are expanded without animation, buttons animate itself, we don't need it basically // CWs are expanded without animation, buttons animate itself, we don't need it basically
(binding.recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false (binding.recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
binding.recyclerView.adapter = adapter binding.recyclerView.adapter = adapter

3
app/src/main/java/com/keylesspalace/tusky/components/trending/TrendingTagsFragment.kt

@ -38,6 +38,7 @@ import com.keylesspalace.tusky.databinding.FragmentTrendingTagsBinding
import com.keylesspalace.tusky.interfaces.ActionButtonActivity import com.keylesspalace.tusky.interfaces.ActionButtonActivity
import com.keylesspalace.tusky.interfaces.RefreshableFragment import com.keylesspalace.tusky.interfaces.RefreshableFragment
import com.keylesspalace.tusky.interfaces.ReselectableFragment import com.keylesspalace.tusky.interfaces.ReselectableFragment
import com.keylesspalace.tusky.util.ensureBottomPadding
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.show
import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation import com.keylesspalace.tusky.util.startActivityWithSlideInAnimation
@ -121,6 +122,8 @@ class TrendingTagsFragment :
} }
private fun setupRecyclerView(adapter: TrendingTagsAdapter) { private fun setupRecyclerView(adapter: TrendingTagsAdapter) {
binding.recyclerView.ensureBottomPadding()
val columnCount = val columnCount =
requireContext().resources.getInteger(R.integer.trending_column_count) requireContext().resources.getInteger(R.integer.trending_column_count)
setupLayoutManager(adapter, columnCount) setupLayoutManager(adapter, columnCount)

11
app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt

@ -27,6 +27,9 @@ import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat.Type.systemBars
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.DataSource
@ -107,6 +110,14 @@ class ViewImageFragment : ViewMediaFragment() {
} }
} }
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
val systemBarInsets = insets.getInsets(systemBars())
val mediaDescriptionBottomPadding = requireContext().resources.getDimensionPixelSize(R.dimen.media_description_sheet_bottom_padding)
binding.mediaDescription.updatePadding(bottom = mediaDescriptionBottomPadding + systemBarInsets.bottom)
insets.inset(0, 0, 0, systemBarInsets.bottom)
}
val singleTapDetector = GestureDetector( val singleTapDetector = GestureDetector(
requireContext(), requireContext(),
object : GestureDetector.SimpleOnGestureListener() { object : GestureDetector.SimpleOnGestureListener() {

15
app/src/main/java/com/keylesspalace/tusky/fragment/ViewVideoFragment.kt

@ -31,6 +31,11 @@ import android.view.ViewGroup
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.annotation.OptIn import androidx.annotation.OptIn
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat.Type.systemBars
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.media3.common.AudioAttributes import androidx.media3.common.AudioAttributes
@ -130,6 +135,16 @@ class ViewVideoFragment : ViewMediaFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
ViewCompat.setOnApplyWindowInsetsListener(binding.mediaDescriptionScrollView) { captionSheet, insets ->
val systemBarInsets = insets.getInsets(systemBars())
captionSheet.updatePadding(bottom = systemBarInsets.bottom)
binding.videoView.updateLayoutParams<ConstraintLayout.LayoutParams> {
bottomMargin = systemBarInsets.bottom
}
insets.inset(0, 0, 0, systemBarInsets.bottom)
}
/** /**
* Handle single taps, flings, and dragging * Handle single taps, flings, and dragging
*/ */

107
app/src/main/java/com/keylesspalace/tusky/util/ViewExtensions.kt

@ -18,9 +18,21 @@ package com.keylesspalace.tusky.util
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import androidx.core.graphics.Insets
import androidx.core.view.OnApplyWindowInsetsListener
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsAnimationCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsCompat.Type.ime
import androidx.core.view.WindowInsetsCompat.Type.systemBars
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.keylesspalace.tusky.R
fun View.show() { fun View.show() {
this.visibility = View.VISIBLE this.visibility = View.VISIBLE
@ -78,3 +90,98 @@ fun TextView.fixTextSelection() {
setTextIsSelectable(false) setTextIsSelectable(false)
post { setTextIsSelectable(true) } post { setTextIsSelectable(true) }
} }
/**
* Makes sure the [RecyclerView] has the correct bottom padding set
* and no system bars or floating action buttons cover the content when it is scrolled all the way up.
* This must be called before window insets are applied (Activity.onCreate or Fragment.onViewCreated).
* The RecyclerView needs to have clipToPadding set to false for this to work.
* @param fab true if there is a [FloatingActionButton] above the RecyclerView
*/
fun RecyclerView.ensureBottomPadding(fab: Boolean = false) {
val bottomPadding = if (fab) {
context.resources.getDimensionPixelSize(R.dimen.recyclerview_bottom_padding_actionbutton)
} else {
context.resources.getDimensionPixelSize(R.dimen.recyclerview_bottom_padding_no_actionbutton)
}
ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
val systemBarsInsets = insets.getInsets(systemBars())
view.updatePadding(bottom = bottomPadding + systemBarsInsets.bottom)
WindowInsetsCompat.Builder(insets)
.setInsets(systemBars(), Insets.of(systemBarsInsets.left, systemBarsInsets.top, systemBarsInsets.right, 0))
.build()
}
}
/** Makes sure a [FloatingActionButton] has the correct bottom margin set
* so it is not covered by any system bars.
*/
fun FloatingActionButton.ensureBottomMargin() {
ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
val bottomInsets = insets.getInsets(systemBars()).bottom
val actionButtonMargin = resources.getDimensionPixelSize(R.dimen.fabMargin)
view.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = bottomInsets + actionButtonMargin
}
insets
}
}
/**
* Combines WindowInsetsAnimationCompat.Callback and OnApplyWindowInsetsListener
* for easy implementation of layouts that animate with they keyboard.
* The animation callback is only called when something animates, so it isn't suitable for initial setup.
* The OnApplyWindowInsetsListener can do that, but the insets it supplies must not be used when an animation is ongoing,
* as that messes with the animation.
*/
fun View.setOnWindowInsetsChangeListener(listener: (WindowInsetsCompat) -> Unit) {
val callback = WindowInsetsCallback(listener)
ViewCompat.setWindowInsetsAnimationCallback(this, callback)
ViewCompat.setOnApplyWindowInsetsListener(this, callback)
}
private class WindowInsetsCallback(
private val listener: (WindowInsetsCompat) -> Unit,
) : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP),
OnApplyWindowInsetsListener {
var imeVisible = false
var deferredInsets: WindowInsetsCompat? = null
override fun onStart(animation: WindowInsetsAnimationCompat, bounds: WindowInsetsAnimationCompat.BoundsCompat): WindowInsetsAnimationCompat.BoundsCompat {
imeVisible = true
return super.onStart(animation, bounds)
}
override fun onProgress(
insets: WindowInsetsCompat,
runningAnimations: List<WindowInsetsAnimationCompat>,
): WindowInsetsCompat {
listener(insets)
return WindowInsetsCompat.CONSUMED
}
override fun onApplyWindowInsets(
view: View,
insets: WindowInsetsCompat,
): WindowInsetsCompat {
val ime = insets.getInsets(ime()).bottom
if (!imeVisible && ime == 0) {
listener(insets)
deferredInsets = null
} else {
deferredInsets = insets
}
return WindowInsetsCompat.CONSUMED
}
override fun onEnd(animation: WindowInsetsAnimationCompat) {
imeVisible = deferredInsets?.isVisible(ime()) == true
deferredInsets?.let { insets ->
listener(insets)
deferredInsets = null
}
}
}

37
app/src/main/res/layout-sw640dp/fragment_timeline.xml

@ -16,10 +16,10 @@
android:id="@+id/progressBar" android:id="@+id/progressBar"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:indeterminate="true"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
android:indeterminate="true"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<com.keylesspalace.tusky.view.TuskySwipeRefreshLayout <com.keylesspalace.tusky.view.TuskySwipeRefreshLayout
@ -27,32 +27,23 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<FrameLayout <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:clipToPadding="false" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.keylesspalace.tusky.view.BackgroundMessageView
android:id="@+id/statusView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="?attr/actionBarSize"
android:src="@android:color/transparent"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/errorphant_error"
tools:visibility="visible" />
</FrameLayout>
</com.keylesspalace.tusky.view.TuskySwipeRefreshLayout> </com.keylesspalace.tusky.view.TuskySwipeRefreshLayout>
<com.keylesspalace.tusky.view.BackgroundMessageView
android:id="@+id/statusView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="?attr/actionBarSize"
android:visibility="gone"
tools:src="@drawable/errorphant_error"
tools:visibility="visible" />
<androidx.core.widget.ContentLoadingProgressBar <androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/topProgressBar" android:id="@+id/topProgressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal" style="@style/Widget.AppCompat.ProgressBar.Horizontal"

32
app/src/main/res/layout-sw640dp/fragment_timeline_notifications.xml

@ -68,28 +68,24 @@
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<FrameLayout <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:background="?android:attr/colorBackground"
android:clipToPadding="false"
android:paddingBottom="@dimen/recyclerview_bottom_padding_actionbutton" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/colorBackground"
android:clipToPadding="false"
android:paddingBottom="@dimen/recyclerview_bottom_padding_actionbutton" />
<com.keylesspalace.tusky.view.BackgroundMessageView
android:id="@+id/statusView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:visibility="gone"
tools:visibility="visible" />
</FrameLayout>
</com.keylesspalace.tusky.view.TuskySwipeRefreshLayout> </com.keylesspalace.tusky.view.TuskySwipeRefreshLayout>
<com.keylesspalace.tusky.view.BackgroundMessageView
android:id="@+id/statusView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:visibility="gone"
tools:visibility="visible" />
<com.google.android.material.progressindicator.CircularProgressIndicator <com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/progressBar" android:id="@+id/progressBar"
android:layout_width="wrap_content" android:layout_width="wrap_content"

2
app/src/main/res/layout/activity_about.xml

@ -16,9 +16,11 @@
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:clipToPadding="false"
android:textDirection="anyRtl"> android:textDirection="anyRtl">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout

10
app/src/main/res/layout/activity_account.xml

@ -425,6 +425,14 @@
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<!-- correct size and color will be set programmatically -->
<View
android:id="@+id/accountStatusBarScrim"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@android:color/transparent"
app:layout_collapseMode="pin" />
<!-- top margin equal to statusbar size will be set programmatically --> <!-- top margin equal to statusbar size will be set programmatically -->
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/accountToolbar" android:id="@+id/accountToolbar"
@ -444,9 +452,9 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:tabGravity="center" app:tabGravity="center"
app:tabIndicatorHeight="4dp"
app:tabIndicator="@drawable/tab_indicator_top" app:tabIndicator="@drawable/tab_indicator_top"
app:tabIndicatorFullWidth="true" app:tabIndicatorFullWidth="true"
app:tabIndicatorHeight="4dp"
app:tabMode="scrollable" /> app:tabMode="scrollable" />
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>

135
app/src/main/res/layout/activity_compose.xml

@ -6,79 +6,87 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.AppBarLayout
android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp" android:background="@android:color/transparent"
android:background="@android:color/transparent"> android:fitsSystemWindows="true">
<ImageView
android:id="@+id/composeAvatar"
android:layout_width="?attr/actionBarSize"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="end"
android:padding="8dp"
tools:ignore="ContentDescription" />
<!--content description will be set in code -->
<Spinner
android:id="@+id/composePostLanguageButton"
style="@style/TuskyImageButton"
android:layout_width="52dp"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:contentDescription="@string/description_post_language"
android:padding="0dp"
android:textColor="?android:attr/textColorTertiary"
android:textSize="?attr/status_text_large"
android:textStyle="bold"
app:tooltipText="@string/description_post_language" />
<com.google.android.material.button.MaterialButton <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/atButton" android:id="@+id/toolbar"
style="@style/TuskyImageButton" android:layout_width="match_parent"
android:layout_width="40dp" android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:layout_gravity="end" <ImageView
android:padding="8dp" android:id="@+id/composeAvatar"
android:text="@string/at_symbol" android:layout_width="?attr/actionBarSize"
android:textColor="?android:textColorTertiary" android:layout_height="?attr/actionBarSize"
android:textSize="?attr/status_text_large" android:layout_gravity="end"
android:textStyle="bold" /> android:padding="8dp"
tools:ignore="ContentDescription" />
<!--content description will be set in code -->
<Spinner
android:id="@+id/composePostLanguageButton"
style="@style/TuskyImageButton"
android:layout_width="52dp"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:contentDescription="@string/description_post_language"
android:padding="0dp"
android:textColor="?android:attr/textColorTertiary"
android:textSize="?attr/status_text_large"
android:textStyle="bold"
app:tooltipText="@string/description_post_language" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/hashButton" android:id="@+id/atButton"
style="@style/TuskyImageButton" style="@style/TuskyImageButton"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:layout_gravity="end"
android:padding="8dp" android:padding="8dp"
android:text="@string/hash_symbol" android:text="@string/at_symbol"
android:textColor="?android:textColorTertiary" android:textColor="?android:textColorTertiary"
android:textSize="?attr/status_text_large" android:textSize="?attr/status_text_large"
android:textStyle="bold" /> android:textStyle="bold" />
<com.google.android.material.button.MaterialButton
android:id="@+id/hashButton"
style="@style/TuskyImageButton"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:padding="8dp"
android:text="@string/hash_symbol"
android:textColor="?android:textColorTertiary"
android:textSize="?attr/status_text_large"
android:textStyle="bold" />
<com.google.android.material.button.MaterialButton
android:id="@+id/descriptionMissingWarningButton"
style="@style/TuskyImageButton"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:contentDescription="@string/hint_media_description_missing"
android:padding="8dp"
app:icon="@drawable/ic_missing_description_24dp"
app:iconTint="@color/tusky_orange_light"
app:tooltipText="@string/hint_media_description_missing" />
<com.google.android.material.button.MaterialButton </com.google.android.material.appbar.MaterialToolbar>
android:id="@+id/descriptionMissingWarningButton"
style="@style/TuskyImageButton"
android:layout_width="40dp"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:contentDescription="@string/hint_media_description_missing"
android:padding="8dp"
app:icon="@drawable/ic_missing_description_24dp"
app:iconTint="@color/tusky_orange_light"
app:tooltipText="@string/hint_media_description_missing" />
</com.google.android.material.appbar.MaterialToolbar> </com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/composeMainScrollView" android:id="@+id/composeMainScrollView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/compose_activity_scrollview_height" android:layout_height="@dimen/compose_activity_scrollview_height"
android:layout_marginTop="?attr/actionBarSize" android:layout_marginBottom="52dp"
android:layout_marginBottom="52dp"> android:clipToPadding="false"
android:paddingTop="8dp"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -290,7 +298,8 @@
android:paddingStart="8dp" android:paddingStart="8dp"
android:paddingTop="4dp" android:paddingTop="4dp"
android:paddingEnd="8dp" android:paddingEnd="8dp"
android:paddingBottom="4dp"> android:paddingBottom="4dp"
app:layout_insetEdge="bottom">
<ImageButton <ImageButton
android:id="@+id/composeAddMediaButton" android:id="@+id/composeAddMediaButton"

2
app/src/main/res/layout/activity_edit_filter.xml

@ -11,8 +11,10 @@
layout="@layout/toolbar_basic" /> layout="@layout/toolbar_basic" />
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout <LinearLayout

1
app/src/main/res/layout/activity_edit_profile.xml

@ -14,6 +14,7 @@
android:id="@+id/scrollView" android:id="@+id/scrollView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout

46
app/src/main/res/layout/activity_license.xml

@ -12,8 +12,10 @@
layout="@layout/toolbar_basic" /> layout="@layout/toolbar_basic" />
<ScrollView <ScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout <LinearLayout
@ -21,15 +23,15 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipChildren="false" android:clipChildren="false"
android:orientation="vertical" android:orientation="vertical"
android:textDirection="anyRtl" android:paddingBottom="12dp"
android:paddingBottom="12dp"> android:textDirection="anyRtl">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="18dp"
android:layout_marginStart="18dp" android:layout_marginStart="18dp"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:layout_marginEnd="18dp"
android:gravity="center_vertical" android:gravity="center_vertical"
android:lineSpacingMultiplier="1.1" android:lineSpacingMultiplier="1.1"
android:text="@string/license_description" /> android:text="@string/license_description" />
@ -37,9 +39,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_apache_2" license:license="@string/license_apache_2"
license:link="https://kotlinlang.org/" license:link="https://kotlinlang.org/"
license:name="Kotlin" /> license:name="Kotlin" />
@ -47,9 +49,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_apache_2" license:license="@string/license_apache_2"
license:link="https://developer.android.com/jetpack/androidx" license:link="https://developer.android.com/jetpack/androidx"
license:name="AndroidX" /> license:name="AndroidX" />
@ -57,9 +59,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_apache_2" license:license="@string/license_apache_2"
license:link="https://github.com/material-components/material-components-android" license:link="https://github.com/material-components/material-components-android"
license:name="Material Components for Android" /> license:name="Material Components for Android" />
@ -67,9 +69,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_apache_2" license:license="@string/license_apache_2"
license:link="https://square.github.io/okhttp/" license:link="https://square.github.io/okhttp/"
license:name="OkHttp" /> license:name="OkHttp" />
@ -77,9 +79,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_apache_2" license:license="@string/license_apache_2"
license:link="https://github.com/google/conscrypt" license:link="https://github.com/google/conscrypt"
license:name="Conscrypt" /> license:name="Conscrypt" />
@ -87,9 +89,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_apache_2" license:license="@string/license_apache_2"
license:link="https://square.github.io/retrofit/" license:link="https://square.github.io/retrofit/"
license:name="Retrofit" /> license:name="Retrofit" />
@ -97,9 +99,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_apache_2" license:license="@string/license_apache_2"
license:link="https://github.com/square/moshi" license:link="https://github.com/square/moshi"
license:name="Moshi" /> license:name="Moshi" />
@ -107,9 +109,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_apache_2" license:license="@string/license_apache_2"
license:link="https://bumptech.github.io/glide/" license:link="https://bumptech.github.io/glide/"
license:name="Glide" /> license:name="Glide" />
@ -117,9 +119,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_apache_2" license:license="@string/license_apache_2"
license:link="https://dagger.dev/" license:link="https://dagger.dev/"
license:name="Dagger 2" /> license:name="Dagger 2" />
@ -127,9 +129,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_apache_2" license:license="@string/license_apache_2"
license:link="https://github.com/mikepenz/MaterialDrawer" license:link="https://github.com/mikepenz/MaterialDrawer"
license:name="MaterialDrawer" /> license:name="MaterialDrawer" />
@ -137,9 +139,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_apache_2" license:license="@string/license_apache_2"
license:link="https://github.com/connyduck/SparkButton" license:link="https://github.com/connyduck/SparkButton"
license:name="SparkButton" /> license:name="SparkButton" />
@ -147,9 +149,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_apache_2" license:license="@string/license_apache_2"
license:link="https://github.com/chrisbanes/PhotoView" license:link="https://github.com/chrisbanes/PhotoView"
license:name="PhotoView" /> license:name="PhotoView" />
@ -157,9 +159,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_apache_2" license:license="@string/license_apache_2"
license:link="https://github.com/CanHub/Android-Image-Cropper" license:link="https://github.com/CanHub/Android-Image-Cropper"
license:name="Android Image Cropper" /> license:name="Android Image Cropper" />
@ -167,9 +169,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_apache_2" license:license="@string/license_apache_2"
license:link="https://github.com/penfeizhou/APNG4Android" license:link="https://github.com/penfeizhou/APNG4Android"
license:name="APNG4Android" /> license:name="APNG4Android" />
@ -177,9 +179,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_apache_2" license:license="@string/license_apache_2"
license:link="https://github.com/C1710/FileMojiCompat" license:link="https://github.com/C1710/FileMojiCompat"
license:name="FileMojiCompat" /> license:name="FileMojiCompat" />
@ -187,9 +189,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_cc_by_4" license:license="@string/license_cc_by_4"
license:link="https://twemoji.twitter.com/" license:link="https://twemoji.twitter.com/"
license:name="Twemoji" /> license:name="Twemoji" />
@ -197,9 +199,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_cc_by_4" license:license="@string/license_cc_by_4"
license:link="https://github.com/c1710/blobmoji" license:link="https://github.com/c1710/blobmoji"
license:name="Blobmoji" /> license:name="Blobmoji" />
@ -207,9 +209,9 @@
<com.keylesspalace.tusky.view.LicenseCard <com.keylesspalace.tusky.view.LicenseCard
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp"
license:license="@string/license_cc_by_sa_4" license:license="@string/license_cc_by_sa_4"
license:link="https://github.com/tuskyapp/artwork" license:link="https://github.com/tuskyapp/artwork"
license:name="Tusky elephant artwork" /> license:name="Tusky elephant artwork" />
@ -218,9 +220,9 @@
android:id="@+id/licenseApacheTextView" android:id="@+id/licenseApacheTextView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="18dp"
android:layout_marginStart="18dp" android:layout_marginStart="18dp"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:layout_marginEnd="18dp"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>

42
app/src/main/res/layout/activity_login.xml

@ -1,19 +1,33 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context="com.keylesspalace.tusky.components.login.LoginActivity"> tools:context="com.keylesspalace.tusky.components.login.LoginActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:background="@android:color/transparent">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:minHeight="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>
<ScrollView <ScrollView
android:id="@+id/loginScrollView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fillViewport="true" android:fillViewport="true"
app:layout_constraintBottom_toBottomOf="parent" android:clipToPadding="false"
app:layout_constraintTop_toTopOf="parent"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -23,11 +37,11 @@
android:padding="16dp"> android:padding="16dp">
<ImageView <ImageView
android:id="@+id/loginLogo"
android:layout_width="160dp" android:layout_width="160dp"
android:layout_height="178dp" android:layout_height="178dp"
android:layout_marginBottom="50dp" android:layout_marginBottom="50dp"
android:contentDescription="@null" android:contentDescription="@null"
android:id="@+id/loginLogo"
app:srcCompat="@drawable/elephant_friend" /> app:srcCompat="@drawable/elephant_friend" />
<LinearLayout <LinearLayout
@ -35,6 +49,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipChildren="false" android:clipChildren="false"
android:layout_marginBottom="16dp"
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
@ -81,8 +96,8 @@
<com.google.android.material.progressindicator.CircularProgressIndicator <com.google.android.material.progressindicator.CircularProgressIndicator
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:indeterminate="true" android:layout_gravity="center"
android:layout_gravity="center" /> android:indeterminate="true" />
<TextView <TextView
android:layout_width="250dp" android:layout_width="250dp"
@ -93,14 +108,7 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView>
<com.google.android.material.appbar.MaterialToolbar </ScrollView>
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_alignParentTop="true"
android:background="@android:color/transparent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

1
app/src/main/res/layout/activity_login_webview.xml

@ -8,6 +8,7 @@
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:liftOnScroll="false"> app:liftOnScroll="false">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar

13
app/src/main/res/layout/activity_main.xml

@ -5,7 +5,7 @@
android:id="@+id/mainDrawerLayout" android:id="@+id/mainDrawerLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true" android:fitsSystemWindows="@bool/is_not_edge_to_edge"
tools:context="com.keylesspalace.tusky.MainActivity"> tools:context="com.keylesspalace.tusky.MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
@ -17,7 +17,9 @@
android:id="@+id/appBar" android:id="@+id/appBar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:liftOnScroll="false"> android:fitsSystemWindows="@bool/is_edge_to_edge"
app:liftOnScroll="false"
app:statusBarForeground="?attr/colorSurface">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/mainToolbar" android:id="@+id/mainToolbar"
@ -58,14 +60,13 @@
android:id="@+id/viewPager" android:id="@+id/viewPager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginBottom="?attr/actionBarSize"
android:background="?attr/windowBackgroundColor" android:background="?attr/windowBackgroundColor"
app:layout_behavior="@string/appbar_scrolling_view_behavior" /> app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<com.google.android.material.bottomappbar.BottomAppBar <com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bottomNav" android:id="@+id/bottomNav"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:backgroundTint="?attr/colorSurface" android:backgroundTint="?attr/colorSurface"
app:contentInsetStart="0dp" app:contentInsetStart="0dp"
@ -73,13 +74,13 @@
app:fabAlignmentMode="end" app:fabAlignmentMode="end"
app:menuAlignmentMode="auto" app:menuAlignmentMode="auto"
app:navigationContentDescription="@string/action_open_drawer" app:navigationContentDescription="@string/action_open_drawer"
app:paddingBottomSystemWindowInsets="false"> app:paddingBottomSystemWindowInsets="true">
<com.keylesspalace.tusky.view.AdaptiveTabLayout <com.keylesspalace.tusky.view.AdaptiveTabLayout
android:id="@+id/bottomTabLayout" android:id="@+id/bottomTabLayout"
style="@style/TuskyTabAppearance" style="@style/TuskyTabAppearance"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="@dimen/bottomAppBarHeight"
android:background="?attr/colorSurface" android:background="?attr/colorSurface"
app:tabGravity="fill" app:tabGravity="fill"
app:tabIndicator="@drawable/tab_indicator_bottom" app:tabIndicator="@drawable/tab_indicator_bottom"

4
app/src/main/res/layout/activity_search.xml

@ -9,8 +9,10 @@
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:layout_collapseMode="pin" app:layout_collapseMode="pin"
app:liftOnScroll="false"> app:liftOnScroll="false"
app:statusBarForeground="?attr/colorSurface">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"

4
app/src/main/res/layout/activity_tab_preference.xml

@ -27,7 +27,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
android:layout_margin="16dp" android:layout_margin="@dimen/fabMargin"
android:contentDescription="@string/action_add_tab" android:contentDescription="@string/action_add_tab"
app:srcCompat="@drawable/ic_plus_24dp" app:srcCompat="@drawable/ic_plus_24dp"
app:tint="?attr/colorOnPrimary" /> app:tint="?attr/colorOnPrimary" />
@ -37,7 +37,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
android:layout_margin="16dp" android:layout_margin="@dimen/fabMargin"
android:visibility="invisible" android:visibility="invisible"
app:strokeWidth="0dp" app:strokeWidth="0dp"
app:cardBackgroundColor="?attr/colorSurface" app:cardBackgroundColor="?attr/colorSurface"

35
app/src/main/res/layout/activity_view_media.xml

@ -1,30 +1,39 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/black" android:background="@color/black">
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/transparent_black"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:minHeight="?attr/actionBarSize"
android:theme="@style/ViewMediaActivity.AppBarLayout"
app:titleTextColor="@color/white" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager2.widget.ViewPager2 <androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager" android:id="@+id/viewPager"
android:layout_width="match_parent" android:layout_width="match_parent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/transparent_black"
android:theme="@style/ViewMediaActivity.AppBarLayout"
app:titleTextColor="@color/white" />
<com.google.android.material.progressindicator.CircularProgressIndicator <com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/progressBarShare" android:id="@+id/progressBarShare"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:indeterminate="true"
android:layout_gravity="center" android:layout_gravity="center"
android:indeterminate="true"
android:visibility="gone" /> android:visibility="gone" />
</FrameLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

47
app/src/main/res/layout/fragment_timeline.xml

@ -1,34 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?android:attr/colorBackground"> android:background="?android:attr/colorBackground">
<com.keylesspalace.tusky.view.TuskySwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false" />
<com.keylesspalace.tusky.view.BackgroundMessageView
android:id="@+id/statusView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="?attr/actionBarSize"
android:visibility="gone" />
</FrameLayout>
</com.keylesspalace.tusky.view.TuskySwipeRefreshLayout>
<com.google.android.material.progressindicator.CircularProgressIndicator <com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/progressBar" android:id="@+id/progressBar"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -39,6 +16,28 @@
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<com.keylesspalace.tusky.view.TuskySwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false" />
</com.keylesspalace.tusky.view.TuskySwipeRefreshLayout>
<com.keylesspalace.tusky.view.BackgroundMessageView
android:id="@+id/statusView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="?attr/actionBarSize"
android:visibility="gone"
tools:src="@drawable/errorphant_error"
tools:visibility="visible" />
<androidx.core.widget.ContentLoadingProgressBar <androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/topProgressBar" android:id="@+id/topProgressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal" style="@style/Widget.AppCompat.ProgressBar.Horizontal"

29
app/src/main/res/layout/fragment_timeline_notifications.xml

@ -61,27 +61,22 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<FrameLayout <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:clipToPadding="false"
<androidx.recyclerview.widget.RecyclerView android:paddingBottom="@dimen/recyclerview_bottom_padding_actionbutton" />
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="@dimen/recyclerview_bottom_padding_actionbutton" />
<com.keylesspalace.tusky.view.BackgroundMessageView
android:id="@+id/statusView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="?attr/actionBarSize"
android:visibility="gone" />
</FrameLayout>
</com.keylesspalace.tusky.view.TuskySwipeRefreshLayout> </com.keylesspalace.tusky.view.TuskySwipeRefreshLayout>
<com.keylesspalace.tusky.view.BackgroundMessageView
android:id="@+id/statusView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="?attr/actionBarSize"
android:visibility="gone" />
<com.google.android.material.progressindicator.CircularProgressIndicator <com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/progressBar" android:id="@+id/progressBar"
android:layout_width="wrap_content" android:layout_width="wrap_content"

28
app/src/main/res/layout/fragment_trending_tags.xml

@ -11,26 +11,22 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<FrameLayout <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="@dimen/recyclerview_bottom_padding_no_actionbutton" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="@dimen/recyclerview_bottom_padding_no_actionbutton" />
<com.keylesspalace.tusky.view.BackgroundMessageView
android:id="@+id/messageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
tools:visibility="visible" />
</FrameLayout>
</com.keylesspalace.tusky.view.TuskySwipeRefreshLayout> </com.keylesspalace.tusky.view.TuskySwipeRefreshLayout>
<com.keylesspalace.tusky.view.BackgroundMessageView
android:id="@+id/messageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
tools:visibility="visible" />
<com.google.android.material.progressindicator.CircularProgressIndicator <com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/progressBar" android:id="@+id/progressBar"
android:layout_width="wrap_content" android:layout_width="wrap_content"

2
app/src/main/res/layout/fragment_view_image.xml

@ -69,7 +69,7 @@
android:lineSpacingMultiplier="1.1" android:lineSpacingMultiplier="1.1"
android:paddingLeft="8dp" android:paddingLeft="8dp"
android:paddingRight="8dp" android:paddingRight="8dp"
android:paddingBottom="8dp" android:paddingBottom="@dimen/media_description_sheet_bottom_padding"
android:textColor="?android:textColorPrimary" android:textColor="?android:textColorPrimary"
android:textIsSelectable="true" android:textIsSelectable="true"
android:textSize="?attr/status_text_medium" android:textSize="?attr/status_text_medium"

1
app/src/main/res/layout/toolbar_basic.xml

@ -4,6 +4,7 @@
android:id="@+id/appbar" android:id="@+id/appbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:liftOnScroll="false"> app:liftOnScroll="false">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar

2
app/src/main/res/values-v27/styles.xml

@ -1,6 +1,7 @@
<resources> <resources>
<style name="TuskyTheme" parent="TuskyBaseTheme"> <style name="TuskyTheme" parent="TuskyBaseTheme">
<item name="android:windowLightStatusBar">@bool/lightNavigationBar</item>
<item name="android:windowLightNavigationBar">@bool/lightNavigationBar</item> <item name="android:windowLightNavigationBar">@bool/lightNavigationBar</item>
<item name="android:navigationBarColor">@color/colorBackground</item> <item name="android:navigationBarColor">@color/colorBackground</item>
<item name="android:navigationBarDividerColor">?attr/dividerColor</item> <item name="android:navigationBarDividerColor">?attr/dividerColor</item>
@ -8,6 +9,7 @@
<!--Black Application Theme Styles--> <!--Black Application Theme Styles-->
<style name="TuskyBlackTheme" parent="TuskyBlackThemeBase"> <style name="TuskyBlackTheme" parent="TuskyBlackThemeBase">
<item name="android:windowLightStatusBar">false</item>
<item name="android:windowLightNavigationBar">false</item> <item name="android:windowLightNavigationBar">false</item>
<item name="android:navigationBarColor">@color/black</item> <item name="android:navigationBarColor">@color/black</item>
<item name="android:navigationBarDividerColor">?attr/dividerColor</item> <item name="android:navigationBarDividerColor">?attr/dividerColor</item>

6
app/src/main/res/values-v35/values.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="is_edge_to_edge">true</bool>
<bool name="is_not_edge_to_edge">false</bool>
</resources>

5
app/src/main/res/values/dimens.xml

@ -82,4 +82,9 @@
<dimen name="caption_dialog_inset">24dp</dimen> <dimen name="caption_dialog_inset">24dp</dimen>
<dimen name="media_description_sheet_bottom_padding">8dp</dimen>
<!-- height of an BottomAppBar according to Material Design guidelines -->
<dimen name="bottomAppBarHeight">80dp</dimen>
</resources> </resources>

3
app/src/main/res/values/integers.xml → app/src/main/res/values/values.xml

@ -3,4 +3,7 @@
<integer name="profile_media_column_count">3</integer> <integer name="profile_media_column_count">3</integer>
<integer name="trending_column_count">1</integer> <integer name="trending_column_count">1</integer>
<bool name="is_edge_to_edge">false</bool>
<bool name="is_not_edge_to_edge">true</bool>
</resources> </resources>

13
gradle/libs.versions.toml

@ -1,27 +1,27 @@
[versions] [versions]
agp = "8.8.0" agp = "8.8.0"
androidx-activity = "1.9.3" androidx-activity = "1.10.0"
androidx-appcompat = "1.7.0" androidx-appcompat = "1.7.0"
androidx-browser = "1.8.0" androidx-browser = "1.8.0"
androidx-cardview = "1.0.0" androidx-cardview = "1.0.0"
androidx-constraintlayout = "2.2.0" androidx-constraintlayout = "2.2.0"
androidx-core = "1.13.1" androidx-core = "1.15.0"
androidx-drawerlayout = "1.2.0" androidx-drawerlayout = "1.2.0"
androidx-exifinterface = "1.3.7" androidx-exifinterface = "1.3.7"
androidx-fragment = "1.8.5" androidx-fragment = "1.8.5"
androidx-hilt = "1.2.0" androidx-hilt = "1.2.0"
androidx-junit = "1.2.1" androidx-junit = "1.2.1"
androidx-lifecycle = "2.8.7" androidx-lifecycle = "2.8.7"
androidx-media3 = "1.4.1" androidx-media3 = "1.5.1"
androidx-paging = "3.3.5" androidx-paging = "3.3.5"
androidx-preference = "1.2.1" androidx-preference = "1.2.1"
androidx-recyclerview = "1.3.2" androidx-recyclerview = "1.4.0"
androidx-sharetarget = "1.2.0" androidx-sharetarget = "1.2.0"
androidx-splashscreen = "1.2.0-alpha01" androidx-splashscreen = "1.2.0-alpha02"
androidx-swiperefresh-layout = "1.1.0" androidx-swiperefresh-layout = "1.1.0"
androidx-testing = "2.2.0" androidx-testing = "2.2.0"
androidx-viewpager2 = "1.1.0" androidx-viewpager2 = "1.1.0"
androidx-work = "2.9.1" androidx-work = "2.10.0"
androidx-room = "2.6.1" androidx-room = "2.6.1"
bouncycastle = "1.70" bouncycastle = "1.70"
conscrypt = "2.5.3" conscrypt = "2.5.3"
@ -30,7 +30,6 @@ diffx = "1.1.1"
emoji2 = "1.4.0" emoji2 = "1.4.0"
filemoji-compat = "3.2.7" filemoji-compat = "3.2.7"
glide = "4.16.0" glide = "4.16.0"
# Deliberate downgrade, https://github.com/tuskyapp/Tusky/issues/3631
glide-animation-plugin = "3.0.2" glide-animation-plugin = "3.0.2"
hilt = "2.55" hilt = "2.55"
kotlin = "2.1.10" kotlin = "2.1.10"

279
gradle/verification-metadata.xml

@ -11,6 +11,14 @@
</trusted-artifacts> </trusted-artifacts>
</configuration> </configuration>
<components> <components>
<component group="androidx.activity" name="activity" version="1.10.0">
<artifact name="activity-1.10.0.aar">
<sha256 value="c72301bc7307769cd6c3e0edb8da5837e7dea6fd89a5cdedfcfc87bd581850e1" origin="Generated by Gradle"/>
</artifact>
<artifact name="activity-1.10.0.module">
<sha256 value="334315c325c3bad343fc5f70b7a5126e268ff0fea38b0f5dc068106a5286898f" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.activity" name="activity" version="1.9.0"> <component group="androidx.activity" name="activity" version="1.9.0">
<artifact name="activity-1.9.0-sources.jar"> <artifact name="activity-1.9.0-sources.jar">
<sha256 value="3b95a45ba13704e4872ba0f2755b13ab7013e7f7c08bb8a208417a2b66622e74" origin="Generated by Gradle"/> <sha256 value="3b95a45ba13704e4872ba0f2755b13ab7013e7f7c08bb8a208417a2b66622e74" origin="Generated by Gradle"/>
@ -46,6 +54,14 @@
<sha256 value="7e6a24dda205e1cb1e9c96fa35ff6f261608087f31d323d4cff42b9716d7ed57" origin="Generated by Gradle"/> <sha256 value="7e6a24dda205e1cb1e9c96fa35ff6f261608087f31d323d4cff42b9716d7ed57" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.activity" name="activity-ktx" version="1.10.0">
<artifact name="activity-ktx-1.10.0.aar">
<sha256 value="3b8e0c51e8aa1a32efb6b2d4181a67dc80784789c166bfe307f70ee44185d510" origin="Generated by Gradle"/>
</artifact>
<artifact name="activity-ktx-1.10.0.module">
<sha256 value="b905385b69b275b0b1bd3c765de9964e64e8fe105c23daa4a776bde66c2c3159" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.activity" name="activity-ktx" version="1.8.1"> <component group="androidx.activity" name="activity-ktx" version="1.8.1">
<artifact name="activity-ktx-1.8.1.module"> <artifact name="activity-ktx-1.8.1.module">
<sha256 value="bea2f811b8d8c1b8a835e11d3c68219097c597c458429d3ceafd728e0ca2c6d1" origin="Generated by Gradle"/> <sha256 value="bea2f811b8d8c1b8a835e11d3c68219097c597c458429d3ceafd728e0ca2c6d1" origin="Generated by Gradle"/>
@ -181,6 +197,14 @@
<sha256 value="5930ea7f21fcb6d0deb2ba32748a0ef7c8fd2c42384860582ba7cd20deb90379" origin="Generated by Gradle"/> <sha256 value="5930ea7f21fcb6d0deb2ba32748a0ef7c8fd2c42384860582ba7cd20deb90379" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.annotation" name="annotation-experimental" version="1.4.1">
<artifact name="annotation-experimental-1.4.1.aar">
<sha256 value="6bd4c7c7476f8260cd3bdbb81183583e93fc9f790c27dea7dc314181cbf87aa0" origin="Generated by Gradle"/>
</artifact>
<artifact name="annotation-experimental-1.4.1.module">
<sha256 value="2ac2f7106e12f263425b4a4dfc80989447fb895675fe902d86759aa74fd12b7d" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.annotation" name="annotation-jvm" version="1.6.0"> <component group="androidx.annotation" name="annotation-jvm" version="1.6.0">
<artifact name="annotation-jvm-1.6.0.module"> <artifact name="annotation-jvm-1.6.0.module">
<sha256 value="3f5a8faa19de667e63dca9730ff8ef0e478e4bafb5feeb8258e5c086246dc90c" origin="Generated by Gradle"/> <sha256 value="3f5a8faa19de667e63dca9730ff8ef0e478e4bafb5feeb8258e5c086246dc90c" origin="Generated by Gradle"/>
@ -281,6 +305,9 @@
</artifact> </artifact>
</component> </component>
<component group="androidx.collection" name="collection" version="1.0.0"> <component group="androidx.collection" name="collection" version="1.0.0">
<artifact name="collection-1.0.0.jar">
<sha256 value="9c8d117b5c2bc120a1cdfeb857e05b495b16c36013570372a708f7827e3ac9f9" origin="Generated by Gradle"/>
</artifact>
<artifact name="collection-1.0.0.pom"> <artifact name="collection-1.0.0.pom">
<sha256 value="a7913a5275ad68e555d2612ebe8c14c367b153e14ca48a1872a64899020e54ef" origin="Generated by Gradle"/> <sha256 value="a7913a5275ad68e555d2612ebe8c14c367b153e14ca48a1872a64899020e54ef" origin="Generated by Gradle"/>
</artifact> </artifact>
@ -306,6 +333,11 @@
<sha256 value="2fd3b523e8276c0254c417b66d8e7fecc5b0b975af5d178afa7f5b813812cfab" origin="Generated by Gradle"/> <sha256 value="2fd3b523e8276c0254c417b66d8e7fecc5b0b975af5d178afa7f5b813812cfab" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.collection" name="collection" version="1.4.2">
<artifact name="collection-1.4.2.module">
<sha256 value="0326d2cf5adbe592312810ca1f71c6c0cc30f753c43f07c7402361b788a7784c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.collection" name="collection-jvm" version="1.4.0"> <component group="androidx.collection" name="collection-jvm" version="1.4.0">
<artifact name="collection-jvm-1.4.0.jar"> <artifact name="collection-jvm-1.4.0.jar">
<sha256 value="d5cf7b72647c7995071588fe870450ff9c8f127f253d2d4851e161b800f67ae0" origin="Generated by Gradle"/> <sha256 value="d5cf7b72647c7995071588fe870450ff9c8f127f253d2d4851e161b800f67ae0" origin="Generated by Gradle"/>
@ -314,6 +346,14 @@
<sha256 value="21b0b02ea68abe418f3dd4e4d42876ecf3bd9a1cada458b460cae1cd9d58ef6d" origin="Generated by Gradle"/> <sha256 value="21b0b02ea68abe418f3dd4e4d42876ecf3bd9a1cada458b460cae1cd9d58ef6d" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.collection" name="collection-jvm" version="1.4.2">
<artifact name="collection-jvm-1.4.2.jar">
<sha256 value="984ce9bd780055eafb8a105aca3f514686bf7b1b6272ec7c645a98ce40fc7db4" origin="Generated by Gradle"/>
</artifact>
<artifact name="collection-jvm-1.4.2.module">
<sha256 value="aad6b3536c0f0e570aa733d5141d70fa6b9b3add370f73a311ca4ca5dee25448" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.collection" name="collection-ktx" version="1.1.0"> <component group="androidx.collection" name="collection-ktx" version="1.1.0">
<artifact name="collection-ktx-1.1.0.jar"> <artifact name="collection-ktx-1.1.0.jar">
<sha256 value="2bfc54475c047131913361f56d0f7f019c6e5bee53eeb0eb7d94a7c499a05227" origin="Generated by Gradle"/> <sha256 value="2bfc54475c047131913361f56d0f7f019c6e5bee53eeb0eb7d94a7c499a05227" origin="Generated by Gradle"/>
@ -330,6 +370,14 @@
<sha256 value="3770999ec32d1c082d1a34cf1d64d16d9eca9b6b1263c979954407f461fba82b" origin="Generated by Gradle"/> <sha256 value="3770999ec32d1c082d1a34cf1d64d16d9eca9b6b1263c979954407f461fba82b" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.collection" name="collection-ktx" version="1.4.2">
<artifact name="collection-ktx-1.4.2.jar">
<sha256 value="c6deada2fac53b8ea6523dbda77597b128006674616f140f04df23264c6d1aa3" origin="Generated by Gradle"/>
</artifact>
<artifact name="collection-ktx-1.4.2.module">
<sha256 value="8a68e297cd92c80789acc033e2b3d7cbd7941f9234335f00c037205df200bcf5" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.compose.runtime" name="runtime" version="1.6.7"> <component group="androidx.compose.runtime" name="runtime" version="1.6.7">
<artifact name="runtime-1.6.7.module"> <artifact name="runtime-1.6.7.module">
<sha256 value="2922f1c5cd195b2d852461cde71c0ed36877e4da5b5cd7cacceb3042fc2604ea" origin="Generated by Gradle"/> <sha256 value="2922f1c5cd195b2d852461cde71c0ed36877e4da5b5cd7cacceb3042fc2604ea" origin="Generated by Gradle"/>
@ -415,6 +463,14 @@
<sha256 value="dae46132cdcd46b798425f7cb78fd65890869b6d26101ccdcd43461a4f51754c" origin="Generated by Gradle"/> <sha256 value="dae46132cdcd46b798425f7cb78fd65890869b6d26101ccdcd43461a4f51754c" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.core" name="core" version="1.12.0">
<artifact name="core-1.12.0.aar">
<sha256 value="42ffa7ca47d7ba8fe1d874c57ef9c7111bc41a2b0c0c21518a39e07d222ded8b" origin="Generated by Gradle"/>
</artifact>
<artifact name="core-1.12.0.module">
<sha256 value="2f63fbeda23ca0919738d09e406de661f21bac583d6e04a1797dcb77e3b6ae95" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.core" name="core" version="1.13.1"> <component group="androidx.core" name="core" version="1.13.1">
<artifact name="core-1.13.1.aar"> <artifact name="core-1.13.1.aar">
<sha256 value="2c27de199535675005553066597a4b20fa1eea7c228ab4ef6b32b5fe39ca1f59" origin="Generated by Gradle"/> <sha256 value="2c27de199535675005553066597a4b20fa1eea7c228ab4ef6b32b5fe39ca1f59" origin="Generated by Gradle"/>
@ -423,6 +479,14 @@
<sha256 value="2a10979bbb3bcd7b25b7f664ab4e9b016fabf2174a26768b677e12e4bea4c7c4" origin="Generated by Gradle"/> <sha256 value="2a10979bbb3bcd7b25b7f664ab4e9b016fabf2174a26768b677e12e4bea4c7c4" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.core" name="core" version="1.15.0">
<artifact name="core-1.15.0.aar">
<sha256 value="432b85a1974076e14b487ece4a28c59a84f1b9efc3fc8be72cd7f05d32055e51" origin="Generated by Gradle"/>
</artifact>
<artifact name="core-1.15.0.module">
<sha256 value="e8a6c386e1765d870012fed2221173d4a2e8d2fd5aec732c51afb4a9f4684519" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.core" name="core-ktx" version="1.13.1"> <component group="androidx.core" name="core-ktx" version="1.13.1">
<artifact name="core-ktx-1.13.1-sources.jar"> <artifact name="core-ktx-1.13.1-sources.jar">
<sha256 value="b6646f76cd2d59210561dffb16b32a4e5fcca6de37ca027f1dae82bfb0cf6493" origin="Generated by Gradle"/> <sha256 value="b6646f76cd2d59210561dffb16b32a4e5fcca6de37ca027f1dae82bfb0cf6493" origin="Generated by Gradle"/>
@ -434,6 +498,14 @@
<sha256 value="ab530b04e2fbe73205484899a3be56c90968873eb496fc52e5ea7596d91035d1" origin="Generated by Gradle"/> <sha256 value="ab530b04e2fbe73205484899a3be56c90968873eb496fc52e5ea7596d91035d1" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.core" name="core-ktx" version="1.15.0">
<artifact name="core-ktx-1.15.0.aar">
<sha256 value="c72c97ff4a32308bcaf3061eea5ddf33455ce413cee4aa1d5c82440c5759d4bc" origin="Generated by Gradle"/>
</artifact>
<artifact name="core-ktx-1.15.0.module">
<sha256 value="bee9b44664ff37e2e222ebcde4b30e38294a3f927451edc611fec7f454ffa442" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.core" name="core-splashscreen" version="1.2.0-alpha01"> <component group="androidx.core" name="core-splashscreen" version="1.2.0-alpha01">
<artifact name="core-splashscreen-1.2.0-alpha01-sources.jar"> <artifact name="core-splashscreen-1.2.0-alpha01-sources.jar">
<sha256 value="5e82448d6f1c3aad499ff0f5f9d24602f880eb8d26fb13525821a3669b3466de" origin="Generated by Gradle"/> <sha256 value="5e82448d6f1c3aad499ff0f5f9d24602f880eb8d26fb13525821a3669b3466de" origin="Generated by Gradle"/>
@ -445,6 +517,14 @@
<sha256 value="dc11ea4e05d403739592d854f833860df7321d7f4262b40463ee54f51bce0107" origin="Generated by Gradle"/> <sha256 value="dc11ea4e05d403739592d854f833860df7321d7f4262b40463ee54f51bce0107" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.core" name="core-splashscreen" version="1.2.0-alpha02">
<artifact name="core-splashscreen-1.2.0-alpha02.aar">
<sha256 value="fd85f37cbac803f3818bb0b4e763a5db93b8d61bd43ab8746164db82270168b1" origin="Generated by Gradle"/>
</artifact>
<artifact name="core-splashscreen-1.2.0-alpha02.module">
<sha256 value="81c50181738c236c79de1867018a1ac25098ecf4a322fdd0864565d2b7d1af0c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.cursoradapter" name="cursoradapter" version="1.0.0"> <component group="androidx.cursoradapter" name="cursoradapter" version="1.0.0">
<artifact name="cursoradapter-1.0.0.aar"> <artifact name="cursoradapter-1.0.0.aar">
<sha256 value="a81c8fe78815fa47df5b749deb52727ad11f9397da58b16017f4eb2c11e28564" origin="Generated by Gradle"/> <sha256 value="a81c8fe78815fa47df5b749deb52727ad11f9397da58b16017f4eb2c11e28564" origin="Generated by Gradle"/>
@ -1021,6 +1101,14 @@
<sha256 value="7d4bc2961cd5bd399e3621d434f0c453dd6cadf891f917a946cc291abdda8f1a" origin="Generated by Gradle"/> <sha256 value="7d4bc2961cd5bd399e3621d434f0c453dd6cadf891f917a946cc291abdda8f1a" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.lifecycle" name="lifecycle-common" version="2.6.2">
<artifact name="lifecycle-common-2.6.2.jar">
<sha256 value="f34831b6c71cd844e1d35d1be49d5e79447c5ab856346531b1e8676fda7374b1" origin="Generated by Gradle"/>
</artifact>
<artifact name="lifecycle-common-2.6.2.module">
<sha256 value="0fa7f28f5cff8a406a4f7870b243cb0d6d7a7c20faa7a2becaff5907ae12f9cc" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-common" version="2.8.1"> <component group="androidx.lifecycle" name="lifecycle-common" version="2.8.1">
<artifact name="lifecycle-common-2.8.1.module"> <artifact name="lifecycle-common-2.8.1.module">
<sha256 value="4442d73add4e6862086c8f979be651c0fa24f3a61f3e30368daaa9ce99fc57d6" origin="Generated by Gradle"/> <sha256 value="4442d73add4e6862086c8f979be651c0fa24f3a61f3e30368daaa9ce99fc57d6" origin="Generated by Gradle"/>
@ -1203,6 +1291,14 @@
<sha256 value="b5f4a08193d7802ac3574d91ef442ae31c633ff3095f1c4973c80d68908a48bc" origin="Generated by Gradle"/> <sha256 value="b5f4a08193d7802ac3574d91ef442ae31c633ff3095f1c4973c80d68908a48bc" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.lifecycle" name="lifecycle-livedata" version="2.6.2">
<artifact name="lifecycle-livedata-2.6.2.aar">
<sha256 value="67359f609dfc2bf65da1270b23033f856064ec279f058e0a70c715f7c9003031" origin="Generated by Gradle"/>
</artifact>
<artifact name="lifecycle-livedata-2.6.2.module">
<sha256 value="19a287b46d1b1ffe297bee0df7dee5183aeba95600ec0de8742adeb5b67c8bab" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-livedata" version="2.8.1"> <component group="androidx.lifecycle" name="lifecycle-livedata" version="2.8.1">
<artifact name="lifecycle-livedata-2.8.1.aar"> <artifact name="lifecycle-livedata-2.8.1.aar">
<sha256 value="1a280b5d6351515907aaa19d3179ec8076095985e979e323a5972bdc5d6b7945" origin="Generated by Gradle"/> <sha256 value="1a280b5d6351515907aaa19d3179ec8076095985e979e323a5972bdc5d6b7945" origin="Generated by Gradle"/>
@ -1267,6 +1363,14 @@
<sha256 value="3f388e9e078901970c2bfcfc02fecae948de4b46be5211919ae07d012ca2980d" origin="Generated by Gradle"/> <sha256 value="3f388e9e078901970c2bfcfc02fecae948de4b46be5211919ae07d012ca2980d" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.lifecycle" name="lifecycle-livedata-core" version="2.6.2">
<artifact name="lifecycle-livedata-core-2.6.2.aar">
<sha256 value="2256780a3cff4a1e57fbb3d442557c17dc363ab8af105bcaf5261d8e2d5db949" origin="Generated by Gradle"/>
</artifact>
<artifact name="lifecycle-livedata-core-2.6.2.module">
<sha256 value="527d0e1ac467d1f47cc20ed7c30db170217299fabb868154cf5d1765e4e4dad9" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-livedata-core" version="2.8.1"> <component group="androidx.lifecycle" name="lifecycle-livedata-core" version="2.8.1">
<artifact name="lifecycle-livedata-core-2.8.1.aar"> <artifact name="lifecycle-livedata-core-2.8.1.aar">
<sha256 value="ed204afc11b8db2b154bee54a2b1af28d46acc54dc05dce463b4fc61e3ee3995" origin="Generated by Gradle"/> <sha256 value="ed204afc11b8db2b154bee54a2b1af28d46acc54dc05dce463b4fc61e3ee3995" origin="Generated by Gradle"/>
@ -1501,11 +1605,24 @@
<sha256 value="98a1c08c10cfd4bdaa89e9c256a9b96c09cdd5bcfbb03a9260817a6d89cc2378" origin="Generated by Gradle"/> <sha256 value="98a1c08c10cfd4bdaa89e9c256a9b96c09cdd5bcfbb03a9260817a6d89cc2378" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.lifecycle" name="lifecycle-runtime" version="2.3.1">
<artifact name="lifecycle-runtime-2.3.1.module">
<sha256 value="2a7b90e5049b674b36bccfd68677b3a0b3178b3f7c2ef7ddf618d3895598c4ce" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-runtime" version="2.6.1"> <component group="androidx.lifecycle" name="lifecycle-runtime" version="2.6.1">
<artifact name="lifecycle-runtime-2.6.1.module"> <artifact name="lifecycle-runtime-2.6.1.module">
<sha256 value="a4cbb01a42d07047bd8d870017c96a1b0b7b4673320e86b66317a13be2ec10c7" origin="Generated by Gradle"/> <sha256 value="a4cbb01a42d07047bd8d870017c96a1b0b7b4673320e86b66317a13be2ec10c7" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.lifecycle" name="lifecycle-runtime" version="2.6.2">
<artifact name="lifecycle-runtime-2.6.2.aar">
<sha256 value="4867fd5279742fba8388821930cb2affe06d81a52814e7e41e70392ea0ef887c" origin="Generated by Gradle"/>
</artifact>
<artifact name="lifecycle-runtime-2.6.2.module">
<sha256 value="ea0131846abe1fe9dea59ac6dfe1f0fb9d8b6d600c9eff9a1fd4ad5ee5e7cbc7" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.lifecycle" name="lifecycle-runtime" version="2.8.1"> <component group="androidx.lifecycle" name="lifecycle-runtime" version="2.8.1">
<artifact name="lifecycle-runtime-2.8.1.aar"> <artifact name="lifecycle-runtime-2.8.1.aar">
<sha256 value="d4ae4e8dc0ca6265b683d7b2333a102a3ee84ad97ccb9fbec4334f5cd0e3f54d" origin="Generated by Gradle"/> <sha256 value="d4ae4e8dc0ca6265b683d7b2333a102a3ee84ad97ccb9fbec4334f5cd0e3f54d" origin="Generated by Gradle"/>
@ -2160,6 +2277,14 @@
<sha256 value="ed4a5a918d5febf5c36f8f8bf86ed41ebd51c633f7f6e366a17daa0faab4a587" origin="Generated by Gradle"/> <sha256 value="ed4a5a918d5febf5c36f8f8bf86ed41ebd51c633f7f6e366a17daa0faab4a587" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.media3" name="media3-common" version="1.5.1">
<artifact name="media3-common-1.5.1.aar">
<sha256 value="ee69fc48b9ea9c5e1381cbf89bcab96e254fd7bd1f31891642bb4e1c1f6c6fd7" origin="Generated by Gradle"/>
</artifact>
<artifact name="media3-common-1.5.1.module">
<sha256 value="c01afb3a57e3d171e41d98d71220ef9cbb7a508118a189d0c79e55c6337fb796" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.media3" name="media3-container" version="1.3.1"> <component group="androidx.media3" name="media3-container" version="1.3.1">
<artifact name="media3-container-1.3.1.aar"> <artifact name="media3-container-1.3.1.aar">
<sha256 value="c6103c11666e03ceabeb6454b5bf0b9b51fdc93647f84dd02ee869a9c086269e" origin="Generated by Gradle"/> <sha256 value="c6103c11666e03ceabeb6454b5bf0b9b51fdc93647f84dd02ee869a9c086269e" origin="Generated by Gradle"/>
@ -2184,6 +2309,14 @@
<sha256 value="333a0fa420c00ef66bbbbdc8ec6cb24bcb85cf8b1d0c730aeb8dfce3bb5615fa" origin="Generated by Gradle"/> <sha256 value="333a0fa420c00ef66bbbbdc8ec6cb24bcb85cf8b1d0c730aeb8dfce3bb5615fa" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.media3" name="media3-container" version="1.5.1">
<artifact name="media3-container-1.5.1.aar">
<sha256 value="b163c43acbb47244b3ffedc5f216f349cfc8ca9585d3b73b7f32b7228fd63dff" origin="Generated by Gradle"/>
</artifact>
<artifact name="media3-container-1.5.1.module">
<sha256 value="369c92fe1ec4d80aa3bd9cbec4fe6757cc4bea24b3a4cddea328ad9629c56d5f" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.media3" name="media3-database" version="1.3.1"> <component group="androidx.media3" name="media3-database" version="1.3.1">
<artifact name="media3-database-1.3.1.aar"> <artifact name="media3-database-1.3.1.aar">
<sha256 value="8386d2c445d194268d4af2d63abb31b028da5f742fc38c6365bc2c40ab641383" origin="Generated by Gradle"/> <sha256 value="8386d2c445d194268d4af2d63abb31b028da5f742fc38c6365bc2c40ab641383" origin="Generated by Gradle"/>
@ -2208,6 +2341,14 @@
<sha256 value="405dceb759e14d60667f536011c7c2ad43c39b63fa54939b68719f991c5f738f" origin="Generated by Gradle"/> <sha256 value="405dceb759e14d60667f536011c7c2ad43c39b63fa54939b68719f991c5f738f" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.media3" name="media3-database" version="1.5.1">
<artifact name="media3-database-1.5.1.aar">
<sha256 value="13bf4ccce6bc9e8ef8e04a62e90fa6c1589b89bee8633f0db9f61ae2c23f6ef6" origin="Generated by Gradle"/>
</artifact>
<artifact name="media3-database-1.5.1.module">
<sha256 value="21239585d06e7c673002e3da85ab6358ce427d64f8ab4253c932d2f4fbddb065" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.media3" name="media3-datasource" version="1.3.1"> <component group="androidx.media3" name="media3-datasource" version="1.3.1">
<artifact name="media3-datasource-1.3.1.aar"> <artifact name="media3-datasource-1.3.1.aar">
<sha256 value="ad94437e3c3dc5b9fb4a5c2dc64aa912a91125519742ec2fc3ae9ebfacc62381" origin="Generated by Gradle"/> <sha256 value="ad94437e3c3dc5b9fb4a5c2dc64aa912a91125519742ec2fc3ae9ebfacc62381" origin="Generated by Gradle"/>
@ -2232,6 +2373,14 @@
<sha256 value="52e072c78f2eb1d51b1b7a7de32282722362f1be05f4bd79f6b301f794e60899" origin="Generated by Gradle"/> <sha256 value="52e072c78f2eb1d51b1b7a7de32282722362f1be05f4bd79f6b301f794e60899" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.media3" name="media3-datasource" version="1.5.1">
<artifact name="media3-datasource-1.5.1.aar">
<sha256 value="b86cfb137a94bf471d6b2802415edd4fe66038dab6986682133786ca7dd43de2" origin="Generated by Gradle"/>
</artifact>
<artifact name="media3-datasource-1.5.1.module">
<sha256 value="a94825f509e053042c918b7e1b4b7752f5bafaf9ba2968c4c3350b6222c5af64" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.media3" name="media3-datasource-okhttp" version="1.3.1"> <component group="androidx.media3" name="media3-datasource-okhttp" version="1.3.1">
<artifact name="media3-datasource-okhttp-1.3.1-sources.jar"> <artifact name="media3-datasource-okhttp-1.3.1-sources.jar">
<sha256 value="35c112f4e1c6c02d13e4f714c14971fb59c236cad507ff7f4f5a879c993144f7" origin="Generated by Gradle"/> <sha256 value="35c112f4e1c6c02d13e4f714c14971fb59c236cad507ff7f4f5a879c993144f7" origin="Generated by Gradle"/>
@ -2259,6 +2408,14 @@
<sha256 value="4bde6bba18cd131e3027289e92de1ae22597b7d88b77ac831cc73cdcc2717e36" origin="Generated by Gradle"/> <sha256 value="4bde6bba18cd131e3027289e92de1ae22597b7d88b77ac831cc73cdcc2717e36" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.media3" name="media3-datasource-okhttp" version="1.5.1">
<artifact name="media3-datasource-okhttp-1.5.1.aar">
<sha256 value="f778c8943d5e9fbc1bd271339a9ed7b2e8708e18b160e7cabd8f979c4e5d32bf" origin="Generated by Gradle"/>
</artifact>
<artifact name="media3-datasource-okhttp-1.5.1.module">
<sha256 value="39f2c03eb7a0832872676e6d6cd0339a0118452528eefcef2d727cee9d6194f8" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.media3" name="media3-decoder" version="1.3.1"> <component group="androidx.media3" name="media3-decoder" version="1.3.1">
<artifact name="media3-decoder-1.3.1.aar"> <artifact name="media3-decoder-1.3.1.aar">
<sha256 value="c4799cd411061a654ad9c73fbc3d1a95945fce40f1bbceaf219e17b8a56fa1c1" origin="Generated by Gradle"/> <sha256 value="c4799cd411061a654ad9c73fbc3d1a95945fce40f1bbceaf219e17b8a56fa1c1" origin="Generated by Gradle"/>
@ -2283,6 +2440,14 @@
<sha256 value="c8a5ffe14418ce0ef005521c37bc9810a2bb01c2e5bd66874553788d14262512" origin="Generated by Gradle"/> <sha256 value="c8a5ffe14418ce0ef005521c37bc9810a2bb01c2e5bd66874553788d14262512" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.media3" name="media3-decoder" version="1.5.1">
<artifact name="media3-decoder-1.5.1.aar">
<sha256 value="6cd78cad4c1ef8aa16876fcd923eefc3894b538960798698a891cc7f419b8b34" origin="Generated by Gradle"/>
</artifact>
<artifact name="media3-decoder-1.5.1.module">
<sha256 value="fc9f6c7dde342308ec395d3db7c517fb089c22978e0465f1eb870ee0570ee88f" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.media3" name="media3-exoplayer" version="1.3.1"> <component group="androidx.media3" name="media3-exoplayer" version="1.3.1">
<artifact name="media3-exoplayer-1.3.1-sources.jar"> <artifact name="media3-exoplayer-1.3.1-sources.jar">
<sha256 value="c30282058a61aa6934ca6b2eaeb47383e233622fbda98977a8893c914f2d5907" origin="Generated by Gradle"/> <sha256 value="c30282058a61aa6934ca6b2eaeb47383e233622fbda98977a8893c914f2d5907" origin="Generated by Gradle"/>
@ -2310,6 +2475,14 @@
<sha256 value="4e153240eae009f97ef4e8aec012443a3ae792545c5354e51b1d232c97f2daae" origin="Generated by Gradle"/> <sha256 value="4e153240eae009f97ef4e8aec012443a3ae792545c5354e51b1d232c97f2daae" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.media3" name="media3-exoplayer" version="1.5.1">
<artifact name="media3-exoplayer-1.5.1.aar">
<sha256 value="11bb9687acd2c0bdd0a99a32006c8ac632ac138ffd996ae7293f375514721b51" origin="Generated by Gradle"/>
</artifact>
<artifact name="media3-exoplayer-1.5.1.module">
<sha256 value="d7b09ec60b5eee17041c7b7cd7233e6964db598ec73ae2037661691454430a75" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.media3" name="media3-extractor" version="1.3.1"> <component group="androidx.media3" name="media3-extractor" version="1.3.1">
<artifact name="media3-extractor-1.3.1.aar"> <artifact name="media3-extractor-1.3.1.aar">
<sha256 value="54400e411960da947d85567bbce3e752fade6bcb7d075c122a4e2cd05379dc62" origin="Generated by Gradle"/> <sha256 value="54400e411960da947d85567bbce3e752fade6bcb7d075c122a4e2cd05379dc62" origin="Generated by Gradle"/>
@ -2334,6 +2507,14 @@
<sha256 value="cac1d03702f3835342287bd40f47d80bd08d8cdbaf2d472df3fdc2e5f2138bc4" origin="Generated by Gradle"/> <sha256 value="cac1d03702f3835342287bd40f47d80bd08d8cdbaf2d472df3fdc2e5f2138bc4" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.media3" name="media3-extractor" version="1.5.1">
<artifact name="media3-extractor-1.5.1.aar">
<sha256 value="f2a146209e040409ed2cea3daa74932011c3b4ac451427f1738569821b21d084" origin="Generated by Gradle"/>
</artifact>
<artifact name="media3-extractor-1.5.1.module">
<sha256 value="86b2a4e43762808d57f331c25b5f7967bc7fab3cffe580563108f9149afd3d9b" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.media3" name="media3-ui" version="1.3.1"> <component group="androidx.media3" name="media3-ui" version="1.3.1">
<artifact name="media3-ui-1.3.1-sources.jar"> <artifact name="media3-ui-1.3.1-sources.jar">
<sha256 value="c3d8d9a0c76996f07e9978e9e1176e344783b93ab3967d9c8b23ea414171a747" origin="Generated by Gradle"/> <sha256 value="c3d8d9a0c76996f07e9978e9e1176e344783b93ab3967d9c8b23ea414171a747" origin="Generated by Gradle"/>
@ -2361,6 +2542,14 @@
<sha256 value="4287c7a4a885b77f332dff61e1d0c81754dcd6be3918f0cbc1a7bb46ba9c1ede" origin="Generated by Gradle"/> <sha256 value="4287c7a4a885b77f332dff61e1d0c81754dcd6be3918f0cbc1a7bb46ba9c1ede" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.media3" name="media3-ui" version="1.5.1">
<artifact name="media3-ui-1.5.1.aar">
<sha256 value="01caff80faaf5cbc4304d70f971a41004865228b0e31188a34b9bd219bb1cdd8" origin="Generated by Gradle"/>
</artifact>
<artifact name="media3-ui-1.5.1.module">
<sha256 value="67d598c80cd09c09919ad6822af8d285b5bdcaafc5f92656d729e6b8a35e0ee6" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.multidex" name="multidex" version="2.0.1"> <component group="androidx.multidex" name="multidex" version="2.0.1">
<artifact name="multidex-2.0.1.aar"> <artifact name="multidex-2.0.1.aar">
<sha256 value="42dd32ff9f97f85771b82a20003a8d70f68ab7b4ba328964312ce0732693db09" origin="Generated by Gradle"/> <sha256 value="42dd32ff9f97f85771b82a20003a8d70f68ab7b4ba328964312ce0732693db09" origin="Generated by Gradle"/>
@ -2626,6 +2815,14 @@
<sha256 value="179fc4d2d022c0a8247e7612feab40b951bd8710f6b452e3ed6dc03a1e52769d" origin="Generated by Gradle"/> <sha256 value="179fc4d2d022c0a8247e7612feab40b951bd8710f6b452e3ed6dc03a1e52769d" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.recyclerview" name="recyclerview" version="1.4.0">
<artifact name="recyclerview-1.4.0.aar">
<sha256 value="0f32ccaffcda74c3f87f0b4cfe6ae78e8aa4eb3ad7dda4f2edf06eedc947a5cf" origin="Generated by Gradle"/>
</artifact>
<artifact name="recyclerview-1.4.0.module">
<sha256 value="631d996322198595129c1d02805346361d811c06f25545fa8bbca567355f6f98" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.resourceinspection" name="resourceinspection-annotation" version="1.0.1"> <component group="androidx.resourceinspection" name="resourceinspection-annotation" version="1.0.1">
<artifact name="resourceinspection-annotation-1.0.1.jar"> <artifact name="resourceinspection-annotation-1.0.1.jar">
<sha256 value="8cff870ec6fb31db48a52f4a792335b4bf8de07e03bd37823181526433ccd5cb" origin="Generated by Gradle"/> <sha256 value="8cff870ec6fb31db48a52f4a792335b4bf8de07e03bd37823181526433ccd5cb" origin="Generated by Gradle"/>
@ -2971,6 +3168,22 @@
<sha256 value="b1fed4309623b6f20bc817d8fbd70e4ea7085e40647694cd399ae58d2f0049e3" origin="Generated by Gradle"/> <sha256 value="b1fed4309623b6f20bc817d8fbd70e4ea7085e40647694cd399ae58d2f0049e3" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.tracing" name="tracing" version="1.2.0">
<artifact name="tracing-1.2.0.aar">
<sha256 value="6faa90390d1fdbf0adb9a99bf99de67b94c6c6f35aea9510593a9d17973736a2" origin="Generated by Gradle"/>
</artifact>
<artifact name="tracing-1.2.0.module">
<sha256 value="d0d8d486b6bd33206dbf3f1a6d167e9b43c268ea63c3321c886b1543ad05ece3" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.tracing" name="tracing-ktx" version="1.2.0">
<artifact name="tracing-ktx-1.2.0.aar">
<sha256 value="c33f9cbd931e6190ca38aa09bf8a7a8a19391d4b0fb7a7605a6b59f2d42580d1" origin="Generated by Gradle"/>
</artifact>
<artifact name="tracing-ktx-1.2.0.module">
<sha256 value="39288b23c71a4391132aec9951f5e27f41727a2cdff69c6e4b4ece49f63e540c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.transition" name="transition" version="1.5.0"> <component group="androidx.transition" name="transition" version="1.5.0">
<artifact name="transition-1.5.0.aar"> <artifact name="transition-1.5.0.aar">
<sha256 value="0aa66a0ea406d25a1091f96a3b753b4b12e44fdc43b91ec52c17831e9c31f54b" origin="Generated by Gradle"/> <sha256 value="0aa66a0ea406d25a1091f96a3b753b4b12e44fdc43b91ec52c17831e9c31f54b" origin="Generated by Gradle"/>
@ -3041,6 +3254,14 @@
<sha256 value="536773d2b2d65c26ce06b8c95e0fb415f1ad25d9b87330170f508689d3ad5ffb" origin="Generated by Gradle"/> <sha256 value="536773d2b2d65c26ce06b8c95e0fb415f1ad25d9b87330170f508689d3ad5ffb" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.work" name="work-runtime" version="2.10.0">
<artifact name="work-runtime-2.10.0.aar">
<sha256 value="2ee10cd3345a7e6e37d50898fd0ebdd91e4a59267aa928effa8e507dae85c88b" origin="Generated by Gradle"/>
</artifact>
<artifact name="work-runtime-2.10.0.module">
<sha256 value="465c1b4750092ebf0b031e06ecfd8011f7e15ace8dccb1e4c47d409f77becba0" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.work" name="work-runtime" version="2.9.0"> <component group="androidx.work" name="work-runtime" version="2.9.0">
<artifact name="work-runtime-2.9.0.aar"> <artifact name="work-runtime-2.9.0.aar">
<sha256 value="8b85f38aa826d902e8a88d2af9dc73a5f43f6618720485f16e742692c5a18f6f" origin="Generated by Gradle"/> <sha256 value="8b85f38aa826d902e8a88d2af9dc73a5f43f6618720485f16e742692c5a18f6f" origin="Generated by Gradle"/>
@ -3057,6 +3278,14 @@
<sha256 value="d544ed64c5cba1861f96f48acdf15f7a06894f3675a16aa815a99f3aa290946b" origin="Generated by Gradle"/> <sha256 value="d544ed64c5cba1861f96f48acdf15f7a06894f3675a16aa815a99f3aa290946b" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.work" name="work-runtime-ktx" version="2.10.0">
<artifact name="work-runtime-ktx-2.10.0.aar">
<sha256 value="8273692ccc17094d746cafd04b679f259cbf501b2ea8475e349fba24ed3976b9" origin="Generated by Gradle"/>
</artifact>
<artifact name="work-runtime-ktx-2.10.0.module">
<sha256 value="80aa6dc6ec0cc210475c820a6d36dc8100d5da9aef7cccac081169a1a2502b04" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.work" name="work-runtime-ktx" version="2.9.0"> <component group="androidx.work" name="work-runtime-ktx" version="2.9.0">
<artifact name="work-runtime-ktx-2.9.0-sources.jar"> <artifact name="work-runtime-ktx-2.9.0-sources.jar">
<sha256 value="c6deada2fac53b8ea6523dbda77597b128006674616f140f04df23264c6d1aa3" origin="Generated by Gradle"/> <sha256 value="c6deada2fac53b8ea6523dbda77597b128006674616f140f04df23264c6d1aa3" origin="Generated by Gradle"/>
@ -3076,6 +3305,14 @@
<sha256 value="711fb95228d07347da52089dfb5d195c51a07dd75528ad51474c397196afb35c" origin="Generated by Gradle"/> <sha256 value="711fb95228d07347da52089dfb5d195c51a07dd75528ad51474c397196afb35c" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="androidx.work" name="work-testing" version="2.10.0">
<artifact name="work-testing-2.10.0.aar">
<sha256 value="80eeb8a0ba60089ef0af4a006fa0a0be2b5b627497c332c2b07226f213261690" origin="Generated by Gradle"/>
</artifact>
<artifact name="work-testing-2.10.0.module">
<sha256 value="4a6dcc76dd6030f939904acb1f829c3056774ccd209ba3ede4a322b2191f7a14" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="androidx.work" name="work-testing" version="2.9.0"> <component group="androidx.work" name="work-testing" version="2.9.0">
<artifact name="work-testing-2.9.0-sources.jar"> <artifact name="work-testing-2.9.0-sources.jar">
<sha256 value="7ed3ec16b4203d209cec89f6e467d28359eba77de5cd9165b6430eea0a64eebf" origin="Generated by Gradle"/> <sha256 value="7ed3ec16b4203d209cec89f6e467d28359eba77de5cd9165b6430eea0a64eebf" origin="Generated by Gradle"/>
@ -10688,6 +10925,17 @@
<sha256 value="452b2d9787b7d366fa8cf5ed9a1c40404542d05effa7a598da03bbbbb76d9f31" origin="Generated by Gradle"/> <sha256 value="452b2d9787b7d366fa8cf5ed9a1c40404542d05effa7a598da03bbbbb76d9f31" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="com.google.guava" name="guava" version="33.3.1-android">
<artifact name="guava-33.3.1-android.jar">
<sha256 value="2c3e41d1b380f2044d257947a3aa82dabf3ae4b978622745254aa18b6cf89ab0" origin="Generated by Gradle"/>
</artifact>
<artifact name="guava-33.3.1-android.module">
<sha256 value="6971614ddeee003e54d2b69c6b0c94ce8a5b5eba980d16455c81df9089a7acb7" origin="Generated by Gradle"/>
</artifact>
<artifact name="guava-33.3.1-jre.jar">
<sha256 value="4bf0e2c5af8e4525c96e8fde17a4f7307f97f8478f11c4c8e35a0e3298ae4e90" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.guava" name="guava" version="33.3.1-jre"> <component group="com.google.guava" name="guava" version="33.3.1-jre">
<artifact name="guava-33.3.1-android.jar"> <artifact name="guava-33.3.1-android.jar">
<sha256 value="2c3e41d1b380f2044d257947a3aa82dabf3ae4b978622745254aa18b6cf89ab0" origin="Generated by Gradle"/> <sha256 value="2c3e41d1b380f2044d257947a3aa82dabf3ae4b978622745254aa18b6cf89ab0" origin="Generated by Gradle"/>
@ -10739,6 +10987,11 @@
<sha256 value="55441db27e8869dfefe053059bdf478bdc7e95585642bf391f0023345fd56287" origin="Generated by Gradle"/> <sha256 value="55441db27e8869dfefe053059bdf478bdc7e95585642bf391f0023345fd56287" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="com.google.guava" name="guava-parent" version="33.3.1-android">
<artifact name="guava-parent-33.3.1-android.pom">
<sha256 value="6e11986ea7250b51f847157e2dc937f32a306804dfce0007a5e81ddb9b95c579" origin="Generated by Conny Duck"/>
</artifact>
</component>
<component group="com.google.guava" name="listenablefuture" version="1.0"> <component group="com.google.guava" name="listenablefuture" version="1.0">
<artifact name="listenablefuture-1.0-sources.jar"> <artifact name="listenablefuture-1.0-sources.jar">
<sha256 value="3d1bd8d4a39b293612a40e547ec51d9ce34fa638d7adeae83871cdbe2923b161" origin="Generated by Gradle"/> <sha256 value="3d1bd8d4a39b293612a40e547ec51d9ce34fa638d7adeae83871cdbe2923b161" origin="Generated by Gradle"/>
@ -12527,6 +12780,11 @@
<sha256 value="e718289ad63441bde3c61ab25b907d9b3152b9a181120832305f309e4d0e8274" origin="Generated by Gradle"/> <sha256 value="e718289ad63441bde3c61ab25b907d9b3152b9a181120832305f309e4d0e8274" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.jetbrains.kotlin" name="kotlin-bom" version="1.8.0">
<artifact name="kotlin-bom-1.8.0.pom">
<sha256 value="681f9878548c0a149c678092fd8b1f4cc7c5c41ba1b24397101c1073a6c76dda" origin="Generated by Conny Duck"/>
</artifact>
</component>
<component group="org.jetbrains.kotlin" name="kotlin-bom" version="1.8.21"> <component group="org.jetbrains.kotlin" name="kotlin-bom" version="1.8.21">
<artifact name="kotlin-bom-1.8.21.pom"> <artifact name="kotlin-bom-1.8.21.pom">
<sha256 value="43f52cd839d8fd872f240491d38fa7df170a87a36e105e700dd939d15572a8ef" origin="Generated by Gradle"/> <sha256 value="43f52cd839d8fd872f240491d38fa7df170a87a36e105e700dd939d15572a8ef" origin="Generated by Gradle"/>
@ -14411,6 +14669,14 @@
<sha256 value="beb7ff0f5ebc63a0b30af2ae1214e0b622a7b7e408240e64a8ea5b213c4d5334" origin="Generated by Gradle"/> <sha256 value="beb7ff0f5ebc63a0b30af2ae1214e0b622a7b7e408240e64a8ea5b213c4d5334" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-android" version="1.7.3">
<artifact name="kotlinx-coroutines-android-1.7.3.jar">
<sha256 value="59fffb26bee12c32dadcfa5d420c2a7db85d3253518128b170efda726613256d" origin="Generated by Gradle"/>
</artifact>
<artifact name="kotlinx-coroutines-android-1.7.3.module">
<sha256 value="48dfd8139edee5481bcc8b25e24d7586aca615f0d1ed2beb242dc7478ed4cee0" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-android" version="1.8.1"> <component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-android" version="1.8.1">
<artifact name="kotlinx-coroutines-android-1.8.1.jar"> <artifact name="kotlinx-coroutines-android-1.8.1.jar">
<sha256 value="a134dacf4e6578b29b32e97aa50548d09cb45e3cb3551ce77ac27e55e265d8f5" origin="Generated by Gradle"/> <sha256 value="a134dacf4e6578b29b32e97aa50548d09cb45e3cb3551ce77ac27e55e265d8f5" origin="Generated by Gradle"/>
@ -14442,6 +14708,11 @@
<sha256 value="b925aa988c40a5c7aa0c77b21373feb18c39414926bd51c76ef44434f5bae9c2" origin="Generated by Gradle"/> <sha256 value="b925aa988c40a5c7aa0c77b21373feb18c39414926bd51c76ef44434f5bae9c2" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-bom" version="1.7.3">
<artifact name="kotlinx-coroutines-bom-1.7.3.pom">
<sha256 value="4e5d1900e6379ef3f5970d04a8f30529adc82f859e8cc107c21ce8149ef666c4" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-bom" version="1.8.0"> <component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-bom" version="1.8.0">
<artifact name="kotlinx-coroutines-bom-1.8.0.pom"> <artifact name="kotlinx-coroutines-bom-1.8.0.pom">
<sha256 value="1239e9dbe1397cd5971342956b2511bc3ace7b641842e4372a088dcfa8b9ad55" origin="Generated by Gradle"/> <sha256 value="1239e9dbe1397cd5971342956b2511bc3ace7b641842e4372a088dcfa8b9ad55" origin="Generated by Gradle"/>
@ -14531,6 +14802,14 @@
<sha256 value="db1fe0110fa82b565d332d39d3f3e62c1dc7eb6238333e3b15e4377fa56e39cb" origin="Generated by Gradle"/> <sha256 value="db1fe0110fa82b565d332d39d3f3e62c1dc7eb6238333e3b15e4377fa56e39cb" origin="Generated by Gradle"/>
</artifact> </artifact>
</component> </component>
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-core-jvm" version="1.7.3">
<artifact name="kotlinx-coroutines-core-jvm-1.7.3.jar">
<sha256 value="1ab3acc38f3e7355c4f9d1ec62107a46fa73c899f3070d055e5d4373dfe67e12" origin="Generated by Gradle"/>
</artifact>
<artifact name="kotlinx-coroutines-core-jvm-1.7.3.module">
<sha256 value="34d6ee99b76ac062b51555b4a70be18349fe5566da79a190614f171c80b6538e" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-core-jvm" version="1.8.0"> <component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-core-jvm" version="1.8.0">
<artifact name="kotlinx-coroutines-core-jvm-1.8.0.module"> <artifact name="kotlinx-coroutines-core-jvm-1.8.0.module">
<sha256 value="ff6a22da40040938751db0ae21177e76517dbf126a76796f5426727bf76c1228" origin="Generated by Gradle"/> <sha256 value="ff6a22da40040938751db0ae21177e76517dbf126a76796f5426727bf76c1228" origin="Generated by Gradle"/>

Loading…
Cancel
Save