Remove: Old Repository classes and layouts

This commit is contained in:
machiav3lli 2022-01-28 01:15:10 +01:00
parent de8952f3ab
commit 4887dc1e61
8 changed files with 0 additions and 1026 deletions

View File

@ -1,487 +0,0 @@
package com.looker.droidify.screen
import android.content.ClipboardManager
import android.content.Context
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.net.Uri
import android.os.Bundle
import android.text.Selection
import android.util.Base64
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.looker.droidify.R
import com.looker.droidify.database.entity.Repository
import com.looker.droidify.databinding.EditRepositoryBinding
import com.looker.droidify.network.Downloader
import com.looker.droidify.service.Connection
import com.looker.droidify.service.SyncService
import com.looker.droidify.utility.RxUtils
import com.looker.droidify.utility.Utils
import com.looker.droidify.utility.extension.resources.getColorFromAttr
import com.looker.droidify.utility.extension.text.nullIfEmpty
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.schedulers.Schedulers
import kotlinx.coroutines.launch
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import java.net.URI
import java.net.URL
import java.nio.charset.Charset
import java.util.*
import kotlin.math.min
class EditRepositoryFragment() : ScreenFragment() {
private var _editRepositoryBinding: EditRepositoryBinding? = null
private val editRepositoryBinding get() = _editRepositoryBinding!!
companion object {
private const val EXTRA_REPOSITORY_ID = "repositoryId"
private val checkPaths = listOf("", "fdroid/repo", "repo")
}
constructor(repositoryId: Long?) : this() {
arguments = Bundle().apply {
repositoryId?.let { putLong(EXTRA_REPOSITORY_ID, it) }
}
}
private class Layout(view: EditRepositoryBinding) {
val address = view.address
val addressMirror = view.addressMirror
val fingerprint = view.fingerprint
val username = view.username
val password = view.password
val overlay = view.overlay
val skip = view.skip
}
private val repositoryId: Long?
get() = requireArguments().let {
if (it.containsKey(EXTRA_REPOSITORY_ID))
it.getLong(EXTRA_REPOSITORY_ID) else null
}
private lateinit var errorColorFilter: PorterDuffColorFilter
private var saveMenuItem: MenuItem? = null
private var layout: Layout? = null
private val syncConnection = Connection(SyncService::class.java)
private var checkDisposable: Disposable? = null
private var takenAddresses = emptySet<String>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_editRepositoryBinding = EditRepositoryBinding.inflate(layoutInflater)
syncConnection.bind(requireContext())
screenActivity.onToolbarCreated(toolbar)
collapsingToolbar.title =
getString(if (repositoryId != null) R.string.edit_repository else R.string.add_repository)
saveMenuItem = toolbar.menu.add(R.string.save)
.setIcon(Utils.getToolbarIcon(toolbar.context, R.drawable.ic_save))
.setEnabled(false)
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS)
.setOnMenuItemClickListener {
onSaveRepositoryClick(true)
true
}
val content = fragmentBinding.fragmentContent
errorColorFilter = PorterDuffColorFilter(
content.context
.getColorFromAttr(R.attr.colorError).defaultColor, PorterDuff.Mode.SRC_IN
)
content.addView(editRepositoryBinding.root)
val layout = Layout(editRepositoryBinding)
this.layout = layout
val validChar: (Char) -> Boolean =
{ it in '0'..'9' || it in 'a'..'f' || it in 'A'..'F' }
layout.fingerprint.doAfterTextChanged { text ->
fun logicalPosition(text: String, position: Int): Int {
return if (position > 0) text.asSequence().take(position)
.count(validChar) else position
}
fun realPosition(text: String, position: Int): Int {
return if (position > 0) {
var left = position
val index = text.indexOfFirst {
validChar(it) && run {
left -= 1
left <= 0
}
}
if (index >= 0) min(index + 1, text.length) else text.length
} else {
position
}
}
val inputString = text.toString()
val outputString = inputString.uppercase(Locale.US)
.filter(validChar).windowed(2, 2, true).take(32).joinToString(separator = " ")
if (inputString != outputString) {
val inputStart = logicalPosition(inputString, Selection.getSelectionStart(text))
val inputEnd = logicalPosition(inputString, Selection.getSelectionEnd(text))
text?.replace(0, text.length, outputString)
Selection.setSelection(
text,
realPosition(outputString, inputStart),
realPosition(outputString, inputEnd)
)
}
}
if (savedInstanceState == null) {
val repository = repositoryId?.let { screenActivity.db.repositoryDao.get(it) }
if (repository == null) {
val clipboardManager =
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val text = clipboardManager.primaryClip
?.let { if (it.itemCount > 0) it else null }
?.getItemAt(0)?.text?.toString().orEmpty()
val (addressText, fingerprintText) = try {
val uri = Uri.parse(URL(text).toString())
val fingerprintText = uri.getQueryParameter("fingerprint")?.nullIfEmpty()
?: uri.getQueryParameter("FINGERPRINT")?.nullIfEmpty()
Pair(
uri.buildUpon().path(uri.path?.pathCropped)
.query(null).fragment(null).build().toString(), fingerprintText
)
} catch (e: Exception) {
Pair(null, null)
}
layout.address.setText(addressText)
layout.fingerprint.setText(fingerprintText)
} else {
layout.address.setText(repository.address)
val mirrors = repository.mirrors.map { it.withoutKnownPath }
if (mirrors.isNotEmpty()) {
layout.addressMirror.visibility = View.VISIBLE
layout.address.apply {
setPaddingRelative(
paddingStart, paddingTop,
paddingEnd + layout.addressMirror.layoutParams.width, paddingBottom
)
}
layout.addressMirror.setOnClickListener {
SelectMirrorDialog(mirrors)
.show(childFragmentManager, SelectMirrorDialog::class.java.name)
}
}
layout.fingerprint.setText(repository.fingerprint)
val (usernameText, passwordText) = repository.authentication.nullIfEmpty()
?.let { if (it.startsWith("Basic ")) it.substring(6) else null }
?.let {
try {
Base64.decode(it, Base64.NO_WRAP).toString(Charset.defaultCharset())
} catch (e: Exception) {
e.printStackTrace()
null
}
}
?.let {
val index = it.indexOf(':')
if (index >= 0) Pair(
it.substring(0, index),
it.substring(index + 1)
) else null
}
?: Pair(null, null)
layout.username.setText(usernameText)
layout.password.setText(passwordText)
}
}
layout.address.doAfterTextChanged { invalidateAddress() }
layout.fingerprint.doAfterTextChanged { invalidateFingerprint() }
layout.username.doAfterTextChanged { invalidateUsernamePassword() }
layout.password.doAfterTextChanged { invalidateUsernamePassword() }
(layout.overlay.parent as ViewGroup).layoutTransition?.setDuration(200L)
layout.overlay.background!!.apply {
mutate()
alpha = 0xcc
}
layout.skip.setOnClickListener {
if (checkDisposable != null) {
checkDisposable?.dispose()
checkDisposable = null
onSaveRepositoryClick(false)
}
}
lifecycleScope.launch {
val list = screenActivity.db.repositoryDao.all
takenAddresses = list.asSequence().filter { it.id != repositoryId }
.flatMap { (it.mirrors + it.address).asSequence() }
.map { it.withoutKnownPath }.toSet()
invalidateAddress()
}
invalidateAddress()
invalidateFingerprint()
invalidateUsernamePassword()
}
override fun onDestroyView() {
super.onDestroyView()
saveMenuItem = null
layout = null
syncConnection.unbind(requireContext())
checkDisposable?.dispose()
checkDisposable = null
_editRepositoryBinding = null
}
private var addressError = false
private var fingerprintError = false
private var usernamePasswordError = false
private fun invalidateAddress() {
invalidateAddress(layout!!.address.text.toString())
}
private fun invalidateAddress(addressText: String) {
val layout = layout!!
val normalizedAddress = normalizeAddress(addressText)
val addressErrorResId = if (normalizedAddress != null) {
if (normalizedAddress.withoutKnownPath in takenAddresses) {
R.string.already_exists
} else {
null
}
} else {
R.string.invalid_address
}
addressError = addressErrorResId != null
addressErrorResId?.let { layout.address.error = getString(it) }
invalidateState()
}
private fun invalidateFingerprint() {
val layout = layout!!
val fingerprint = layout.fingerprint.text.toString().replace(" ", "")
val fingerprintInvalid = fingerprint.isNotEmpty() && fingerprint.length != 64
fingerprintError = fingerprintInvalid
invalidateState()
}
private fun invalidateUsernamePassword() {
val layout = layout!!
val username = layout.username.text.toString()
val password = layout.password.text.toString()
val usernameInvalid = username.contains(':')
val usernameEmpty = username.isEmpty() && password.isNotEmpty()
val passwordEmpty = username.isNotEmpty() && password.isEmpty()
usernamePasswordError = usernameInvalid || usernameEmpty || passwordEmpty
invalidateState()
}
private fun invalidateState() {
val layout = layout!!
saveMenuItem!!.isEnabled = !addressError && !fingerprintError &&
!usernamePasswordError && checkDisposable == null
layout.apply {
sequenceOf(address, addressMirror, fingerprint, username, password)
.forEach { it.isEnabled = checkDisposable == null }
}
layout.overlay.visibility = if (checkDisposable != null) View.VISIBLE else View.GONE
}
private val String.pathCropped: String
get() {
val index = indexOfLast { it != '/' }
return if (index >= 0 && index < length - 1) substring(0, index + 1) else this
}
private val String.withoutKnownPath: String
get() {
val cropped = pathCropped
val endsWith = checkPaths.asSequence().filter { it.isNotEmpty() }
.sortedByDescending { it.length }.find { cropped.endsWith("/$it") }
return if (endsWith != null) cropped.substring(
0,
cropped.length - endsWith.length - 1
) else cropped
}
private fun normalizeAddress(address: String): String? {
val uri = try {
val uri = URI(address)
if (uri.isAbsolute) uri.normalize() else null
} catch (e: Exception) {
null
}
val path = uri?.path?.pathCropped
return if (uri != null && path != null) {
try {
URI(
uri.scheme,
uri.userInfo,
uri.host,
uri.port,
path,
uri.query,
uri.fragment
).toString()
} catch (e: Exception) {
null
}
} else {
null
}
}
private fun setMirror(address: String) {
layout?.address?.setText(address)
}
private fun onSaveRepositoryClick(check: Boolean) {
if (checkDisposable == null) {
val layout = layout!!
val address = normalizeAddress(layout.address.text.toString())!!
val fingerprint = layout.fingerprint.text.toString().replace(" ", "")
val username = layout.username.text.toString().nullIfEmpty()
val password = layout.password.text.toString().nullIfEmpty()
val paths = sequenceOf("", "fdroid/repo", "repo")
val authentication = username?.let { u ->
password
?.let { p ->
Base64.encodeToString(
"$u:$p".toByteArray(Charset.defaultCharset()),
Base64.NO_WRAP
)
}
}
?.let { "Basic $it" }.orEmpty()
if (check) {
checkDisposable = paths
.fold(Single.just("")) { oldAddressSingle, checkPath ->
oldAddressSingle
.flatMap { oldAddress ->
if (oldAddress.isEmpty()) {
val builder = Uri.parse(address).buildUpon()
.let {
if (checkPath.isEmpty()) it else it.appendEncodedPath(
checkPath
)
}
val newAddress = builder.build()
val indexAddress = builder.appendPath("index.jar").build()
RxUtils
.callSingle {
Downloader
.createCall(
Request.Builder().method("HEAD", null)
.url(indexAddress.toString().toHttpUrl()),
authentication,
null
)
}
.subscribeOn(Schedulers.io())
.map { if (it.code == 200) newAddress.toString() else "" }
} else {
Single.just(oldAddress)
}
}
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe { result, throwable ->
checkDisposable = null
throwable?.printStackTrace()
val resultAddress =
result?.let { if (it.isEmpty()) null else it } ?: address
val allow = resultAddress == address || run {
layout.address.setText(resultAddress)
invalidateAddress(resultAddress)
!addressError
}
if (allow) {
onSaveRepositoryProceedInvalidate(
resultAddress,
fingerprint,
authentication
)
} else {
invalidateState()
}
}
invalidateState()
} else {
onSaveRepositoryProceedInvalidate(address, fingerprint, authentication)
}
}
}
private fun onSaveRepositoryProceedInvalidate(
address: String,
fingerprint: String,
authentication: String,
) {
val binder = syncConnection.binder
if (binder != null) {
val repositoryId = repositoryId
if (repositoryId != null && binder.isCurrentlySyncing(repositoryId)) {
MessageDialog(MessageDialog.Message.CantEditSyncing).show(childFragmentManager)
invalidateState()
} else {
val repository = repositoryId?.let { screenActivity.db.repositoryDao.get(it) }
?.edit(address, fingerprint, authentication)
?: Repository.newRepository(address, fingerprint, authentication)
val changedRepository = screenActivity.db.repositoryDao.put(repository)
if (repositoryId == null && changedRepository.enabled) {
binder.sync(changedRepository)
}
requireActivity().onBackPressed()
}
} else {
invalidateState()
}
}
class SelectMirrorDialog() : DialogFragment() {
companion object {
private const val EXTRA_MIRRORS = "mirrors"
}
constructor(mirrors: List<String>) : this() {
arguments = Bundle().apply {
putStringArrayList(EXTRA_MIRRORS, ArrayList(mirrors))
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): AlertDialog {
val mirrors = requireArguments().getStringArrayList(EXTRA_MIRRORS)!!
return MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.select_mirror)
.setItems(mirrors.toTypedArray()) { _, position ->
(parentFragment as EditRepositoryFragment)
.setMirror(mirrors[position])
}
.setNegativeButton(R.string.cancel, null)
.create()
}
}
}

