diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 08b0af2..1f5ec28 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -52,6 +52,9 @@
+
diff --git a/app/src/main/java/com/afollestad/nocknock/ui/AddSiteActivity.kt b/app/src/main/java/com/afollestad/nocknock/ui/AddSiteActivity.kt
index 083ec7b..6d4d861 100644
--- a/app/src/main/java/com/afollestad/nocknock/ui/AddSiteActivity.kt
+++ b/app/src/main/java/com/afollestad/nocknock/ui/AddSiteActivity.kt
@@ -20,9 +20,11 @@ import androidx.appcompat.app.AppCompatActivity
import com.afollestad.nocknock.R
import com.afollestad.nocknock.data.ServerModel
import com.afollestad.nocknock.data.ServerStatus.WAITING
+import com.afollestad.nocknock.data.ValidationMode
import com.afollestad.nocknock.data.ValidationMode.JAVASCRIPT
import com.afollestad.nocknock.data.ValidationMode.STATUS_CODE
import com.afollestad.nocknock.data.ValidationMode.TERM_SEARCH
+import com.afollestad.nocknock.data.indexToValidationMode
import com.afollestad.nocknock.engine.db.ServerModelStore
import com.afollestad.nocknock.engine.statuscheck.CheckStatusManager
import com.afollestad.nocknock.utilities.ext.injector
@@ -56,6 +58,7 @@ import kotlinx.coroutines.launch
import java.lang.System.currentTimeMillis
import javax.inject.Inject
import kotlin.math.max
+import kotlin.properties.Delegates.notNull
private const val KEY_FAB_X = "fab_x"
private const val KEY_FAB_Y = "fab_y"
@@ -76,11 +79,19 @@ fun MainActivity.intentToAdd(
/** @author Aidan Follestad (afollestad) */
class AddSiteActivity : AppCompatActivity(), View.OnClickListener {
+ companion object {
+ private const val REVEAL_DURATION = 300L
+ }
+
private var isClosing: Boolean = false
@Inject lateinit var serverModelStore: ServerModelStore
@Inject lateinit var checkStatusManager: CheckStatusManager
+ private var revealCx by notNull()
+ private var revealCy by notNull()
+ private var revealRadius by notNull()
+
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -91,7 +102,19 @@ class AddSiteActivity : AppCompatActivity(), View.OnClickListener {
if (savedInstanceState == null) {
rootView.conceal()
- rootView.onLayout { circularRevealActivity() }
+ rootView.onLayout {
+ val fabSize = intent.getIntExtra(KEY_FAB_SIZE, 0)
+ val fabX = intent.getFloatExtra(KEY_FAB_X, 0f)
+ .toInt()
+ val fabY = intent.getFloatExtra(KEY_FAB_Y, 0f)
+ .toInt()
+
+ revealCx = fabX + fabSize / 2
+ revealCy = (fabY + toolbar.measuredHeight + fabSize / 2)
+ revealRadius = max(revealCx, revealCy).toFloat()
+
+ circularRevealActivity()
+ }
}
inputUrl.setOnFocusChangeListener { _, hasFocus ->
@@ -141,27 +164,23 @@ class AddSiteActivity : AppCompatActivity(), View.OnClickListener {
doneBtn.setOnClickListener(this)
}
+ private fun circularRevealActivity() {
+ val circularReveal =
+ createCircularReveal(rootView, revealCx, revealCy, 0f, revealRadius)
+ .apply {
+ duration = REVEAL_DURATION
+ interpolator = DecelerateInterpolator()
+ }
+ rootView.show()
+ circularReveal.start()
+ }
+
private fun closeActivityWithReveal() {
- if (isClosing) {
- return
- }
-
+ if (isClosing) return
isClosing = true
- val fabSize = intent.getIntExtra(KEY_FAB_SIZE, toolbar!!.measuredHeight)
-
- val defaultCx = rootView.measuredWidth / 2f
- val cx =
- intent.getFloatExtra(KEY_FAB_X, defaultCx).toInt() + fabSize / 2
-
- val defaultCy = rootView.measuredHeight / 2f
- val cy = (intent.getFloatExtra(KEY_FAB_Y, defaultCy).toInt() +
- toolbar!!.measuredHeight +
- fabSize / 2)
-
- val initialRadius = max(cx, cy).toFloat()
- createCircularReveal(rootView, cx, cy, initialRadius, 0f)
+ createCircularReveal(rootView, revealCx, revealCy, revealRadius, 0f)
.apply {
- duration = 300
+ duration = REVEAL_DURATION
interpolator = AccelerateInterpolator()
onEnd {
rootView.conceal()
@@ -172,20 +191,6 @@ class AddSiteActivity : AppCompatActivity(), View.OnClickListener {
}
}
- private fun circularRevealActivity() {
- val cx = rootView.measuredWidth / 2
- val cy = rootView.measuredHeight / 2
- val finalRadius = Math.max(cx, cy)
- .toFloat()
- val circularReveal = createCircularReveal(rootView, cx, cy, 0f, finalRadius)
- .apply {
- duration = 300
- interpolator = DecelerateInterpolator()
- }
- rootView.show()
- circularReveal.start()
- }
-
// Done button
override fun onClick(view: View) {
isClosing = true
@@ -223,14 +228,14 @@ class AddSiteActivity : AppCompatActivity(), View.OnClickListener {
}
val selectedCheckInterval = checkIntervalLayout.getSelectedCheckInterval()
- val selectedValidationMode = getSelectedValidationMode()
- val selectedValidationContent = getSelectedValidationContent()
+ val selectedValidationMode =
+ responseValidationMode.selectedItemPosition.indexToValidationMode()
newModel = newModel.copy(
checkInterval = selectedCheckInterval,
lastCheck = currentTimeMillis() - selectedCheckInterval,
validationMode = selectedValidationMode,
- validationContent = selectedValidationContent
+ validationContent = selectedValidationMode.validationContent()
)
rootView.scopeWhileAttached(Main) {
@@ -250,25 +255,9 @@ class AddSiteActivity : AppCompatActivity(), View.OnClickListener {
override fun onBackPressed() = closeActivityWithReveal()
- private fun getSelectedValidationMode() = when (responseValidationMode.selectedItemPosition) {
- 0 -> STATUS_CODE
- 1 -> TERM_SEARCH
- 2 -> JAVASCRIPT
- else -> {
- throw IllegalStateException(
- "Unexpected validation mode index: ${responseValidationMode.selectedItemPosition}"
- )
- }
- }
-
- private fun getSelectedValidationContent() = when (responseValidationMode.selectedItemPosition) {
- 0 -> null
- 1 -> responseValidationSearchTerm.trimmedText()
- 2 -> scriptInputLayout.getCode()
- else -> {
- throw IllegalStateException(
- "Unexpected validation mode index: ${responseValidationMode.selectedItemPosition}"
- )
- }
+ private fun ValidationMode.validationContent() = when (this) {
+ STATUS_CODE -> null
+ TERM_SEARCH -> responseValidationSearchTerm.trimmedText()
+ JAVASCRIPT -> scriptInputLayout.getCode()
}
}
diff --git a/app/src/main/java/com/afollestad/nocknock/ui/MainActivity.kt b/app/src/main/java/com/afollestad/nocknock/ui/MainActivity.kt
index 2df975c..3cceb52 100644
--- a/app/src/main/java/com/afollestad/nocknock/ui/MainActivity.kt
+++ b/app/src/main/java/com/afollestad/nocknock/ui/MainActivity.kt
@@ -5,8 +5,6 @@
*/
package com.afollestad.nocknock.ui
-import android.animation.ObjectAnimator
-import android.animation.ObjectAnimator.ofFloat
import android.annotation.SuppressLint
import android.app.ActivityOptions.makeSceneTransitionAnimation
import android.content.BroadcastReceiver
@@ -15,12 +13,6 @@ import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import android.util.Log
-import android.view.Menu
-import android.view.MenuItem
-import android.view.View
-import android.view.View.X
-import android.view.View.Y
-import android.view.animation.PathInterpolator
import androidx.appcompat.app.AppCompatActivity
import androidx.core.text.HtmlCompat
import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY
@@ -40,17 +32,16 @@ import com.afollestad.nocknock.engine.statuscheck.CheckStatusJob.Companion.KEY_U
import com.afollestad.nocknock.engine.statuscheck.CheckStatusManager
import com.afollestad.nocknock.notifications.NockNotificationManager
import com.afollestad.nocknock.utilities.ext.injector
-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.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
import kotlinx.android.synthetic.main.activity_main.list
import kotlinx.android.synthetic.main.activity_main.rootView
+import kotlinx.android.synthetic.main.activity_main.toolbar
+import kotlinx.android.synthetic.main.include_empty_view.emptyText
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.async
@@ -58,12 +49,11 @@ import kotlinx.coroutines.launch
import javax.inject.Inject
/** @author Aidan Follestad (afollestad) */
-class MainActivity : AppCompatActivity(), View.OnClickListener {
+class MainActivity : AppCompatActivity() {
companion object {
private const val ADD_SITE_RQ = 6969
private const val VIEW_SITE_RQ = 6923
- private const val REVEAL_DURATION = 250L
private fun log(message: String) {
if (BuildConfig.DEBUG) {
@@ -72,10 +62,6 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
}
}
- private var fabAnimator: ObjectAnimator? = null
- private var originalFabX: Float = 0.toFloat()
- private var originalFabY: Float = 0.toFloat()
-
private val intentReceiver = object : BroadcastReceiver() {
override fun onReceive(
context: Context,
@@ -105,13 +91,26 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
injector().injectInto(this)
setContentView(R.layout.activity_main)
+ toolbar.inflateMenu(R.menu.menu_main)
+ toolbar.setOnMenuItemClickListener { item ->
+ if (item.itemId == R.id.about) {
+ AboutDialog.show(this)
+ }
+ return@setOnMenuItemClickListener true
+ }
+
adapter = ServerAdapter(this::onSiteSelected)
list.layoutManager = LinearLayoutManager(this)
list.adapter = adapter
list.addItemDecoration(DividerItemDecoration(this, VERTICAL))
- fab.setOnClickListener(this)
+ fab.setOnClickListener {
+ startActivityForResult(
+ intentToAdd(fab.x, fab.y, fab.measuredWidth),
+ ADD_SITE_RQ
+ )
+ }
notificationManager.createChannels()
ensureCheckJobs()
@@ -152,46 +151,6 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
}
}
- override fun onCreateOptionsMenu(menu: Menu): Boolean {
- menuInflater.inflate(R.menu.menu_main, menu)
- return super.onCreateOptionsMenu(menu)
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- if (item.itemId == R.id.about) {
- AboutDialog.show(this)
- return true
- }
- return super.onOptionsItemSelected(item)
- }
-
- // FAB clicked
- override fun onClick(view: View) {
- originalFabX = fab.x
- originalFabY = fab.y
-
- fabAnimator?.cancel()
- fabAnimator = ofFloat(view, X, Y, bezierCurve(fab, list))
- .apply {
- interpolator = PathInterpolator(.5f, .5f)
- duration = REVEAL_DURATION
- onEnd {
- startActivityForResult(
- intentToAdd(originalFabX, originalFabY, fab.measuredWidth),
- ADD_SITE_RQ
- )
- fab.postDelayed(
- {
- fab.x = originalFabX
- fab.y = originalFabY
- },
- REVEAL_DURATION * 2
- )
- }
- start()
- }
- }
-
override fun onActivityResult(
requestCode: Int,
resultCode: Int,
diff --git a/app/src/main/java/com/afollestad/nocknock/ui/ViewSiteActivity.kt b/app/src/main/java/com/afollestad/nocknock/ui/ViewSiteActivity.kt
index 8c3c6fb..cb6048d 100644
--- a/app/src/main/java/com/afollestad/nocknock/ui/ViewSiteActivity.kt
+++ b/app/src/main/java/com/afollestad/nocknock/ui/ViewSiteActivity.kt
@@ -24,12 +24,15 @@ import androidx.core.text.HtmlCompat
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.nocknock.BuildConfig
import com.afollestad.nocknock.R
+import com.afollestad.nocknock.data.LAST_CHECK_NONE
import com.afollestad.nocknock.data.ServerModel
import com.afollestad.nocknock.data.ServerStatus.CHECKING
import com.afollestad.nocknock.data.ServerStatus.WAITING
+import com.afollestad.nocknock.data.ValidationMode
import com.afollestad.nocknock.data.ValidationMode.JAVASCRIPT
import com.afollestad.nocknock.data.ValidationMode.STATUS_CODE
import com.afollestad.nocknock.data.ValidationMode.TERM_SEARCH
+import com.afollestad.nocknock.data.indexToValidationMode
import com.afollestad.nocknock.data.textRes
import com.afollestad.nocknock.engine.db.ServerModelStore
import com.afollestad.nocknock.engine.statuscheck.CheckStatusJob.Companion.ACTION_STATUS_UPDATE
@@ -41,12 +44,17 @@ import com.afollestad.nocknock.utilities.ext.isHttpOrHttps
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.viewcomponents.ext.dimenFloat
+import com.afollestad.nocknock.viewcomponents.ext.disable
+import com.afollestad.nocknock.viewcomponents.ext.enable
import com.afollestad.nocknock.viewcomponents.ext.hide
import com.afollestad.nocknock.viewcomponents.ext.onItemSelected
+import com.afollestad.nocknock.viewcomponents.ext.onScroll
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.disableChecksButton
import kotlinx.android.synthetic.main.activity_viewsite.doneBtn
import kotlinx.android.synthetic.main.activity_viewsite.iconStatus
import kotlinx.android.synthetic.main.activity_viewsite.inputName
@@ -56,6 +64,7 @@ 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.scrollView
import kotlinx.android.synthetic.main.activity_viewsite.textLastCheckResult
import kotlinx.android.synthetic.main.activity_viewsite.textNextCheck
import kotlinx.android.synthetic.main.activity_viewsite.textUrlWarning
@@ -122,6 +131,14 @@ class ViewSiteActivity : AppCompatActivity(),
setOnMenuItemClickListener(this@ViewSiteActivity)
}
+ scrollView.onScroll {
+ toolbar.elevation = if (it > toolbar.height / 4) {
+ toolbar.dimenFloat(R.dimen.default_elevation)
+ } else {
+ 0f
+ }
+ }
+
inputUrl.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
val inputStr = inputUrl.text
@@ -184,7 +201,7 @@ class ViewSiteActivity : AppCompatActivity(),
inputName.setText(this.name)
inputUrl.setText(this.url)
- if (this.lastCheck == 0L) {
+ if (this.lastCheck == LAST_CHECK_NONE) {
textLastCheckResult.setText(R.string.none)
} else {
val statusText = this.status.textRes()
@@ -195,16 +212,8 @@ class ViewSiteActivity : AppCompatActivity(),
}
}
- if (this.checkInterval == 0L) {
- textNextCheck.setText(R.string.none_turned_off)
- checkIntervalLayout.clear()
- } else {
- var lastCheck = this.lastCheck
- if (lastCheck == 0L) {
- lastCheck = currentTimeMillis()
- }
- textNextCheck.text = (lastCheck + this.checkInterval).formatDate()
- }
+ textNextCheck.text = (this.lastCheck + this.checkInterval).formatDate()
+ checkIntervalLayout.set(this.checkInterval)
responseValidationMode.setSelection(validationMode.value - 1)
@@ -267,14 +276,14 @@ class ViewSiteActivity : AppCompatActivity(),
}
val selectedCheckInterval = checkIntervalLayout.getSelectedCheckInterval()
- val selectedValidationMode = getSelectedValidationMode()
- val selectedValidationContent = getSelectedValidationContent()
+ val selectedValidationMode =
+ responseValidationMode.selectedItemPosition.indexToValidationMode()
currentModel = currentModel.copy(
checkInterval = selectedCheckInterval,
lastCheck = currentTimeMillis() - selectedCheckInterval,
validationMode = selectedValidationMode,
- validationContent = selectedValidationContent
+ validationContent = selectedValidationMode.validationContent()
)
return true
@@ -307,6 +316,7 @@ class ViewSiteActivity : AppCompatActivity(),
R.id.refresh -> {
rootView.scopeWhileAttached(Main) {
launch(coroutineContext) {
+ disableChecksButton.disable()
loadingProgress.setLoading()
updateModelFromInput(false)
currentModel = currentModel.copy(status = WAITING)
@@ -317,6 +327,7 @@ class ViewSiteActivity : AppCompatActivity(),
checkStatusManager.cancelCheck(currentModel)
checkStatusManager.scheduleCheck(currentModel, rightNow = true)
loadingProgress.setDone()
+ disableChecksButton.enable()
}
}
return true
@@ -363,25 +374,9 @@ class ViewSiteActivity : AppCompatActivity(),
item.isEnabled = currentModel.status != CHECKING && currentModel.status != WAITING
}
- private fun getSelectedValidationMode() = when (responseValidationMode.selectedItemPosition) {
- 0 -> STATUS_CODE
- 1 -> TERM_SEARCH
- 2 -> JAVASCRIPT
- else -> {
- throw IllegalStateException(
- "Unexpected validation mode index: ${responseValidationMode.selectedItemPosition}"
- )
- }
- }
-
- private fun getSelectedValidationContent() = when (responseValidationMode.selectedItemPosition) {
- 0 -> null
- 1 -> responseValidationSearchTerm.trimmedText()
- 2 -> scriptInputLayout.getCode()
- else -> {
- throw IllegalStateException(
- "Unexpected validation mode index: ${responseValidationMode.selectedItemPosition}"
- )
- }
+ private fun ValidationMode.validationContent() = when (this) {
+ STATUS_CODE -> null
+ TERM_SEARCH -> responseValidationSearchTerm.trimmedText()
+ JAVASCRIPT -> scriptInputLayout.getCode()
}
}
diff --git a/app/src/main/res/layout/activity_addsite.xml b/app/src/main/res/layout/activity_addsite.xml
index 89d3624..ff6f3e2 100644
--- a/app/src/main/res/layout/activity_addsite.xml
+++ b/app/src/main/res/layout/activity_addsite.xml
@@ -19,9 +19,10 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:theme="@style/FlatToolbarTheme"
app:navigationIcon="@drawable/ic_action_close"
app:title="@string/add_site"
- app:titleTextColor="?android:textColorPrimary"
+ app:titleTextColor="#FFFFFF"
/>
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 8ce914d..2064711 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,5 +1,6 @@
-
-
+ android:orientation="vertical"
+ >
-
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_viewsite.xml b/app/src/main/res/layout/activity_viewsite.xml
index b0b9fef..02b984f 100644
--- a/app/src/main/res/layout/activity_viewsite.xml
+++ b/app/src/main/res/layout/activity_viewsite.xml
@@ -15,16 +15,20 @@
android:orientation="vertical"
>
+
@@ -66,7 +70,7 @@
android:id="@+id/inputName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:fontFamily="sans-serif-light"
+ android:fontFamily="@font/lato"
android:hint="@string/site_name"
android:inputType="textPersonName|textCapWords|textAutoCorrect"
android:singleLine="true"
@@ -74,14 +78,14 @@
android:textColorHint="?android:textColorSecondary"
android:textSize="@dimen/body_font_size"
android:transitionName="site_name"
- tools:ignore="UnusedAttribute"
+ tools:ignore="Autofill,UnusedAttribute"
/>
+
+
diff --git a/app/src/main/res/layout/include_empty_view.xml b/app/src/main/res/layout/include_empty_view.xml
new file mode 100644
index 0000000..1adf674
--- /dev/null
+++ b/app/src/main/res/layout/include_empty_view.xml
@@ -0,0 +1,14 @@
+
+
diff --git a/app/src/main/res/layout/list_item_server.xml b/app/src/main/res/layout/list_item_server.xml
index fd0863e..e1e8728 100644
--- a/app/src/main/res/layout/list_item_server.xml
+++ b/app/src/main/res/layout/list_item_server.xml
@@ -43,7 +43,7 @@
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/content_inset_half"
android:layout_toStartOf="@+id/textInterval"
- android:fontFamily="sans-serif-medium"
+ android:fontFamily="@font/lato"
android:singleLine="true"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/title_font_size"
@@ -57,7 +57,7 @@
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
- android:fontFamily="sans-serif-light"
+ android:fontFamily="@font/lato_light"
android:singleLine="true"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/caption_font_size"
@@ -71,7 +71,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/list_text_spacing"
- android:fontFamily="sans-serif"
+ android:fontFamily="@font/lato"
android:singleLine="true"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/body_font_size"
@@ -84,7 +84,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/list_text_spacing"
- android:fontFamily="sans-serif"
+ android:fontFamily="@font/lato"
android:singleLine="true"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/body_font_size"
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 50f16a5..c0fa7eb 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,6 +1,6 @@
-
+
- Nock Nock (BETA)
+ Nock Nock
No sites added!
@@ -29,7 +29,8 @@
Remove Site
%1$s from your sites?]]>
Remove
- Save
+ Save Changes
+ Disable Automatic Checks
View Site
Last Check Result
Next Check
@@ -39,13 +40,23 @@
Refresh Status
- Warning: this app checks for server availability with HTTP requests. It\'s recommended that you use an HTTP URL.
-
+ Warning: this app checks for server availability with HTTP requests. It\'s recommended that you
+ use an HTTP URL.
+
Response Validation Mode
Search term…
- The HTTP status code is checked. If it\'s a successful status code, the site passes the check.
- The status code check is done first. If it\'s successful, the response body is checked. If it contains your search term, the site passes the check.
- The status code check is done first. If it\'s successful, the response body is passed to the JavaScript function above. If the function returns true, the site passes the check. Throw an exception to pass custom error messages to Nock Nock.
+
+ The HTTP status code is checked. If it\'s a successful status code, the site passes the check.
+
+
+ The status code check is done first. If it\'s successful, the response body is checked.
+ If it contains your search term, the site passes the check.
+
+
+ The status code check is done first. If it\'s successful, the response body is passed to the
+ JavaScript function above. If the function returns true, the site passes the check. Throw an
+ exception to pass custom error messages to Nock Nock.
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 781d2c4..1df7c54 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -1,6 +1,6 @@
-
+
+
+
+
+
+
+
+
diff --git a/data/src/main/java/com/afollestad/nocknock/data/ServerModel.kt b/data/src/main/java/com/afollestad/nocknock/data/ServerModel.kt
index a0eac69..df10e4e 100644
--- a/data/src/main/java/com/afollestad/nocknock/data/ServerModel.kt
+++ b/data/src/main/java/com/afollestad/nocknock/data/ServerModel.kt
@@ -12,17 +12,21 @@ import com.afollestad.nocknock.utilities.ext.timeString
import java.io.Serializable
import java.lang.System.currentTimeMillis
+const val CHECK_INTERVAL_UNSET = -1L
+const val LAST_CHECK_NONE = -1L
+
/** @author Aidan Follestad (afollestad)*/
data class ServerModel(
var id: Int = 0,
- val name: String = "Unknown",
- val url: String = "Unknown",
+ val name: String,
+ val url: String,
val status: ServerStatus = OK,
- val checkInterval: Long = 0,
- val lastCheck: Long = 0,
+ val checkInterval: Long = CHECK_INTERVAL_UNSET,
+ val lastCheck: Long = LAST_CHECK_NONE,
val reason: String? = null,
val validationMode: ValidationMode,
- val validationContent: String? = null
+ val validationContent: String? = null,
+ val disabled: Boolean = false
) : Serializable {
companion object {
@@ -36,8 +40,9 @@ data class ServerModel(
const val COLUMN_REASON = "reason"
const val COLUMN_VALIDATION_MODE = "validation_mode"
const val COLUMN_VALIDATION_CONTENT = "validation_content"
+ const val COLUMN_DISABLED = "disabled"
- const val DEFAULT_SORT_ORDER = "$COLUMN_NAME ASC"
+ const val DEFAULT_SORT_ORDER = "$COLUMN_NAME ASC, $COLUMN_DISABLED DESC"
fun pull(cursor: Cursor): ServerModel {
return ServerModel(
@@ -51,7 +56,8 @@ data class ServerModel(
validationMode = cursor.getInt(
cursor.getColumnIndex(COLUMN_VALIDATION_MODE)
).toValidationMode(),
- validationContent = cursor.getString(cursor.getColumnIndex(COLUMN_VALIDATION_CONTENT))
+ validationContent = cursor.getString(cursor.getColumnIndex(COLUMN_VALIDATION_CONTENT)),
+ disabled = cursor.getInt(cursor.getColumnIndex(COLUMN_DISABLED)) == 1
)
}
}
@@ -73,5 +79,6 @@ data class ServerModel(
put(COLUMN_REASON, reason)
put(COLUMN_VALIDATION_MODE, validationMode.value)
put(COLUMN_VALIDATION_CONTENT, validationContent)
+ put(COLUMN_DISABLED, disabled)
}
}
diff --git a/data/src/main/java/com/afollestad/nocknock/data/ValidationMode.kt b/data/src/main/java/com/afollestad/nocknock/data/ValidationMode.kt
index e209931..f9a1586 100644
--- a/data/src/main/java/com/afollestad/nocknock/data/ValidationMode.kt
+++ b/data/src/main/java/com/afollestad/nocknock/data/ValidationMode.kt
@@ -19,7 +19,16 @@ enum class ValidationMode(val value: Int) {
JAVASCRIPT.value -> JAVASCRIPT
else -> throw IllegalArgumentException("Unknown validationMode: $value")
}
+
+ fun fromIndex(index: Int) = when (index) {
+ 0 -> STATUS_CODE
+ 1 -> TERM_SEARCH
+ 2 -> JAVASCRIPT
+ else -> throw IllegalArgumentException("Index out of range: $index")
+ }
}
}
fun Int.toValidationMode() = ValidationMode.fromValue(this)
+
+fun Int.indexToValidationMode() = ValidationMode.fromIndex(this)
diff --git a/engine/src/main/java/com/afollestad/nocknock/engine/db/ServerModelDbHelper.kt b/engine/src/main/java/com/afollestad/nocknock/engine/db/ServerModelDbHelper.kt
index 7b8bcfc..6996f32 100644
--- a/engine/src/main/java/com/afollestad/nocknock/engine/db/ServerModelDbHelper.kt
+++ b/engine/src/main/java/com/afollestad/nocknock/engine/db/ServerModelDbHelper.kt
@@ -20,7 +20,8 @@ private const val SQL_CREATE_ENTRIES =
"${ServerModel.COLUMN_LAST_CHECK} INTEGER," +
"${ServerModel.COLUMN_REASON} TEXT," +
"${ServerModel.COLUMN_VALIDATION_MODE} INTEGER," +
- "${ServerModel.COLUMN_VALIDATION_CONTENT} TEXT)"
+ "${ServerModel.COLUMN_VALIDATION_CONTENT} TEXT," +
+ "${ServerModel.COLUMN_DISABLED} INTEGER)"
private const val SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS ${ServerModel.TABLE_NAME}"
@@ -29,7 +30,7 @@ class ServerModelDbHelper(context: Context) : SQLiteOpenHelper(
context, DATABASE_NAME, null, DATABASE_VERSION
) {
companion object {
- const val DATABASE_VERSION = 1
+ const val DATABASE_VERSION = 2
const val DATABASE_NAME = "ServerModels.db"
}
diff --git a/utilities/src/main/java/com/afollestad/nocknock/utilities/ext/DateExt.kt b/utilities/src/main/java/com/afollestad/nocknock/utilities/ext/DateExt.kt
index 6e41212..d29c261 100644
--- a/utilities/src/main/java/com/afollestad/nocknock/utilities/ext/DateExt.kt
+++ b/utilities/src/main/java/com/afollestad/nocknock/utilities/ext/DateExt.kt
@@ -10,6 +10,9 @@ import java.util.Date
import java.util.Locale
fun Long.formatDate(): String {
+ if (this <= 0) {
+ return "(None)"
+ }
val df = SimpleDateFormat("MMMM dd, hh:mm:ss a", Locale.getDefault())
return df.format(Date(this))
}
diff --git a/utilities/src/main/java/com/afollestad/nocknock/utilities/util/MathUtil.kt b/utilities/src/main/java/com/afollestad/nocknock/utilities/util/MathUtil.kt
deleted file mode 100644
index 7c5a98f..0000000
--- a/utilities/src/main/java/com/afollestad/nocknock/utilities/util/MathUtil.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed under Apache-2.0
- *
- * Designed and developed by Aidan Follestad (@afollestad)
- */
-package com.afollestad.nocknock.utilities.util
-
-import android.graphics.Path
-import android.view.View
-
-/** @author Aidan Follestad (afollestad) */
-object MathUtil {
-
- fun bezierCurve(
- targetView: View,
- rootView: View
- ): Path {
- val fabCenterX = (targetView.x + targetView.measuredWidth / 2).toInt()
- val fabCenterY = (targetView.y + targetView.measuredHeight / 2).toInt()
-
- val endCenterX = rootView.measuredWidth / 2 - targetView.measuredWidth / 2
- val endCenterY = rootView.measuredHeight / 2 - targetView.measuredHeight / 2
-
- val halfX = (fabCenterX - endCenterX) / 2
- val halfY = (fabCenterY - endCenterY) / 2
-
- var controlX = endCenterX + halfX
- var controlY = endCenterY + halfY
-
- controlY -= halfY
- controlX += halfX
-
- val path = Path()
- path.moveTo(targetView.x, targetView.y)
- path.quadTo(
- controlX.toFloat(),
- controlY.toFloat(),
- endCenterX.toFloat(),
- endCenterY.toFloat()
- )
-
- return path
- }
-}
diff --git a/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/CheckIntervalLayout.kt b/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/CheckIntervalLayout.kt
index 694924b..1b35548 100644
--- a/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/CheckIntervalLayout.kt
+++ b/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/CheckIntervalLayout.kt
@@ -74,11 +74,6 @@ class CheckIntervalLayout(
}
}
- fun clear() {
- input.setText("")
- spinner.setSelection(0)
- }
-
@CheckResult fun getSelectedCheckInterval(): Long {
val intervalInput = input.textAsLong()
val spinnerPos = spinner.selectedItemPosition
diff --git a/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/LoadingIndicatorFrame.kt b/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/LoadingIndicatorFrame.kt
index 731f2bc..7c2d0db 100644
--- a/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/LoadingIndicatorFrame.kt
+++ b/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/LoadingIndicatorFrame.kt
@@ -27,6 +27,8 @@ class LoadingIndicatorFrame(
setBackgroundColor(ContextCompat.getColor(context, R.color.loading_indicator_frame_background))
hide() // hide self by default
inflate(context, R.layout.loading_indicator_frame, this)
+ isClickable = true
+ isFocusable = true
}
fun setLoading() {
diff --git a/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/ext/ScrollViewExt.kt b/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/ext/ScrollViewExt.kt
new file mode 100644
index 0000000..1c45cd5
--- /dev/null
+++ b/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/ext/ScrollViewExt.kt
@@ -0,0 +1,11 @@
+/*
+ * Licensed under Apache-2.0
+ *
+ * Designed and developed by Aidan Follestad (@afollestad)
+ */
+package com.afollestad.nocknock.viewcomponents.ext
+
+import android.widget.ScrollView
+
+fun ScrollView.onScroll(cb: (y: Int) -> Unit) =
+ viewTreeObserver.addOnScrollChangedListener { cb(scrollY) }
diff --git a/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/ext/ViewExt.kt b/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/ext/ViewExt.kt
index 24691d0..5f86e74 100644
--- a/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/ext/ViewExt.kt
+++ b/viewcomponents/src/main/java/com/afollestad/nocknock/viewcomponents/ext/ViewExt.kt
@@ -24,6 +24,14 @@ fun View.hide() {
visibility = GONE
}
+fun View.enable() {
+ isEnabled = true
+}
+
+fun View.disable() {
+ isEnabled = false
+}
+
fun View.showOrHide(show: Boolean) = if (show) show() else hide()
fun View.onLayout(cb: () -> Unit) {
diff --git a/viewcomponents/src/main/res/font/fira_mono.xml b/viewcomponents/src/main/res/font/fira_mono.xml
new file mode 100644
index 0000000..7975d1b
--- /dev/null
+++ b/viewcomponents/src/main/res/font/fira_mono.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/viewcomponents/src/main/res/font/lato.xml b/viewcomponents/src/main/res/font/lato.xml
new file mode 100644
index 0000000..015fa0c
--- /dev/null
+++ b/viewcomponents/src/main/res/font/lato.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/viewcomponents/src/main/res/font/lato_black.xml b/viewcomponents/src/main/res/font/lato_black.xml
new file mode 100644
index 0000000..dfabc45
--- /dev/null
+++ b/viewcomponents/src/main/res/font/lato_black.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/viewcomponents/src/main/res/font/lato_light.xml b/viewcomponents/src/main/res/font/lato_light.xml
new file mode 100644
index 0000000..e502755
--- /dev/null
+++ b/viewcomponents/src/main/res/font/lato_light.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/viewcomponents/src/main/res/layout/check_interval_layout.xml b/viewcomponents/src/main/res/layout/check_interval_layout.xml
index 05a8948..227c408 100644
--- a/viewcomponents/src/main/res/layout/check_interval_layout.xml
+++ b/viewcomponents/src/main/res/layout/check_interval_layout.xml
@@ -5,13 +5,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
+ tools:parentTag="android.widget.LinearLayout"
>
diff --git a/viewcomponents/src/main/res/layout/list_item_spinner.xml b/viewcomponents/src/main/res/layout/list_item_spinner.xml
index b6170b9..8469e57 100644
--- a/viewcomponents/src/main/res/layout/list_item_spinner.xml
+++ b/viewcomponents/src/main/res/layout/list_item_spinner.xml
@@ -3,7 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/button_height"
- android:fontFamily="sans-serif-light"
+ android:fontFamily="@font/lato"
android:gravity="center_vertical|start"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/body_font_size"
diff --git a/viewcomponents/src/main/res/layout/list_item_spinner_dropdown.xml b/viewcomponents/src/main/res/layout/list_item_spinner_dropdown.xml
index 4d99ee8..141b174 100644
--- a/viewcomponents/src/main/res/layout/list_item_spinner_dropdown.xml
+++ b/viewcomponents/src/main/res/layout/list_item_spinner_dropdown.xml
@@ -3,7 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/button_height"
- android:fontFamily="sans-serif-light"
+ android:fontFamily="@font/lato"
android:gravity="center_vertical|start"
android:paddingLeft="@dimen/content_inset"
android:paddingRight="@dimen/content_inset"
diff --git a/viewcomponents/src/main/res/layout/loading_indicator_frame.xml b/viewcomponents/src/main/res/layout/loading_indicator_frame.xml
index 8584f55..af0e65b 100644
--- a/viewcomponents/src/main/res/layout/loading_indicator_frame.xml
+++ b/viewcomponents/src/main/res/layout/loading_indicator_frame.xml
@@ -4,12 +4,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
>
-
-
diff --git a/viewcomponents/src/main/res/values/dimens.xml b/viewcomponents/src/main/res/values/dimens.xml
index 09514e8..2601771 100644
--- a/viewcomponents/src/main/res/values/dimens.xml
+++ b/viewcomponents/src/main/res/values/dimens.xml
@@ -9,6 +9,7 @@
12dp
16dp
24dp
+ 32dp
14sp
14sp
diff --git a/viewcomponents/src/main/res/values/font_certs.xml b/viewcomponents/src/main/res/values/font_certs.xml
new file mode 100644
index 0000000..d2226ac
--- /dev/null
+++ b/viewcomponents/src/main/res/values/font_certs.xml
@@ -0,0 +1,17 @@
+
+
+
+ - @array/com_google_android_gms_fonts_certs_dev
+ - @array/com_google_android_gms_fonts_certs_prod
+
+
+ -
+ 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
+
+
+
diff --git a/viewcomponents/src/main/res/values/preloaded_fonts.xml b/viewcomponents/src/main/res/values/preloaded_fonts.xml
new file mode 100644
index 0000000..6a94ecb
--- /dev/null
+++ b/viewcomponents/src/main/res/values/preloaded_fonts.xml
@@ -0,0 +1,9 @@
+
+
+
+ - @font/fira_mono
+ - @font/lato
+ - @font/lato_black
+ - @font/lato_light
+
+