Reformatted Code and Updated VersionName/VersionCode

This commit is contained in:
Mohit 2021-06-08 21:28:45 +05:30
parent 93442f74d1
commit efc02add3d
6 changed files with 980 additions and 845 deletions

View File

@ -25,8 +25,8 @@ android {
applicationId 'com.looker.droidify' applicationId 'com.looker.droidify'
minSdkVersion 28 minSdkVersion 28
targetSdkVersion 29 targetSdkVersion 29
versionCode 1 versionCode 2
versionName '0.1' versionName '0.2'
def languages = [ 'en' ] def languages = [ 'en' ]
buildConfigField 'String[]', 'LANGUAGES', '{ "' + languages.join('", "') + '" }' buildConfigField 'String[]', 'LANGUAGES', '{ "' + languages.join('", "') + '" }'

View File

@ -20,11 +20,6 @@ import android.widget.FrameLayout
import android.widget.TextView import android.widget.TextView
import android.widget.Toolbar import android.widget.Toolbar
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.schedulers.Schedulers
import com.looker.droidify.R import com.looker.droidify.R
import com.looker.droidify.database.Database import com.looker.droidify.database.Database
import com.looker.droidify.entity.Repository import com.looker.droidify.entity.Repository
@ -33,15 +28,22 @@ import com.looker.droidify.service.Connection
import com.looker.droidify.service.SyncService import com.looker.droidify.service.SyncService
import com.looker.droidify.utility.RxUtils import com.looker.droidify.utility.RxUtils
import com.looker.droidify.utility.Utils import com.looker.droidify.utility.Utils
import com.looker.droidify.utility.extension.resources.* import com.looker.droidify.utility.extension.resources.getColorFromAttr
import com.looker.droidify.utility.extension.text.* import com.looker.droidify.utility.extension.resources.inflate
import com.looker.droidify.utility.extension.text.nullIfEmpty
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.schedulers.Schedulers
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request import okhttp3.Request
import java.net.URI import java.net.URI
import java.net.URL import java.net.URL
import java.nio.charset.Charset import java.nio.charset.Charset
import java.util.Locale import java.util.*
import kotlin.math.* import kotlin.collections.ArrayList
import kotlin.math.min
class EditRepositoryFragment() : ScreenFragment() { class EditRepositoryFragment() : ScreenFragment() {
companion object { companion object {
@ -71,8 +73,10 @@ class EditRepositoryFragment(): ScreenFragment() {
} }
private val repositoryId: Long? private val repositoryId: Long?
get() = requireArguments().let { if (it.containsKey(EXTRA_REPOSITORY_ID)) get() = requireArguments().let {
it.getLong(EXTRA_REPOSITORY_ID) else null } if (it.containsKey(EXTRA_REPOSITORY_ID))
it.getLong(EXTRA_REPOSITORY_ID) else null
}
private lateinit var errorColorFilter: PorterDuffColorFilter private lateinit var errorColorFilter: PorterDuffColorFilter
@ -85,7 +89,11 @@ class EditRepositoryFragment(): ScreenFragment() {
private var takenAddresses = emptySet<String>() private var takenAddresses = emptySet<String>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.fragment, container, false) return inflater.inflate(R.layout.fragment, container, false)
} }
@ -114,8 +122,10 @@ class EditRepositoryFragment(): ScreenFragment() {
} }
val content = view.findViewById<FrameLayout>(R.id.fragment_content)!! val content = view.findViewById<FrameLayout>(R.id.fragment_content)!!
errorColorFilter = PorterDuffColorFilter(content.context errorColorFilter = PorterDuffColorFilter(
.getColorFromAttr(R.attr.colorError).defaultColor, PorterDuff.Mode.SRC_IN) content.context
.getColorFromAttr(R.attr.colorError).defaultColor, PorterDuff.Mode.SRC_IN
)
content.addView(content.inflate(R.layout.edit_repository)) content.addView(content.inflate(R.layout.edit_repository))
val layout = Layout(content) val layout = Layout(content)
@ -123,13 +133,17 @@ class EditRepositoryFragment(): ScreenFragment() {
layout.fingerprint.hint = generateSequence { "FF" }.take(32).joinToString(separator = " ") layout.fingerprint.hint = generateSequence { "FF" }.take(32).joinToString(separator = " ")
layout.fingerprint.addTextChangedListener(object : TextWatcher { layout.fingerprint.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) = Unit override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) =
Unit
override fun onTextChanged(s: CharSequence, start: Int, count: Int, after: Int) = Unit override fun onTextChanged(s: CharSequence, start: Int, count: Int, after: Int) = Unit
private val validChar: (Char) -> Boolean = { it in '0' .. '9' || it in 'a' .. 'f' || it in 'A' .. 'F' } private val validChar: (Char) -> Boolean =
{ it in '0'..'9' || it in 'a'..'f' || it in 'A'..'F' }
private fun logicalPosition(s: String, position: Int): Int { private fun logicalPosition(s: String, position: Int): Int {
return if (position > 0) s.asSequence().take(position).count(validChar) else position return if (position > 0) s.asSequence().take(position)
.count(validChar) else position
} }
private fun realPosition(s: String, position: Int): Int { private fun realPosition(s: String, position: Int): Int {
@ -149,13 +163,17 @@ class EditRepositoryFragment(): ScreenFragment() {
override fun afterTextChanged(s: Editable) { override fun afterTextChanged(s: Editable) {
val inputString = s.toString() val inputString = s.toString()
val outputString = inputString.toUpperCase(Locale.US) val outputString = inputString.uppercase(Locale.US)
.filter(validChar).windowed(2, 2, true).take(32).joinToString(separator = " ") .filter(validChar).windowed(2, 2, true).take(32).joinToString(separator = " ")
if (inputString != outputString) { if (inputString != outputString) {
val inputStart = logicalPosition(inputString, Selection.getSelectionStart(s)) val inputStart = logicalPosition(inputString, Selection.getSelectionStart(s))
val inputEnd = logicalPosition(inputString, Selection.getSelectionEnd(s)) val inputEnd = logicalPosition(inputString, Selection.getSelectionEnd(s))
s.replace(0, s.length, outputString) s.replace(0, s.length, outputString)
Selection.setSelection(s, realPosition(outputString, inputStart), realPosition(outputString, inputEnd)) Selection.setSelection(
s,
realPosition(outputString, inputStart),
realPosition(outputString, inputEnd)
)
} }
} }
}) })
@ -163,7 +181,8 @@ class EditRepositoryFragment(): ScreenFragment() {
if (savedInstanceState == null) { if (savedInstanceState == null) {
val repository = repositoryId?.let(Database.RepositoryAdapter::get) val repository = repositoryId?.let(Database.RepositoryAdapter::get)
if (repository == null) { if (repository == null) {
val clipboardManager = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clipboardManager =
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val text = clipboardManager.primaryClip val text = clipboardManager.primaryClip
?.let { if (it.itemCount > 0) it else null } ?.let { if (it.itemCount > 0) it else null }
?.getItemAt(0)?.text?.toString().orEmpty() ?.getItemAt(0)?.text?.toString().orEmpty()
@ -171,8 +190,10 @@ class EditRepositoryFragment(): ScreenFragment() {
val uri = Uri.parse(URL(text).toString()) val uri = Uri.parse(URL(text).toString())
val fingerprintText = uri.getQueryParameter("fingerprint")?.nullIfEmpty() val fingerprintText = uri.getQueryParameter("fingerprint")?.nullIfEmpty()
?: uri.getQueryParameter("FINGERPRINT")?.nullIfEmpty() ?: uri.getQueryParameter("FINGERPRINT")?.nullIfEmpty()
Pair(uri.buildUpon().path(uri.path?.pathCropped) Pair(
.query(null).fragment(null).build().toString(), fingerprintText) uri.buildUpon().path(uri.path?.pathCropped)
.query(null).fragment(null).build().toString(), fingerprintText
)
} catch (e: Exception) { } catch (e: Exception) {
Pair(null, null) Pair(null, null)
} }
@ -183,10 +204,16 @@ class EditRepositoryFragment(): ScreenFragment() {
val mirrors = repository.mirrors.map { it.withoutKnownPath } val mirrors = repository.mirrors.map { it.withoutKnownPath }
if (mirrors.isNotEmpty()) { if (mirrors.isNotEmpty()) {
layout.addressMirror.visibility = View.VISIBLE layout.addressMirror.visibility = View.VISIBLE
layout.address.apply { setPaddingRelative(paddingStart, paddingTop, layout.address.apply {
paddingEnd + layout.addressMirror.layoutParams.width, paddingBottom) } setPaddingRelative(
layout.addressMirror.setOnClickListener { SelectMirrorDialog(mirrors) paddingStart, paddingTop,
.show(childFragmentManager, SelectMirrorDialog::class.java.name) } paddingEnd + layout.addressMirror.layoutParams.width, paddingBottom
)
}
layout.addressMirror.setOnClickListener {
SelectMirrorDialog(mirrors)
.show(childFragmentManager, SelectMirrorDialog::class.java.name)
}
} }
layout.fingerprint.setText(repository.fingerprint) layout.fingerprint.setText(repository.fingerprint)
val (usernameText, passwordText) = repository.authentication.nullIfEmpty() val (usernameText, passwordText) = repository.authentication.nullIfEmpty()
@ -201,7 +228,10 @@ class EditRepositoryFragment(): ScreenFragment() {
} }
?.let { ?.let {
val index = it.indexOf(':') val index = it.indexOf(':')
if (index >= 0) Pair(it.substring(0, index), it.substring(index + 1)) else null if (index >= 0) Pair(
it.substring(0, index),
it.substring(index + 1)
) else null
} }
?: Pair(null, null) ?: Pair(null, null)
layout.username.setText(usernameText) layout.username.setText(usernameText)
@ -232,7 +262,7 @@ class EditRepositoryFragment(): ScreenFragment() {
.observeOn(Schedulers.io()) .observeOn(Schedulers.io())
.flatMapSingle { RxUtils.querySingle { Database.RepositoryAdapter.getAll(it) } } .flatMapSingle { RxUtils.querySingle { Database.RepositoryAdapter.getAll(it) } }
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe { .subscribe { it ->
takenAddresses = it.asSequence().filter { it.id != repositoryId } takenAddresses = it.asSequence().filter { it.id != repositoryId }
.flatMap { (it.mirrors + it.address).asSequence() } .flatMap { (it.mirrors + it.address).asSequence() }
.map { it.withoutKnownPath }.toSet() .map { it.withoutKnownPath }.toSet()
@ -310,7 +340,8 @@ class EditRepositoryFragment(): ScreenFragment() {
val usernameInvalid = username.contains(':') val usernameInvalid = username.contains(':')
val usernameEmpty = username.isEmpty() && password.isNotEmpty() val usernameEmpty = username.isEmpty() && password.isNotEmpty()
val passwordEmpty = username.isNotEmpty() && password.isEmpty() val passwordEmpty = username.isNotEmpty() && password.isEmpty()
layout.usernameError.visibility = if (usernameInvalid || usernameEmpty) View.VISIBLE else View.GONE layout.usernameError.visibility =
if (usernameInvalid || usernameEmpty) View.VISIBLE else View.GONE
layout.passwordError.visibility = if (passwordEmpty) View.VISIBLE else View.GONE layout.passwordError.visibility = if (passwordEmpty) View.VISIBLE else View.GONE
if (usernameInvalid) { if (usernameInvalid) {
layout.usernameError.setText(R.string.invalid_username_format) layout.usernameError.setText(R.string.invalid_username_format)
@ -330,8 +361,10 @@ class EditRepositoryFragment(): ScreenFragment() {
val layout = layout!! val layout = layout!!
saveMenuItem!!.isEnabled = !addressError && !fingerprintError && saveMenuItem!!.isEnabled = !addressError && !fingerprintError &&
!usernamePasswordError && checkDisposable == null !usernamePasswordError && checkDisposable == null
layout.apply { sequenceOf(address, addressMirror, fingerprint, username, password) layout.apply {
.forEach { it.isEnabled = checkDisposable == null } } sequenceOf(address, addressMirror, fingerprint, username, password)
.forEach { it.isEnabled = checkDisposable == null }
}
layout.overlay.visibility = if (checkDisposable != null) View.VISIBLE else View.GONE layout.overlay.visibility = if (checkDisposable != null) View.VISIBLE else View.GONE
} }
@ -346,7 +379,10 @@ class EditRepositoryFragment(): ScreenFragment() {
val cropped = pathCropped val cropped = pathCropped
val endsWith = checkPaths.asSequence().filter { it.isNotEmpty() } val endsWith = checkPaths.asSequence().filter { it.isNotEmpty() }
.sortedByDescending { it.length }.find { cropped.endsWith("/$it") } .sortedByDescending { it.length }.find { cropped.endsWith("/$it") }
return if (endsWith != null) cropped.substring(0, cropped.length - endsWith.length - 1) else cropped return if (endsWith != null) cropped.substring(
0,
cropped.length - endsWith.length - 1
) else cropped
} }
private fun normalizeAddress(address: String): String? { private fun normalizeAddress(address: String): String? {
@ -359,7 +395,15 @@ class EditRepositoryFragment(): ScreenFragment() {
val path = uri?.path?.pathCropped val path = uri?.path?.pathCropped
return if (uri != null && path != null) { return if (uri != null && path != null) {
try { try {
URI(uri.scheme, uri.userInfo, uri.host, uri.port, path, uri.query, uri.fragment).toString() URI(
uri.scheme,
uri.userInfo,
uri.host,
uri.port,
path,
uri.query,
uri.fragment
).toString()
} catch (e: Exception) { } catch (e: Exception) {
null null
} }
@ -385,23 +429,41 @@ class EditRepositoryFragment(): ScreenFragment() {
val username = layout.username.text.toString().nullIfEmpty() val username = layout.username.text.toString().nullIfEmpty()
val password = layout.password.text.toString().nullIfEmpty() val password = layout.password.text.toString().nullIfEmpty()
val paths = sequenceOf("", "fdroid/repo", "repo") val paths = sequenceOf("", "fdroid/repo", "repo")
val authentication = username?.let { u -> password val authentication = username?.let { u ->
?.let { p -> Base64.encodeToString("$u:$p".toByteArray(Charset.defaultCharset()), Base64.NO_WRAP) } } password
?.let { p ->
Base64.encodeToString(
"$u:$p".toByteArray(Charset.defaultCharset()),
Base64.NO_WRAP
)
}
}
?.let { "Basic $it" }.orEmpty() ?.let { "Basic $it" }.orEmpty()
if (check) { if (check) {
checkDisposable = paths checkDisposable = paths
.fold(Single.just("")) { oldAddressSingle, checkPath -> oldAddressSingle .fold(Single.just("")) { oldAddressSingle, checkPath ->
oldAddressSingle
.flatMap { oldAddress -> .flatMap { oldAddress ->
if (oldAddress.isEmpty()) { if (oldAddress.isEmpty()) {
val builder = Uri.parse(address).buildUpon() val builder = Uri.parse(address).buildUpon()
.let { if (checkPath.isEmpty()) it else it.appendEncodedPath(checkPath) } .let {
if (checkPath.isEmpty()) it else it.appendEncodedPath(
checkPath
)
}
val newAddress = builder.build() val newAddress = builder.build()
val indexAddress = builder.appendPath("index.jar").build() val indexAddress = builder.appendPath("index.jar").build()
RxUtils RxUtils
.callSingle { Downloader .callSingle {
.createCall(Request.Builder().method("HEAD", null) Downloader
.url(indexAddress.toString().toHttpUrl()), authentication, null) } .createCall(
Request.Builder().method("HEAD", null)
.url(indexAddress.toString().toHttpUrl()),
authentication,
null
)
}
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.map { if (it.code == 200) newAddress.toString() else "" } .map { if (it.code == 200) newAddress.toString() else "" }
} else { } else {
@ -413,14 +475,19 @@ class EditRepositoryFragment(): ScreenFragment() {
.subscribe { result, throwable -> .subscribe { result, throwable ->
checkDisposable = null checkDisposable = null
throwable?.printStackTrace() throwable?.printStackTrace()
val resultAddress = result?.let { if (it.isEmpty()) null else it } ?: address val resultAddress =
result?.let { if (it.isEmpty()) null else it } ?: address
val allow = resultAddress == address || run { val allow = resultAddress == address || run {
layout.address.setText(resultAddress) layout.address.setText(resultAddress)
invalidateAddress(resultAddress) invalidateAddress(resultAddress)
!addressError !addressError
} }
if (allow) { if (allow) {
onSaveRepositoryProceedInvalidate(resultAddress, fingerprint, authentication) onSaveRepositoryProceedInvalidate(
resultAddress,
fingerprint,
authentication
)
} else { } else {
invalidateState() invalidateState()
} }
@ -432,7 +499,11 @@ class EditRepositoryFragment(): ScreenFragment() {
} }
} }
private fun onSaveRepositoryProceedInvalidate(address: String, fingerprint: String, authentication: String) { private fun onSaveRepositoryProceedInvalidate(
address: String,
fingerprint: String,
authentication: String
) {
val binder = syncConnection.binder val binder = syncConnection.binder
if (binder != null) { if (binder != null) {
val repositoryId = repositoryId val repositoryId = repositoryId
@ -475,8 +546,10 @@ class EditRepositoryFragment(): ScreenFragment() {
val mirrors = requireArguments().getStringArrayList(EXTRA_MIRRORS)!! val mirrors = requireArguments().getStringArrayList(EXTRA_MIRRORS)!!
return AlertDialog.Builder(requireContext()) return AlertDialog.Builder(requireContext())
.setTitle(R.string.select_mirror) .setTitle(R.string.select_mirror)
.setItems(mirrors.toTypedArray()) { _, position -> (parentFragment as EditRepositoryFragment) .setItems(mirrors.toTypedArray()) { _, position ->
.setMirror(mirrors[position]) } (parentFragment as EditRepositoryFragment)
.setMirror(mirrors[position])
}
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.create() .create()
} }

View File

@ -8,11 +8,13 @@ import androidx.recyclerview.widget.RecyclerView
import com.looker.droidify.R import com.looker.droidify.R
import com.looker.droidify.database.Database import com.looker.droidify.database.Database
import com.looker.droidify.entity.Repository import com.looker.droidify.entity.Repository
import com.looker.droidify.utility.extension.resources.* import com.looker.droidify.utility.extension.resources.inflate
import com.looker.droidify.widget.CursorRecyclerAdapter import com.looker.droidify.widget.CursorRecyclerAdapter
class RepositoriesAdapter(private val onClick: (Repository) -> Unit, class RepositoriesAdapter(
private val onSwitch: (repository: Repository, isEnabled: Boolean) -> Boolean): private val onClick: (Repository) -> Unit,
private val onSwitch: (repository: Repository, isEnabled: Boolean) -> Boolean
) :
CursorRecyclerAdapter<RepositoriesAdapter.ViewType, RecyclerView.ViewHolder>() { CursorRecyclerAdapter<RepositoriesAdapter.ViewType, RecyclerView.ViewHolder>() {
enum class ViewType { REPOSITORY } enum class ViewType { REPOSITORY }
@ -34,7 +36,10 @@ class RepositoriesAdapter(private val onClick: (Repository) -> Unit,
return Database.RepositoryAdapter.transform(moveTo(position)) return Database.RepositoryAdapter.transform(moveTo(position))
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: ViewType): RecyclerView.ViewHolder { override fun onCreateViewHolder(
parent: ViewGroup,
viewType: ViewType
): RecyclerView.ViewHolder {
return ViewHolder(parent.inflate(R.layout.repository_item)).apply { return ViewHolder(parent.inflate(R.layout.repository_item)).apply {
itemView.setOnClickListener { onClick(getRepository(adapterPosition)) } itemView.setOnClickListener { onClick(getRepository(adapterPosition)) }
enabled.setOnCheckedChangeListener { _, isChecked -> enabled.setOnCheckedChangeListener { _, isChecked ->

View File

@ -21,7 +21,11 @@ class RepositoriesFragment: ScreenFragment(), CursorOwner.Callback {
private val syncConnection = Connection(SyncService::class.java) private val syncConnection = Connection(SyncService::class.java)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.fragment, container, false).apply { return inflater.inflate(R.layout.fragment, container, false).apply {
val content = findViewById<FrameLayout>(R.id.fragment_content)!! val content = findViewById<FrameLayout>(R.id.fragment_content)!!
content.addView(RecyclerView(content.context).apply { content.addView(RecyclerView(content.context).apply {
@ -30,8 +34,10 @@ class RepositoriesFragment: ScreenFragment(), CursorOwner.Callback {
isMotionEventSplittingEnabled = false isMotionEventSplittingEnabled = false
setHasFixedSize(true) setHasFixedSize(true)
adapter = RepositoriesAdapter({ screenActivity.navigateRepository(it.id) }, adapter = RepositoriesAdapter({ screenActivity.navigateRepository(it.id) },
{ repository, isEnabled -> repository.enabled != isEnabled && { repository, isEnabled ->
syncConnection.binder?.setEnabled(repository, isEnabled) == true }) repository.enabled != isEnabled &&
syncConnection.binder?.setEnabled(repository, isEnabled) == true
})
recyclerView = this recyclerView = this
}, FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT) }, FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
} }

View File

@ -9,22 +9,19 @@ import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.FrameLayout import android.widget.*
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView
import android.widget.Toolbar
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.disposables.Disposable
import com.looker.droidify.R import com.looker.droidify.R
import com.looker.droidify.database.Database import com.looker.droidify.database.Database
import com.looker.droidify.service.Connection import com.looker.droidify.service.Connection
import com.looker.droidify.service.SyncService import com.looker.droidify.service.SyncService
import com.looker.droidify.utility.Utils import com.looker.droidify.utility.Utils
import com.looker.droidify.utility.extension.resources.* import com.looker.droidify.utility.extension.resources.getColorFromAttr
import java.util.Date import com.looker.droidify.utility.extension.resources.inflate
import java.util.Locale import com.looker.droidify.utility.extension.resources.sizeScaled
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.disposables.Disposable
import java.util.*
class RepositoryFragment() : ScreenFragment() { class RepositoryFragment() : ScreenFragment() {
companion object { companion object {
@ -45,7 +42,11 @@ class RepositoryFragment(): ScreenFragment() {
private val syncConnection = Connection(SyncService::class.java) private val syncConnection = Connection(SyncService::class.java)
private var repositoryDisposable: Disposable? = null private var repositoryDisposable: Disposable? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.fragment, container, false) return inflater.inflate(R.layout.fragment, container, false)
} }
@ -75,7 +76,9 @@ class RepositoryFragment(): ScreenFragment() {
.setIcon(Utils.getToolbarIcon(toolbar.context, R.drawable.ic_delete)) .setIcon(Utils.getToolbarIcon(toolbar.context, R.drawable.ic_delete))
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS) .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS)
.setOnMenuItemClickListener { .setOnMenuItemClickListener {
MessageDialog(MessageDialog.Message.DeleteRepositoryConfirm).show(childFragmentManager) MessageDialog(MessageDialog.Message.DeleteRepositoryConfirm).show(
childFragmentManager
)
true true
} }
} }
@ -84,12 +87,20 @@ class RepositoryFragment(): ScreenFragment() {
val scroll = ScrollView(content.context) val scroll = ScrollView(content.context)
scroll.id = android.R.id.list scroll.id = android.R.id.list
scroll.isFillViewport = true scroll.isFillViewport = true
content.addView(scroll, FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT) content.addView(
scroll,
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
)
val layout = LinearLayout(scroll.context) val layout = LinearLayout(scroll.context)
layout.orientation = LinearLayout.VERTICAL layout.orientation = LinearLayout.VERTICAL
resources.sizeScaled(8).let { layout.setPadding(0, it, 0, it) } resources.sizeScaled(8).let { layout.setPadding(0, it, 0, it) }
this.layout = layout this.layout = layout
scroll.addView(layout, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) scroll.addView(
layout,
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -116,7 +127,8 @@ class RepositoryFragment(): ScreenFragment() {
val lastUpdated = repository.updated val lastUpdated = repository.updated
if (lastUpdated > 0L) { if (lastUpdated > 0L) {
val date = Date(repository.updated) val date = Date(repository.updated)
val format = if (DateUtils.isToday(date.time)) DateUtils.FORMAT_SHOW_TIME else val format =
if (DateUtils.isToday(date.time)) DateUtils.FORMAT_SHOW_TIME else
DateUtils.FORMAT_SHOW_TIME or DateUtils.FORMAT_SHOW_DATE DateUtils.FORMAT_SHOW_TIME or DateUtils.FORMAT_SHOW_DATE
DateUtils.formatDateTime(layout.context, date.time, format) DateUtils.formatDateTime(layout.context, date.time, format)
} else { } else {
@ -124,24 +136,35 @@ class RepositoryFragment(): ScreenFragment() {
} }
}) })
if (repository.enabled && (repository.lastModified.isNotEmpty() || repository.entityTag.isNotEmpty())) { if (repository.enabled && (repository.lastModified.isNotEmpty() || repository.entityTag.isNotEmpty())) {
layout.addTitleText(R.string.number_of_applications, layout.addTitleText(
Database.ProductAdapter.getCount(repository.id).toString()) R.string.number_of_applications,
Database.ProductAdapter.getCount(repository.id).toString()
)
} }
} else { } else {
layout.addTitleText(R.string.description, getString(R.string.repository_not_used_DESC)) layout.addTitleText(
R.string.description,
getString(R.string.repository_not_used_DESC)
)
} }
if (repository.fingerprint.isEmpty()) { if (repository.fingerprint.isEmpty()) {
if (repository.updated > 0L) { if (repository.updated > 0L) {
val builder = SpannableStringBuilder(getString(R.string.repository_unsigned_DESC)) val builder =
builder.setSpan(ForegroundColorSpan(layout.context.getColorFromAttr(R.attr.colorError).defaultColor), SpannableStringBuilder(getString(R.string.repository_unsigned_DESC))
0, builder.length, SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE) builder.setSpan(
ForegroundColorSpan(layout.context.getColorFromAttr(R.attr.colorError).defaultColor),
0, builder.length, SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE
)
layout.addTitleText(R.string.fingerprint, builder) layout.addTitleText(R.string.fingerprint, builder)
} }
} else { } else {
val fingerprint = SpannableStringBuilder(repository.fingerprint.windowed(2, 2, false) val fingerprint =
.take(32).joinToString(separator = " ") { it.toUpperCase(Locale.US) }) SpannableStringBuilder(repository.fingerprint.windowed(2, 2, false)
fingerprint.setSpan(TypefaceSpan("monospace"), 0, fingerprint.length, .take(32).joinToString(separator = " ") { it.uppercase(Locale.US) })
SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE) fingerprint.setSpan(
TypefaceSpan("monospace"), 0, fingerprint.length,
SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE
)
layout.addTitleText(R.string.fingerprint, fingerprint) layout.addTitleText(R.string.fingerprint, fingerprint)
} }
} }

View File

@ -18,9 +18,9 @@ import com.looker.droidify.content.Preferences
import com.looker.droidify.database.CursorOwner import com.looker.droidify.database.CursorOwner
import com.looker.droidify.utility.KParcelable import com.looker.droidify.utility.KParcelable
import com.looker.droidify.utility.Utils import com.looker.droidify.utility.Utils
import com.looker.droidify.utility.extension.android.* import com.looker.droidify.utility.extension.android.Android
import com.looker.droidify.utility.extension.resources.* import com.looker.droidify.utility.extension.resources.getDrawableFromAttr
import com.looker.droidify.utility.extension.text.* import com.looker.droidify.utility.extension.text.nullIfEmpty
abstract class ScreenActivity : FragmentActivity() { abstract class ScreenActivity : FragmentActivity() {
companion object { companion object {
@ -32,8 +32,10 @@ abstract class ScreenActivity: FragmentActivity() {
class Install(val packageName: String?, val cacheFileName: String?) : SpecialIntent() class Install(val packageName: String?, val cacheFileName: String?) : SpecialIntent()
} }
private class FragmentStackItem(val className: String, val arguments: Bundle?, private class FragmentStackItem(
val savedState: Fragment.SavedState?): KParcelable { val className: String, val arguments: Bundle?,
val savedState: Fragment.SavedState?
) : KParcelable {
override fun writeToParcel(dest: Parcel, flags: Int) { override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeString(className) dest.writeString(className)
dest.writeByte(if (arguments != null) 1 else 0) dest.writeByte(if (arguments != null) 1 else 0)
@ -43,11 +45,16 @@ abstract class ScreenActivity: FragmentActivity() {
} }
companion object { companion object {
@Suppress("unused") @JvmField val CREATOR = KParcelable.creator { @Suppress("unused")
@JvmField
val CREATOR = KParcelable.creator {
val className = it.readString()!! val className = it.readString()!!
val arguments = if (it.readByte().toInt() == 0) null else Bundle.CREATOR.createFromParcel(it) val arguments =
if (it.readByte().toInt() == 0) null else Bundle.CREATOR.createFromParcel(it)
arguments?.classLoader = ScreenActivity::class.java.classLoader arguments?.classLoader = ScreenActivity::class.java.classLoader
val savedState = if (it.readByte().toInt() == 0) null else Fragment.SavedState.CREATOR.createFromParcel(it) val savedState = if (it.readByte()
.toInt() == 0
) null else Fragment.SavedState.CREATOR.createFromParcel(it)
FragmentStackItem(className, arguments, savedState) FragmentStackItem(className, arguments, savedState)
} }
} }
@ -72,10 +79,16 @@ abstract class ScreenActivity: FragmentActivity() {
setTheme(Preferences[Preferences.Key.Theme].getResId(resources.configuration)) setTheme(Preferences[Preferences.Key.Theme].getResId(resources.configuration))
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or window.decorView.systemUiVisibility =
window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
addContentView(FrameLayout(this).apply { id = R.id.main_content }, addContentView(
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)) FrameLayout(this).apply { id = R.id.main_content },
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
)
if (savedInstanceState == null) { if (savedInstanceState == null) {
cursorOwner = CursorOwner() cursorOwner = CursorOwner()
@ -114,14 +127,17 @@ abstract class ScreenActivity: FragmentActivity() {
private fun replaceFragment(fragment: Fragment, open: Boolean?) { private fun replaceFragment(fragment: Fragment, open: Boolean?) {
if (open != null) { if (open != null) {
currentFragment?.view?.translationZ = (if (open) Int.MIN_VALUE else Int.MAX_VALUE).toFloat() currentFragment?.view?.translationZ =
(if (open) Int.MIN_VALUE else Int.MAX_VALUE).toFloat()
} }
supportFragmentManager supportFragmentManager
.beginTransaction() .beginTransaction()
.apply { .apply {
if (open != null) { if (open != null) {
setCustomAnimations(if (open) R.animator.slide_in else 0, setCustomAnimations(
if (open) R.animator.slide_in_keep else R.animator.slide_out) if (open) R.animator.slide_in else 0,
if (open) R.animator.slide_in_keep else R.animator.slide_out
)
} }
} }
.replace(R.id.main_content, fragment) .replace(R.id.main_content, fragment)
@ -129,8 +145,14 @@ abstract class ScreenActivity: FragmentActivity() {
} }
private fun pushFragment(fragment: Fragment) { private fun pushFragment(fragment: Fragment) {
currentFragment?.let { fragmentStack.add(FragmentStackItem(it::class.java.name, it.arguments, currentFragment?.let {
supportFragmentManager.saveFragmentInstanceState(it))) } fragmentStack.add(
FragmentStackItem(
it::class.java.name, it.arguments,
supportFragmentManager.saveFragmentInstanceState(it)
)
)
}
replaceFragment(fragment, true) replaceFragment(fragment, true)
} }
@ -157,7 +179,8 @@ abstract class ScreenActivity: FragmentActivity() {
internal fun onToolbarCreated(toolbar: Toolbar) { internal fun onToolbarCreated(toolbar: Toolbar) {
if (fragmentStack.isNotEmpty()) { if (fragmentStack.isNotEmpty()) {
toolbar.navigationIcon = toolbar.context.getDrawableFromAttr(android.R.attr.homeAsUpIndicator) toolbar.navigationIcon =
toolbar.context.getDrawableFromAttr(android.R.attr.homeAsUpIndicator)
toolbar.setNavigationOnClickListener { onBackPressed() } toolbar.setNavigationOnClickListener { onBackPressed() }
} }
} }
@ -237,14 +260,19 @@ abstract class ScreenActivity: FragmentActivity() {
} }
// TODO Handle deprecation // TODO Handle deprecation
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
startActivity(Intent(Intent.ACTION_INSTALL_PACKAGE) startActivity(
.setDataAndType(uri, "application/vnd.android.package-archive").setFlags(flags)) Intent(Intent.ACTION_INSTALL_PACKAGE)
.setDataAndType(uri, "application/vnd.android.package-archive").setFlags(flags)
)
} }
internal fun navigateProduct(packageName: String) = pushFragment(ProductFragment(packageName)) internal fun navigateProduct(packageName: String) = pushFragment(ProductFragment(packageName))
internal fun navigateRepositories() = pushFragment(RepositoriesFragment()) internal fun navigateRepositories() = pushFragment(RepositoriesFragment())
internal fun navigatePreferences() = pushFragment(PreferencesFragment()) internal fun navigatePreferences() = pushFragment(SettingsFragment())
internal fun navigateAddRepository() = pushFragment(EditRepositoryFragment(null)) internal fun navigateAddRepository() = pushFragment(EditRepositoryFragment(null))
internal fun navigateRepository(repositoryId: Long) = pushFragment(RepositoryFragment(repositoryId)) internal fun navigateRepository(repositoryId: Long) =
internal fun navigateEditRepository(repositoryId: Long) = pushFragment(EditRepositoryFragment(repositoryId)) pushFragment(RepositoryFragment(repositoryId))
internal fun navigateEditRepository(repositoryId: Long) =
pushFragment(EditRepositoryFragment(repositoryId))
} }