View File

@ -1,91 +0,0 @@
package com.looker.droidify.screen
import android.content.res.ColorStateList
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import coil.load
import com.google.android.material.card.MaterialCardView
import com.google.android.material.imageview.ShapeableImageView
import com.google.android.material.textview.MaterialTextView
import com.looker.droidify.R
import com.looker.droidify.database.entity.Repository
import com.looker.droidify.utility.extension.resources.clear
import com.looker.droidify.utility.extension.resources.getColorFromAttr
import com.looker.droidify.utility.extension.resources.inflate
import com.looker.droidify.utility.getRepository
import com.looker.droidify.widget.CursorRecyclerAdapter
class RepositoriesAdapter(
private val onClick: (Repository) -> Unit,
private val onSwitch: (repository: Repository, isEnabled: Boolean) -> Boolean,
) :
CursorRecyclerAdapter<RepositoriesAdapter.ViewType, RecyclerView.ViewHolder>() {
enum class ViewType { REPOSITORY }
private class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val item = itemView.findViewById<MaterialCardView>(R.id.repository_item)!!
val checkMark = itemView.findViewById<ShapeableImageView>(R.id.repository_state)!!
val repoName = itemView.findViewById<MaterialTextView>(R.id.repository_name)!!
val repoDesc = itemView.findViewById<MaterialTextView>(R.id.repository_description)!!
var isEnabled = true
val textColor: ColorStateList
get() = if (isEnabled) itemView.context.getColorFromAttr(R.attr.colorOnPrimaryContainer)
else itemView.context.getColorFromAttr(R.attr.colorOnBackground)
}
override val viewTypeClass: Class<ViewType>
get() = ViewType::class.java
override fun getItemEnumViewType(position: Int): ViewType {
return ViewType.REPOSITORY
}
private fun getRepository(position: Int): Repository {
return moveTo(position).getRepository()
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: ViewType,
): RecyclerView.ViewHolder {
return ViewHolder(parent.inflate(R.layout.repository_item)).apply {
itemView.setOnLongClickListener {
onClick(getRepository(adapterPosition))
true
}
itemView.setOnClickListener {
isEnabled = !isEnabled
onSwitch(getRepository(adapterPosition), isEnabled)
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder as ViewHolder
val repository = getRepository(position)
holder.isEnabled = repository.enabled
holder.repoName.text = repository.name
holder.repoDesc.text = repository.description.trim()
holder.item.setCardBackgroundColor(
if (repository.enabled) holder.item.context.getColorFromAttr(R.attr.colorPrimaryContainer)
else holder.item.context.getColorFromAttr(android.R.attr.colorBackground)
)
holder.checkMark.apply {
if (repository.enabled) load(R.drawable.ic_check)
else clear()
}
holder.textColor.let {
holder.repoName.setTextColor(it)
holder.repoDesc.setTextColor(it)
holder.checkMark.imageTintList = it
}
}
}

View File

@ -1,82 +0,0 @@
package com.looker.droidify.screen
import android.database.Cursor
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.looker.droidify.R
import com.looker.droidify.database.CursorOwner
import com.looker.droidify.service.Connection
import com.looker.droidify.service.SyncService
import com.looker.droidify.utility.Utils
import com.looker.droidify.utility.extension.resources.getDrawableCompat
import me.zhanghai.android.fastscroll.FastScrollerBuilder
class RepositoriesFragment : ScreenFragment(), CursorOwner.Callback {
private var recyclerView: RecyclerView? = null
private val syncConnection = Connection(SyncService::class.java)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
val view = fragmentBinding.root.apply {
val content = fragmentBinding.fragmentContent
content.addView(RecyclerView(content.context).apply {
id = android.R.id.list
layoutManager = LinearLayoutManager(context)
isMotionEventSplittingEnabled = false
setHasFixedSize(true)
adapter = RepositoriesAdapter({ screenActivity.navigateRepository(it.id) },
{ repository, isEnabled ->
repository.enabled != isEnabled &&
syncConnection.binder?.setEnabled(repository, isEnabled) == true
})
recyclerView = this
FastScrollerBuilder(this)
.useMd2Style()
.setThumbDrawable(this.context.getDrawableCompat(R.drawable.scrollbar_thumb))
.build()
})
}
this.toolbar = fragmentBinding.toolbar
this.collapsingToolbar = fragmentBinding.collapsingToolbar
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
syncConnection.bind(requireContext())
screenActivity.cursorOwner.attach(this, CursorOwner.Request.Repositories)
screenActivity.onToolbarCreated(toolbar)
toolbar.menu.add(R.string.add_repository)
.setIcon(Utils.getToolbarIcon(toolbar.context, R.drawable.ic_add))
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS)
.setOnMenuItemClickListener {
view.post { screenActivity.navigateAddRepository() }
true
}
collapsingToolbar.title = getString(R.string.repositories)
}
override fun onDestroyView() {
super.onDestroyView()
recyclerView = null
syncConnection.unbind(requireContext())
screenActivity.cursorOwner.detach(this)
}
override fun onCursorData(request: CursorOwner.Request, cursor: Cursor?) {
(recyclerView?.adapter as? RepositoriesAdapter)?.cursor = cursor
}
}

View File

@ -1,176 +0,0 @@
package com.looker.droidify.screen
import android.os.Bundle
import android.text.SpannableStringBuilder
import android.text.format.DateUtils
import android.text.style.ForegroundColorSpan
import android.text.style.TypefaceSpan
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.LinearLayoutCompat
import androidx.core.widget.NestedScrollView
import androidx.lifecycle.lifecycleScope
import com.looker.droidify.R
import com.looker.droidify.databinding.TitleTextItemBinding
import com.looker.droidify.service.Connection
import com.looker.droidify.service.SyncService
import com.looker.droidify.utility.Utils
import com.looker.droidify.utility.extension.resources.getColorFromAttr
import com.looker.droidify.utility.extension.resources.sizeScaled
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.util.*
class RepositoryFragment() : ScreenFragment() {
private var titleBinding: TitleTextItemBinding? = null
companion object {
private const val EXTRA_REPOSITORY_ID = "repositoryId"
}
constructor(repositoryId: Long) : this() {
arguments = Bundle().apply {
putLong(EXTRA_REPOSITORY_ID, repositoryId)
}
}
private val repositoryId: Long
get() = requireArguments().getLong(EXTRA_REPOSITORY_ID)
private var layout: LinearLayoutCompat? = null
private val syncConnection = Connection(SyncService::class.java)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
titleBinding = TitleTextItemBinding.inflate(layoutInflater)
syncConnection.bind(requireContext())
lifecycleScope.launch(Dispatchers.Main) { updateRepositoryView() }
screenActivity.onToolbarCreated(toolbar)
collapsingToolbar.title = getString(R.string.repository)
toolbar.menu.apply {
add(R.string.edit_repository)
.setIcon(Utils.getToolbarIcon(toolbar.context, R.drawable.ic_edit))
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS)
.setOnMenuItemClickListener {
view.post { screenActivity.navigateEditRepository(repositoryId) }
true
}
add(R.string.delete)
.setIcon(Utils.getToolbarIcon(toolbar.context, R.drawable.ic_delete))
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS)
.setOnMenuItemClickListener {
MessageDialog(MessageDialog.Message.DeleteRepositoryConfirm).show(
childFragmentManager
)
true
}
}
val content = fragmentBinding.fragmentContent
val scroll = NestedScrollView(content.context)
scroll.id = android.R.id.list
scroll.isFillViewport = true
content.addView(scroll)
val layout = LinearLayoutCompat(scroll.context)
layout.orientation = LinearLayoutCompat.VERTICAL
resources.sizeScaled(8).let { layout.setPadding(0, it, 0, it) }
this.layout = layout
scroll.addView(
layout,
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
override fun onDestroyView() {
super.onDestroyView()
layout = null
titleBinding = null
syncConnection.unbind(requireContext())
}
private fun updateRepositoryView() {
val repository = screenActivity.db.repositoryDao.get(repositoryId)
val layout = layout!!
layout.removeAllViews()
if (repository == null) {
layout.addTitleText(R.string.address, getString(R.string.unknown))
} else {
layout.addTitleText(R.string.address, repository.address)
if (repository.updated > 0L) {
collapsingToolbar.title = repository.name
layout.addTitleText(R.string.name, repository.name)
layout.addTitleText(R.string.description, repository.description.replace('\n', ' '))
layout.addTitleText(R.string.recently_updated, run {
val lastUpdated = repository.updated
if (lastUpdated > 0L) {
val date = Date(repository.updated)
val format =
if (DateUtils.isToday(date.time)) DateUtils.FORMAT_SHOW_TIME else
DateUtils.FORMAT_SHOW_TIME or DateUtils.FORMAT_SHOW_DATE
DateUtils.formatDateTime(layout.context, date.time, format)
} else {
getString(R.string.unknown)
}
})
if (repository.enabled && (repository.lastModified.isNotEmpty() || repository.entityTag.isNotEmpty())) {
layout.addTitleText(
R.string.number_of_applications,
screenActivity.db.productDao.countForRepository(repository.id).toString()
)
}
} else {
layout.addTitleText(
R.string.description,
getString(R.string.repository_not_used_DESC)
)
}
if (repository.fingerprint.isEmpty()) {
if (repository.updated > 0L) {
val builder =
SpannableStringBuilder(getString(R.string.repository_unsigned_DESC))
builder.setSpan(
ForegroundColorSpan(layout.context.getColorFromAttr(R.attr.colorError).defaultColor),
0, builder.length, SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE
)
layout.addTitleText(R.string.fingerprint, builder)
}
} else {
val fingerprint =
SpannableStringBuilder(repository.fingerprint.windowed(2, 2, false)
.take(32).joinToString(separator = " ") { it.uppercase(Locale.US) })
fingerprint.setSpan(
TypefaceSpan("monospace"), 0, fingerprint.length,
SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE
)
layout.addTitleText(R.string.fingerprint, fingerprint)
}
}
}
private fun LinearLayoutCompat.addTitleText(titleResId: Int, text: CharSequence) {
if (text.isNotEmpty()) {
val binding = TitleTextItemBinding.inflate(layoutInflater)
val layout = binding.root
val titleView = binding.title
val textView = binding.text
titleView.setText(titleResId)
textView.text = text
addView(layout)
}
}
internal fun onDeleteConfirm() {
if (syncConnection.binder?.deleteRepository(repositoryId) == true) {
requireActivity().onBackPressed()
}
}
}

