mirror of
https://github.com/Aviortheking/Neo-Store.git
synced 2025-04-24 03:42:15 +00:00
Remove: Old Repository classes and layouts
This commit is contained in:
parent
de8952f3ab
commit
4887dc1e61
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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))
|
|
||||||
}
|
}
|
||||||
|
@ -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() }
|
||||||
|
@ -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>
|
|
@ -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>
|
|
Loading…
x
Reference in New Issue
Block a user