Added ktlint.

This commit is contained in:
Erik C. Thauvin 2018-11-06 01:14:50 -08:00
parent 1ea0e84239
commit 4c314683c7
19 changed files with 325 additions and 259 deletions

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View file

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Erik's Code Style" />
</state>
</component>

View file

@ -1,3 +1,7 @@
plugins {
id "org.jlleitschuh.gradle.ktlint" version "6.2.1"
}
//noinspection GradleCompatible //noinspection GradleCompatible
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
@ -72,4 +76,4 @@ repositories {
kapt { kapt {
//generateStubs = true //generateStubs = true
} }

View file

@ -25,12 +25,10 @@ import org.acra.ACRA
import org.acra.ReportingInteractionMode import org.acra.ReportingInteractionMode
import org.acra.annotation.ReportsCrashes import org.acra.annotation.ReportsCrashes
@ReportsCrashes(mailTo = "erik@thauvin.net", @ReportsCrashes(mailTo = "erik@thauvin.net",
mode = ReportingInteractionMode.DIALOG, mode = ReportingInteractionMode.DIALOG,
reportSenderFactoryClasses = arrayOf(CrashEmailFactory::class), reportSenderFactoryClasses = arrayOf(CrashEmailFactory::class),
reportDialogClass = CrashReportActivity::class) reportDialogClass = CrashReportActivity::class)
open class App : Application() { open class App : Application() {
override fun attachBaseContext(base: Context) { override fun attachBaseContext(base: Context) {
super.attachBaseContext(base) super.attachBaseContext(base)

View file

@ -27,7 +27,11 @@ import android.graphics.Typeface
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import android.text.* import android.text.Html
import android.text.InputFilter
import android.text.InputType
import android.text.Spanned
import android.text.TextUtils
import android.util.TypedValue import android.util.TypedValue
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
@ -45,9 +49,22 @@ import net.thauvin.erik.android.tesremoteprogrammer.models.Configurations
import net.thauvin.erik.android.tesremoteprogrammer.util.Dtmf import net.thauvin.erik.android.tesremoteprogrammer.util.Dtmf
import net.thauvin.erik.android.tesremoteprogrammer.util.isDKS import net.thauvin.erik.android.tesremoteprogrammer.util.isDKS
import net.thauvin.erik.android.tesremoteprogrammer.util.isDigits import net.thauvin.erik.android.tesremoteprogrammer.util.isDigits
import org.jetbrains.anko.* import org.jetbrains.anko.AnkoLogger
import org.jetbrains.anko.alert
import org.jetbrains.anko.bottomPadding
import org.jetbrains.anko.cancelButton
import org.jetbrains.anko.design.textInputEditText import org.jetbrains.anko.design.textInputEditText
import org.jetbrains.anko.design.textInputLayout import org.jetbrains.anko.design.textInputLayout
import org.jetbrains.anko.dip
import org.jetbrains.anko.horizontalPadding
import org.jetbrains.anko.info
import org.jetbrains.anko.listView
import org.jetbrains.anko.padding
import org.jetbrains.anko.singleLine
import org.jetbrains.anko.startActivity
import org.jetbrains.anko.textView
import org.jetbrains.anko.topPadding
import org.jetbrains.anko.verticalLayout
import permissions.dispatcher.NeedsPermission import permissions.dispatcher.NeedsPermission
import permissions.dispatcher.RuntimePermissions import permissions.dispatcher.RuntimePermissions
import java.io.FileNotFoundException import java.io.FileNotFoundException
@ -55,7 +72,9 @@ import java.io.InputStreamReader
import java.io.ObjectInputStream import java.io.ObjectInputStream
import java.io.ObjectOutputStream import java.io.ObjectOutputStream
import java.text.DateFormat import java.text.DateFormat
import java.util.* import java.util.ArrayList
import java.util.Date
import java.util.Locale
@RuntimePermissions @RuntimePermissions
class MainActivity : AppCompatActivity(), AnkoLogger { class MainActivity : AppCompatActivity(), AnkoLogger {
@ -64,13 +83,13 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
private val configurationsData = "configurations.dat" private val configurationsData = "configurations.dat"
private val currentConfigData = "config.dat" private val currentConfigData = "config.dat"
private val defaultConfigurations = listOf( private val defaultConfigurations = listOf(
R.raw.dks_1802, R.raw.dks_1802,
R.raw.dks_1802_epd, R.raw.dks_1802_epd,
R.raw.dks_1812, R.raw.dks_1812,
R.raw.dks_1819, R.raw.dks_1819,
R.raw.linear_ae_100, R.raw.linear_ae_100,
R.raw.linear_ae_500, R.raw.linear_ae_500,
R.raw.dks_1803_1808_1810) R.raw.dks_1803_1808_1810)
private val readRequestCode = 42 private val readRequestCode = 42
companion object { companion object {
@ -97,7 +116,7 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
defaultConfigurations.forEach { defaultConfigurations.forEach {
config = Gson().fromJson(InputStreamReader(resources.openRawResource(it)), config = Gson().fromJson(InputStreamReader(resources.openRawResource(it)),
Config::class.java) Config::class.java)
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
val errors = StringBuilder() val errors = StringBuilder()
@ -126,7 +145,7 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
extraTitle = "Last Update" extraTitle = "Last Update"
extra = DateFormat.getDateInstance(DateFormat.LONG, Locale.getDefault()).format( extra = DateFormat.getDateInstance(DateFormat.LONG, Locale.getDefault()).format(
Date(BuildConfig.TIMESTAMP)).toString() Date(BuildConfig.TIMESTAMP)).toString()
emailAddress = "erik@thauvin.net" emailAddress = "erik@thauvin.net"
emailSubject = "${getString(R.string.app_name)} ${BuildConfig.VERSION_NAME} Support" emailSubject = "${getString(R.string.app_name)} ${BuildConfig.VERSION_NAME} Support"
@ -154,7 +173,7 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
val tmp: Config? = try { val tmp: Config? = try {
Gson().fromJson(InputStreamReader(contentResolver.openInputStream(intent.data)), Gson().fromJson(InputStreamReader(contentResolver.openInputStream(intent.data)),
Config::class.java) Config::class.java)
} catch (jse: JsonSyntaxException) { } catch (jse: JsonSyntaxException) {
val cause = jse.cause val cause = jse.cause
if (cause != null) { if (cause != null) {
@ -291,8 +310,8 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
if (validateFields(fields)) { if (validateFields(fields)) {
saveConfig() saveConfig()
startActivity<ProgrammingActivity>( startActivity<ProgrammingActivity>(
"net.thauvin.erik.android.tesremoteprogrammer.models.Params" to config.params, "net.thauvin.erik.android.tesremoteprogrammer.models.Params" to config.params,
"net.thauvin.erik.android.tesremoteprogrammer.models.Option" to opts[position]) "net.thauvin.erik.android.tesremoteprogrammer.models.Option" to opts[position])
} }
} }
} }
@ -340,16 +359,17 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
} }
alert.show() alert.show()
} }
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
@SuppressLint("NeedOnRequestPermissionsResult") @SuppressLint("NeedOnRequestPermissionsResult")
override fun onRequestPermissionsResult(requestCode: Int, override fun onRequestPermissionsResult(
permissions: Array<out String>, requestCode: Int,
grantResults: IntArray) { permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults) super.onRequestPermissionsResult(requestCode, permissions, grantResults)
onRequestPermissionsResult(requestCode, grantResults) onRequestPermissionsResult(requestCode, grantResults)
} }
@ -365,9 +385,9 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
} }
private fun saveConfigurations(confs: Configurations) = private fun saveConfigurations(confs: Configurations) =
ObjectOutputStream(openFileOutput(configurationsData, Context.MODE_PRIVATE)).use { ObjectOutputStream(openFileOutput(configurationsData, Context.MODE_PRIVATE)).use {
it.writeObject(confs) it.writeObject(confs)
} }
private fun saveConfig(backup: Boolean = true) { private fun saveConfig(backup: Boolean = true) {
if (backup) { if (backup) {
@ -397,7 +417,6 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
errors.append(getString(R.string.validate_missing_param, "type")) errors.append(getString(R.string.validate_missing_param, "type"))
} else if (!Dtmf.isValidType(type)) { } else if (!Dtmf.isValidType(type)) {
errors.append(getString(R.string.validate_invalid_param, "type")) errors.append(getString(R.string.validate_invalid_param, "type"))
} }
// size // size
@ -426,49 +445,49 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
// title // title
if (title.isBlank()) { if (title.isBlank()) {
errors.append(getString( errors.append(getString(
R.string.validate_missing_opts_prop, R.string.validate_missing_opts_prop,
i + 1, i + 1,
"title")) "title"))
} }
// nosteps/nodial // nosteps/nodial
if (nosteps && nodial) { if (nosteps && nodial) {
errors.append(getString( errors.append(getString(
R.string.validate_invalid_option, R.string.validate_invalid_option,
i + 1, i + 1,
"nodial/nosteps")) "nodial/nosteps"))
} }
// dtmf // dtmf
if (dtmf.isBlank()) { if (dtmf.isBlank()) {
errors.append(getString( errors.append(getString(
R.string.validate_missing_opts_prop, R.string.validate_missing_opts_prop,
i + 1, i + 1,
"dtmf")) "dtmf"))
} else if (!nodial && fields.isEmpty()) { // fields missing } else if (!nodial && fields.isEmpty()) { // fields missing
errors.append(getString( errors.append(getString(
R.string.validate_missing_opts_prop, R.string.validate_missing_opts_prop,
i + 1, i + 1,
"fields")) "fields"))
} else { } else {
val blank = "\\0" val blank = "\\0"
val mock = Dtmf.mock(option, blank) val mock = Dtmf.mock(option, blank)
if (!mock.contains(MainActivity.PAUSE)) { // no pause if (!mock.contains(MainActivity.PAUSE)) { // no pause
errors.append(getString( errors.append(getString(
R.string.validate_invalid_opts_prop, R.string.validate_invalid_opts_prop,
i + 1, i + 1,
"dtmf", "dtmf",
getString(R.string.validate_dtmf_nopause))) getString(R.string.validate_dtmf_nopause)))
} }
if (!Dtmf.validate(mock, if (!Dtmf.validate(mock,
"${MainActivity.PAUSE}${params.ack}${params.alt}$blank", nodial)) { "${MainActivity.PAUSE}${params.ack}${params.alt}$blank", nodial)) {
errors.append(getString( errors.append(getString(
R.string.validate_invalid_opts_prop, R.string.validate_invalid_opts_prop,
i + 1, i + 1,
"dtmf", "dtmf",
mock.replace(blank, "&#10003;"))) mock.replace(blank, "&#10003;")))
} }
} }
@ -476,78 +495,78 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
fields.forEachIndexed { j, field -> fields.forEachIndexed { j, field ->
if (field == null) { if (field == null) {
errors.append(getString( errors.append(getString(
R.string.validate_syntax_error, R.string.validate_syntax_error,
"opts[${i + 1}], field[$j]")) "opts[${i + 1}], field[$j]"))
} else { } else {
with(field) { with(field) {
// size // size
if (size <= 0) { if (size <= 0) {
errors.append(getString( errors.append(getString(
R.string.validate_invalid_field_prop, R.string.validate_invalid_field_prop,
i + 1, i + 1,
j + 1, j + 1,
resources.getQuantityString(R.plurals.error_prop, 1), resources.getQuantityString(R.plurals.error_prop, 1),
"size=$size")) "size=$size"))
} }
// digits // digits
if (digits.isNotBlank() && !digits.isDigits()) { if (digits.isNotBlank() && !digits.isDigits()) {
errors.append(getString( errors.append(getString(
R.string.validate_invalid_field_prop, R.string.validate_invalid_field_prop,
i + 1, i + 1,
j + 1, j + 1,
resources.getQuantityString(R.plurals.error_prop, 1), resources.getQuantityString(R.plurals.error_prop, 1),
"digits='$digits'")) "digits='$digits'"))
} }
// minSize // minSize
if (minSize >= 0 && minSize > size) { if (minSize >= 0 && minSize > size) {
errors.append(getString( errors.append(getString(
R.string.validate_invalid_field_prop, R.string.validate_invalid_field_prop,
i + 1, i + 1,
j + 1, j + 1,
resources.getQuantityString(R.plurals.error_prop, 2), resources.getQuantityString(R.plurals.error_prop, 2),
"minSize=$minSize > size=$size")) "minSize=$minSize > size=$size"))
} }
// numeric fields only // numeric fields only
if (!alpha) { if (!alpha) {
if (minSize == 0) { if (minSize == 0) {
errors.append(getString( errors.append(getString(
R.string.validate_invalid_field_prop, R.string.validate_invalid_field_prop,
i + 1, i + 1,
j + 1, j + 1,
resources.getQuantityString(R.plurals.error_prop, 1), resources.getQuantityString(R.plurals.error_prop, 1),
"minSize=$minSize")) "minSize=$minSize"))
} }
// min/max // min/max
if (min >= 0 || max >= 0) { if (min >= 0 || max >= 0) {
if (max < 1) { if (max < 1) {
errors.append(getString( errors.append(getString(
R.string.validate_invalid_field_prop, R.string.validate_invalid_field_prop,
i + 1, i + 1,
j + 1, j + 1,
resources.getQuantityString(R.plurals.error_prop, 1), resources.getQuantityString(R.plurals.error_prop, 1),
"max=$max")) "max=$max"))
} }
if (min < 0) { if (min < 0) {
errors.append(getString( errors.append(getString(
R.string.validate_invalid_field_prop, R.string.validate_invalid_field_prop,
i + 1, i + 1,
j + 1, j + 1,
resources.getQuantityString(R.plurals.error_prop, 1), resources.getQuantityString(R.plurals.error_prop, 1),
"min=$min")) "min=$min"))
} }
if (min > max) { if (min > max) {
errors.append(getString( errors.append(getString(
R.string.validate_invalid_field_prop, R.string.validate_invalid_field_prop,
i + 1, i + 1,
j + 1, j + 1,
resources.getQuantityString(R.plurals.error_prop, 2), resources.getQuantityString(R.plurals.error_prop, 2),
"min=$min > max=$max")) "min=$min > max=$max"))
} }
} }
@ -557,11 +576,11 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
if (min >= 0 && minSize > 0) { if (min >= 0 && minSize > 0) {
if (min.toString().length != minSize) { if (min.toString().length != minSize) {
errors.append(getString( errors.append(getString(
R.string.validate_invalid_field_prop, R.string.validate_invalid_field_prop,
i + 1, i + 1,
j + 1, j + 1,
resources.getQuantityString(R.plurals.error_prop, 2), resources.getQuantityString(R.plurals.error_prop, 2),
"minSize=$minSize/min=$min")) "minSize=$minSize/min=$min"))
} }
} }
@ -569,11 +588,11 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
if (size > 0 && max > 0) { if (size > 0 && max > 0) {
if (max.toString().length != size) { if (max.toString().length != size) {
errors.append(getString( errors.append(getString(
R.string.validate_invalid_field_prop, R.string.validate_invalid_field_prop,
i + 1, i + 1,
j + 1, j + 1,
resources.getQuantityString(R.plurals.error_prop, 2), resources.getQuantityString(R.plurals.error_prop, 2),
"size=$size/max=$max")) "size=$size/max=$max"))
} }
} }
} }
@ -582,9 +601,9 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
// unused fields // unused fields
if (!dtmf.contains(Dtmf.DTMF_FIELD.format(j + 1))) { if (!dtmf.contains(Dtmf.DTMF_FIELD.format(j + 1))) {
errors.append(getString( errors.append(getString(
R.string.validate_unused_field, R.string.validate_unused_field,
i + 1, i + 1,
j + 1)) j + 1))
} }
} }
} }
@ -608,7 +627,7 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
isValid = false isValid = false
} else if (second > 0 && first.text.length != second) { } else if (second > 0 && first.text.length != second) {
first.error = getString(R.string.error_invalid_size, second, first.error = getString(R.string.error_invalid_size, second,
resources.getQuantityString(R.plurals.error_digit, second), "") resources.getQuantityString(R.plurals.error_digit, second), "")
isValid = false isValid = false
} }
} }

View file

@ -32,7 +32,9 @@ import android.text.SpannableString
import android.text.TextUtils import android.text.TextUtils
import android.text.style.ImageSpan import android.text.style.ImageSpan
import android.util.TypedValue import android.util.TypedValue
import android.view.Gravity.* import android.view.Gravity.BOTTOM
import android.view.Gravity.END
import android.view.Gravity.START
import android.view.ViewManager import android.view.ViewManager
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
@ -47,16 +49,29 @@ import net.thauvin.erik.android.tesremoteprogrammer.util.Dtmf
import net.thauvin.erik.android.tesremoteprogrammer.util.isDKS import net.thauvin.erik.android.tesremoteprogrammer.util.isDKS
import net.thauvin.erik.android.tesremoteprogrammer.util.isLinear import net.thauvin.erik.android.tesremoteprogrammer.util.isLinear
import net.thauvin.erik.android.tesremoteprogrammer.widget.ScrollAwareFABBehavior import net.thauvin.erik.android.tesremoteprogrammer.widget.ScrollAwareFABBehavior
import org.jetbrains.anko.* import org.jetbrains.anko.AnkoLogger
import org.jetbrains.anko.bottomPadding
import org.jetbrains.anko.custom.ankoView import org.jetbrains.anko.custom.ankoView
import org.jetbrains.anko.design.coordinatorLayout import org.jetbrains.anko.design.coordinatorLayout
import org.jetbrains.anko.design.floatingActionButton import org.jetbrains.anko.design.floatingActionButton
import org.jetbrains.anko.design.textInputEditText import org.jetbrains.anko.design.textInputEditText
import org.jetbrains.anko.design.textInputLayout import org.jetbrains.anko.design.textInputLayout
import org.jetbrains.anko.dip
import org.jetbrains.anko.horizontalPadding
import org.jetbrains.anko.imageResource
import org.jetbrains.anko.info
import org.jetbrains.anko.makeCall
import org.jetbrains.anko.matchParent
import org.jetbrains.anko.padding
import org.jetbrains.anko.singleLine
import org.jetbrains.anko.startActivity
import org.jetbrains.anko.support.v4.nestedScrollView import org.jetbrains.anko.support.v4.nestedScrollView
import org.jetbrains.anko.topPadding
import org.jetbrains.anko.verticalLayout
import org.jetbrains.anko.wrapContent
import permissions.dispatcher.NeedsPermission import permissions.dispatcher.NeedsPermission
import permissions.dispatcher.RuntimePermissions import permissions.dispatcher.RuntimePermissions
import java.util.* import java.util.ArrayList
@RuntimePermissions @RuntimePermissions
class ProgrammingActivity : AppCompatActivity(), AnkoLogger { class ProgrammingActivity : AppCompatActivity(), AnkoLogger {
@ -100,7 +115,6 @@ class ProgrammingActivity : AppCompatActivity(), AnkoLogger {
typeface = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC) typeface = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC)
freezesText = true freezesText = true
}.lparams(width = matchParent, height = matchParent) }.lparams(width = matchParent, height = matchParent)
} else { } else {
val it = option.fields.iterator() val it = option.fields.iterator()
while (it.hasNext()) { while (it.hasNext()) {
@ -129,11 +143,11 @@ class ProgrammingActivity : AppCompatActivity(), AnkoLogger {
inputFilters.add(NumberFilter(field.digits, if (field.alt) params.alt else empty)) inputFilters.add(NumberFilter(field.digits, if (field.alt) params.alt else empty))
if (field.max != -1 && field.min != -1) { if (field.max != -1 && field.min != -1) {
inputFilters.add( inputFilters.add(
MinMaxFilter( MinMaxFilter(
field.min, field.min,
field.max, field.max,
field.size, field.size,
params.type.isDKS() || field.zeros)) params.type.isDKS() || field.zeros))
} }
} }
@ -203,15 +217,15 @@ class ProgrammingActivity : AppCompatActivity(), AnkoLogger {
} }
startActivity<StepsActivity>( startActivity<StepsActivity>(
StepsActivity.EXTRA_STEPS to "$begin${dtmf.replace(MainActivity.QUOTE, empty)}$end".split(MainActivity.PAUSE)) StepsActivity.EXTRA_STEPS to "$begin${dtmf.replace(MainActivity.QUOTE, empty)}$end".split(MainActivity.PAUSE))
} else { } else {
Snackbar.make(this@coordinatorLayout, Snackbar.make(this@coordinatorLayout,
getString(R.string.error_invalid_dtmf, dtmf), getString(R.string.error_invalid_dtmf, dtmf),
Snackbar.LENGTH_LONG).show() Snackbar.LENGTH_LONG).show()
} }
} else { } else {
Snackbar.make(this@coordinatorLayout, R.string.error_invalid_field, Snackbar.make(this@coordinatorLayout, R.string.error_invalid_field,
Snackbar.LENGTH_LONG).show() Snackbar.LENGTH_LONG).show()
} }
} }
} }
@ -234,12 +248,12 @@ class ProgrammingActivity : AppCompatActivity(), AnkoLogger {
callWithPermissionCheck(params.phone, dtmf) callWithPermissionCheck(params.phone, dtmf)
} else { } else {
Snackbar.make(this@coordinatorLayout, Snackbar.make(this@coordinatorLayout,
getString(R.string.error_invalid_dtmf, dtmf), getString(R.string.error_invalid_dtmf, dtmf),
Snackbar.LENGTH_LONG).show() Snackbar.LENGTH_LONG).show()
} }
} else { } else {
Snackbar.make(this@coordinatorLayout, R.string.error_invalid_field, Snackbar.make(this@coordinatorLayout, R.string.error_invalid_field,
Snackbar.LENGTH_LONG).show() Snackbar.LENGTH_LONG).show()
} }
} else { } else {
val text = SpannableString(getString(R.string.error_invalid_field_for_call) + " ") val text = SpannableString(getString(R.string.error_invalid_field_for_call) + " ")
@ -252,9 +266,11 @@ class ProgrammingActivity : AppCompatActivity(), AnkoLogger {
} }
@SuppressLint("NeedOnRequestPermissionsResult") @SuppressLint("NeedOnRequestPermissionsResult")
override fun onRequestPermissionsResult(requestCode: Int, override fun onRequestPermissionsResult(
permissions: Array<out String>, requestCode: Int,
grantResults: IntArray) { permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults) super.onRequestPermissionsResult(requestCode, permissions, grantResults)
onRequestPermissionsResult(requestCode, grantResults) onRequestPermissionsResult(requestCode, grantResults)
} }
@ -295,16 +311,16 @@ class ProgrammingActivity : AppCompatActivity(), AnkoLogger {
} else if (!validateSize(v.length(), if ((!type.isDKS() && !zeros) && min >= 0) min.toString().length else minSize, size)) { } else if (!validateSize(v.length(), if ((!type.isDKS() && !zeros) && min >= 0) min.toString().length else minSize, size)) {
if (minSize > 0) { if (minSize > 0) {
v.error = getString( v.error = getString(
R.string.error_invalid_size, R.string.error_invalid_size,
minSize, minSize,
resources.getQuantityString(R.plurals.error_digit, minSize), resources.getQuantityString(R.plurals.error_digit, minSize),
getString(R.string.error_minimum)) getString(R.string.error_minimum))
} else { } else {
v.error = getString( v.error = getString(
R.string.error_invalid_size, R.string.error_invalid_size,
size, size,
resources.getQuantityString(R.plurals.error_digit, size), resources.getQuantityString(R.plurals.error_digit, size),
empty) empty)
} }
isValid = false isValid = false
} else if (min >= 0 && max > 0) { } else if (min >= 0 && max > 0) {

View file

@ -22,8 +22,9 @@ import android.app.FragmentManager
import android.os.Bundle import android.os.Bundle
import android.support.v13.app.FragmentStatePagerAdapter import android.support.v13.app.FragmentStatePagerAdapter
import android.support.v4.app.FragmentActivity import android.support.v4.app.FragmentActivity
import kotlinx.android.synthetic.main.activity_steps.* import kotlinx.android.synthetic.main.activity_steps.indicator
import java.util.* import kotlinx.android.synthetic.main.activity_steps.pager
import java.util.ArrayList
class StepsActivity : FragmentActivity() { class StepsActivity : FragmentActivity() {
companion object { companion object {
@ -39,8 +40,10 @@ class StepsActivity : FragmentActivity() {
indicator.fades = false indicator.fades = false
} }
private inner class StepsAdapter(fm: FragmentManager, private inner class StepsAdapter(
steps: ArrayList<String>) : FragmentStatePagerAdapter(fm) { fm: FragmentManager,
steps: ArrayList<String>
) : FragmentStatePagerAdapter(fm) {
private val steps = ArrayList<String>(steps) private val steps = ArrayList<String>(steps)
override fun getItem(position: Int): Fragment = StepsFragment.create(position, steps) override fun getItem(position: Int): Fragment = StepsFragment.create(position, steps)

View file

@ -22,8 +22,9 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_steps.* import kotlinx.android.synthetic.main.fragment_steps.frag_steps
import java.util.* import kotlinx.android.synthetic.main.fragment_steps.frag_steps_title
import java.util.ArrayList
class StepsFragment : Fragment() { class StepsFragment : Fragment() {
private var pageNumber: Int = 0 private var pageNumber: Int = 0
@ -51,9 +52,11 @@ class StepsFragment : Fragment() {
pageNumber = arguments.getInt(ARG_PAGE) pageNumber = arguments.getInt(ARG_PAGE)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(
savedInstanceState: Bundle?): View? = inflater: LayoutInflater,
inflater.inflate(R.layout.fragment_steps, container, false) as ViewGroup container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_steps, container, false) as ViewGroup
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)

View file

@ -22,13 +22,14 @@ import android.text.SpannableStringBuilder
import android.text.Spanned import android.text.Spanned
class AlphaFilter(private val extras: String) : InputFilter { class AlphaFilter(private val extras: String) : InputFilter {
override fun filter(source: CharSequence, override fun filter(
start: Int, source: CharSequence,
end: Int, start: Int,
dest: Spanned, end: Int,
dstart: Int, dest: Spanned,
dend: Int): CharSequence? { dstart: Int,
dend: Int
): CharSequence? {
if (source is SpannableStringBuilder) { if (source is SpannableStringBuilder) {
for (i in end - 1 downTo start) { for (i in end - 1 downTo start) {
val c = source[i] val c = source[i]
@ -40,9 +41,9 @@ class AlphaFilter(private val extras: String) : InputFilter {
} else { } else {
val sb = StringBuilder() val sb = StringBuilder()
(start until end) (start until end)
.map { source[it] } .map { source[it] }
.filter { it.isLetterOrDigit() || extras.contains(it) } .filter { it.isLetterOrDigit() || extras.contains(it) }
.forEach { sb.append(it) } .forEach { sb.append(it) }
return sb.toString() return sb.toString()
} }
} }

View file

@ -27,13 +27,14 @@ class NumberFilter(allowed: String, alt: String) : InputFilter, AnkoLogger {
private val allowed: String private val allowed: String
private val digits = "0123456789" private val digits = "0123456789"
override fun filter(source: CharSequence, override fun filter(
start: Int, source: CharSequence,
end: Int, start: Int,
dest: Spanned, end: Int,
dstart: Int, dest: Spanned,
dend: Int): CharSequence? { dstart: Int,
dend: Int
): CharSequence? {
if (source is SpannableStringBuilder) { if (source is SpannableStringBuilder) {
for (i in end - 1 downTo start) { for (i in end - 1 downTo start) {
val c = source[i] val c = source[i]
@ -45,9 +46,9 @@ class NumberFilter(allowed: String, alt: String) : InputFilter, AnkoLogger {
} else { } else {
val sb = StringBuilder() val sb = StringBuilder()
(start until end) (start until end)
.map { source[it] } .map { source[it] }
.filter { allowed.contains(it) } .filter { allowed.contains(it) }
.forEach { sb.append(it.toUpperCase()) } .forEach { sb.append(it.toUpperCase()) }
return sb.toString() return sb.toString()
} }
} }

View file

@ -23,9 +23,11 @@ import java.io.Serializable
data class Config(var params: Params, var opts: List<Option>) : Parcelable, Serializable, Comparable<Config> { data class Config(var params: Params, var opts: List<Option>) : Parcelable, Serializable, Comparable<Config> {
companion object { companion object {
private @JvmStatic val serialVersionUID: Long = 1 @JvmStatic
private val serialVersionUID: Long = 1
@JvmField val CREATOR: Parcelable.Creator<Config> = object : Parcelable.Creator<Config> { @JvmField
val CREATOR: Parcelable.Creator<Config> = object : Parcelable.Creator<Config> {
override fun createFromParcel(source: Parcel): Config = Config(source) override fun createFromParcel(source: Parcel): Config = Config(source)
override fun newArray(size: Int): Array<Config?> = arrayOfNulls(size) override fun newArray(size: Int): Array<Config?> = arrayOfNulls(size)
} }
@ -34,8 +36,8 @@ data class Config(var params: Params, var opts: List<Option>) : Parcelable, Seri
constructor() : this(Params(), emptyList<Option>()) constructor() : this(Params(), emptyList<Option>())
constructor(source: Parcel) : this( constructor(source: Parcel) : this(
source.readParcelable<Params>(Params::class.java.classLoader), source.readParcelable<Params>(Params::class.java.classLoader),
source.createTypedArrayList(Option.CREATOR)) source.createTypedArrayList(Option.CREATOR))
override fun compareTo(other: Config): Int = params.name.compareTo(other.params.name) override fun compareTo(other: Config): Int = params.name.compareTo(other.params.name)

View file

@ -18,11 +18,12 @@
package net.thauvin.erik.android.tesremoteprogrammer.models package net.thauvin.erik.android.tesremoteprogrammer.models
import java.io.Serializable import java.io.Serializable
import java.util.* import java.util.HashMap
class Configurations : Serializable { class Configurations : Serializable {
companion object { companion object {
private @JvmStatic val serialVersionUID: Long = 1 @JvmStatic
private val serialVersionUID: Long = 1
} }
val configs = HashMap<String, Config>() val configs = HashMap<String, Config>()

View file

@ -21,20 +21,23 @@ import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
import java.io.Serializable import java.io.Serializable
data class Field(var hint: String, data class Field(
var digits: String, var hint: String,
var alpha: Boolean, var digits: String,
val alt: Boolean, var alpha: Boolean,
val zeros: Boolean, val alt: Boolean,
var minSize: Int, val zeros: Boolean,
var size: Int, var minSize: Int,
var min: Int, var size: Int,
var max: Int) : Parcelable, Serializable { var min: Int,
var max: Int
) : Parcelable, Serializable {
companion object { companion object {
private @JvmStatic val serialVersionUID: Long = 1 @JvmStatic
private val serialVersionUID: Long = 1
@JvmField val CREATOR: Parcelable.Creator<Field> = object : Parcelable.Creator<Field> { @JvmField
val CREATOR: Parcelable.Creator<Field> = object : Parcelable.Creator<Field> {
override fun createFromParcel(source: Parcel): Field = Field(source) override fun createFromParcel(source: Parcel): Field = Field(source)
override fun newArray(size: Int): Array<Field?> = arrayOfNulls(size) override fun newArray(size: Int): Array<Field?> = arrayOfNulls(size)
} }
@ -43,15 +46,15 @@ data class Field(var hint: String,
constructor() : this("", "", false, false, false, -1, -1, -1, -1) constructor() : this("", "", false, false, false, -1, -1, -1, -1)
constructor(source: Parcel) : this( constructor(source: Parcel) : this(
source.readString(), source.readString(),
source.readString(), source.readString(),
1 == source.readInt(), 1 == source.readInt(),
1 == source.readInt(), 1 == source.readInt(),
1 == source.readInt(), 1 == source.readInt(),
source.readInt(), source.readInt(),
source.readInt(), source.readInt(),
source.readInt(), source.readInt(),
source.readInt()) source.readInt())
override fun describeContents() = 0 override fun describeContents() = 0

View file

@ -21,16 +21,19 @@ import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
import java.io.Serializable import java.io.Serializable
data class Option(var title: String, data class Option(
var fields: List<Field?>, var title: String,
var nodial: Boolean, var fields: List<Field?>,
var nosteps: Boolean, var nodial: Boolean,
var dtmf: String) : Parcelable, Serializable, Comparable<Option> { var nosteps: Boolean,
var dtmf: String
) : Parcelable, Serializable, Comparable<Option> {
companion object { companion object {
private @JvmStatic val serialVersionUID: Long = 1 @JvmStatic
private val serialVersionUID: Long = 1
@JvmField val CREATOR: Parcelable.Creator<Option> = object : Parcelable.Creator<Option> { @JvmField
val CREATOR: Parcelable.Creator<Option> = object : Parcelable.Creator<Option> {
override fun createFromParcel(source: Parcel): Option = Option(source) override fun createFromParcel(source: Parcel): Option = Option(source)
override fun newArray(size: Int): Array<Option?> = arrayOfNulls(size) override fun newArray(size: Int): Array<Option?> = arrayOfNulls(size)
} }
@ -39,11 +42,11 @@ data class Option(var title: String,
constructor() : this("", emptyList(), false, false, "") constructor() : this("", emptyList(), false, false, "")
constructor(source: Parcel) : this( constructor(source: Parcel) : this(
source.readString(), source.readString(),
source.createTypedArrayList(Field.CREATOR), source.createTypedArrayList(Field.CREATOR),
1 == source.readInt(), 1 == source.readInt(),
1 == source.readInt(), 1 == source.readInt(),
source.readString()) source.readString())
override fun compareTo(other: Option): Int = title.compareTo(other.title) override fun compareTo(other: Option): Int = title.compareTo(other.title)
@ -56,5 +59,4 @@ data class Option(var title: String,
dest?.writeInt((if (nosteps) 1 else 0)) dest?.writeInt((if (nosteps) 1 else 0))
dest?.writeString(dtmf) dest?.writeString(dtmf)
} }
} }

View file

@ -21,20 +21,23 @@ import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
import java.io.Serializable import java.io.Serializable
data class Params(var name: String, data class Params(
var type: String, var name: String,
var phone: String, var type: String,
var master: String, var phone: String,
var size: Int, var master: String,
var ack: String, var size: Int,
var alt: String, var ack: String,
var begin: String, var alt: String,
var end: String) : Parcelable, Serializable { var begin: String,
var end: String
) : Parcelable, Serializable {
companion object { companion object {
private @JvmStatic val serialVersionUID: Long = 1 @JvmStatic
private val serialVersionUID: Long = 1
@JvmField val CREATOR: Parcelable.Creator<Params> = object : Parcelable.Creator<Params> { @JvmField
val CREATOR: Parcelable.Creator<Params> = object : Parcelable.Creator<Params> {
override fun createFromParcel(source: Parcel): Params = Params(source) override fun createFromParcel(source: Parcel): Params = Params(source)
override fun newArray(size: Int): Array<Params?> = arrayOfNulls(size) override fun newArray(size: Int): Array<Params?> = arrayOfNulls(size)
} }
@ -43,15 +46,15 @@ data class Params(var name: String,
constructor() : this("", "", "", "", -1, "", "", "", "") constructor() : this("", "", "", "", -1, "", "", "", "")
constructor(source: Parcel) : this( constructor(source: Parcel) : this(
source.readString(), source.readString(),
source.readString(), source.readString(),
source.readString(), source.readString(),
source.readString(), source.readString(),
source.readInt(), source.readInt(),
source.readString(), source.readString(),
source.readString(), source.readString(),
source.readString(), source.readString(),
source.readString()) source.readString())
override fun describeContents() = 0 override fun describeContents() = 0

View file

@ -36,7 +36,7 @@ class CrashEmail(private val config: ACRAConfiguration) : ReportSender {
override fun send(context: Context, errorContent: CrashReportData) { override fun send(context: Context, errorContent: CrashReportData) {
val subject = context.getString(R.string.crash_report_subject, val subject = context.getString(R.string.crash_report_subject,
context.getString(R.string.app_name)) context.getString(R.string.app_name))
val body = buildBody(errorContent) val body = buildBody(errorContent)
val emailIntent = Intent(android.content.Intent.ACTION_SENDTO) val emailIntent = Intent(android.content.Intent.ACTION_SENDTO)

View file

@ -24,5 +24,5 @@ import org.acra.sender.ReportSenderFactory
class CrashEmailFactory : ReportSenderFactory { class CrashEmailFactory : ReportSenderFactory {
override fun create(context: Context, config: ACRAConfiguration): ReportSender = override fun create(context: Context, config: ACRAConfiguration): ReportSender =
CrashEmail(config) CrashEmail(config)
} }

View file

@ -28,7 +28,7 @@ import org.acra.dialog.BaseCrashReportDialog
import org.jetbrains.anko.find import org.jetbrains.anko.find
class CrashReportActivity : BaseCrashReportDialog(), DialogInterface.OnDismissListener, class CrashReportActivity : BaseCrashReportDialog(), DialogInterface.OnDismissListener,
DialogInterface.OnClickListener { DialogInterface.OnClickListener {
private var comment: EditText? = null private var comment: EditText? = null
companion object { companion object {
@ -39,11 +39,11 @@ class CrashReportActivity : BaseCrashReportDialog(), DialogInterface.OnDismissLi
super.init(savedInstanceState) super.init(savedInstanceState)
val dialog = AlertDialog.Builder(this) val dialog = AlertDialog.Builder(this)
.setTitle(getString(R.string.crash_dialog_title, getString(R.string.app_name))) .setTitle(getString(R.string.crash_dialog_title, getString(R.string.app_name)))
.setView(R.layout.crash_report_dialog) .setView(R.layout.crash_report_dialog)
.setPositiveButton(R.string.ok, this) .setPositiveButton(R.string.ok, this)
.setNegativeButton(R.string.cancel, this) .setNegativeButton(R.string.cancel, this)
.create() .create()
dialog.setCanceledOnTouchOutside(false) dialog.setCanceledOnTouchOutside(false)
dialog.setOnDismissListener(this) dialog.setOnDismissListener(this)

View file

@ -20,7 +20,7 @@ package net.thauvin.erik.android.tesremoteprogrammer.util
import android.widget.EditText import android.widget.EditText
import net.thauvin.erik.android.tesremoteprogrammer.MainActivity import net.thauvin.erik.android.tesremoteprogrammer.MainActivity
import net.thauvin.erik.android.tesremoteprogrammer.models.Option import net.thauvin.erik.android.tesremoteprogrammer.models.Option
import java.util.* import java.util.ArrayList
class Dtmf { class Dtmf {
companion object { companion object {
@ -89,7 +89,6 @@ class Dtmf {
fun isValidType(type: String): Boolean = type.equals(DKS, true) || type.equals(LINEAR, true) fun isValidType(type: String): Boolean = type.equals(DKS, true) || type.equals(LINEAR, true)
private fun linearAlphaToDigits(text: String): String { private fun linearAlphaToDigits(text: String): String {
val result = StringBuffer() val result = StringBuffer()
@ -150,22 +149,24 @@ class Dtmf {
return result.toString() return result.toString()
} }
fun build(type: String, fun build(
master: String, type: String,
ack: String, master: String,
option: Option, ack: String,
fields: ArrayList<EditText>): String { option: Option,
fields: ArrayList<EditText>
): String {
val replace = arrayListOf(Pair(DTMF_MASTER, master)) val replace = arrayListOf(Pair(DTMF_MASTER, master))
fields.forEachIndexed { i, field -> fields.forEachIndexed { i, field ->
replace.add(Pair(DTMF_FIELD.format(i + 1), replace.add(Pair(DTMF_FIELD.format(i + 1),
if (option.fields[i]!!.alpha && type.isDKS()) { if (option.fields[i]!!.alpha && type.isDKS()) {
dksAlphaToDigits(field.text.toString(), ack) dksAlphaToDigits(field.text.toString(), ack)
} else if (option.fields[i]!!.alpha && type.isLinear()) { } else if (option.fields[i]!!.alpha && type.isLinear()) {
linearAlphaToDigits(field.text.toString()) linearAlphaToDigits(field.text.toString())
} else { } else {
field.text.toString() field.text.toString()
})) }))
} }
return option.dtmf.replaceAll(replace.toTypedArray()) return option.dtmf.replaceAll(replace.toTypedArray())

View file

@ -23,26 +23,30 @@ import android.support.v4.view.ViewCompat
import android.view.View import android.view.View
class ScrollAwareFABBehavior : FloatingActionButton.Behavior() { class ScrollAwareFABBehavior : FloatingActionButton.Behavior() {
override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, override fun onStartNestedScroll(
child: FloatingActionButton, coordinatorLayout: CoordinatorLayout,
directTargetChild: View, child: FloatingActionButton,
target: View, directTargetChild: View,
nestedScrollAxes: Int, target: View,
type: Int): Boolean = nestedScrollAxes: Int,
nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL || type: Int
super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, ): Boolean =
nestedScrollAxes, type) nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL ||
super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target,
nestedScrollAxes, type)
override fun onNestedScroll(coordinatorLayout: CoordinatorLayout, override fun onNestedScroll(
child: FloatingActionButton, coordinatorLayout: CoordinatorLayout,
target: View, child: FloatingActionButton,
dxConsumed: Int, target: View,
dyConsumed: Int, dxConsumed: Int,
dxUnconsumed: Int, dyConsumed: Int,
dyUnconsumed: Int, dxUnconsumed: Int,
type: Int) { dyUnconsumed: Int,
type: Int
) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed, type) dxUnconsumed, dyUnconsumed, type)
if (dyConsumed > 0 && child.visibility == View.VISIBLE) { if (dyConsumed > 0 && child.visibility == View.VISIBLE) {
// see: https://stackoverflow.com/a/42082313/5640587 // see: https://stackoverflow.com/a/42082313/5640587
child.hide(object : FloatingActionButton.OnVisibilityChangedListener() { child.hide(object : FloatingActionButton.OnVisibilityChangedListener() {