View File

@ -260,12 +260,5 @@ abstract class ScreenActivity : AppCompatActivity() {
} }
internal fun navigateProduct(packageName: String) = pushFragment(AppDetailFragment(packageName)) internal fun navigateProduct(packageName: String) = pushFragment(AppDetailFragment(packageName))
internal fun navigateRepositories() = pushFragment(RepositoriesFragment())
internal fun navigatePreferences() = pushFragment(SettingsFragment()) internal fun navigatePreferences() = pushFragment(SettingsFragment())
internal fun navigateAddRepository() = pushFragment(EditRepositoryFragment(null))
internal fun navigateRepository(repositoryId: Long) =
pushFragment(RepositoryFragment(repositoryId))
internal fun navigateEditRepository(repositoryId: Long) =
pushFragment(EditRepositoryFragment(repositoryId))
} }

View File

@ -177,12 +177,6 @@ class TabsFragment : ScreenFragment() {
true true
} }
add(1, 0, 0, R.string.repositories)
.setOnMenuItemClickListener {
view.post { screenActivity.navigateRepositories() }
true
}
add(1, 0, 0, R.string.settings) add(1, 0, 0, R.string.settings)
.setOnMenuItemClickListener { .setOnMenuItemClickListener {
view.post { screenActivity.navigatePreferences() } view.post { screenActivity.navigatePreferences() }

View File

@ -1,134 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true">
<androidx.core.widget.NestedScrollView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="12dp"
android:paddingTop="4dp"
android:paddingBottom="12dp">
<com.google.android.material.circularreveal.CircularRevealFrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/address"
android:paddingVertical="12dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/address"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/address_mirror"
android:layout_width="36dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:background="?android:attr/actionBarItemBackground"
android:scaleType="center"
android:src="@drawable/ic_arrow_down"
android:tint="?android:attr/textColorSecondary"
android:tintMode="src_in"
android:visibility="gone"
tools:ignore="ContentDescription" />
</com.google.android.material.circularreveal.CircularRevealFrameLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/fingerprint"
android:paddingVertical="12dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/fingerprint"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/username"
android:paddingVertical="12dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/username"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/password"
android:paddingVertical="12dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.core.widget.NestedScrollView>
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/colorBackground"
android:clickable="true"
android:gravity="center"
android:orientation="vertical"
tools:ignore="KeyboardInaccessibleWidget">
<com.google.android.material.progressindicator.LinearProgressIndicator
style="@style/Theme.Progress"
android:layout_width="200dp"
android:layout_height="wrap_content" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:fontFamily="sans-serif-light"
android:text="@string/checking_repository"
android:textColor="?android:attr/textColorPrimary"
android:textSize="20sp" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/skip"
style="@android:style/Widget.Material.Button.Borderless.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/skip" />
</androidx.appcompat.widget.LinearLayoutCompat>
</FrameLayout>
</layout>

View File

@ -1,43 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
</data>
<com.google.android.material.card.MaterialCardView
android:id="@+id/repository_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/text_margin"
android:layout_marginVertical="8dp">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/text_margin"
android:orientation="vertical"
android:paddingEnd="48dp">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/repository_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?attr/textAppearanceTitleMedium" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/repository_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true" />
</androidx.appcompat.widget.LinearLayoutCompat>
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/repository_state"
android:layout_width="24dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:layout_marginEnd="24dp" />
</com.google.android.material.card.MaterialCardView>
</layout>