Updated dependencies and cleanup

This commit is contained in:
Erik C. Thauvin 2019-09-26 15:27:41 -07:00
parent 23d2953c6b
commit df097598cd
30 changed files with 601 additions and 321 deletions

View file

@ -1,29 +1,116 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
<codeStyleSettings language="XML">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>

4
.idea/encodings.xml generated Normal file
View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>

21
.idea/misc.xml generated
View file

@ -5,26 +5,41 @@
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="4">
<list size="12">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
<item index="4" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
<item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
<item index="10" class="java.lang.String" itemvalue="android.annotation.Nullable" />
<item index="11" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="4">
<list size="11">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
<item index="4" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
<item index="6" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
<item index="9" class="java.lang.String" itemvalue="android.annotation.NonNull" />
<item index="10" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="JDK" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View file

@ -1,21 +1,20 @@
plugins {
id "org.jlleitschuh.gradle.ktlint" version "6.2.1"
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-android-extensions'
id 'kotlin-kapt'
id 'org.jmailen.kotlinter' version '2.1.1'
id 'com.github.ben-manes.versions' version '0.25.0'
}
//noinspection GradleCompatible
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 27
compileSdkVersion 28
buildToolsVersion '28.0.3'
defaultConfig {
applicationId "net.thauvin.erik.android.tesremoteprogrammer"
minSdkVersion 21
targetSdkVersion 27
minSdkVersion 26
targetSdkVersion 28
versionCode 1
versionName "1.0"
buildConfigField "long", "TIMESTAMP", System.currentTimeMillis() + "L"
@ -32,8 +31,8 @@ android {
}
ext {
anko_version = '0.10.7'
support_version = "27.1.1"
anko_version = '0.10.8'
support_version = "28.0.0"
}
dependencies {
@ -54,9 +53,6 @@ dependencies {
// https://github.com/JakeWharton/ViewPagerIndicator
implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar'
// https://github.com/AndroidDeveloperLB/AutoFitTextView
implementation 'com.github.AndroidDeveloperLB:AutoFitTextView:4'
// https://github.com/hotchemi/PermissionsDispatcher
// Version 4.x does not work.
implementation 'com.github.hotchemi:permissionsdispatcher:3.3.1'
@ -82,3 +78,10 @@ repositories {
kapt {
//generateStubs = true
}
kotlinter {
ignoreFailures = false
reporters = ['html']
experimentalRules = false
disabledRules = ['import-ordering']
}

View file

@ -1,22 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.thauvin.erik.android.tesremoteprogrammer">
package="net.thauvin.erik.android.tesremoteprogrammer">
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:name=".App"
android:allowBackup="true"
android:fullBackupContent="@xml/mybackupscheme"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".SplashActivity"
android:name=".App"
android:allowBackup="true"
android:fullBackupContent="@xml/mybackupscheme"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/SplashTheme">
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".SplashActivity"
android:label="@string/app_name"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -24,21 +24,21 @@
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:screenOrientation="portrait" />
android:name=".MainActivity"
android:screenOrientation="portrait" />
<activity
android:name=".ProgrammingActivity"
android:screenOrientation="portrait" />
android:name=".ProgrammingActivity"
android:screenOrientation="portrait" />
<activity
android:name=".StepsActivity"
android:screenOrientation="landscape" />
android:name=".StepsActivity"
android:screenOrientation="landscape" />
<activity
android:name=".reporting.CrashReportActivity"
android:excludeFromRecents="true"
android:finishOnTaskLaunch="true"
android:launchMode="singleInstance"
android:process=":error_report" />
android:name=".reporting.CrashReportActivity"
android:excludeFromRecents="true"
android:finishOnTaskLaunch="true"
android:launchMode="singleInstance"
android:process=":error_report" />
</application>
</manifest>
</manifest>

View file

@ -1,7 +1,7 @@
/*
* App.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -25,13 +25,16 @@ import org.acra.ACRA
import org.acra.ReportingInteractionMode
import org.acra.annotation.ReportsCrashes
@ReportsCrashes(mailTo = "erik@thauvin.net",
@Suppress("unused")
@ReportsCrashes(
mailTo = "erik@thauvin.net",
mode = ReportingInteractionMode.DIALOG,
reportSenderFactoryClasses = [CrashEmailFactory::class],
reportDialogClass = CrashReportActivity::class)
reportDialogClass = CrashReportActivity::class
)
open class App : Application() {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
ACRA.init(this)
}
}
}

View file

@ -1,7 +1,7 @@
/*
* MainActivity.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,13 +24,11 @@ import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.graphics.Typeface
import android.os.Build
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
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.view.Menu
@ -89,7 +87,8 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
R.raw.dks_1819,
R.raw.linear_ae_100,
R.raw.linear_ae_500,
R.raw.dks_1803_1808_1810)
R.raw.dks_1803_1808_1810
)
private val readRequestCode = 42
companion object {
@ -97,14 +96,7 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
const val QUOTE = "'"
}
private fun fromHtml(s: String): Spanned {
if (Build.VERSION.SDK_INT >= 24) {
return Html.fromHtml(s, Html.FROM_HTML_MODE_LEGACY)
} else {
@Suppress("DEPRECATION")
return Html.fromHtml(s)
}
}
private fun fromHtml(s: String) = Html.fromHtml(s, Html.FROM_HTML_MODE_LEGACY)
private fun initConfigurations() {
try {
@ -115,8 +107,10 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
val confs = Configurations()
defaultConfigurations.forEach {
config = Gson().fromJson(InputStreamReader(resources.openRawResource(it)),
Config::class.java)
config = Gson().fromJson(
InputStreamReader(resources.openRawResource(it)),
Config::class.java
)
if (BuildConfig.DEBUG) {
val errors = StringBuilder()
@ -145,7 +139,8 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
extraTitle = "Last Update"
extra = DateFormat.getDateInstance(DateFormat.LONG, Locale.getDefault()).format(
Date(BuildConfig.TIMESTAMP)).toString()
Date(BuildConfig.TIMESTAMP)
).toString()
emailAddress = "erik@thauvin.net"
emailSubject = "${getString(R.string.app_name)} ${BuildConfig.VERSION_NAME} Support"
@ -172,8 +167,10 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
val errors = StringBuilder()
val tmp: Config? = try {
Gson().fromJson(InputStreamReader(contentResolver.openInputStream(intent.data)),
Config::class.java)
Gson().fromJson(
InputStreamReader(contentResolver.openInputStream(intent.data!!)),
Config::class.java
)
} catch (jse: JsonSyntaxException) {
val cause = jse.cause
if (cause != null) {
@ -221,7 +218,7 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
// config name
textView {
text = name.toUpperCase()
text = name.toUpperCase(Locale.getDefault())
bottomPadding = dip(5)
typeface = Typeface.DEFAULT_BOLD
setTextSize(TypedValue.COMPLEX_UNIT_DIP, 24f)
@ -241,7 +238,12 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
setText(phone)
}
setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_call_black_24dp, 0)
setCompoundDrawablesWithIntrinsicBounds(
0,
0,
R.drawable.ic_call_black_24dp,
0
)
setOnFocusChangeListener { view, hasFocus ->
if (!hasFocus) {
phone = (view as EditText).text.toString()
@ -259,7 +261,12 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD
hint = getString(R.string.hint_master_code)
filters = arrayOf(InputFilter.LengthFilter(size))
setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_lock_question_black_24dp, 0)
setCompoundDrawablesWithIntrinsicBounds(
0,
0,
R.drawable.ic_lock_question_black_24dp,
0
)
imeOptions = EditorInfo.IME_ACTION_DONE
if (master.isNotBlank()) {
@ -303,7 +310,11 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
titles.add(it.title)
}
adapter = ArrayAdapter<String>(this@MainActivity, android.R.layout.simple_list_item_1, titles)
adapter = ArrayAdapter(
this@MainActivity,
android.R.layout.simple_list_item_1,
titles
)
isTextFilterEnabled = true
isScrollbarFadingEnabled = false
onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ ->
@ -311,7 +322,8 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
saveConfig()
startActivity<ProgrammingActivity>(
"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]
)
}
}
}
@ -444,129 +456,194 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
with(option) {
// title
if (title.isBlank()) {
errors.append(getString(
R.string.validate_missing_opts_prop,
i + 1,
"title"))
errors.append(
getString(
R.string.validate_missing_opts_prop,
i + 1,
"title"
)
)
}
// nosteps/nodial
if (nosteps && nodial) {
errors.append(getString(
R.string.validate_invalid_option,
i + 1,
"nodial/nosteps"))
errors.append(
getString(
R.string.validate_invalid_option,
i + 1,
"nodial/nosteps"
)
)
}
// dtmf
if (dtmf.isBlank()) {
errors.append(getString(
R.string.validate_missing_opts_prop,
i + 1,
"dtmf"))
errors.append(
getString(
R.string.validate_missing_opts_prop,
i + 1,
"dtmf"
)
)
} else if (!nodial && fields.isEmpty()) { // fields missing
errors.append(getString(
R.string.validate_missing_opts_prop,
i + 1,
"fields"))
errors.append(
getString(
R.string.validate_missing_opts_prop,
i + 1,
"fields"
)
)
} else {
val blank = "\\0"
val mock = Dtmf.mock(option, blank)
if (!mock.contains(MainActivity.PAUSE)) { // no pause
errors.append(getString(
R.string.validate_invalid_opts_prop,
i + 1,
"dtmf",
getString(R.string.validate_dtmf_nopause)))
if (!mock.contains(PAUSE)) { // no pause
errors.append(
getString(
R.string.validate_invalid_opts_prop,
i + 1,
"dtmf",
getString(R.string.validate_dtmf_nopause)
)
)
}
if (!Dtmf.validate(mock,
"${MainActivity.PAUSE}${params.ack}${params.alt}$blank", nodial)) {
errors.append(getString(
R.string.validate_invalid_opts_prop,
i + 1,
"dtmf",
mock.replace(blank, "&#10003;")))
if (!Dtmf.validate(
mock,
"$PAUSE${params.ack}${params.alt}$blank", nodial
)) {
errors.append(
getString(
R.string.validate_invalid_opts_prop,
i + 1,
"dtmf",
mock.replace(blank, "&#10003;")
)
)
}
}
// fields
fields.forEachIndexed { j, field ->
if (field == null) {
errors.append(getString(
R.string.validate_syntax_error,
"opts[${i + 1}], field[$j]"))
errors.append(
getString(
R.string.validate_syntax_error,
"opts[${i + 1}], field[$j]"
)
)
} else {
with(field) {
// size
if (size <= 0) {
errors.append(getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(R.plurals.error_prop, 1),
"size=$size"))
errors.append(
getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(
R.plurals.error_prop,
1
),
"size=$size"
)
)
}
// digits
if (digits.isNotBlank() && !digits.isDigits()) {
errors.append(getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(R.plurals.error_prop, 1),
"digits='$digits'"))
errors.append(
getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(
R.plurals.error_prop,
1
),
"digits='$digits'"
)
)
}
// minSize
if (minSize >= 0 && minSize > size) {
errors.append(getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(R.plurals.error_prop, 2),
"minSize=$minSize > size=$size"))
errors.append(
getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(
R.plurals.error_prop,
2
),
"minSize=$minSize > size=$size"
)
)
}
// numeric fields only
if (!alpha) {
if (minSize == 0) {
errors.append(getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(R.plurals.error_prop, 1),
"minSize=$minSize"))
errors.append(
getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(
R.plurals.error_prop,
1
),
"minSize=$minSize"
)
)
}
// min/max
if (min >= 0 || max >= 0) {
if (max < 1) {
errors.append(getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(R.plurals.error_prop, 1),
"max=$max"))
errors.append(
getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(
R.plurals.error_prop,
1
),
"max=$max"
)
)
}
if (min < 0) {
errors.append(getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(R.plurals.error_prop, 1),
"min=$min"))
errors.append(
getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(
R.plurals.error_prop,
1
),
"min=$min"
)
)
}
if (min > max) {
errors.append(getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(R.plurals.error_prop, 2),
"min=$min > max=$max"))
errors.append(
getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(
R.plurals.error_prop,
2
),
"min=$min > max=$max"
)
)
}
}
@ -575,24 +652,36 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
// minSize/min
if (min >= 0 && minSize > 0) {
if (min.toString().length != minSize) {
errors.append(getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(R.plurals.error_prop, 2),
"minSize=$minSize/min=$min"))
errors.append(
getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(
R.plurals.error_prop,
2
),
"minSize=$minSize/min=$min"
)
)
}
}
// size/max
if (size > 0 && max > 0) {
if (max.toString().length != size) {
errors.append(getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(R.plurals.error_prop, 2),
"size=$size/max=$max"))
errors.append(
getString(
R.string.validate_invalid_field_prop,
i + 1,
j + 1,
resources.getQuantityString(
R.plurals.error_prop,
2
),
"size=$size/max=$max"
)
)
}
}
}
@ -600,10 +689,13 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
// unused fields
if (!dtmf.contains(Dtmf.DTMF_FIELD.format(j + 1))) {
errors.append(getString(
R.string.validate_unused_field,
i + 1,
j + 1))
errors.append(
getString(
R.string.validate_unused_field,
i + 1,
j + 1
)
)
}
}
}
@ -626,8 +718,10 @@ class MainActivity : AppCompatActivity(), AnkoLogger {
first.error = getString(R.string.error_required)
isValid = false
} else if (second > 0 && first.text.length != second) {
first.error = getString(R.string.error_invalid_size, second,
resources.getQuantityString(R.plurals.error_digit, second), "")
first.error = getString(
R.string.error_invalid_size, second,
resources.getQuantityString(R.plurals.error_digit, second), ""
)
isValid = false
}
}

View file

@ -1,7 +1,7 @@
/*
* ProgrammingActivity.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -77,8 +77,8 @@ class ProgrammingActivity : AppCompatActivity(), AnkoLogger {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val params: Params = intent.extras.getParcelable("net.thauvin.erik.android.tesremoteprogrammer.models.Params")
val option: Option = intent.extras.getParcelable("net.thauvin.erik.android.tesremoteprogrammer.models.Option")
val params: Params = intent.extras!!.getParcelable("net.thauvin.erik.android.tesremoteprogrammer.models.Params")
val option: Option = intent.extras!!.getParcelable("net.thauvin.erik.android.tesremoteprogrammer.models.Option")
val fields = arrayListOf<EditText>()
coordinatorLayout {
@ -135,14 +135,21 @@ class ProgrammingActivity : AppCompatActivity(), AnkoLogger {
}
} else {
inputType = InputType.TYPE_CLASS_PHONE
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) {
inputFilters.add(
MinMaxFilter(
field.min,
field.max,
field.size,
params.type.isDKS() || field.zeros))
params.type.isDKS() || field.zeros
)
)
}
}
@ -196,8 +203,18 @@ class ProgrammingActivity : AppCompatActivity(), AnkoLogger {
elevation = dip(6).toFloat()
}.setOnClickListener {
if (validateFields(params.type, fields, option)) {
val dtmf = Dtmf.build(params.type, params.master, params.ack, option, fields)
if (Dtmf.validate(dtmf, "${MainActivity.PAUSE}${params.ack}${params.alt}", option.nodial)) {
val dtmf = Dtmf.build(
params.type,
params.master,
params.ack,
option,
fields
)
if (Dtmf.validate(
dtmf,
"${MainActivity.PAUSE}${params.ack}${params.alt}",
option.nodial
)) {
val begin = if (params.begin.isNotBlank()) {
"${params.begin}${MainActivity.PAUSE}"
} else {
@ -245,21 +262,40 @@ class ProgrammingActivity : AppCompatActivity(), AnkoLogger {
}.setOnClickListener {
if (validateFieldsForCall(params.type, fields)) {
if (validateFields(params.type, fields, option)) {
val dtmf = Dtmf.build(params.type, params.master, params.ack, option, fields)
if (Dtmf.validate(dtmf, "${MainActivity.PAUSE}${params.ack}${params.alt}", option.nodial)) {
val dtmf = Dtmf.build(
params.type,
params.master,
params.ack,
option,
fields
)
if (Dtmf.validate(
dtmf,
"${MainActivity.PAUSE}${params.ack}${params.alt}",
option.nodial
)) {
callWithPermissionCheck(params.phone, dtmf)
} else {
Snackbar.make(this@coordinatorLayout,
Snackbar.make(
this@coordinatorLayout,
getString(R.string.error_invalid_dtmf, dtmf),
Snackbar.LENGTH_LONG).show()
Snackbar.LENGTH_LONG
).show()
}
} else {
Snackbar.make(this@coordinatorLayout, R.string.error_invalid_field,
Snackbar.LENGTH_LONG).show()
Snackbar.make(
this@coordinatorLayout, R.string.error_invalid_field,
Snackbar.LENGTH_LONG
).show()
}
} else {
val text = SpannableString(getString(R.string.error_invalid_field_for_call) + " ")
text.setSpan(ImageSpan(context, R.drawable.ic_menu_dialpad_lt), text.length - 1, text.length, 0)
text.setSpan(
ImageSpan(context, R.drawable.ic_menu_dialpad_lt),
text.length - 1,
text.length,
0
)
Snackbar.make(this@coordinatorLayout, text, Snackbar.LENGTH_LONG).show()
}
}
@ -310,19 +346,25 @@ class ProgrammingActivity : AppCompatActivity(), AnkoLogger {
if (v.text.isNullOrBlank()) {
v.error = getString(R.string.error_required)
isValid = false
} 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) {
v.error = getString(
R.string.error_invalid_size,
minSize,
resources.getQuantityString(R.plurals.error_digit, minSize),
getString(R.string.error_minimum))
getString(R.string.error_minimum)
)
} else {
v.error = getString(
R.string.error_invalid_size,
size,
resources.getQuantityString(R.plurals.error_digit, size),
empty)
empty
)
}
isValid = false
} else if (min >= 0 && max > 0) {

View file

@ -1,7 +1,7 @@
/*
* SplashActivity.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,11 +22,10 @@ import android.support.v7.app.AppCompatActivity
import org.jetbrains.anko.startActivity
class SplashActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startActivity<MainActivity>()
finish()
}
}
}

View file

@ -1,7 +1,7 @@
/*
* StepsActivity.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View file

@ -1,7 +1,7 @@
/*
* StepsFragment.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -63,7 +63,11 @@ class StepsFragment : Fragment() {
if (steps.isEmpty()) {
activity.finish()
} else {
frag_steps_title.text = getString(R.string.title_template_step, pageNumber + 1, steps.size)
frag_steps_title.text = getString(
R.string.title_template_step,
pageNumber + 1,
steps.size
)
frag_steps.text = steps[pageNumber]
}
}

View file

@ -1,7 +1,7 @@
/*
* AlphaFilter.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -47,4 +47,4 @@ class AlphaFilter(private val extras: String) : InputFilter {
return sb.toString()
}
}
}
}

View file

@ -1,7 +1,7 @@
/*
* MinMaxFilter.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,9 +21,21 @@ import android.text.InputFilter
import android.text.Spanned
import org.jetbrains.anko.AnkoLogger
class MinMaxFilter(private val min: Int, private val max: Int, private val size: Int, private val zeros: Boolean) : InputFilter, AnkoLogger {
class MinMaxFilter(
private val min: Int,
private val max: Int,
private val size: Int,
private val zeros: Boolean
) : InputFilter, AnkoLogger {
override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? {
override fun filter(
source: CharSequence,
start: Int,
end: Int,
dest: Spanned,
dstart: Int,
dend: Int
): CharSequence? {
val input = (dest.toString() + source.toString())
if (isInRange(input, size, min, max, zeros)) {
@ -56,4 +68,4 @@ class MinMaxFilter(private val min: Int, private val max: Int, private val size:
return false
}
}
}
}

View file

@ -1,7 +1,7 @@
/*
* NumberFilter.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -27,6 +27,14 @@ class NumberFilter(allowed: String, alt: String) : InputFilter, AnkoLogger {
private val allowed: String
private val digits = "0123456789"
init {
this.allowed = if (allowed.isDigits()) {
"$allowed$alt"
} else {
"$digits$alt"
}
}
override fun filter(
source: CharSequence,
start: Int,
@ -52,12 +60,4 @@ class NumberFilter(allowed: String, alt: String) : InputFilter, AnkoLogger {
return sb.toString()
}
}
init {
this.allowed = if (allowed.isDigits()) {
"$allowed$alt"
} else {
"$digits$alt"
}
}
}
}

View file

@ -1,7 +1,7 @@
/*
* Config.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,7 +21,10 @@ import android.os.Parcel
import android.os.Parcelable
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 {
@JvmStatic
private val serialVersionUID: Long = 1
@ -37,7 +40,8 @@ data class Config(var params: Params, var opts: List<Option>) : Parcelable, Seri
constructor(source: Parcel) : this(
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)
@ -47,4 +51,4 @@ data class Config(var params: Params, var opts: List<Option>) : Parcelable, Seri
dest?.writeParcelable(params, 0)
dest?.writeTypedList(opts)
}
}
}

View file

@ -1,7 +1,7 @@
/*
* Configurations.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -27,4 +27,4 @@ class Configurations : Serializable {
}
val configs = HashMap<String, Config>()
}
}

View file

@ -1,7 +1,7 @@
/*
* Field.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -54,7 +54,8 @@ data class Field(
source.readInt(),
source.readInt(),
source.readInt(),
source.readInt())
source.readInt()
)
override fun describeContents() = 0

View file

@ -1,7 +1,7 @@
/*
* Option.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -46,7 +46,8 @@ data class Option(
source.createTypedArrayList(Field.CREATOR),
1 == source.readInt(),
1 == source.readInt(),
source.readString())
source.readString()
)
override fun compareTo(other: Option): Int = title.compareTo(other.title)
@ -59,4 +60,4 @@ data class Option(
dest?.writeInt((if (nosteps) 1 else 0))
dest?.writeString(dtmf)
}
}
}

View file

@ -1,7 +1,7 @@
/*
* Params.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -54,7 +54,8 @@ data class Params(
source.readString(),
source.readString(),
source.readString(),
source.readString())
source.readString()
)
override fun describeContents() = 0
@ -69,4 +70,4 @@ data class Params(
dest?.writeString(begin)
dest?.writeString(end)
}
}
}

View file

@ -1,7 +1,7 @@
/*
* CrashEmail.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -35,8 +35,10 @@ class CrashEmail(private val config: ACRAConfiguration) : ReportSender {
@Throws(ReportSenderException::class)
override fun send(context: Context, errorContent: CrashReportData) {
val subject = context.getString(R.string.crash_report_subject,
context.getString(R.string.app_name))
val subject = context.getString(
R.string.crash_report_subject,
context.getString(R.string.app_name)
)
val body = buildBody(errorContent)
val emailIntent = Intent(android.content.Intent.ACTION_SENDTO)
@ -63,4 +65,4 @@ class CrashEmail(private val config: ACRAConfiguration) : ReportSender {
}
return builder.toString()
}
}
}

View file

@ -1,7 +1,7 @@
/*
* CrashEmailFactory.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -25,4 +25,4 @@ import org.acra.sender.ReportSenderFactory
class CrashEmailFactory : ReportSenderFactory {
override fun create(context: Context, config: ACRAConfiguration): ReportSender =
CrashEmail(config)
}
}

View file

@ -1,7 +1,7 @@
/*
* CrashReportActivity.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -28,11 +28,11 @@ import org.acra.dialog.BaseCrashReportDialog
import org.jetbrains.anko.find
class CrashReportActivity : BaseCrashReportDialog(), DialogInterface.OnDismissListener,
DialogInterface.OnClickListener {
DialogInterface.OnClickListener {
private var comment: EditText? = null
companion object {
private val STATE_USER_COMMENT = "comment"
private const val STATE_USER_COMMENT = "comment"
}
override fun init(savedInstanceState: Bundle?) {
@ -70,4 +70,4 @@ class CrashReportActivity : BaseCrashReportDialog(), DialogInterface.OnDismissLi
outState.putString(STATE_USER_COMMENT, comment!!.text.toString())
super.onSaveInstanceState(outState)
}
}
}

View file

@ -1,7 +1,7 @@
/*
* Dtmf.kt
*
* Copyright 2016-2018 Erik C. Thauvin (erik@thauvin.net)
* Copyright 2016-2019 Erik C. Thauvin (erik@thauvin.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,12 +24,12 @@ import java.util.ArrayList
class Dtmf {
companion object {
val DTMF_MASTER = "[MASTER]"
val DTMF_FIELD = "[FIELD:%1\$d]"
val DKS = "dks"
val LINEAR = "linear"
val DKS_EXTRAS = " "
val LINEAR_EXTRAS = ", -."
const val DTMF_MASTER = "[MASTER]"
const val DTMF_FIELD = "[FIELD:%1\$d]"
const val DKS = "dks"
const val LINEAR = "linear"
const val DKS_EXTRAS = " "
const val LINEAR_EXTRAS = ", -."
private fun dksAlphaToDigits(text: String, ack: String): String {
val result = StringBuffer()
@ -159,14 +159,18 @@ class Dtmf {
val replace = arrayListOf(Pair(DTMF_MASTER, master))
fields.forEachIndexed { i, field ->
replace.add(Pair(DTMF_FIELD.format(i + 1),
if (option.fields[i]!!.alpha && type.isDKS()) {
dksAlphaToDigits(field.text.toString(), ack)
} else if (option.fields[i]!!.alpha && type.isLinear()) {
linearAlphaToDigits(field.text.toString())
} else {
field.text.toString()
}))
replace.add(
Pair(
DTMF_FIELD.format(i + 1),
if (option.fields[i]!!.alpha && type.isDKS()) {
dksAlphaToDigits(field.text.toString(), ack)
} else if (option.fields[i]!!.alpha && type.isLinear()) {
linearAlphaToDigits(field.text.toString())
} else {
field.text.toString()
}
)
)
}
return option.dtmf.replaceAll(replace.toTypedArray())
@ -195,4 +199,4 @@ class Dtmf {
return option.dtmf.replaceAll(replace.toTypedArray())
}
}
}
}

View file

@ -5,8 +5,8 @@
<item>
<bitmap
android:gravity="center"
android:src="@drawable/splash"/>
android:gravity="center"
android:src="@drawable/splash" />
</item>
</layer-list>
</layer-list>

View file

@ -1,18 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<com.viewpagerindicator.UnderlinePageIndicator
android:id="@+id/indicator"
style="@style/UnderlinePageIndicator"
android:layout_width="match_parent"
android:layout_height="5dp" />
</LinearLayout>
android:id="@+id/indicator"
style="@style/UnderlinePageIndicator"
android:layout_width="match_parent"
android:layout_height="5dp" />
</LinearLayout>

View file

@ -1,38 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="24dp"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:paddingTop="20dp">
<TextView
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crash_dialog_text" />
android:orientation="vertical"
android:paddingLeft="24dp"
android:paddingTop="20dp"
android:paddingRight="24dp"
android:paddingBottom="24dp">
<TextView
android:id="@android:id/text2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:labelFor="@android:id/input"
android:text="@string/crash_dialog_comment_prompt" />
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crash_dialog_text" />
<TextView
android:id="@android:id/text2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:labelFor="@android:id/input"
android:text="@string/crash_dialog_comment_prompt" />
<EditText
android:id="@android:id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:inputType="textMultiLine|textCapSentences" />
android:id="@android:id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:inputType="textMultiLine|textCapSentences" />
</LinearLayout>
</ScrollView>
</ScrollView>

View file

@ -1,15 +1,15 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<item
android:id="@+id/action_confs"
android:orderInCategory="100"
android:title="@string/action_config"
app:showAsAction="never" />
android:id="@+id/action_confs"
android:orderInCategory="100"
android:title="@string/action_config"
app:showAsAction="never" />
<item
android:id="@+id/action_about"
android:orderInCategory="101"
android:title="@string/action_about"
app:showAsAction="never" />
android:id="@+id/action_about"
android:orderInCategory="101"
android:title="@string/action_about"
app:showAsAction="never" />
</menu>

View file

@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
<include domain="file" path="configurations.dat"/>
<include domain="file" path="config.dat"/>
</full-backup-content>
<include
domain="file"
path="configurations.dat" />
<include
domain="file"
path="config.dat" />
</full-backup-content>

View file

@ -1,13 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.3.0'
ext.kotlin_version = '1.3.41'
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong

View file

@ -1,6 +1,6 @@
#Sun Nov 04 14:30:01 PST 2018
#Wed Sep 25 10:52:41 PDT 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip