mirror of
https://github.com/afollestad/nock-nock.git
synced 2025-04-20 03:25:14 +00:00
Begin to modularize reused view layouts, switch to Material Components theme
This commit is contained in:
parent
c7096e8746
commit
6dfff5bb12
41 changed files with 502 additions and 365 deletions
1
.idea/modules.xml
generated
1
.idea/modules.xml
generated
|
@ -8,6 +8,7 @@
|
|||
<module fileurl="file://$PROJECT_DIR$/nock-nock.iml" filepath="$PROJECT_DIR$/nock-nock.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/notifications/notifications.iml" filepath="$PROJECT_DIR$/notifications/notifications.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/utilities/utilities.iml" filepath="$PROJECT_DIR$/utilities/utilities.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/viewcomponents/viewcomponents.iml" filepath="$PROJECT_DIR$/viewcomponents/viewcomponents.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
|
|
|
@ -22,6 +22,7 @@ dependencies {
|
|||
implementation project(':utilities')
|
||||
implementation project(':engine')
|
||||
implementation project(':notifications')
|
||||
implementation project(':viewcomponents')
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:' + versions.androidx
|
||||
implementation 'androidx.recyclerview:recyclerview:' + versions.androidx
|
||||
|
|
|
@ -25,33 +25,30 @@ import com.afollestad.nocknock.data.ValidationMode.STATUS_CODE
|
|||
import com.afollestad.nocknock.data.ValidationMode.TERM_SEARCH
|
||||
import com.afollestad.nocknock.engine.db.ServerModelStore
|
||||
import com.afollestad.nocknock.engine.statuscheck.CheckStatusManager
|
||||
import com.afollestad.nocknock.utilities.ext.conceal
|
||||
import com.afollestad.nocknock.utilities.ext.hide
|
||||
import com.afollestad.nocknock.utilities.ext.injector
|
||||
import com.afollestad.nocknock.utilities.ext.onEnd
|
||||
import com.afollestad.nocknock.utilities.ext.onItemSelected
|
||||
import com.afollestad.nocknock.utilities.ext.onLayout
|
||||
import com.afollestad.nocknock.utilities.ext.scopeWhileAttached
|
||||
import com.afollestad.nocknock.utilities.ext.show
|
||||
import com.afollestad.nocknock.utilities.ext.showOrHide
|
||||
import com.afollestad.nocknock.utilities.ext.textAsLong
|
||||
import com.afollestad.nocknock.utilities.ext.trimmedText
|
||||
import kotlinx.android.synthetic.main.activity_addsite.checkIntervalInput
|
||||
import kotlinx.android.synthetic.main.activity_addsite.checkIntervalSpinner
|
||||
import kotlinx.android.synthetic.main.activity_addsite.content_loading_progress
|
||||
import com.afollestad.nocknock.viewcomponents.ext.conceal
|
||||
import com.afollestad.nocknock.viewcomponents.ext.hide
|
||||
import com.afollestad.nocknock.viewcomponents.ext.onItemSelected
|
||||
import com.afollestad.nocknock.viewcomponents.ext.onLayout
|
||||
import com.afollestad.nocknock.viewcomponents.ext.show
|
||||
import com.afollestad.nocknock.viewcomponents.ext.showOrHide
|
||||
import com.afollestad.nocknock.viewcomponents.ext.trimmedText
|
||||
import kotlinx.android.synthetic.main.activity_addsite.checkIntervalLayout
|
||||
import kotlinx.android.synthetic.main.activity_addsite.doneBtn
|
||||
import kotlinx.android.synthetic.main.activity_addsite.inputName
|
||||
import kotlinx.android.synthetic.main.activity_addsite.inputUrl
|
||||
import kotlinx.android.synthetic.main.activity_addsite.loadingProgress
|
||||
import kotlinx.android.synthetic.main.activity_addsite.nameTiLayout
|
||||
import kotlinx.android.synthetic.main.activity_addsite.responseValidationMode
|
||||
import kotlinx.android.synthetic.main.activity_addsite.responseValidationSearchTerm
|
||||
import kotlinx.android.synthetic.main.activity_addsite.rootView
|
||||
import kotlinx.android.synthetic.main.activity_addsite.scriptInputLayout
|
||||
import kotlinx.android.synthetic.main.activity_addsite.textUrlWarning
|
||||
import kotlinx.android.synthetic.main.activity_addsite.toolbar
|
||||
import kotlinx.android.synthetic.main.activity_addsite.urlTiLayout
|
||||
import kotlinx.android.synthetic.main.activity_addsite.validationModeDescription
|
||||
import kotlinx.android.synthetic.main.include_script_input.responseValidationScript
|
||||
import kotlinx.android.synthetic.main.include_script_input.responseValidationScriptInput
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.async
|
||||
|
@ -97,14 +94,6 @@ class AddSiteActivity : AppCompatActivity(), View.OnClickListener {
|
|||
rootView.onLayout { circularRevealActivity() }
|
||||
}
|
||||
|
||||
val intervalOptionsAdapter = ArrayAdapter(
|
||||
this,
|
||||
R.layout.list_item_spinner,
|
||||
resources.getStringArray(R.array.interval_options)
|
||||
)
|
||||
intervalOptionsAdapter.setDropDownViewResource(R.layout.list_item_spinner_dropdown)
|
||||
checkIntervalSpinner.adapter = intervalOptionsAdapter
|
||||
|
||||
inputUrl.setOnFocusChangeListener { _, hasFocus ->
|
||||
if (!hasFocus) {
|
||||
val inputStr = inputUrl.text
|
||||
|
@ -137,7 +126,7 @@ class AddSiteActivity : AppCompatActivity(), View.OnClickListener {
|
|||
responseValidationMode.adapter = validationOptionsAdapter
|
||||
responseValidationMode.onItemSelected { pos ->
|
||||
responseValidationSearchTerm.showOrHide(pos == 1)
|
||||
responseValidationScript.showOrHide(pos == 2)
|
||||
scriptInputLayout.showOrHide(pos == 2)
|
||||
|
||||
validationModeDescription.setText(
|
||||
when (pos) {
|
||||
|
@ -200,14 +189,14 @@ class AddSiteActivity : AppCompatActivity(), View.OnClickListener {
|
|||
// Done button
|
||||
override fun onClick(view: View) {
|
||||
isClosing = true
|
||||
var model = ServerModel(
|
||||
var newModel = ServerModel(
|
||||
name = inputName.trimmedText(),
|
||||
url = inputUrl.trimmedText(),
|
||||
status = WAITING,
|
||||
validationMode = STATUS_CODE
|
||||
)
|
||||
|
||||
if (model.name.isEmpty()) {
|
||||
if (newModel.name.isEmpty()) {
|
||||
nameTiLayout.error = getString(R.string.please_enter_name)
|
||||
isClosing = false
|
||||
return
|
||||
|
@ -215,42 +204,42 @@ class AddSiteActivity : AppCompatActivity(), View.OnClickListener {
|
|||
nameTiLayout.error = null
|
||||
}
|
||||
|
||||
if (model.url.isEmpty()) {
|
||||
if (newModel.url.isEmpty()) {
|
||||
urlTiLayout.error = getString(R.string.please_enter_url)
|
||||
isClosing = false
|
||||
return
|
||||
} else {
|
||||
urlTiLayout.error = null
|
||||
if (!WEB_URL.matcher(model.url).find()) {
|
||||
if (!WEB_URL.matcher(newModel.url).find()) {
|
||||
urlTiLayout.error = getString(R.string.please_enter_valid_url)
|
||||
isClosing = false
|
||||
return
|
||||
} else {
|
||||
val uri = Uri.parse(model.url)
|
||||
val uri = Uri.parse(newModel.url)
|
||||
if (uri.scheme == null) {
|
||||
model = model.copy(url = "http://${model.url}")
|
||||
newModel = newModel.copy(url = "http://${newModel.url}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val parsedCheckInterval = getParsedCheckInterval()
|
||||
val selectedCheckInterval = checkIntervalLayout.getSelectedCheckInterval()
|
||||
val selectedValidationMode = getSelectedValidationMode()
|
||||
val selectedValidationContent = getSelectedValidationContent()
|
||||
|
||||
model = model.copy(
|
||||
checkInterval = parsedCheckInterval,
|
||||
lastCheck = currentTimeMillis() - parsedCheckInterval,
|
||||
newModel = newModel.copy(
|
||||
checkInterval = selectedCheckInterval,
|
||||
lastCheck = currentTimeMillis() - selectedCheckInterval,
|
||||
validationMode = selectedValidationMode,
|
||||
validationContent = selectedValidationContent
|
||||
)
|
||||
|
||||
rootView.scopeWhileAttached(Main) {
|
||||
launch(coroutineContext) {
|
||||
content_loading_progress.show()
|
||||
val storedModel = async(IO) { serverModelStore.put(model) }.await()
|
||||
loadingProgress.setLoading()
|
||||
val storedModel = async(IO) { serverModelStore.put(newModel) }.await()
|
||||
checkStatusManager.cancelCheck(storedModel)
|
||||
checkStatusManager.scheduleCheck(storedModel, rightNow = true)
|
||||
content_loading_progress.hide()
|
||||
loadingProgress.setDone()
|
||||
|
||||
setResult(RESULT_OK)
|
||||
finish()
|
||||
|
@ -261,16 +250,6 @@ class AddSiteActivity : AppCompatActivity(), View.OnClickListener {
|
|||
|
||||
override fun onBackPressed() = closeActivityWithReveal()
|
||||
|
||||
private fun getParsedCheckInterval(): Long {
|
||||
val intervalInput = checkIntervalInput.textAsLong()
|
||||
return when (checkIntervalSpinner.selectedItemPosition) {
|
||||
0 -> intervalInput * (60 * 1000)
|
||||
1 -> intervalInput * (60 * 60 * 1000)
|
||||
2 -> intervalInput * (60 * 60 * 24 * 1000)
|
||||
else -> intervalInput * (60 * 60 * 24 * 7 * 1000)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSelectedValidationMode() = when (responseValidationMode.selectedItemPosition) {
|
||||
0 -> STATUS_CODE
|
||||
1 -> TERM_SEARCH
|
||||
|
@ -285,7 +264,7 @@ class AddSiteActivity : AppCompatActivity(), View.OnClickListener {
|
|||
private fun getSelectedValidationContent() = when (responseValidationMode.selectedItemPosition) {
|
||||
0 -> null
|
||||
1 -> responseValidationSearchTerm.trimmedText()
|
||||
2 -> responseValidationScriptInput.trimmedText()
|
||||
2 -> scriptInputLayout.getCode()
|
||||
else -> {
|
||||
throw IllegalStateException(
|
||||
"Unexpected validation mode index: ${responseValidationMode.selectedItemPosition}"
|
||||
|
|
|
@ -44,8 +44,8 @@ import com.afollestad.nocknock.utilities.ext.onEnd
|
|||
import com.afollestad.nocknock.utilities.ext.safeRegisterReceiver
|
||||
import com.afollestad.nocknock.utilities.ext.safeUnregisterReceiver
|
||||
import com.afollestad.nocknock.utilities.ext.scopeWhileAttached
|
||||
import com.afollestad.nocknock.utilities.ext.show
|
||||
import com.afollestad.nocknock.utilities.ext.showOrHide
|
||||
import com.afollestad.nocknock.viewcomponents.ext.show
|
||||
import com.afollestad.nocknock.viewcomponents.ext.showOrHide
|
||||
import com.afollestad.nocknock.utilities.util.MathUtil.bezierCurve
|
||||
import kotlinx.android.synthetic.main.activity_main.emptyText
|
||||
import kotlinx.android.synthetic.main.activity_main.fab
|
||||
|
|
|
@ -35,46 +35,38 @@ import com.afollestad.nocknock.engine.db.ServerModelStore
|
|||
import com.afollestad.nocknock.engine.statuscheck.CheckStatusJob.Companion.ACTION_STATUS_UPDATE
|
||||
import com.afollestad.nocknock.engine.statuscheck.CheckStatusManager
|
||||
import com.afollestad.nocknock.notifications.NockNotificationManager
|
||||
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.formatDate
|
||||
import com.afollestad.nocknock.utilities.ext.hide
|
||||
import com.afollestad.nocknock.utilities.ext.injector
|
||||
import com.afollestad.nocknock.utilities.ext.isHttpOrHttps
|
||||
import com.afollestad.nocknock.utilities.ext.onItemSelected
|
||||
import com.afollestad.nocknock.utilities.ext.safeRegisterReceiver
|
||||
import com.afollestad.nocknock.utilities.ext.safeUnregisterReceiver
|
||||
import com.afollestad.nocknock.utilities.ext.scopeWhileAttached
|
||||
import com.afollestad.nocknock.utilities.ext.show
|
||||
import com.afollestad.nocknock.utilities.ext.showOrHide
|
||||
import com.afollestad.nocknock.utilities.ext.textAsLong
|
||||
import com.afollestad.nocknock.utilities.ext.trimmedText
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.checkIntervalInput
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.checkIntervalSpinner
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.content_loading_progress
|
||||
import com.afollestad.nocknock.viewcomponents.ext.hide
|
||||
import com.afollestad.nocknock.viewcomponents.ext.onItemSelected
|
||||
import com.afollestad.nocknock.viewcomponents.ext.show
|
||||
import com.afollestad.nocknock.viewcomponents.ext.showOrHide
|
||||
import com.afollestad.nocknock.viewcomponents.ext.trimmedText
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.checkIntervalLayout
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.doneBtn
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.iconStatus
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.inputName
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.inputUrl
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.loadingProgress
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.responseValidationMode
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.responseValidationSearchTerm
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.rootView
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.scriptInputLayout
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.textLastCheckResult
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.textNextCheck
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.textUrlWarning
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.toolbar
|
||||
import kotlinx.android.synthetic.main.activity_viewsite.validationModeDescription
|
||||
import kotlinx.android.synthetic.main.include_script_input.responseValidationScript
|
||||
import kotlinx.android.synthetic.main.include_script_input.responseValidationScriptInput
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import java.lang.System.currentTimeMillis
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.ceil
|
||||
|
||||
private const val KEY_VIEW_MODEL = "site_model"
|
||||
|
||||
|
@ -130,14 +122,6 @@ class ViewSiteActivity : AppCompatActivity(),
|
|||
setOnMenuItemClickListener(this@ViewSiteActivity)
|
||||
}
|
||||
|
||||
val intervalOptionsAdapter = ArrayAdapter(
|
||||
this,
|
||||
R.layout.list_item_spinner,
|
||||
resources.getStringArray(R.array.interval_options)
|
||||
)
|
||||
intervalOptionsAdapter.setDropDownViewResource(R.layout.list_item_spinner_dropdown)
|
||||
checkIntervalSpinner.adapter = intervalOptionsAdapter
|
||||
|
||||
inputUrl.setOnFocusChangeListener { _, hasFocus ->
|
||||
if (!hasFocus) {
|
||||
val inputStr = inputUrl.text
|
||||
|
@ -170,7 +154,7 @@ class ViewSiteActivity : AppCompatActivity(),
|
|||
|
||||
responseValidationMode.onItemSelected { pos ->
|
||||
responseValidationSearchTerm.showOrHide(pos == 1)
|
||||
responseValidationScript.showOrHide(pos == 2)
|
||||
scriptInputLayout.showOrHide(pos == 2)
|
||||
|
||||
validationModeDescription.setText(
|
||||
when (pos) {
|
||||
|
@ -213,45 +197,13 @@ class ViewSiteActivity : AppCompatActivity(),
|
|||
|
||||
if (this.checkInterval == 0L) {
|
||||
textNextCheck.setText(R.string.none_turned_off)
|
||||
checkIntervalInput.setText("")
|
||||
checkIntervalSpinner.setSelection(0)
|
||||
checkIntervalLayout.clear()
|
||||
} else {
|
||||
var lastCheck = this.lastCheck
|
||||
if (lastCheck == 0L) {
|
||||
lastCheck = currentTimeMillis()
|
||||
}
|
||||
textNextCheck.text = (lastCheck + this.checkInterval).formatDate()
|
||||
|
||||
when {
|
||||
this.checkInterval >= WEEK -> {
|
||||
checkIntervalInput.setText(
|
||||
ceil((this.checkInterval.toFloat() / WEEK).toDouble()).toInt().toString()
|
||||
)
|
||||
checkIntervalSpinner.setSelection(3)
|
||||
}
|
||||
this.checkInterval >= DAY -> {
|
||||
checkIntervalInput.setText(
|
||||
ceil((this.checkInterval.toFloat() / DAY.toFloat()).toDouble()).toInt().toString()
|
||||
)
|
||||
checkIntervalSpinner.setSelection(2)
|
||||
}
|
||||
this.checkInterval >= HOUR -> {
|
||||
checkIntervalInput.setText(
|
||||
ceil((this.checkInterval.toFloat() / HOUR.toFloat()).toDouble()).toInt().toString()
|
||||
)
|
||||
checkIntervalSpinner.setSelection(1)
|
||||
}
|
||||
this.checkInterval >= MINUTE -> {
|
||||
checkIntervalInput.setText(
|
||||
ceil((this.checkInterval.toFloat() / MINUTE.toFloat()).toDouble()).toInt().toString()
|
||||
)
|
||||
checkIntervalSpinner.setSelection(0)
|
||||
}
|
||||
else -> {
|
||||
checkIntervalInput.setText("0")
|
||||
checkIntervalSpinner.setSelection(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
responseValidationMode.setSelection(validationMode.value - 1)
|
||||
|
@ -259,13 +211,11 @@ class ViewSiteActivity : AppCompatActivity(),
|
|||
when (this.validationMode) {
|
||||
TERM_SEARCH -> responseValidationSearchTerm.setText(this.validationContent ?: "")
|
||||
JAVASCRIPT -> {
|
||||
responseValidationScriptInput.setText(
|
||||
this.validationContent ?: getString(R.string.default_js)
|
||||
)
|
||||
scriptInputLayout.setCode(this.validationContent)
|
||||
}
|
||||
else -> {
|
||||
responseValidationSearchTerm.setText("")
|
||||
responseValidationScriptInput.setText("")
|
||||
scriptInputLayout.clear()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -316,13 +266,13 @@ class ViewSiteActivity : AppCompatActivity(),
|
|||
}
|
||||
}
|
||||
|
||||
val parsedCheckInterval = getParsedCheckInterval()
|
||||
val selectedCheckInterval = checkIntervalLayout.getSelectedCheckInterval()
|
||||
val selectedValidationMode = getSelectedValidationMode()
|
||||
val selectedValidationContent = getSelectedValidationContent()
|
||||
|
||||
currentModel = currentModel.copy(
|
||||
checkInterval = parsedCheckInterval,
|
||||
lastCheck = currentTimeMillis() - parsedCheckInterval,
|
||||
checkInterval = selectedCheckInterval,
|
||||
lastCheck = currentTimeMillis() - selectedCheckInterval,
|
||||
validationMode = selectedValidationMode,
|
||||
validationContent = selectedValidationContent
|
||||
)
|
||||
|
@ -334,10 +284,10 @@ class ViewSiteActivity : AppCompatActivity(),
|
|||
override fun onClick(view: View) {
|
||||
rootView.scopeWhileAttached(Main) {
|
||||
launch(coroutineContext) {
|
||||
content_loading_progress.show()
|
||||
loadingProgress.setLoading()
|
||||
if (!updateModelFromInput(true)) {
|
||||
// Validation didn't pass
|
||||
content_loading_progress.hide()
|
||||
loadingProgress.setDone()
|
||||
return@launch
|
||||
}
|
||||
|
||||
|
@ -345,7 +295,7 @@ class ViewSiteActivity : AppCompatActivity(),
|
|||
checkStatusManager.cancelCheck(currentModel)
|
||||
checkStatusManager.scheduleCheck(currentModel, rightNow = true)
|
||||
|
||||
content_loading_progress.hide()
|
||||
loadingProgress.setDone()
|
||||
setResult(RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
|
@ -357,7 +307,7 @@ class ViewSiteActivity : AppCompatActivity(),
|
|||
R.id.refresh -> {
|
||||
rootView.scopeWhileAttached(Main) {
|
||||
launch(coroutineContext) {
|
||||
content_loading_progress.show()
|
||||
loadingProgress.setLoading()
|
||||
updateModelFromInput(false)
|
||||
currentModel = currentModel.copy(status = WAITING)
|
||||
displayCurrentModel()
|
||||
|
@ -366,7 +316,7 @@ class ViewSiteActivity : AppCompatActivity(),
|
|||
|
||||
checkStatusManager.cancelCheck(currentModel)
|
||||
checkStatusManager.scheduleCheck(currentModel, rightNow = true)
|
||||
content_loading_progress.hide()
|
||||
loadingProgress.setDone()
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
@ -400,9 +350,9 @@ class ViewSiteActivity : AppCompatActivity(),
|
|||
private fun performRemoveSite(model: ServerModel) {
|
||||
rootView.scopeWhileAttached(Main) {
|
||||
launch(coroutineContext) {
|
||||
content_loading_progress.show()
|
||||
loadingProgress.setLoading()
|
||||
async(IO) { serverModelStore.delete(model) }.await()
|
||||
content_loading_progress.hide()
|
||||
loadingProgress.setDone()
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
@ -413,16 +363,6 @@ class ViewSiteActivity : AppCompatActivity(),
|
|||
item.isEnabled = currentModel.status != CHECKING && currentModel.status != WAITING
|
||||
}
|
||||
|
||||
private fun getParsedCheckInterval(): Long {
|
||||
val intervalInput = checkIntervalInput.textAsLong()
|
||||
return when (checkIntervalSpinner.selectedItemPosition) {
|
||||
0 -> intervalInput * (60 * 1000)
|
||||
1 -> intervalInput * (60 * 60 * 1000)
|
||||
2 -> intervalInput * (60 * 60 * 24 * 1000)
|
||||
else -> intervalInput * (60 * 60 * 24 * 7 * 1000)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSelectedValidationMode() = when (responseValidationMode.selectedItemPosition) {
|
||||
0 -> STATUS_CODE
|
||||
1 -> TERM_SEARCH
|
||||
|
@ -437,7 +377,7 @@ class ViewSiteActivity : AppCompatActivity(),
|
|||
private fun getSelectedValidationContent() = when (responseValidationMode.selectedItemPosition) {
|
||||
0 -> null
|
||||
1 -> responseValidationSearchTerm.trimmedText()
|
||||
2 -> responseValidationScriptInput.trimmedText()
|
||||
2 -> scriptInputLayout.getCode()
|
||||
else -> {
|
||||
throw IllegalStateException(
|
||||
"Unexpected validation mode index: ${responseValidationMode.selectedItemPosition}"
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
|
||||
<solid android:color="@color/md_green"/>
|
||||
|
||||
<stroke android:color="#424242"/>
|
||||
|
||||
<size
|
||||
android:width="@dimen/list_circle_size"
|
||||
android:height="@dimen/list_circle_size"/>
|
||||
</shape>
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
|
||||
<solid android:color="@color/md_red"/>
|
||||
|
||||
<stroke android:color="#424242"/>
|
||||
|
||||
<size
|
||||
android:width="@dimen/list_circle_size"
|
||||
android:height="@dimen/list_circle_size"/>
|
||||
</shape>
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
|
||||
<solid android:color="@color/md_yellow"/>
|
||||
|
||||
<stroke android:color="#424242"/>
|
||||
|
||||
<size
|
||||
android:width="@dimen/list_circle_size"
|
||||
android:height="@dimen/list_circle_size"/>
|
||||
</shape>
|
|
@ -96,62 +96,13 @@
|
|||
tools:text="Warning: this app checks for server availability with HTTP requests. It's recommended that you use an HTTP URL."
|
||||
/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="@dimen/content_inset"
|
||||
android:background="@color/dividerColorDark"
|
||||
/>
|
||||
<include layout="@layout/include_divider"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/checkIntervalLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/content_inset"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/check_interval"
|
||||
android:textColor="?colorAccent"
|
||||
android:textSize="@dimen/caption_font_size"
|
||||
/>
|
||||
|
||||
<LinearLayout
|
||||
<com.afollestad.nocknock.viewcomponents.CheckIntervalLayout
|
||||
android:id="@+id/checkIntervalLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="2"
|
||||
>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/checkIntervalInput"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginEnd="@dimen/content_inset_half"
|
||||
android:layout_marginStart="-4dp"
|
||||
android:layout_weight="1"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:hint="0"
|
||||
android:inputType="number"
|
||||
android:textSize="@dimen/body_font_size"
|
||||
tools:ignore="HardcodedText,LabelFor"
|
||||
/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/checkIntervalSpinner"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_marginEnd="-4dp"
|
||||
android:layout_weight="1"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="@dimen/content_inset"
|
||||
android:background="@color/dividerColorDark"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
|
@ -186,7 +137,14 @@
|
|||
tools:ignore="Autofill"
|
||||
/>
|
||||
|
||||
<include layout="@layout/include_script_input"/>
|
||||
<com.afollestad.nocknock.viewcomponents.JavaScriptInputLayout
|
||||
android:id="@+id/scriptInputLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/content_inset"
|
||||
android:layout_marginTop="@dimen/content_inset_half"
|
||||
android:background="@color/colorPrimaryDark"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/validationModeDescription"
|
||||
|
@ -199,15 +157,13 @@
|
|||
android:textSize="@dimen/body_font_size"
|
||||
/>
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/doneBtn"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="-4dp"
|
||||
android:layout_marginRight="-4dp"
|
||||
android:layout_marginTop="@dimen/content_inset"
|
||||
android:text="@string/done"
|
||||
android:textColor="#fff"
|
||||
style="@style/AccentButton"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -216,21 +172,10 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/content_loading_progress"
|
||||
<com.afollestad.nocknock.viewcomponents.LoadingIndicatorFrame
|
||||
android:id="@+id/loadingProgress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#40000000"
|
||||
android:visibility="gone"
|
||||
>
|
||||
|
||||
<ProgressBar
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
style="?android:progressBarStyleLarge"
|
||||
/>
|
||||
|
||||
</FrameLayout>
|
||||
/>
|
||||
|
||||
</FrameLayout>
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
android:orientation="horizontal"
|
||||
>
|
||||
|
||||
<com.afollestad.nocknock.views.StatusImageView
|
||||
<com.afollestad.nocknock.viewcomponents.StatusImageView
|
||||
android:id="@+id/iconStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -110,56 +110,19 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
<include
|
||||
layout="@layout/include_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="@dimen/content_inset_less"
|
||||
android:background="@color/dividerColorDark"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/checkIntervalLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/content_inset"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/check_interval"
|
||||
android:textColor="?colorAccent"
|
||||
android:textSize="@dimen/caption_font_size"
|
||||
/>
|
||||
|
||||
<LinearLayout
|
||||
<com.afollestad.nocknock.viewcomponents.CheckIntervalLayout
|
||||
android:id="@+id/checkIntervalLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="2"
|
||||
>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/checkIntervalInput"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginEnd="@dimen/content_inset_half"
|
||||
android:layout_marginStart="-4dp"
|
||||
android:layout_weight="1"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:hint="0"
|
||||
android:inputType="number"
|
||||
android:textSize="@dimen/body_font_size"
|
||||
tools:ignore="HardcodedText,LabelFor"
|
||||
/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/checkIntervalSpinner"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_marginEnd="-4dp"
|
||||
android:layout_weight="1"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
android:layout_marginTop="@dimen/content_inset"
|
||||
/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
|
@ -197,9 +160,17 @@
|
|||
android:hint="@string/search_term"
|
||||
android:textSize="@dimen/body_font_size"
|
||||
android:visibility="gone"
|
||||
tools:ignore="Autofill,TextFields"
|
||||
/>
|
||||
|
||||
<include layout="@layout/include_script_input"/>
|
||||
<com.afollestad.nocknock.viewcomponents.JavaScriptInputLayout
|
||||
android:id="@+id/scriptInputLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/content_inset"
|
||||
android:layout_marginTop="@dimen/content_inset_half"
|
||||
android:background="@color/colorPrimaryDark"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/validationModeDescription"
|
||||
|
@ -212,12 +183,7 @@
|
|||
android:textSize="@dimen/body_font_size"
|
||||
/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="@dimen/content_inset"
|
||||
android:background="@color/dividerColorDark"
|
||||
/>
|
||||
<include layout="@layout/include_divider"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -259,15 +225,13 @@
|
|||
tools:text="In 2 hours"
|
||||
/>
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/doneBtn"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="-4dp"
|
||||
android:layout_marginRight="-4dp"
|
||||
android:layout_marginTop="@dimen/content_inset_more"
|
||||
android:text="@string/save"
|
||||
android:textColor="#fff"
|
||||
style="@style/AccentButton"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -276,21 +240,10 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/content_loading_progress"
|
||||
<com.afollestad.nocknock.viewcomponents.LoadingIndicatorFrame
|
||||
android:id="@+id/loadingProgress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#40000000"
|
||||
android:visibility="gone"
|
||||
>
|
||||
|
||||
<ProgressBar
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
style="?android:progressBarStyleLarge"
|
||||
/>
|
||||
|
||||
</FrameLayout>
|
||||
/>
|
||||
|
||||
</FrameLayout>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
android:paddingTop="@dimen/content_inset_less"
|
||||
>
|
||||
|
||||
<com.afollestad.nocknock.views.StatusImageView
|
||||
<com.afollestad.nocknock.viewcomponents.StatusImageView
|
||||
android:id="@+id/iconStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string-array name="interval_options">
|
||||
<item>Minute(s)</item>
|
||||
<item>Hour(s)</item>
|
||||
<item>Day(s)</item>
|
||||
<item>Week(s)</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="site_long_options" translatable="false">
|
||||
<item>@string/refresh_status</item>
|
||||
<item>@string/remove_site</item>
|
||||
|
|
|
@ -7,10 +7,4 @@
|
|||
|
||||
<color name="dividerColor">#EEEEEE</color>
|
||||
|
||||
<color name="md_red">#E53935</color>
|
||||
<color name="md_yellow">#FDD835</color>
|
||||
<color name="md_green">#43A047</color>
|
||||
|
||||
<color name="dividerColorDark">#37474F</color>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -2,20 +2,11 @@
|
|||
|
||||
<dimen name="title_font_size">20sp</dimen>
|
||||
<dimen name="medium_text_size">16sp</dimen>
|
||||
<dimen name="body_font_size">14sp</dimen>
|
||||
<dimen name="caption_font_size">12sp</dimen>
|
||||
<dimen name="empty_text_size">26sp</dimen>
|
||||
|
||||
<dimen name="content_inset_half">8dp</dimen>
|
||||
<dimen name="content_inset_less">12dp</dimen>
|
||||
<dimen name="content_inset">16dp</dimen>
|
||||
<dimen name="content_inset_more">24dp</dimen>
|
||||
|
||||
<dimen name="list_circle_size">42dp</dimen>
|
||||
<dimen name="list_text_spacing">4dp</dimen>
|
||||
|
||||
<dimen name="fab_elevation">4dp</dimen>
|
||||
<dimen name="fab_elevation_pressed">8dp</dimen>
|
||||
<dimen name="button_height">52dp</dimen>
|
||||
<dimen name="code_font_size">14sp</dimen>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
<string name="add_site">Add Site</string>
|
||||
<string name="site_name">Site Name</string>
|
||||
<string name="site_url">Site URL</string>
|
||||
<string name="check_interval">Check Interval</string>
|
||||
<string name="done">Done</string>
|
||||
<string name="please_enter_name">Please enter a name!</string>
|
||||
<string name="please_enter_url">Please enter a URL.</string>
|
||||
|
@ -42,9 +41,6 @@
|
|||
<string name="warning_http_url">
|
||||
Warning: this app checks for server availability with HTTP requests. It\'s recommended that you use an HTTP URL.
|
||||
</string>
|
||||
<string name="default_js">var responseObj = JSON.parse(response);\nreturn responseObj.success === true;</string>
|
||||
<string name="function_declaration">function validate(response) {</string>
|
||||
<string name="function_end">}</string>
|
||||
<string name="response_validation_mode">Response Validation Mode</string>
|
||||
<string name="search_term">Search term…</string>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<resources>
|
||||
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<item name="android:textColorSecondary">#727272</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Ink" parent="Theme.AppCompat.NoActionBar">
|
||||
<style name="AppTheme.Ink" parent="Theme.MaterialComponents.NoActionBar">
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
|
@ -27,7 +27,7 @@
|
|||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="AccentButton" parent="Widget.AppCompat.Button.Colored">
|
||||
<style name="AccentButton" parent="Widget.MaterialComponents.Button">
|
||||
<item name="android:textColor">#fff</item>
|
||||
<item name="android:colorButtonNormal">@color/colorAccent</item>
|
||||
</style>
|
||||
|
|
|
@ -1 +1 @@
|
|||
include ':app', ':engine', ':notifications', ':data', ':utilities'
|
||||
include ':app', ':engine', ':notifications', ':data', ':utilities', ':viewcomponents'
|
||||
|
|
1
viewcomponents/.gitignore
vendored
Normal file
1
viewcomponents/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/build
|
29
viewcomponents/build.gradle
Normal file
29
viewcomponents/build.gradle
Normal file
|
@ -0,0 +1,29 @@
|
|||
apply from: '../dependencies.gradle'
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
android {
|
||||
compileSdkVersion versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion versions.minSdk
|
||||
targetSdkVersion versions.compileSdk
|
||||
versionCode versions.publishVersionCode
|
||||
versionName versions.publishVersion
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':utilities')
|
||||
implementation project(':data')
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:' + versions.androidx
|
||||
|
||||
api 'com.squareup.okhttp3:okhttp:' + versions.okHttp
|
||||
|
||||
implementation 'com.google.dagger:dagger:' + versions.dagger
|
||||
kapt 'com.google.dagger:dagger-compiler:' + versions.dagger
|
||||
}
|
||||
|
||||
apply from: '../spotless.gradle'
|
2
viewcomponents/src/main/AndroidManifest.xml
Normal file
2
viewcomponents/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,2 @@
|
|||
<manifest
|
||||
package="com.afollestad.nocknock.viewcomponents"/>
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Licensed under Apache-2.0
|
||||
*
|
||||
* Designed and developed by Aidan Follestad (@afollestad)
|
||||
*/
|
||||
package com.afollestad.nocknock.viewcomponents
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.LinearLayout
|
||||
import androidx.annotation.CheckResult
|
||||
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.viewcomponents.R.array
|
||||
import com.afollestad.nocknock.viewcomponents.ext.textAsLong
|
||||
import kotlinx.android.synthetic.main.check_interval_layout.view.input
|
||||
import kotlinx.android.synthetic.main.check_interval_layout.view.spinner
|
||||
import kotlin.math.ceil
|
||||
|
||||
/** @author Aidan Follestad (afollestad) */
|
||||
class CheckIntervalLayout(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null
|
||||
) : LinearLayout(context, attrs) {
|
||||
|
||||
companion object {
|
||||
private const val INDEX_MINUTE = 0
|
||||
private const val INDEX_HOUR = 1
|
||||
private const val INDEX_DAY = 2
|
||||
private const val INDEX_WEEK = 3
|
||||
}
|
||||
|
||||
init {
|
||||
orientation = VERTICAL
|
||||
inflate(context, R.layout.check_interval_layout, this)
|
||||
}
|
||||
|
||||
override fun onFinishInflate() {
|
||||
super.onFinishInflate()
|
||||
val spinnerAdapter = ArrayAdapter(
|
||||
context,
|
||||
R.layout.list_item_spinner,
|
||||
resources.getStringArray(array.interval_options)
|
||||
)
|
||||
spinnerAdapter.setDropDownViewResource(R.layout.list_item_spinner_dropdown)
|
||||
spinner.adapter = spinnerAdapter
|
||||
}
|
||||
|
||||
fun set(interval: Long) {
|
||||
when {
|
||||
interval >= WEEK -> {
|
||||
input.setText(calculateDisplayValue(interval, WEEK))
|
||||
spinner.setSelection(3)
|
||||
}
|
||||
interval >= DAY -> {
|
||||
input.setText(calculateDisplayValue(interval, DAY))
|
||||
spinner.setSelection(2)
|
||||
}
|
||||
interval >= HOUR -> {
|
||||
input.setText(calculateDisplayValue(interval, HOUR))
|
||||
spinner.setSelection(1)
|
||||
}
|
||||
interval >= MINUTE -> {
|
||||
input.setText(calculateDisplayValue(interval, MINUTE))
|
||||
spinner.setSelection(0)
|
||||
}
|
||||
else -> {
|
||||
input.setText("0")
|
||||
spinner.setSelection(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
input.setText("")
|
||||
spinner.setSelection(0)
|
||||
}
|
||||
|
||||
@CheckResult fun getSelectedCheckInterval(): Long {
|
||||
val intervalInput = input.textAsLong()
|
||||
val spinnerPos = spinner.selectedItemPosition
|
||||
return when (spinnerPos) {
|
||||
INDEX_MINUTE -> intervalInput * MINUTE
|
||||
INDEX_HOUR -> intervalInput * HOUR
|
||||
INDEX_DAY -> intervalInput * DAY
|
||||
INDEX_WEEK -> intervalInput * WEEK
|
||||
else -> throw IllegalStateException("Unexpected index: $spinnerPos")
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateDisplayValue(
|
||||
interval: Long,
|
||||
by: Long
|
||||
): String {
|
||||
val intervalFloat = interval.toFloat()
|
||||
val byFloat = by.toFloat()
|
||||
return ceil(intervalFloat / byFloat).toInt()
|
||||
.toString()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Licensed under Apache-2.0
|
||||
*
|
||||
* Designed and developed by Aidan Follestad (@afollestad)
|
||||
*/
|
||||
package com.afollestad.nocknock.viewcomponents
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.HorizontalScrollView
|
||||
import androidx.annotation.CheckResult
|
||||
import com.afollestad.nocknock.viewcomponents.ext.dimenFloat
|
||||
import com.afollestad.nocknock.viewcomponents.ext.dimenInt
|
||||
import com.afollestad.nocknock.viewcomponents.ext.trimmedText
|
||||
import kotlinx.android.synthetic.main.javascript_input_layout.view.userInput
|
||||
|
||||
/** @author Aidan Follestad (afollestad) */
|
||||
class JavaScriptInputLayout(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null
|
||||
) : HorizontalScrollView(context, attrs) {
|
||||
|
||||
init {
|
||||
val contentInset = dimenInt(R.dimen.content_inset)
|
||||
val contentInsetHalf = dimenInt(R.dimen.content_inset_half)
|
||||
setPadding(
|
||||
contentInsetHalf, // left
|
||||
contentInset, // top
|
||||
contentInsetHalf, // right
|
||||
contentInset // bottom
|
||||
)
|
||||
elevation = dimenFloat(R.dimen.default_elevation)
|
||||
inflate(context, R.layout.javascript_input_layout, this)
|
||||
}
|
||||
|
||||
fun setCode(code: String?) {
|
||||
if (code.isNullOrEmpty()) {
|
||||
setDefaultCode()
|
||||
return
|
||||
}
|
||||
userInput.setText(code.trim())
|
||||
}
|
||||
|
||||
fun setDefaultCode() = userInput.setText(R.string.default_js)
|
||||
|
||||
@CheckResult fun getCode() = userInput.trimmedText()
|
||||
|
||||
fun clear() = userInput.setText("")
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Licensed under Apache-2.0
|
||||
*
|
||||
* Designed and developed by Aidan Follestad (@afollestad)
|
||||
*/
|
||||
package com.afollestad.nocknock.viewcomponents
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.afollestad.nocknock.viewcomponents.ext.hide
|
||||
import com.afollestad.nocknock.viewcomponents.ext.show
|
||||
|
||||
/** @author Aidan Follestad (@afollestad) */
|
||||
class LoadingIndicatorFrame(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null
|
||||
) : FrameLayout(context, attrs) {
|
||||
companion object {
|
||||
private const val SHOW_DELAY_MS = 200L
|
||||
}
|
||||
|
||||
private val showRunnable = Runnable { show() }
|
||||
|
||||
init {
|
||||
setBackgroundColor(ContextCompat.getColor(context, R.color.loading_indicator_frame_background))
|
||||
hide() // hide self by default
|
||||
inflate(context, R.layout.loading_indicator_frame, this)
|
||||
}
|
||||
|
||||
fun setLoading() {
|
||||
handler.postDelayed(showRunnable, SHOW_DELAY_MS)
|
||||
}
|
||||
|
||||
fun setDone() {
|
||||
handler.removeCallbacks(showRunnable)
|
||||
hide()
|
||||
}
|
||||
}
|
|
@ -3,12 +3,11 @@
|
|||
*
|
||||
* Designed and developed by Aidan Follestad (@afollestad)
|
||||
*/
|
||||
package com.afollestad.nocknock.views
|
||||
package com.afollestad.nocknock.viewcomponents
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import com.afollestad.nocknock.R
|
||||
import com.afollestad.nocknock.data.ServerStatus
|
||||
import com.afollestad.nocknock.data.ServerStatus.CHECKING
|
||||
import com.afollestad.nocknock.data.ServerStatus.ERROR
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Designed and developed by Aidan Follestad (@afollestad)
|
||||
*/
|
||||
package com.afollestad.nocknock.utilities.ext
|
||||
package com.afollestad.nocknock.viewcomponents.ext
|
||||
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
|
@ -13,6 +13,7 @@ import android.view.ViewTreeObserver
|
|||
import android.widget.AdapterView
|
||||
import android.widget.Spinner
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.DimenRes
|
||||
|
||||
fun View.show() {
|
||||
visibility = VISIBLE
|
||||
|
@ -59,3 +60,7 @@ fun View.onLayout(cb: () -> Unit) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun View.dimenFloat(@DimenRes res: Int) = resources.getDimension(res)
|
||||
|
||||
fun View.dimenInt(@DimenRes res: Int) = resources.getDimensionPixelSize(res)
|
10
viewcomponents/src/main/res/drawable/green_circle.xml
Normal file
10
viewcomponents/src/main/res/drawable/green_circle.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/md_green"/>
|
||||
<stroke android:color="#424242"/>
|
||||
<size
|
||||
android:height="@dimen/list_circle_size"
|
||||
android:width="@dimen/list_circle_size"/>
|
||||
</shape>
|
10
viewcomponents/src/main/res/drawable/red_circle.xml
Normal file
10
viewcomponents/src/main/res/drawable/red_circle.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/md_red"/>
|
||||
<stroke android:color="#424242"/>
|
||||
<size
|
||||
android:height="@dimen/list_circle_size"
|
||||
android:width="@dimen/list_circle_size"/>
|
||||
</shape>
|
|
@ -1,8 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
android:viewportWidth="24.0"
|
||||
android:width="24dp">
|
||||
<path
|
||||
android:fillColor="#fff"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"/>
|
|
@ -1,8 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
android:viewportWidth="24.0"
|
||||
android:width="24dp">
|
||||
<path
|
||||
android:fillColor="#fff"
|
||||
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
|
|
@ -1,8 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
android:viewportWidth="24.0"
|
||||
android:width="24dp">
|
||||
<path
|
||||
android:fillColor="#fff"
|
||||
android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
|
10
viewcomponents/src/main/res/drawable/yellow_circle.xml
Normal file
10
viewcomponents/src/main/res/drawable/yellow_circle.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/md_yellow"/>
|
||||
<stroke android:color="#424242"/>
|
||||
<size
|
||||
android:height="@dimen/list_circle_size"
|
||||
android:width="@dimen/list_circle_size"/>
|
||||
</shape>
|
53
viewcomponents/src/main/res/layout/check_interval_layout.xml
Normal file
53
viewcomponents/src/main/res/layout/check_interval_layout.xml
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif"
|
||||
android:text="@string/check_interval"
|
||||
android:textColor="?colorAccent"
|
||||
android:textSize="@dimen/caption_font_size"
|
||||
/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="2"
|
||||
>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/input"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:layout_marginEnd="@dimen/content_inset_half"
|
||||
android:layout_marginStart="-4dp"
|
||||
android:layout_weight="1"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:hint="0"
|
||||
android:inputType="number"
|
||||
android:textSize="@dimen/body_font_size"
|
||||
tools:ignore="Autofill,HardcodedText,LabelFor"
|
||||
/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_marginEnd="-4dp"
|
||||
android:layout_weight="1"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</merge>
|
8
viewcomponents/src/main/res/layout/include_divider.xml
Normal file
8
viewcomponents/src/main/res/layout/include_divider.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<View
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="@dimen/content_inset"
|
||||
android:background="@color/dividerColorDark"
|
||||
/>
|
|
@ -1,19 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<HorizontalScrollView
|
||||
<merge
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/responseValidationScript"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/content_inset"
|
||||
android:layout_marginTop="@dimen/content_inset_half"
|
||||
android:background="@color/colorPrimaryDark"
|
||||
android:elevation="@dimen/fab_elevation"
|
||||
android:paddingBottom="@dimen/content_inset"
|
||||
android:paddingLeft="@dimen/content_inset_half"
|
||||
android:paddingRight="@dimen/content_inset_half"
|
||||
android:paddingTop="@dimen/content_inset"
|
||||
android:scrollbars="none"
|
||||
tools:ignore="UnusedAttribute"
|
||||
>
|
||||
|
||||
|
@ -34,7 +24,7 @@
|
|||
/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/responseValidationScriptInput"
|
||||
android:id="@+id/userInput"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@null"
|
||||
|
@ -62,4 +52,4 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
</HorizontalScrollView>
|
||||
</merge>
|
|
@ -6,4 +6,5 @@
|
|||
android:fontFamily="sans-serif-light"
|
||||
android:gravity="center_vertical|start"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="@dimen/body_font_size" />
|
||||
android:textSize="@dimen/body_font_size"
|
||||
/>
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
>
|
||||
|
||||
<ProgressBar
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
style="?android:progressBarStyleLarge"
|
||||
/>
|
||||
|
||||
</merge>
|
11
viewcomponents/src/main/res/values/arrays.xml
Normal file
11
viewcomponents/src/main/res/values/arrays.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string-array name="interval_options">
|
||||
<item>Minute(s)</item>
|
||||
<item>Hour(s)</item>
|
||||
<item>Day(s)</item>
|
||||
<item>Week(s)</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
12
viewcomponents/src/main/res/values/colors.xml
Normal file
12
viewcomponents/src/main/res/values/colors.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<color name="dividerColorDark">#37474F</color>
|
||||
|
||||
<color name="loading_indicator_frame_background">#40000000</color>
|
||||
|
||||
<color name="md_red">#E53935</color>
|
||||
<color name="md_yellow">#FDD835</color>
|
||||
<color name="md_green">#43A047</color>
|
||||
|
||||
</resources>
|
19
viewcomponents/src/main/res/values/dimens.xml
Normal file
19
viewcomponents/src/main/res/values/dimens.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<dimen name="list_circle_size">42dp</dimen>
|
||||
|
||||
<dimen name="default_elevation">4dp</dimen>
|
||||
|
||||
<dimen name="content_inset_half">8dp</dimen>
|
||||
<dimen name="content_inset_less">12dp</dimen>
|
||||
<dimen name="content_inset">16dp</dimen>
|
||||
<dimen name="content_inset_more">24dp</dimen>
|
||||
|
||||
<dimen name="code_font_size">14sp</dimen>
|
||||
<dimen name="body_font_size">14sp</dimen>
|
||||
<dimen name="caption_font_size">12sp</dimen>
|
||||
|
||||
<dimen name="button_height">52dp</dimen>
|
||||
|
||||
</resources>
|
10
viewcomponents/src/main/res/values/strings.xml
Normal file
10
viewcomponents/src/main/res/values/strings.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="default_js">var responseObj = JSON.parse(response);\nreturn responseObj.success === true;</string>
|
||||
<string name="function_declaration">function validate(response) {</string>
|
||||
<string name="function_end">}</string>
|
||||
|
||||
<string name="check_interval">Check Interval</string>
|
||||
|
||||
</resources>
|
Loading…
Add table
Reference in a new issue