diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ed559231b..fa344b86b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -144,8 +144,6 @@
android:resource="@xml/searchable" />
-
-
diff --git a/app/src/main/java/app/pachli/MainActivity.kt b/app/src/main/java/app/pachli/MainActivity.kt
index 1dc018bd0..72bd30c87 100644
--- a/app/src/main/java/app/pachli/MainActivity.kt
+++ b/app/src/main/java/app/pachli/MainActivity.kt
@@ -97,6 +97,7 @@ import app.pachli.core.navigation.TrendingActivityIntent
import app.pachli.core.network.model.Account
import app.pachli.core.network.model.Notification
import app.pachli.core.preferences.PrefKeys
+import app.pachli.core.ui.reduceSwipeSensitivity
import app.pachli.databinding.ActivityMainBinding
import app.pachli.db.DraftsAlert
import app.pachli.interfaces.ActionButtonActivity
@@ -110,7 +111,6 @@ import app.pachli.usecase.LogoutUsecase
import app.pachli.util.await
import app.pachli.util.deleteStaleCachedMedia
import app.pachli.util.getDimension
-import app.pachli.util.reduceSwipeSensitivity
import app.pachli.util.unsafeLazy
import app.pachli.util.updateShortcut
import at.connyduck.calladapter.networkresult.fold
diff --git a/app/src/main/java/app/pachli/components/account/AccountActivity.kt b/app/src/main/java/app/pachli/components/account/AccountActivity.kt
index 6bca184ad..99786ec02 100644
--- a/app/src/main/java/app/pachli/components/account/AccountActivity.kt
+++ b/app/src/main/java/app/pachli/components/account/AccountActivity.kt
@@ -77,6 +77,7 @@ import app.pachli.core.network.model.Relationship
import app.pachli.core.network.parseAsMastodonHtml
import app.pachli.core.preferences.AppTheme
import app.pachli.core.preferences.PrefKeys
+import app.pachli.core.ui.reduceSwipeSensitivity
import app.pachli.databinding.ActivityAccountBinding
import app.pachli.db.DraftsAlert
import app.pachli.interfaces.ActionButtonActivity
@@ -86,7 +87,6 @@ import app.pachli.util.Error
import app.pachli.util.Loading
import app.pachli.util.Success
import app.pachli.util.getDomain
-import app.pachli.util.reduceSwipeSensitivity
import app.pachli.util.setClickableText
import app.pachli.view.showMuteAccountDialog
import com.bumptech.glide.Glide
diff --git a/app/src/main/java/app/pachli/components/search/SearchActivity.kt b/app/src/main/java/app/pachli/components/search/SearchActivity.kt
index 483a6b383..dfc21dad6 100644
--- a/app/src/main/java/app/pachli/components/search/SearchActivity.kt
+++ b/app/src/main/java/app/pachli/components/search/SearchActivity.kt
@@ -31,8 +31,8 @@ import app.pachli.components.search.adapter.SearchPagerAdapter
import app.pachli.core.activity.BottomSheetActivity
import app.pachli.core.common.extensions.viewBinding
import app.pachli.core.preferences.PrefKeys
+import app.pachli.core.ui.reduceSwipeSensitivity
import app.pachli.databinding.ActivitySearchBinding
-import app.pachli.util.reduceSwipeSensitivity
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
diff --git a/app/src/main/java/app/pachli/components/trending/TrendingActivity.kt b/app/src/main/java/app/pachli/components/trending/TrendingActivity.kt
index 905d65961..1e2d5dedf 100644
--- a/app/src/main/java/app/pachli/components/trending/TrendingActivity.kt
+++ b/app/src/main/java/app/pachli/components/trending/TrendingActivity.kt
@@ -32,9 +32,9 @@ import app.pachli.components.timeline.TimelineFragment
import app.pachli.core.activity.BottomSheetActivity
import app.pachli.core.common.extensions.viewBinding
import app.pachli.core.network.model.TimelineKind
+import app.pachli.core.ui.reduceSwipeSensitivity
import app.pachli.databinding.ActivityTrendingBinding
import app.pachli.interfaces.AppBarLayoutHost
-import app.pachli.util.reduceSwipeSensitivity
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
diff --git a/app/src/main/java/app/pachli/util/ViewExtensions.kt b/app/src/main/java/app/pachli/util/ViewExtensions.kt
index d1dada305..adc20f4ea 100644
--- a/app/src/main/java/app/pachli/util/ViewExtensions.kt
+++ b/app/src/main/java/app/pachli/util/ViewExtensions.kt
@@ -17,43 +17,6 @@
package app.pachli.util
import android.widget.TextView
-import androidx.recyclerview.widget.RecyclerView
-import androidx.viewpager2.widget.ViewPager2
-import timber.log.Timber
-
-/**
- * Reduce ViewPager2's sensitivity to horizontal swipes.
- */
-fun ViewPager2.reduceSwipeSensitivity() {
- // ViewPager2 is very sensitive to horizontal motion when swiping vertically, and will
- // trigger a page transition if the user's swipe is only a few tens of degrees off from
- // vertical. This is a problem if the underlying content is a list that the user wants
- // to scroll vertically -- it's far too easy to trigger an accidental horizontal swipe.
- //
- // One way to stop this is to reach in to ViewPager2's RecyclerView and adjust the amount
- // of touch slop it has.
- //
- // See https://issuetracker.google.com/issues/139867645 and
- // https://bladecoder.medium.com/fixing-recyclerview-nested-scrolling-in-opposite-direction-f587be5c1a04
- // for more (the approach in that Medium article works, but is still quite sensitive to
- // horizontal movement while scrolling).
- try {
- val recyclerViewField = ViewPager2::class.java.getDeclaredField("mRecyclerView")
- recyclerViewField.isAccessible = true
- val recyclerView = recyclerViewField.get(this) as RecyclerView
-
- val touchSlopField = RecyclerView::class.java.getDeclaredField("mTouchSlop")
- touchSlopField.isAccessible = true
- val touchSlop = touchSlopField.get(recyclerView) as Int
- // Experimentally, 2 seems to be a sweet-spot, requiring a downward swipe that's at least
- // 45 degrees off the vertical to trigger a change. This is consistent with maximum angle
- // supported to open the nav. drawer.
- val scaleFactor = 2
- touchSlopField.set(recyclerView, touchSlop * scaleFactor)
- } catch (e: Exception) {
- Timber.tag("reduceSwipeSensitibity").w(e)
- }
-}
/**
* TextViews with an ancestor RecyclerView can forget that they are selectable. Toggling
diff --git a/core/navigation/src/main/kotlin/app/pachli/core/navigation/Navigation.kt b/core/navigation/src/main/kotlin/app/pachli/core/navigation/Navigation.kt
index 94d7bd81f..acd5c9e92 100644
--- a/core/navigation/src/main/kotlin/app/pachli/core/navigation/Navigation.kt
+++ b/core/navigation/src/main/kotlin/app/pachli/core/navigation/Navigation.kt
@@ -549,12 +549,6 @@ class InstanceListActivityIntent(context: Context) : Intent() {
}
}
-class LicenseActivityIntent(context: Context) : Intent() {
- init {
- setClassName(context, QuadrantConstants.LICENSE_ACTIVITY)
- }
-}
-
class ListActivityIntent(context: Context) : Intent() {
init {
setClassName(context, QuadrantConstants.LISTS_ACTIVITY)
@@ -567,12 +561,6 @@ class LoginWebViewActivityIntent(context: Context) : Intent() {
}
}
-class PrivacyPolicyActivityIntent(context: Context) : Intent() {
- init {
- setClassName(context, QuadrantConstants.PRIVACY_POLICY_ACTIVITY)
- }
-}
-
class ScheduledStatusActivityIntent(context: Context) : Intent() {
init {
setClassName(context, QuadrantConstants.SCHEDULED_STATUS_ACTIVITY)
diff --git a/core/ui/src/main/kotlin/app/pachli/core/ui/ViewPager2Extensions.kt b/core/ui/src/main/kotlin/app/pachli/core/ui/ViewPager2Extensions.kt
new file mode 100644
index 000000000..9e7148fbd
--- /dev/null
+++ b/core/ui/src/main/kotlin/app/pachli/core/ui/ViewPager2Extensions.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2024 Pachli Association
+ *
+ * This file is a part of Pachli.
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with Pachli; if not,
+ * see .
+ */
+
+package app.pachli.core.ui
+
+import androidx.recyclerview.widget.RecyclerView
+import androidx.viewpager2.widget.ViewPager2
+import timber.log.Timber
+
+/**
+ * Reduce ViewPager2's sensitivity to horizontal swipes.
+ */
+fun ViewPager2.reduceSwipeSensitivity() {
+ // ViewPager2 is very sensitive to horizontal motion when swiping vertically, and will
+ // trigger a page transition if the user's swipe is only a few tens of degrees off from
+ // vertical. This is a problem if the underlying content is a list that the user wants
+ // to scroll vertically -- it's far too easy to trigger an accidental horizontal swipe.
+ //
+ // One way to stop this is to reach in to ViewPager2's RecyclerView and adjust the amount
+ // of touch slop it has.
+ //
+ // See https://issuetracker.google.com/issues/139867645 and
+ // https://bladecoder.medium.com/fixing-recyclerview-nested-scrolling-in-opposite-direction-f587be5c1a04
+ // for more (the approach in that Medium article works, but is still quite sensitive to
+ // horizontal movement while scrolling).
+ try {
+ val recyclerViewField = ViewPager2::class.java.getDeclaredField("mRecyclerView")
+ recyclerViewField.isAccessible = true
+ val recyclerView = recyclerViewField.get(this) as RecyclerView
+
+ val touchSlopField = RecyclerView::class.java.getDeclaredField("mTouchSlop")
+ touchSlopField.isAccessible = true
+ val touchSlop = touchSlopField.get(recyclerView) as Int
+ // Experimentally, 2 seems to be a sweet-spot, requiring a downward swipe that's at least
+ // 45 degrees off the vertical to trigger a change. This is consistent with maximum angle
+ // supported to open the nav. drawer.
+ val scaleFactor = 2
+ touchSlopField.set(recyclerView, touchSlop * scaleFactor)
+ } catch (e: Exception) {
+ Timber.tag("reduceSwipeSensitibity").w(e)
+ }
+}
diff --git a/feature/about/build.gradle.kts b/feature/about/build.gradle.kts
index 442d14498..a53f8516d 100644
--- a/feature/about/build.gradle.kts
+++ b/feature/about/build.gradle.kts
@@ -47,6 +47,7 @@ dependencies {
implementation(projects.core.activity)
implementation(projects.core.common)
implementation(projects.core.data)
+ implementation(projects.core.designsystem)
implementation(projects.core.navigation)
implementation(projects.core.ui)
@@ -57,4 +58,7 @@ dependencies {
implementation(libs.bundles.androidx)
implementation(libs.bundles.aboutlibraries)
+
+ // For FixedSizeDrawable
+ implementation(libs.glide.core)
}
diff --git a/feature/about/src/main/kotlin/app/pachli/feature/about/AboutActivity.kt b/feature/about/src/main/kotlin/app/pachli/feature/about/AboutActivity.kt
index b13378a7a..867b0083a 100644
--- a/feature/about/src/main/kotlin/app/pachli/feature/about/AboutActivity.kt
+++ b/feature/about/src/main/kotlin/app/pachli/feature/about/AboutActivity.kt
@@ -17,137 +17,83 @@
package app.pachli.feature.about
-import android.content.ClipData
-import android.content.ClipboardManager
-import android.content.Context
-import android.os.Build
+import android.annotation.SuppressLint
import android.os.Bundle
-import android.text.SpannableString
-import android.text.SpannableStringBuilder
-import android.text.method.LinkMovementMethod
-import android.text.style.URLSpan
-import android.text.util.Linkify
-import android.widget.TextView
-import android.widget.Toast
-import androidx.annotation.StringRes
-import androidx.lifecycle.lifecycleScope
+import androidx.activity.OnBackPressedCallback
+import androidx.appcompat.content.res.AppCompatResources
+import androidx.core.view.MenuProvider
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+import androidx.viewpager2.adapter.FragmentStateAdapter
import app.pachli.core.activity.BottomSheetActivity
-import app.pachli.core.common.extensions.hide
-import app.pachli.core.common.extensions.show
-import app.pachli.core.common.util.versionName
-import app.pachli.core.data.repository.InstanceInfoRepository
-import app.pachli.core.navigation.LicenseActivityIntent
-import app.pachli.core.navigation.PrivacyPolicyActivityIntent
-import app.pachli.core.ui.NoUnderlineURLSpan
+import app.pachli.core.designsystem.R as DR
+import app.pachli.core.ui.reduceSwipeSensitivity
import app.pachli.feature.about.databinding.ActivityAboutBinding
+import com.bumptech.glide.request.target.FixedSizeDrawable
+import com.google.android.material.tabs.TabLayoutMediator
+import com.mikepenz.aboutlibraries.LibsBuilder
import dagger.hilt.android.AndroidEntryPoint
-import javax.inject.Inject
-import kotlinx.coroutines.launch
@AndroidEntryPoint
-class AboutActivity : BottomSheetActivity() {
- @Inject
- lateinit var instanceInfoRepository: InstanceInfoRepository
-
+class AboutActivity : BottomSheetActivity(), MenuProvider {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityAboutBinding.inflate(layoutInflater)
setContentView(binding.root)
- setSupportActionBar(binding.includedToolbar.toolbar)
+ setSupportActionBar(binding.toolbar)
+ binding.toolbar.run {
+ val navIconSize = resources.getDimensionPixelSize(DR.dimen.avatar_toolbar_nav_icon_size)
+ navigationIcon = FixedSizeDrawable(
+ AppCompatResources.getDrawable(this@AboutActivity, DR.mipmap.ic_launcher),
+ navIconSize,
+ navIconSize,
+ )
+ }
supportActionBar?.run {
- setDisplayHomeAsUpEnabled(true)
+ setTitle(R.string.app_name)
setDisplayShowHomeEnabled(true)
}
- setTitle(R.string.about_title_activity)
+ val adapter = AboutFragmentAdapter(this)
+ binding.pager.adapter = adapter
+ binding.pager.reduceSwipeSensitivity()
- binding.versionTextView.text = getString(
- R.string.about_app_version,
- getString(
- R.string.app_name,
- ),
- versionName(this),
+ TabLayoutMediator(binding.tabLayout, binding.pager) { tab, position ->
+ tab.text = adapter.title(position)
+ }.attach()
+
+ onBackPressedDispatcher.addCallback(
+ this,
+ object : OnBackPressedCallback(true) {
+ @SuppressLint("SyntheticAccessor")
+ override fun handleOnBackPressed() {
+ if (binding.pager.currentItem != 0) binding.pager.currentItem = 0 else finish()
+ }
+ },
)
-
- binding.deviceInfo.text = getString(
- R.string.about_device_info,
- Build.MANUFACTURER,
- Build.MODEL,
- Build.VERSION.RELEASE,
- Build.VERSION.SDK_INT,
- )
-
- lifecycleScope.launch {
- accountManager.activeAccount?.let { account ->
- val instanceInfo = instanceInfoRepository.getInstanceInfo()
- binding.accountInfo.text = getString(
- R.string.about_account_info,
- account.username,
- account.domain,
- instanceInfo.version,
- )
- binding.accountInfoTitle.show()
- binding.accountInfo.show()
- }
- }
-
- if (BuildConfig.CUSTOM_INSTANCE.isBlank()) {
- binding.aboutPoweredBy.hide()
- }
-
- binding.aboutLicenseInfoTextView.setClickableTextWithoutUnderlines(R.string.about_pachli_license)
- binding.aboutWebsiteInfoTextView.setClickableTextWithoutUnderlines(R.string.about_project_site)
- binding.aboutBugsFeaturesInfoTextView.setClickableTextWithoutUnderlines(R.string.about_bug_feature_request_site)
-
- binding.aboutPrivacyPolicyTextView.setOnClickListener {
- startActivity(PrivacyPolicyActivityIntent(this))
- }
-
- binding.appProfileButton.setOnClickListener {
- viewUrl(BuildConfig.SUPPORT_ACCOUNT_URL)
- }
-
- binding.aboutLicensesButton.setOnClickListener {
- startActivityWithSlideInAnimation(LicenseActivityIntent(this))
- }
-
- binding.copyDeviceInfo.setOnClickListener {
- val text = "${binding.versionTextView.text}\n\nDevice:\n\n${binding.deviceInfo.text}\n\nAccount:\n\n${binding.accountInfo.text}"
- val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
- val clip = ClipData.newPlainText("Pachli version information", text)
- clipboard.setPrimaryClip(clip)
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) {
- Toast.makeText(
- this,
- getString(R.string.about_copied),
- Toast.LENGTH_SHORT,
- ).show()
- }
- }
}
}
-internal fun TextView.setClickableTextWithoutUnderlines(@StringRes textId: Int) {
- val text = SpannableString(context.getText(textId))
+class AboutFragmentAdapter(val activity: FragmentActivity) : FragmentStateAdapter(activity) {
+ override fun getItemCount() = 3
- Linkify.addLinks(text, Linkify.WEB_URLS)
-
- val builder = SpannableStringBuilder(text)
- val urlSpans = text.getSpans(0, text.length, URLSpan::class.java)
- for (span in urlSpans) {
- val start = builder.getSpanStart(span)
- val end = builder.getSpanEnd(span)
- val flags = builder.getSpanFlags(span)
-
- val customSpan = NoUnderlineURLSpan(span.url)
-
- builder.removeSpan(span)
- builder.setSpan(customSpan, start, end, flags)
+ override fun createFragment(position: Int): Fragment {
+ return when (position) {
+ 0 -> AboutFragment.newInstance()
+ 1 -> LibsBuilder().supportFragment()
+ 2 -> PrivacyPolicyFragment.newInstance()
+ else -> throw IllegalStateException()
+ }
}
- setText(builder)
- linksClickable = true
- movementMethod = LinkMovementMethod.getInstance()
+ fun title(position: Int): CharSequence {
+ return when (position) {
+ 0 -> "About"
+ 1 -> activity.getString(R.string.title_licenses)
+ 2 -> activity.getString(R.string.about_privacy_policy)
+ else -> throw IllegalStateException()
+ }
+ }
}
diff --git a/feature/about/src/main/kotlin/app/pachli/feature/about/AboutFragment.kt b/feature/about/src/main/kotlin/app/pachli/feature/about/AboutFragment.kt
new file mode 100644
index 000000000..9021618b9
--- /dev/null
+++ b/feature/about/src/main/kotlin/app/pachli/feature/about/AboutFragment.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2024 Pachli Association
+ *
+ * This file is a part of Pachli.
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with Pachli; if not,
+ * see .
+ */
+
+package app.pachli.feature.about
+
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.os.Build
+import android.os.Bundle
+import android.text.SpannableString
+import android.text.SpannableStringBuilder
+import android.text.method.LinkMovementMethod
+import android.text.style.URLSpan
+import android.text.util.Linkify
+import android.view.View
+import android.widget.TextView
+import android.widget.Toast
+import androidx.annotation.StringRes
+import androidx.core.content.ContextCompat.getSystemService
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
+import app.pachli.core.activity.BottomSheetActivity
+import app.pachli.core.common.extensions.hide
+import app.pachli.core.common.extensions.show
+import app.pachli.core.common.extensions.viewBinding
+import app.pachli.core.common.util.versionName
+import app.pachli.core.ui.NoUnderlineURLSpan
+import app.pachli.feature.about.databinding.FragmentAboutBinding
+import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
+
+@AndroidEntryPoint
+class AboutFragment : Fragment(R.layout.fragment_about) {
+ private val viewModel: AboutFragmentViewModel by viewModels()
+
+ private val binding by viewBinding(FragmentAboutBinding::bind)
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ val version = getString(
+ R.string.about_app_version,
+ getString(
+ R.string.app_name,
+ ),
+ versionName(requireContext()),
+ )
+
+ binding.versionTextView.text = version
+
+ val deviceInfo = getString(
+ R.string.about_device_info,
+ Build.MANUFACTURER,
+ Build.MODEL,
+ Build.VERSION.RELEASE,
+ Build.VERSION.SDK_INT,
+ )
+ binding.deviceInfo.text = deviceInfo
+
+ lifecycleScope.launch {
+ viewModel.accountInfo.collect {
+ binding.accountInfo.text = it
+ binding.accountInfoTitle.show()
+ binding.accountInfo.show()
+ binding.copyDeviceInfo.show()
+ }
+ }
+
+ if (BuildConfig.CUSTOM_INSTANCE.isBlank()) {
+ binding.aboutPoweredBy.hide()
+ }
+
+ binding.aboutLicenseInfoTextView.setClickableTextWithoutUnderlines(R.string.about_pachli_license)
+ binding.aboutWebsiteInfoTextView.setClickableTextWithoutUnderlines(R.string.about_project_site)
+ binding.aboutBugsFeaturesInfoTextView.setClickableTextWithoutUnderlines(R.string.about_bug_feature_request_site)
+
+ binding.appProfileButton.setOnClickListener {
+ (activity as? BottomSheetActivity)?.viewUrl(BuildConfig.SUPPORT_ACCOUNT_URL)
+ }
+
+ binding.copyDeviceInfo.setOnClickListener {
+ val text = "$version\n\nDevice:\n\n$deviceInfo\n\nAccount:\n\n${binding.accountInfo.text}"
+ val clipboard = getSystemService(requireContext(), ClipboardManager::class.java) as ClipboardManager
+ val clip = ClipData.newPlainText("Pachli version information", text)
+ clipboard.setPrimaryClip(clip)
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) {
+ Toast.makeText(
+ requireContext(),
+ getString(R.string.about_copied),
+ Toast.LENGTH_SHORT,
+ ).show()
+ }
+ }
+ }
+
+ companion object {
+ fun newInstance() = AboutFragment()
+ }
+}
+
+internal fun TextView.setClickableTextWithoutUnderlines(@StringRes textId: Int) {
+ val text = SpannableString(context.getText(textId))
+
+ Linkify.addLinks(text, Linkify.WEB_URLS)
+
+ val builder = SpannableStringBuilder(text)
+ val urlSpans = text.getSpans(0, text.length, URLSpan::class.java)
+ for (span in urlSpans) {
+ val start = builder.getSpanStart(span)
+ val end = builder.getSpanEnd(span)
+ val flags = builder.getSpanFlags(span)
+
+ val customSpan = NoUnderlineURLSpan(span.url)
+
+ builder.removeSpan(span)
+ builder.setSpan(customSpan, start, end, flags)
+ }
+
+ setText(builder)
+ linksClickable = true
+ movementMethod = LinkMovementMethod.getInstance()
+}
diff --git a/feature/about/src/main/kotlin/app/pachli/feature/about/AboutFragmentViewModel.kt b/feature/about/src/main/kotlin/app/pachli/feature/about/AboutFragmentViewModel.kt
new file mode 100644
index 000000000..ae0a90aa7
--- /dev/null
+++ b/feature/about/src/main/kotlin/app/pachli/feature/about/AboutFragmentViewModel.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2024 Pachli Association
+ *
+ * This file is a part of Pachli.
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with Pachli; if not,
+ * see .
+ */
+
+package app.pachli.feature.about
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.viewModelScope
+import app.pachli.core.accounts.AccountManager
+import app.pachli.core.data.repository.InstanceInfoRepository
+import dagger.hilt.android.lifecycle.HiltViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.launch
+
+@HiltViewModel
+class AboutFragmentViewModel @Inject constructor(
+ private val application: Application,
+ private val accountManager: AccountManager,
+ private val instanceInfoRepository: InstanceInfoRepository,
+) : AndroidViewModel(application) {
+ private val _accountInfo = MutableSharedFlow()
+ val accountInfo: Flow = _accountInfo.asSharedFlow()
+
+ init {
+ viewModelScope.launch {
+ accountManager.activeAccount?.let { account ->
+ val instanceInfo = instanceInfoRepository.getInstanceInfo()
+ _accountInfo.emit(
+ application.getString(
+ R.string.about_account_info,
+ account.username,
+ account.domain,
+ instanceInfo.version,
+ ),
+ )
+ }
+ }
+ }
+}
diff --git a/feature/about/src/main/kotlin/app/pachli/feature/about/LicenseActivity.kt b/feature/about/src/main/kotlin/app/pachli/feature/about/LicenseActivity.kt
deleted file mode 100644
index 647689a66..000000000
--- a/feature/about/src/main/kotlin/app/pachli/feature/about/LicenseActivity.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2023 Pachli Association
- *
- * This file is a part of Pachli.
- *
- * This program is free software; you can redistribute it and/or modify it under the terms of the
- * GNU General Public License as published by the Free Software Foundation; either version 3 of the
- * License, or (at your option) any later version.
- *
- * Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
- * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
- * Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with Pachli; if not,
- * see .
- */
-
-package app.pachli.feature.about
-
-import android.os.Bundle
-import androidx.fragment.app.commit
-import app.pachli.core.activity.BaseActivity
-import app.pachli.feature.about.databinding.ActivityLicenseBinding
-import com.mikepenz.aboutlibraries.LibsBuilder
-import dagger.hilt.android.AndroidEntryPoint
-
-@AndroidEntryPoint
-class LicenseActivity : BaseActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- val binding = ActivityLicenseBinding.inflate(layoutInflater)
- setContentView(binding.root)
-
- setSupportActionBar(binding.includedToolbar.toolbar)
- supportActionBar?.run {
- setDisplayHomeAsUpEnabled(true)
- setDisplayShowHomeEnabled(true)
- }
-
- setTitle(R.string.title_licenses)
-
- val fragment = LibsBuilder().supportFragment()
- if (savedInstanceState == null) {
- supportFragmentManager.commit {
- setReorderingAllowed(true)
- add(R.id.fragment_licenses, fragment)
- }
- }
- }
-}
diff --git a/feature/about/src/main/kotlin/app/pachli/feature/about/PrivacyPolicyActivity.kt b/feature/about/src/main/kotlin/app/pachli/feature/about/PrivacyPolicyFragment.kt
similarity index 65%
rename from feature/about/src/main/kotlin/app/pachli/feature/about/PrivacyPolicyActivity.kt
rename to feature/about/src/main/kotlin/app/pachli/feature/about/PrivacyPolicyFragment.kt
index e19b73179..3a9b4a171 100644
--- a/feature/about/src/main/kotlin/app/pachli/feature/about/PrivacyPolicyActivity.kt
+++ b/feature/about/src/main/kotlin/app/pachli/feature/about/PrivacyPolicyFragment.kt
@@ -19,17 +19,23 @@ package app.pachli.feature.about
import android.os.Bundle
import android.util.Base64
-import app.pachli.core.activity.BaseActivity
-import app.pachli.feature.about.databinding.ActivityPrivacyPolicyBinding
+import android.view.View
+import androidx.fragment.app.Fragment
+import app.pachli.core.common.extensions.viewBinding
+import app.pachli.feature.about.databinding.FragmentPrivacyPolicyBinding
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
-class PrivacyPolicyActivity : BaseActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- val binding = ActivityPrivacyPolicyBinding.inflate(layoutInflater)
- setContentView(binding.root)
+class PrivacyPolicyFragment : Fragment(R.layout.fragment_privacy_policy) {
+ private val binding by viewBinding(FragmentPrivacyPolicyBinding::bind)
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
val encoded = Base64.encodeToString(markdownR.html.PRIVACY_md.toByteArray(), Base64.NO_PADDING)
binding.policy.loadData(encoded, "text/html", "base64")
}
+
+ companion object {
+ fun newInstance() = PrivacyPolicyFragment()
+ }
}
diff --git a/feature/about/src/main/res/layout/activity_about.xml b/feature/about/src/main/res/layout/activity_about.xml
index 9e5d1ddd6..7a5e98cbe 100644
--- a/feature/about/src/main/res/layout/activity_about.xml
+++ b/feature/about/src/main/res/layout/activity_about.xml
@@ -1,4 +1,21 @@
+
+
-
-
-
+ android:layout_height="wrap_content"
+ android:elevation="@dimen/actionbar_elevation"
+ app:elevationOverlayEnabled="false">
-
+
+
+ app:tabGravity="fill"
+ app:tabMode="fixed" />
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/feature/about/src/main/res/layout/activity_license.xml b/feature/about/src/main/res/layout/activity_license.xml
deleted file mode 100644
index 1dc64ebb6..000000000
--- a/feature/about/src/main/res/layout/activity_license.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/feature/about/src/main/res/layout/fragment_about.xml b/feature/about/src/main/res/layout/fragment_about.xml
new file mode 100644
index 000000000..a94c9907f
--- /dev/null
+++ b/feature/about/src/main/res/layout/fragment_about.xml
@@ -0,0 +1,160 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/feature/about/src/main/res/layout/activity_privacy_policy.xml b/feature/about/src/main/res/layout/fragment_privacy_policy.xml
similarity index 84%
rename from feature/about/src/main/res/layout/activity_privacy_policy.xml
rename to feature/about/src/main/res/layout/fragment_privacy_policy.xml
index c0e569acd..c232f310d 100644
--- a/feature/about/src/main/res/layout/activity_privacy_policy.xml
+++ b/feature/about/src/main/res/layout/fragment_privacy_policy.xml
@@ -18,4 +18,6 @@
+ android:layout_height="match_parent"
+ android:layout_marginStart="@dimen/text_content_margin"
+ android:layout_marginEnd="@dimen/text_content_margin" />