mirror of
https://github.com/afollestad/nock-nock.git
synced 2025-04-20 03:25:14 +00:00
Basic certificate URI validation
This commit is contained in:
parent
26d6d9abf8
commit
cd1651672f
7 changed files with 84 additions and 31 deletions
|
@ -28,7 +28,7 @@ import com.afollestad.nocknock.data.model.ValidationMode
|
|||
import com.afollestad.nocknock.ui.DarkModeSwitchActivity
|
||||
import com.afollestad.nocknock.ui.viewsite.KEY_SITE
|
||||
import com.afollestad.nocknock.utilities.ext.onTextChanged
|
||||
import com.afollestad.nocknock.utilities.ext.toUri
|
||||
import com.afollestad.nocknock.utilities.ext.setTextAndMaintainSelection
|
||||
import com.afollestad.nocknock.utilities.livedata.distinct
|
||||
import com.afollestad.nocknock.viewcomponents.ext.dimenFloat
|
||||
import com.afollestad.nocknock.viewcomponents.ext.onScroll
|
||||
|
@ -141,6 +141,13 @@ class AddSiteActivity : DarkModeSwitchActivity() {
|
|||
minutesData = viewModel.retryPolicyMinutes
|
||||
)
|
||||
|
||||
// SSL certificate
|
||||
sslCertificateInput.onTextChanged { viewModel.certificateUri.value = it }
|
||||
viewModel.certificateUri.distinct()
|
||||
.observe(this, Observer { sslCertificateInput.setTextAndMaintainSelection(it) })
|
||||
viewModel.onCertificateError()
|
||||
.toViewError(this, sslCertificateInput)
|
||||
|
||||
// Headers
|
||||
headersLayout.attach(viewModel.headers)
|
||||
}
|
||||
|
@ -179,9 +186,6 @@ class AddSiteActivity : DarkModeSwitchActivity() {
|
|||
}
|
||||
|
||||
// SSL certificate
|
||||
sslCertificateInput.onTextChanged { viewModel.certificateUri.value = it.toUri() }
|
||||
viewModel.certificateUri.distinct()
|
||||
.observe(this, Observer { sslCertificateInput.setText(it.toString()) })
|
||||
sslCertificateBrowse.setOnClickListener {
|
||||
val intent = Intent(ACTION_OPEN_DOCUMENT).apply {
|
||||
addCategory(CATEGORY_OPENABLE)
|
||||
|
@ -193,12 +197,11 @@ class AddSiteActivity : DarkModeSwitchActivity() {
|
|||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
appToolbar.elevation =
|
||||
if (scrollView.scrollY > appToolbar.measuredHeight / 2) {
|
||||
appToolbar.dimenFloat(R.dimen.default_elevation)
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
appToolbar.elevation = if (scrollView.scrollY > appToolbar.measuredHeight / 2) {
|
||||
appToolbar.dimenFloat(R.dimen.default_elevation)
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package com.afollestad.nocknock.ui.addsite
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.annotation.CheckResult
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.annotation.VisibleForTesting.PRIVATE
|
||||
|
@ -40,6 +39,7 @@ import com.afollestad.nocknock.data.putSite
|
|||
import com.afollestad.nocknock.engine.validation.ValidationExecutor
|
||||
import com.afollestad.nocknock.ui.ScopedViewModel
|
||||
import com.afollestad.nocknock.utilities.ext.MINUTE
|
||||
import com.afollestad.nocknock.utilities.ext.toUri
|
||||
import com.afollestad.nocknock.utilities.livedata.map
|
||||
import com.afollestad.nocknock.viewcomponents.ext.isNullOrLessThan
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
|
@ -69,7 +69,7 @@ class AddSiteViewModel(
|
|||
val retryPolicyTimes = MutableLiveData<Int>()
|
||||
val retryPolicyMinutes = MutableLiveData<Int>()
|
||||
val headers = MutableLiveData<List<Header>>()
|
||||
val certificateUri = MutableLiveData<Uri>()
|
||||
val certificateUri = MutableLiveData<String>()
|
||||
|
||||
@OnLifecycleEvent(ON_START)
|
||||
fun setDefaults() {
|
||||
|
@ -91,6 +91,7 @@ class AddSiteViewModel(
|
|||
private val validationSearchTermError = MutableLiveData<Int?>()
|
||||
private val validationScriptError = MutableLiveData<Int?>()
|
||||
private val checkIntervalValueError = MutableLiveData<Int?>()
|
||||
private val certificateError = MutableLiveData<Int?>()
|
||||
|
||||
// Expose private properties or calculated properties
|
||||
@CheckResult fun onIsLoading(): LiveData<Boolean> = isLoading
|
||||
|
@ -130,6 +131,8 @@ class AddSiteViewModel(
|
|||
|
||||
@CheckResult fun onCheckIntervalError(): LiveData<Int?> = checkIntervalValueError
|
||||
|
||||
@CheckResult fun onCertificateError(): LiveData<Int?> = certificateError
|
||||
|
||||
// Actions
|
||||
fun commit(done: () -> Unit) {
|
||||
scope.launch {
|
||||
|
@ -228,6 +231,25 @@ class AddSiteViewModel(
|
|||
validationScriptError.value = null
|
||||
}
|
||||
|
||||
// Validate SSL certificate
|
||||
val certString = certificateUri.value
|
||||
if (certString != null) {
|
||||
val rawCertUri = certString.toUri()
|
||||
val certUri = if (rawCertUri.scheme == null) {
|
||||
rawCertUri.buildUpon()
|
||||
.scheme("file")
|
||||
.build()
|
||||
} else {
|
||||
rawCertUri
|
||||
}
|
||||
if (certUri.scheme != "content" && certUri.scheme != "file") {
|
||||
errorCount++
|
||||
certificateError.value = R.string.please_enter_validCertUri
|
||||
} else {
|
||||
certificateError.value = null
|
||||
}
|
||||
}
|
||||
|
||||
if (errorCount > 0) {
|
||||
return null
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import com.afollestad.nocknock.data.model.Site
|
|||
import com.afollestad.nocknock.data.model.ValidationMode
|
||||
import com.afollestad.nocknock.ui.DarkModeSwitchActivity
|
||||
import com.afollestad.nocknock.utilities.ext.onTextChanged
|
||||
import com.afollestad.nocknock.utilities.ext.toUri
|
||||
import com.afollestad.nocknock.utilities.ext.setTextAndMaintainSelection
|
||||
import com.afollestad.nocknock.utilities.livedata.distinct
|
||||
import com.afollestad.nocknock.utilities.providers.IntentProvider
|
||||
import com.afollestad.nocknock.viewcomponents.ext.dimenFloat
|
||||
|
@ -158,6 +158,13 @@ class ViewSiteActivity : DarkModeSwitchActivity() {
|
|||
minutesData = viewModel.retryPolicyMinutes
|
||||
)
|
||||
|
||||
// SSL certificate
|
||||
sslCertificateInput.onTextChanged { viewModel.certificateUri.value = it }
|
||||
viewModel.certificateUri.distinct()
|
||||
.observe(this, Observer { sslCertificateInput.setTextAndMaintainSelection(it) })
|
||||
viewModel.onCertificateError()
|
||||
.toViewError(this, sslCertificateInput)
|
||||
|
||||
// Headers
|
||||
headersLayout.attach(viewModel.headers)
|
||||
|
||||
|
@ -214,7 +221,7 @@ class ViewSiteActivity : DarkModeSwitchActivity() {
|
|||
.isVisible = it
|
||||
})
|
||||
|
||||
// Done button
|
||||
// Done item text
|
||||
viewModel.onDoneButtonText()
|
||||
.observe(this, Observer {
|
||||
toolbar.menu.findItem(R.id.commit)
|
||||
|
@ -222,9 +229,6 @@ class ViewSiteActivity : DarkModeSwitchActivity() {
|
|||
})
|
||||
|
||||
// SSL certificate
|
||||
sslCertificateInput.onTextChanged { viewModel.certificateUri.value = it.toUri() }
|
||||
viewModel.certificateUri.distinct()
|
||||
.observe(this, Observer { sslCertificateInput.setText(it.toString()) })
|
||||
sslCertificateBrowse.setOnClickListener {
|
||||
val intent = Intent(ACTION_OPEN_DOCUMENT).apply {
|
||||
addCategory(CATEGORY_OPENABLE)
|
||||
|
@ -236,12 +240,11 @@ class ViewSiteActivity : DarkModeSwitchActivity() {
|
|||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
appToolbar.elevation =
|
||||
if (scrollView.scrollY > appToolbar.measuredHeight / 2) {
|
||||
appToolbar.dimenFloat(R.dimen.default_elevation)
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
appToolbar.elevation = if (scrollView.scrollY > appToolbar.measuredHeight / 2) {
|
||||
appToolbar.dimenFloat(R.dimen.default_elevation)
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package com.afollestad.nocknock.ui.viewsite
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.annotation.CheckResult
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.annotation.VisibleForTesting.PRIVATE
|
||||
|
@ -24,9 +23,9 @@ import androidx.lifecycle.LiveData
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import com.afollestad.nocknock.R
|
||||
import com.afollestad.nocknock.data.AppDatabase
|
||||
import com.afollestad.nocknock.data.model.RetryPolicy
|
||||
import com.afollestad.nocknock.data.deleteSite
|
||||
import com.afollestad.nocknock.data.model.Header
|
||||
import com.afollestad.nocknock.data.model.RetryPolicy
|
||||
import com.afollestad.nocknock.data.model.Site
|
||||
import com.afollestad.nocknock.data.model.Status
|
||||
import com.afollestad.nocknock.data.model.Status.WAITING
|
||||
|
@ -41,6 +40,7 @@ import com.afollestad.nocknock.engine.validation.ValidationExecutor
|
|||
import com.afollestad.nocknock.notifications.NockNotificationManager
|
||||
import com.afollestad.nocknock.ui.ScopedViewModel
|
||||
import com.afollestad.nocknock.utilities.ext.formatDate
|
||||
import com.afollestad.nocknock.utilities.ext.toUri
|
||||
import com.afollestad.nocknock.utilities.livedata.map
|
||||
import com.afollestad.nocknock.utilities.livedata.zip
|
||||
import com.afollestad.nocknock.utilities.providers.StringProvider
|
||||
|
@ -77,7 +77,7 @@ class ViewSiteViewModel(
|
|||
val retryPolicyTimes = MutableLiveData<Int>()
|
||||
val retryPolicyMinutes = MutableLiveData<Int>()
|
||||
val headers = MutableLiveData<List<Header>>()
|
||||
val certificateUri = MutableLiveData<Uri>()
|
||||
val certificateUri = MutableLiveData<String>()
|
||||
internal val disabled = MutableLiveData<Boolean>()
|
||||
internal val lastResult = MutableLiveData<ValidationResult?>()
|
||||
|
||||
|
@ -89,6 +89,7 @@ class ViewSiteViewModel(
|
|||
private val validationSearchTermError = MutableLiveData<Int?>()
|
||||
private val validationScriptError = MutableLiveData<Int?>()
|
||||
private val checkIntervalValueError = MutableLiveData<Int?>()
|
||||
private val certificateError = MutableLiveData<Int?>()
|
||||
|
||||
// Expose private properties or calculated properties
|
||||
@CheckResult fun onIsLoading(): LiveData<Boolean> = isLoading
|
||||
|
@ -131,6 +132,8 @@ class ViewSiteViewModel(
|
|||
@CheckResult fun onDisableChecksVisibility(): LiveData<Boolean> =
|
||||
disabled.map { !it }
|
||||
|
||||
@CheckResult fun onCertificateError(): LiveData<Int?> = certificateError
|
||||
|
||||
@CheckResult fun onDoneButtonText(): LiveData<Int> =
|
||||
disabled.map {
|
||||
if (it) R.string.renable_and_save_changes
|
||||
|
@ -307,6 +310,25 @@ class ViewSiteViewModel(
|
|||
validationScriptError.value = null
|
||||
}
|
||||
|
||||
// Validate SSL certificate
|
||||
val certString = certificateUri.value
|
||||
if (certString != null) {
|
||||
val rawCertUri = certString.toUri()
|
||||
val certUri = if (rawCertUri.scheme == null) {
|
||||
rawCertUri.buildUpon()
|
||||
.scheme("file")
|
||||
.build()
|
||||
} else {
|
||||
rawCertUri
|
||||
}
|
||||
if (certUri.scheme != "content" && certUri.scheme != "file") {
|
||||
errorCount++
|
||||
certificateError.value = R.string.please_enter_validCertUri
|
||||
} else {
|
||||
certificateError.value = null
|
||||
}
|
||||
}
|
||||
|
||||
if (errorCount > 0) {
|
||||
return null
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import com.afollestad.nocknock.utilities.ext.DAY
|
|||
import com.afollestad.nocknock.utilities.ext.HOUR
|
||||
import com.afollestad.nocknock.utilities.ext.MINUTE
|
||||
import com.afollestad.nocknock.utilities.ext.WEEK
|
||||
import com.afollestad.nocknock.utilities.ext.toUri
|
||||
import kotlin.math.ceil
|
||||
|
||||
fun ViewSiteViewModel.setModel(site: Site) {
|
||||
|
@ -56,9 +55,7 @@ fun ViewSiteViewModel.setModel(site: Site) {
|
|||
setCheckInterval(settings.validationIntervalMs)
|
||||
setRetryPolicy(site.retryPolicy)
|
||||
headers.value = site.headers
|
||||
if (settings.certificate != null) {
|
||||
certificateUri.value = settings.certificate!!.toUri()
|
||||
}
|
||||
certificateUri.value = settings.certificate
|
||||
|
||||
this.disabled.value = settings.disabled
|
||||
this.lastResult.value = site.lastResult
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
<string name="please_enter_search_term">Please input a search term.</string>
|
||||
<string name="please_enter_javaScript">Please input a validation script.</string>
|
||||
<string name="please_enter_networkTimeout">Please enter a network timeout greater than 0.</string>
|
||||
<string name="please_enter_validCertUri">Certificate should be a valid file or content URI.</string>
|
||||
|
||||
<string name="options">Options</string>
|
||||
<string name="remove_site">Remove Site</string>
|
||||
|
|
|
@ -21,7 +21,12 @@ import android.widget.EditText
|
|||
import androidx.annotation.IntRange
|
||||
import kotlin.math.min
|
||||
|
||||
fun EditText.setTextAndMaintainSelection(text: CharSequence) {
|
||||
fun EditText.setTextAndMaintainSelection(text: CharSequence?) {
|
||||
if (text == null) {
|
||||
setText("")
|
||||
return
|
||||
}
|
||||
|
||||
val formerStart = min(selectionStart, text.length)
|
||||
val formerEnd = min(selectionEnd, text.length)
|
||||
setText(text)
|
||||
|
|
Loading…
Add table
Reference in a new issue