change: Display "About" information in tabs (#420)
Previously, `AboutActivity` had buttons and links to show the privacy policy and licenses of dependencies. Change this to a selection of fragments in tabs, one tab each for: - General "About" information - Licenses - Privacy Policy The information shown hasn't changed, but this lays the groundwork for including additional tabs in the future for information like server rules, detected capabilities, or troubleshooting information.
This commit is contained in:
parent
7c5181d5c2
commit
41c702fc1b
|
@ -144,8 +144,6 @@
|
||||||
android:resource="@xml/searchable" />
|
android:resource="@xml/searchable" />
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".ListsActivity" />
|
<activity android:name=".ListsActivity" />
|
||||||
<activity android:name=".feature.about.LicenseActivity" />
|
|
||||||
<activity android:name=".feature.about.PrivacyPolicyActivity" />
|
|
||||||
<activity android:name=".components.filters.FiltersActivity" />
|
<activity android:name=".components.filters.FiltersActivity" />
|
||||||
<activity android:name=".components.trending.TrendingActivity" />
|
<activity android:name=".components.trending.TrendingActivity" />
|
||||||
<activity android:name=".components.followedtags.FollowedTagsActivity" />
|
<activity android:name=".components.followedtags.FollowedTagsActivity" />
|
||||||
|
|
|
@ -97,6 +97,7 @@ import app.pachli.core.navigation.TrendingActivityIntent
|
||||||
import app.pachli.core.network.model.Account
|
import app.pachli.core.network.model.Account
|
||||||
import app.pachli.core.network.model.Notification
|
import app.pachli.core.network.model.Notification
|
||||||
import app.pachli.core.preferences.PrefKeys
|
import app.pachli.core.preferences.PrefKeys
|
||||||
|
import app.pachli.core.ui.reduceSwipeSensitivity
|
||||||
import app.pachli.databinding.ActivityMainBinding
|
import app.pachli.databinding.ActivityMainBinding
|
||||||
import app.pachli.db.DraftsAlert
|
import app.pachli.db.DraftsAlert
|
||||||
import app.pachli.interfaces.ActionButtonActivity
|
import app.pachli.interfaces.ActionButtonActivity
|
||||||
|
@ -110,7 +111,6 @@ import app.pachli.usecase.LogoutUsecase
|
||||||
import app.pachli.util.await
|
import app.pachli.util.await
|
||||||
import app.pachli.util.deleteStaleCachedMedia
|
import app.pachli.util.deleteStaleCachedMedia
|
||||||
import app.pachli.util.getDimension
|
import app.pachli.util.getDimension
|
||||||
import app.pachli.util.reduceSwipeSensitivity
|
|
||||||
import app.pachli.util.unsafeLazy
|
import app.pachli.util.unsafeLazy
|
||||||
import app.pachli.util.updateShortcut
|
import app.pachli.util.updateShortcut
|
||||||
import at.connyduck.calladapter.networkresult.fold
|
import at.connyduck.calladapter.networkresult.fold
|
||||||
|
|
|
@ -77,6 +77,7 @@ import app.pachli.core.network.model.Relationship
|
||||||
import app.pachli.core.network.parseAsMastodonHtml
|
import app.pachli.core.network.parseAsMastodonHtml
|
||||||
import app.pachli.core.preferences.AppTheme
|
import app.pachli.core.preferences.AppTheme
|
||||||
import app.pachli.core.preferences.PrefKeys
|
import app.pachli.core.preferences.PrefKeys
|
||||||
|
import app.pachli.core.ui.reduceSwipeSensitivity
|
||||||
import app.pachli.databinding.ActivityAccountBinding
|
import app.pachli.databinding.ActivityAccountBinding
|
||||||
import app.pachli.db.DraftsAlert
|
import app.pachli.db.DraftsAlert
|
||||||
import app.pachli.interfaces.ActionButtonActivity
|
import app.pachli.interfaces.ActionButtonActivity
|
||||||
|
@ -86,7 +87,6 @@ import app.pachli.util.Error
|
||||||
import app.pachli.util.Loading
|
import app.pachli.util.Loading
|
||||||
import app.pachli.util.Success
|
import app.pachli.util.Success
|
||||||
import app.pachli.util.getDomain
|
import app.pachli.util.getDomain
|
||||||
import app.pachli.util.reduceSwipeSensitivity
|
|
||||||
import app.pachli.util.setClickableText
|
import app.pachli.util.setClickableText
|
||||||
import app.pachli.view.showMuteAccountDialog
|
import app.pachli.view.showMuteAccountDialog
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
|
|
|
@ -31,8 +31,8 @@ import app.pachli.components.search.adapter.SearchPagerAdapter
|
||||||
import app.pachli.core.activity.BottomSheetActivity
|
import app.pachli.core.activity.BottomSheetActivity
|
||||||
import app.pachli.core.common.extensions.viewBinding
|
import app.pachli.core.common.extensions.viewBinding
|
||||||
import app.pachli.core.preferences.PrefKeys
|
import app.pachli.core.preferences.PrefKeys
|
||||||
|
import app.pachli.core.ui.reduceSwipeSensitivity
|
||||||
import app.pachli.databinding.ActivitySearchBinding
|
import app.pachli.databinding.ActivitySearchBinding
|
||||||
import app.pachli.util.reduceSwipeSensitivity
|
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
|
|
|
@ -32,9 +32,9 @@ import app.pachli.components.timeline.TimelineFragment
|
||||||
import app.pachli.core.activity.BottomSheetActivity
|
import app.pachli.core.activity.BottomSheetActivity
|
||||||
import app.pachli.core.common.extensions.viewBinding
|
import app.pachli.core.common.extensions.viewBinding
|
||||||
import app.pachli.core.network.model.TimelineKind
|
import app.pachli.core.network.model.TimelineKind
|
||||||
|
import app.pachli.core.ui.reduceSwipeSensitivity
|
||||||
import app.pachli.databinding.ActivityTrendingBinding
|
import app.pachli.databinding.ActivityTrendingBinding
|
||||||
import app.pachli.interfaces.AppBarLayoutHost
|
import app.pachli.interfaces.AppBarLayoutHost
|
||||||
import app.pachli.util.reduceSwipeSensitivity
|
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
|
@ -17,43 +17,6 @@
|
||||||
package app.pachli.util
|
package app.pachli.util
|
||||||
|
|
||||||
import android.widget.TextView
|
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
|
* TextViews with an ancestor RecyclerView can forget that they are selectable. Toggling
|
||||||
|
|
|
@ -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() {
|
class ListActivityIntent(context: Context) : Intent() {
|
||||||
init {
|
init {
|
||||||
setClassName(context, QuadrantConstants.LISTS_ACTIVITY)
|
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() {
|
class ScheduledStatusActivityIntent(context: Context) : Intent() {
|
||||||
init {
|
init {
|
||||||
setClassName(context, QuadrantConstants.SCHEDULED_STATUS_ACTIVITY)
|
setClassName(context, QuadrantConstants.SCHEDULED_STATUS_ACTIVITY)
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,6 +47,7 @@ dependencies {
|
||||||
implementation(projects.core.activity)
|
implementation(projects.core.activity)
|
||||||
implementation(projects.core.common)
|
implementation(projects.core.common)
|
||||||
implementation(projects.core.data)
|
implementation(projects.core.data)
|
||||||
|
implementation(projects.core.designsystem)
|
||||||
implementation(projects.core.navigation)
|
implementation(projects.core.navigation)
|
||||||
implementation(projects.core.ui)
|
implementation(projects.core.ui)
|
||||||
|
|
||||||
|
@ -57,4 +58,7 @@ dependencies {
|
||||||
implementation(libs.bundles.androidx)
|
implementation(libs.bundles.androidx)
|
||||||
|
|
||||||
implementation(libs.bundles.aboutlibraries)
|
implementation(libs.bundles.aboutlibraries)
|
||||||
|
|
||||||
|
// For FixedSizeDrawable
|
||||||
|
implementation(libs.glide.core)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,137 +17,83 @@
|
||||||
|
|
||||||
package app.pachli.feature.about
|
package app.pachli.feature.about
|
||||||
|
|
||||||
import android.content.ClipData
|
import android.annotation.SuppressLint
|
||||||
import android.content.ClipboardManager
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.SpannableString
|
import androidx.activity.OnBackPressedCallback
|
||||||
import android.text.SpannableStringBuilder
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
import android.text.method.LinkMovementMethod
|
import androidx.core.view.MenuProvider
|
||||||
import android.text.style.URLSpan
|
import androidx.fragment.app.Fragment
|
||||||
import android.text.util.Linkify
|
import androidx.fragment.app.FragmentActivity
|
||||||
import android.widget.TextView
|
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import app.pachli.core.activity.BottomSheetActivity
|
import app.pachli.core.activity.BottomSheetActivity
|
||||||
import app.pachli.core.common.extensions.hide
|
import app.pachli.core.designsystem.R as DR
|
||||||
import app.pachli.core.common.extensions.show
|
import app.pachli.core.ui.reduceSwipeSensitivity
|
||||||
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.feature.about.databinding.ActivityAboutBinding
|
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 dagger.hilt.android.AndroidEntryPoint
|
||||||
import javax.inject.Inject
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class AboutActivity : BottomSheetActivity() {
|
class AboutActivity : BottomSheetActivity(), MenuProvider {
|
||||||
@Inject
|
|
||||||
lateinit var instanceInfoRepository: InstanceInfoRepository
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
val binding = ActivityAboutBinding.inflate(layoutInflater)
|
val binding = ActivityAboutBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
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 {
|
supportActionBar?.run {
|
||||||
setDisplayHomeAsUpEnabled(true)
|
setTitle(R.string.app_name)
|
||||||
setDisplayShowHomeEnabled(true)
|
setDisplayShowHomeEnabled(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
setTitle(R.string.about_title_activity)
|
val adapter = AboutFragmentAdapter(this)
|
||||||
|
binding.pager.adapter = adapter
|
||||||
|
binding.pager.reduceSwipeSensitivity()
|
||||||
|
|
||||||
binding.versionTextView.text = getString(
|
TabLayoutMediator(binding.tabLayout, binding.pager) { tab, position ->
|
||||||
R.string.about_app_version,
|
tab.text = adapter.title(position)
|
||||||
getString(
|
}.attach()
|
||||||
R.string.app_name,
|
|
||||||
),
|
onBackPressedDispatcher.addCallback(
|
||||||
versionName(this),
|
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) {
|
class AboutFragmentAdapter(val activity: FragmentActivity) : FragmentStateAdapter(activity) {
|
||||||
val text = SpannableString(context.getText(textId))
|
override fun getItemCount() = 3
|
||||||
|
|
||||||
Linkify.addLinks(text, Linkify.WEB_URLS)
|
override fun createFragment(position: Int): Fragment {
|
||||||
|
return when (position) {
|
||||||
val builder = SpannableStringBuilder(text)
|
0 -> AboutFragment.newInstance()
|
||||||
val urlSpans = text.getSpans(0, text.length, URLSpan::class.java)
|
1 -> LibsBuilder().supportFragment()
|
||||||
for (span in urlSpans) {
|
2 -> PrivacyPolicyFragment.newInstance()
|
||||||
val start = builder.getSpanStart(span)
|
else -> throw IllegalStateException()
|
||||||
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)
|
fun title(position: Int): CharSequence {
|
||||||
linksClickable = true
|
return when (position) {
|
||||||
movementMethod = LinkMovementMethod.getInstance()
|
0 -> "About"
|
||||||
|
1 -> activity.getString(R.string.title_licenses)
|
||||||
|
2 -> activity.getString(R.string.about_privacy_policy)
|
||||||
|
else -> throw IllegalStateException()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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<String>()
|
||||||
|
val accountInfo: Flow<String> = _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,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,17 +19,23 @@ package app.pachli.feature.about
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import app.pachli.core.activity.BaseActivity
|
import android.view.View
|
||||||
import app.pachli.feature.about.databinding.ActivityPrivacyPolicyBinding
|
import androidx.fragment.app.Fragment
|
||||||
|
import app.pachli.core.common.extensions.viewBinding
|
||||||
|
import app.pachli.feature.about.databinding.FragmentPrivacyPolicyBinding
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class PrivacyPolicyActivity : BaseActivity() {
|
class PrivacyPolicyFragment : Fragment(R.layout.fragment_privacy_policy) {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
private val binding by viewBinding(FragmentPrivacyPolicyBinding::bind)
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
val binding = ActivityPrivacyPolicyBinding.inflate(layoutInflater)
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
setContentView(binding.root)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
val encoded = Base64.encodeToString(markdownR.html.PRIVACY_md.toByteArray(), Base64.NO_PADDING)
|
val encoded = Base64.encodeToString(markdownR.html.PRIVACY_md.toByteArray(), Base64.NO_PADDING)
|
||||||
binding.policy.loadData(encoded, "text/html", "base64")
|
binding.policy.loadData(encoded, "text/html", "base64")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun newInstance() = PrivacyPolicyFragment()
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,21 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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 <http://www.gnu.org/licenses>.
|
||||||
|
-->
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
|
||||||
|
@ -6,215 +23,32 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context="app.pachli.feature.about.AboutActivity">
|
tools:context="app.pachli.feature.about.AboutActivity">
|
||||||
|
|
||||||
<include
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/includedToolbar"
|
android:id="@+id/appBar"
|
||||||
layout="@layout/toolbar_basic" />
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
android:elevation="@dimen/actionbar_elevation"
|
||||||
|
app:elevationOverlayEnabled="false">
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:layout_scrollFlags="scroll|enterAlways" />
|
||||||
|
|
||||||
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
android:id="@+id/tab_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
app:tabGravity="fill"
|
||||||
android:textDirection="anyRtl">
|
app:tabMode="fixed" />
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.viewpager2.widget.ViewPager2
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/pager"
|
||||||
android:layout_height="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:focusableInTouchMode="true"
|
android:layout_height="match_parent"
|
||||||
android:gravity="center"
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingTop="16dp"
|
|
||||||
android:paddingBottom="16dp">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/logo"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:src="@mipmap/ic_launcher"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:ignore="ContentDescription" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/versionTextView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Large"
|
|
||||||
android:textColor="?colorOnSurface"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
android:textStyle="normal"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/logo"
|
|
||||||
tools:text="Pachli Test" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/deviceInfoTitle"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:layout_marginStart="@dimen/text_content_margin"
|
|
||||||
android:layout_marginEnd="@dimen/text_content_margin"
|
|
||||||
android:lineSpacingMultiplier="1.1"
|
|
||||||
android:text="@string/about_device_info_title"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/versionTextView"
|
|
||||||
tools:text="Your device" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/deviceInfo"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:lineSpacingMultiplier="1.1"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/deviceInfoTitle"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/deviceInfoTitle"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/deviceInfoTitle" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/accountInfoTitle"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:lineSpacingMultiplier="1.1"
|
|
||||||
android:text="@string/about_account_info_title"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/copyDeviceInfo"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/deviceInfo"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/deviceInfo"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/accountInfo"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:lineSpacingMultiplier="1.1"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/accountInfoTitle"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/accountInfoTitle"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/accountInfoTitle"
|
|
||||||
tools:text="\@Pachli@mastodon.social\nVersion: xxx"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/copyDeviceInfo"
|
|
||||||
style="@style/AppImageButton"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:contentDescription="@string/about_copy"
|
|
||||||
android:layout_marginEnd="@dimen/text_content_margin"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/accountInfo"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:srcCompat="@drawable/ic_content_copy_24" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/about_powered_by"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="@string/about_powered_by_pachli"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/copyDeviceInfo"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/deviceInfo"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/accountInfo" />
|
|
||||||
|
|
||||||
<app.pachli.core.ui.ClickableSpanTextView
|
|
||||||
android:id="@+id/aboutLicenseInfoTextView"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:hyphenationFrequency="full"
|
|
||||||
android:lineSpacingMultiplier="1.2"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/copyDeviceInfo"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/accountInfo"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/about_powered_by"
|
|
||||||
tools:text="@string/about_pachli_license" />
|
|
||||||
|
|
||||||
<app.pachli.core.ui.ClickableSpanTextView
|
|
||||||
android:id="@+id/aboutWebsiteInfoTextView"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:lineSpacingMultiplier="1.2"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/aboutLicenseInfoTextView"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/aboutLicenseInfoTextView"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/aboutLicenseInfoTextView"
|
|
||||||
tools:text="@string/about_project_site" />
|
|
||||||
|
|
||||||
<app.pachli.core.ui.ClickableSpanTextView
|
|
||||||
android:id="@+id/aboutBugsFeaturesInfoTextView"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:lineSpacingMultiplier="1.2"
|
|
||||||
android:text="@string/about_bug_feature_request_site"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/aboutWebsiteInfoTextView"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/aboutWebsiteInfoTextView"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/aboutWebsiteInfoTextView" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/aboutPrivacyPolicyTextView"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:lineSpacingMultiplier="1.2"
|
|
||||||
android:text="@string/about_privacy_policy"
|
|
||||||
android:textColor="?android:attr/textColorLink"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/aboutWebsiteInfoTextView"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/aboutWebsiteInfoTextView"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/aboutBugsFeaturesInfoTextView" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/appProfileButton"
|
|
||||||
style="@style/AppButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:lineSpacingMultiplier="1.2"
|
|
||||||
android:maxWidth="320dp"
|
|
||||||
android:text="@string/about_pachli_account"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:layout_marginEnd="@dimen/text_content_margin"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/aboutLicensesButton"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/aboutBugsFeaturesInfoTextView"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/aboutPrivacyPolicyTextView" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/aboutLicensesButton"
|
|
||||||
style="@style/AppButton.Outlined"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:lineSpacingMultiplier="1.2"
|
|
||||||
android:maxWidth="320dp"
|
|
||||||
android:text="@string/title_licenses"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:layout_marginEnd="@dimen/text_content_margin"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/appProfileButton"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/appProfileButton" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<include layout="@layout/item_status_bottom_sheet" />
|
<include layout="@layout/item_status_bottom_sheet" />
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
tools:context="app.pachli.feature.about.LicenseActivity">
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/includedToolbar"
|
|
||||||
layout="@layout/toolbar_basic" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:lineSpacingMultiplier="1.1"
|
|
||||||
android:text="@string/license_description" />
|
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
|
||||||
android:id="@+id/fragment_licenses"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
</LinearLayout>
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:textDirection="anyRtl"
|
||||||
|
tools:context="app.pachli.feature.about.AboutActivity">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/versionTextView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
android:textStyle="normal"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="Pachli Test" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/deviceInfoTitle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginStart="@dimen/text_content_margin"
|
||||||
|
android:layout_marginEnd="@dimen/text_content_margin"
|
||||||
|
android:lineSpacingMultiplier="1.1"
|
||||||
|
android:text="@string/about_device_info_title"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/versionTextView"
|
||||||
|
tools:text="Your device" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/deviceInfo"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:lineSpacingMultiplier="1.1"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/deviceInfoTitle"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/deviceInfoTitle"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/deviceInfoTitle" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/accountInfoTitle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:lineSpacingMultiplier="1.1"
|
||||||
|
android:text="@string/about_account_info_title"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/copyDeviceInfo"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/deviceInfo"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/deviceInfo"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/accountInfo"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:lineSpacingMultiplier="1.1"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/accountInfoTitle"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/accountInfoTitle"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/accountInfoTitle"
|
||||||
|
tools:text="\@Pachli@mastodon.social\nVersion: xxx"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/copyDeviceInfo"
|
||||||
|
style="@style/AppImageButton"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:contentDescription="@string/about_copy"
|
||||||
|
android:layout_marginEnd="@dimen/text_content_margin"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/accountInfo"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:srcCompat="@drawable/ic_content_copy_24" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/about_powered_by"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="@string/about_powered_by_pachli"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/copyDeviceInfo"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/deviceInfo"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/accountInfo" />
|
||||||
|
|
||||||
|
<app.pachli.core.ui.ClickableSpanTextView
|
||||||
|
android:id="@+id/aboutLicenseInfoTextView"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:hyphenationFrequency="full"
|
||||||
|
android:lineSpacingMultiplier="1.2"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/copyDeviceInfo"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/accountInfo"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/about_powered_by"
|
||||||
|
tools:text="@string/about_pachli_license" />
|
||||||
|
|
||||||
|
<app.pachli.core.ui.ClickableSpanTextView
|
||||||
|
android:id="@+id/aboutWebsiteInfoTextView"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:lineSpacingMultiplier="1.2"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/aboutLicenseInfoTextView"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/aboutLicenseInfoTextView"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/aboutLicenseInfoTextView"
|
||||||
|
tools:text="@string/about_project_site" />
|
||||||
|
|
||||||
|
<app.pachli.core.ui.ClickableSpanTextView
|
||||||
|
android:id="@+id/aboutBugsFeaturesInfoTextView"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:lineSpacingMultiplier="1.2"
|
||||||
|
android:text="@string/about_bug_feature_request_site"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/aboutWebsiteInfoTextView"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/aboutWebsiteInfoTextView"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/aboutWebsiteInfoTextView" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/appProfileButton"
|
||||||
|
style="@style/AppButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:lineSpacingMultiplier="1.2"
|
||||||
|
android:maxWidth="320dp"
|
||||||
|
android:text="@string/about_pachli_account"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:layout_marginEnd="@dimen/text_content_margin"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/aboutBugsFeaturesInfoTextView"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/aboutBugsFeaturesInfoTextView" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
|
@ -18,4 +18,6 @@
|
||||||
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
|
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:id="@+id/policy"
|
android:id="@+id/policy"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="@dimen/text_content_margin"
|
||||||
|
android:layout_marginEnd="@dimen/text_content_margin" />
|
Loading…
Reference in New Issue