diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d0bfde..d7b8d0a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,12 @@ jobs: chmod a+x ktlint sudo mv ktlint /usr/local/bin/ - name: Run - run: ktlint + run: ktlint --reporter sarif -l none > ktlint.sarif + - name: Upload SARIF + uses: github/codeql-action/upload-sarif@v2 + if: success() || failure() + with: + sarif_file: ktlint.sarif detekt: runs-on: ubuntu-latest steps: @@ -33,6 +38,14 @@ jobs: run: | chmod +x gradlew ./gradlew detekt + - name: Upload SARIF + uses: github/codeql-action/upload-sarif@v2 + if: success() || failure() + with: + sarif_file: app/build/reports/detekt/detekt.sarif + - name: Job Summary + if: success() || failure() + run: cat ./app/build/reports/detekt/detekt.md >> $GITHUB_STEP_SUMMARY build: runs-on: ubuntu-latest steps: diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 065eeb7..e29038b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -54,6 +54,11 @@ android { kotlinOptions { jvmTarget = "17" } + detekt { + config.setFrom(file("detekt-config.yml")) + buildUponDefaultConfig = true + basePath = rootProject.projectDir.absolutePath + } lint { disable += "MissingTranslation" } diff --git a/app/detekt-config.yml b/app/detekt-config.yml new file mode 100644 index 0000000..2dd9220 --- /dev/null +++ b/app/detekt-config.yml @@ -0,0 +1,784 @@ +build: + maxIssues: 0 + excludeCorrectable: true + weights: + # complexity: 2 + # LongParameterList: 1 + # style: 1 + # comments: 1 + +config: + validation: true + warningsAsErrors: true + checkExhaustiveness: true + # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' + excludes: '' + +processors: + active: true + exclude: + - 'DetektProgressListener' + # - 'KtFileCountProcessor' + # - 'PackageCountProcessor' + # - 'ClassCountProcessor' + # - 'FunctionCountProcessor' + # - 'PropertyCountProcessor' + # - 'ProjectComplexityProcessor' + # - 'ProjectCognitiveComplexityProcessor' + # - 'ProjectLLOCProcessor' + # - 'ProjectCLOCProcessor' + # - 'ProjectLOCProcessor' + # - 'ProjectSLOCProcessor' + # - 'LicenseHeaderLoaderExtension' + +console-reports: + active: true + exclude: + - 'ProjectStatisticsReport' + - 'ComplexityReport' + - 'NotificationReport' + - 'FindingsReport' + - 'FileBasedFindingsReport' + # - 'LiteFindingsReport' + +output-reports: + active: true + exclude: + # - 'TxtOutputReport' + # - 'XmlOutputReport' + # - 'HtmlOutputReport' + # - 'MdOutputReport' + # - 'SarifOutputReport' + +comments: + active: true + AbsentOrWrongFileLicense: + active: false + licenseTemplateFile: 'license.template' + licenseTemplateIsRegex: true + CommentOverPrivateFunction: + active: true + CommentOverPrivateProperty: + active: true + DeprecatedBlockTag: + active: true + EndOfSentenceFormat: + active: true + endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' + KDocReferencesNonPublicProperty: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + OutdatedDocumentation: + active: true + matchTypeParameters: true + matchDeclarationsOrder: true + allowParamOnConstructorProperties: true + UndocumentedPublicClass: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + searchInNestedClass: true + searchInInnerClass: true + searchInInnerObject: true + searchInInnerInterface: true + searchInProtectedClass: true + UndocumentedPublicFunction: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + searchProtectedFunction: true + UndocumentedPublicProperty: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + searchProtectedProperty: true + +complexity: + active: true + CognitiveComplexMethod: + active: false + threshold: 15 + ComplexCondition: + active: true + threshold: 4 + ComplexInterface: + active: true + threshold: 10 + includeStaticDeclarations: true + includePrivateDeclarations: true + ignoreOverloaded: true + CyclomaticComplexMethod: + active: true + threshold: 15 + ignoreSingleWhenExpression: true + ignoreSimpleWhenEntries: true + ignoreNestingFunctions: true + nestingFunctions: + - 'also' + - 'apply' + - 'forEach' + - 'isNotNull' + - 'ifNull' + - 'let' + - 'run' + - 'use' + - 'with' + LabeledExpression: + active: true + ignoredLabels: [] + LargeClass: + active: true + threshold: 600 + LongMethod: + active: true + threshold: 60 + LongParameterList: + active: true + functionThreshold: 6 + constructorThreshold: 7 + ignoreDefaultParameters: true + ignoreDataClasses: true + ignoreAnnotatedParameter: [] + MethodOverloading: + active: true + threshold: 6 + NamedArguments: + active: true + threshold: 3 + ignoreArgumentsMatchingNames: true + NestedBlockDepth: + active: true + threshold: 4 + NestedScopeFunctions: + active: true + threshold: 1 + functions: + - 'kotlin.apply' + - 'kotlin.run' + - 'kotlin.with' + - 'kotlin.let' + - 'kotlin.also' + ReplaceSafeCallChainWithRun: + active: true + StringLiteralDuplication: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + threshold: 3 + ignoreAnnotation: true + excludeStringsWithLessThan5Characters: true + ignoreStringsRegex: '$^' + TooManyFunctions: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + thresholdInFiles: 11 + thresholdInClasses: 11 + thresholdInInterfaces: 11 + thresholdInObjects: 11 + thresholdInEnums: 11 + ignoreDeprecated: true + ignorePrivate: true + ignoreOverridden: true + +coroutines: + active: true + GlobalCoroutineUsage: + active: true + InjectDispatcher: + active: true + dispatcherNames: + - 'IO' + - 'Default' + - 'Unconfined' + RedundantSuspendModifier: + active: true + SleepInsteadOfDelay: + active: true + SuspendFunSwallowedCancellation: + active: true + SuspendFunWithCoroutineScopeReceiver: + active: true + SuspendFunWithFlowReturnType: + active: true + +empty-blocks: + active: true + EmptyCatchBlock: + active: true + allowedExceptionNameRegex: '_|(ignore|expected).*' + EmptyClassBlock: + active: true + EmptyDefaultConstructor: + active: true + EmptyDoWhileBlock: + active: true + EmptyElseBlock: + active: true + EmptyFinallyBlock: + active: true + EmptyForBlock: + active: true + EmptyFunctionBlock: + active: true + ignoreOverridden: true + EmptyIfBlock: + active: true + EmptyInitBlock: + active: true + EmptyKtFile: + active: true + EmptySecondaryConstructor: + active: true + EmptyTryBlock: + active: true + EmptyWhenBlock: + active: true + EmptyWhileBlock: + active: true + +exceptions: + active: true + ExceptionRaisedInUnexpectedLocation: + active: true + methodNames: + - 'equals' + - 'finalize' + - 'hashCode' + - 'toString' + InstanceOfCheckForException: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + NotImplementedDeclaration: + active: true + ObjectExtendsThrowable: + active: true + PrintStackTrace: + active: true + RethrowCaughtException: + active: true + ReturnFromFinally: + active: true + ignoreLabeled: true + SwallowedException: + active: true + ignoredExceptionTypes: + - 'InterruptedException' + - 'MalformedURLException' + - 'NumberFormatException' + - 'ParseException' + allowedExceptionNameRegex: '_|(ignore|expected).*' + ThrowingExceptionFromFinally: + active: true + ThrowingExceptionInMain: + active: true + ThrowingExceptionsWithoutMessageOrCause: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + exceptions: + - 'ArrayIndexOutOfBoundsException' + - 'Exception' + - 'IllegalArgumentException' + - 'IllegalMonitorStateException' + - 'IllegalStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + ThrowingNewInstanceOfSameException: + active: true + TooGenericExceptionCaught: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + exceptionNames: + - 'ArrayIndexOutOfBoundsException' + - 'Error' + - 'Exception' + - 'IllegalMonitorStateException' + - 'IndexOutOfBoundsException' + - 'NullPointerException' + - 'RuntimeException' + - 'Throwable' + allowedExceptionNameRegex: '_|(ignore|expected).*' + TooGenericExceptionThrown: + active: true + exceptionNames: + - 'Error' + - 'Exception' + - 'RuntimeException' + - 'Throwable' + +naming: + active: true + BooleanPropertyNaming: + active: true + allowedPattern: '^(is|has|are)' + ClassNaming: + active: true + classPattern: '[A-Z][a-zA-Z0-9]*' + ConstructorParameterNaming: + active: true + parameterPattern: '[a-z][A-Za-z0-9]*' + privateParameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + EnumNaming: + active: true + enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' + ForbiddenClassName: + active: true + forbiddenName: [] + FunctionMaxLength: + active: false + maximumFunctionNameLength: 30 + FunctionMinLength: + active: true + minimumFunctionNameLength: 3 + FunctionNaming: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + functionPattern: '[a-z][a-zA-Z0-9]*' + excludeClassPattern: '$^' + FunctionParameterNaming: + active: true + parameterPattern: '[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + InvalidPackageDeclaration: + active: true + rootPackage: '' + requireRootInDeclaration: true + LambdaParameterNaming: + active: true + parameterPattern: '[a-z][A-Za-z0-9]*|_' + MatchingDeclarationName: + active: true + mustBeFirst: true + MemberNameEqualsClassName: + active: true + ignoreOverridden: true + NoNameShadowing: + active: true + NonBooleanPropertyPrefixedWithIs: + active: true + ObjectPropertyNaming: + active: true + constantPattern: '[A-Za-z][_A-Za-z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' + PackageNaming: + active: true + packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' + TopLevelPropertyNaming: + active: true + constantPattern: '[A-Z][_A-Z0-9]*' + propertyPattern: '[A-Za-z][_A-Za-z0-9]*' + privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' + VariableMaxLength: + active: true + maximumVariableNameLength: 64 + VariableMinLength: + active: true + minimumVariableNameLength: 1 + VariableNaming: + active: true + variablePattern: '[a-z][A-Za-z0-9]*' + privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' + excludeClassPattern: '$^' + +performance: + active: true + ArrayPrimitive: + active: true + CouldBeSequence: + active: true + threshold: 3 + ForEachOnRange: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + SpreadOperator: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + UnnecessaryPartOfBinaryExpression: + active: true + UnnecessaryTemporaryInstantiation: + active: true + +potential-bugs: + active: true + AvoidReferentialEquality: + active: true + forbiddenTypePatterns: + - 'kotlin.String' + CastNullableToNonNullableType: + active: true + CastToNullableType: + active: true + Deprecation: + active: true + DontDowncastCollectionTypes: + active: true + DoubleMutabilityForCollection: + active: true + mutableTypes: + - 'kotlin.collections.MutableList' + - 'kotlin.collections.MutableMap' + - 'kotlin.collections.MutableSet' + - 'java.util.ArrayList' + - 'java.util.LinkedHashSet' + - 'java.util.HashSet' + - 'java.util.LinkedHashMap' + - 'java.util.HashMap' + ElseCaseInsteadOfExhaustiveWhen: + active: true + ignoredSubjectTypes: [] + EqualsAlwaysReturnsTrueOrFalse: + active: true + EqualsWithHashCodeExist: + active: true + ExitOutsideMain: + active: true + ExplicitGarbageCollectionCall: + active: true + HasPlatformType: + active: true + IgnoredReturnValue: + active: true + restrictToConfig: true + returnValueAnnotations: + - 'CheckResult' + - '*.CheckResult' + - 'CheckReturnValue' + - '*.CheckReturnValue' + ignoreReturnValueAnnotations: + - 'CanIgnoreReturnValue' + - '*.CanIgnoreReturnValue' + returnValueTypes: + - 'kotlin.sequences.Sequence' + - 'kotlinx.coroutines.flow.*Flow' + - 'java.util.stream.*Stream' + ignoreFunctionCall: [] + ImplicitDefaultLocale: + active: true + ImplicitUnitReturnType: + active: true + allowExplicitReturnType: true + InvalidRange: + active: true + IteratorHasNextCallsNextMethod: + active: true + IteratorNotThrowingNoSuchElementException: + active: true + LateinitUsage: + active: false + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + ignoreOnClassesPattern: '' + MapGetWithNotNullAssertionOperator: + active: true + MissingPackageDeclaration: + active: true + excludes: ['**/*.kts'] + NullCheckOnMutableProperty: + active: true + NullableToStringCall: + active: true + PropertyUsedBeforeDeclaration: + active: true + UnconditionalJumpStatementInLoop: + active: true + UnnecessaryNotNullCheck: + active: true + UnnecessaryNotNullOperator: + active: true + UnnecessarySafeCall: + active: true + UnreachableCatchBlock: + active: true + UnreachableCode: + active: true + UnsafeCallOnNullableType: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**'] + UnsafeCast: + active: true + UnusedUnaryOperator: + active: true + UselessPostfixExpression: + active: true + WrongEqualsTypeParameter: + active: true + +style: + active: true + AlsoCouldBeApply: + active: true + BracesOnIfStatements: + active: true + singleLine: 'never' + multiLine: 'always' + BracesOnWhenStatements: + active: true + singleLine: 'necessary' + multiLine: 'consistent' + CanBeNonNullable: + active: true + CascadingCallWrapping: + active: true + includeElvis: true + ClassOrdering: + active: true + CollapsibleIfStatements: + active: true + DataClassContainsFunctions: + active: true + conversionFunctionPrefix: + - 'to' + allowOperators: true + DataClassShouldBeImmutable: + active: true + DestructuringDeclarationWithTooManyEntries: + active: true + maxDestructuringEntries: 3 + DoubleNegativeLambda: + active: true + negativeFunctions: + - reason: 'Use `takeIf` instead.' + value: 'takeUnless' + - reason: 'Use `all` instead.' + value: 'none' + negativeFunctionNameParts: + - 'not' + - 'non' + EqualsNullCall: + active: true + EqualsOnSignatureLine: + active: true + ExplicitCollectionElementAccessMethod: + active: true + ExplicitItLambdaParameter: + active: true + ExpressionBodySyntax: + active: true + includeLineWrapping: true + ForbiddenAnnotation: + active: true + annotations: + - reason: 'it is a java annotation. Use `Suppress` instead.' + value: 'java.lang.SuppressWarnings' + - reason: 'it is a java annotation. Use `kotlin.Deprecated` instead.' + value: 'java.lang.Deprecated' + - reason: 'it is a java annotation. Use `kotlin.annotation.MustBeDocumented` instead.' + value: 'java.lang.annotation.Documented' + - reason: 'it is a java annotation. Use `kotlin.annotation.Target` instead.' + value: 'java.lang.annotation.Target' + - reason: 'it is a java annotation. Use `kotlin.annotation.Retention` instead.' + value: 'java.lang.annotation.Retention' + - reason: 'it is a java annotation. Use `kotlin.annotation.Repeatable` instead.' + value: 'java.lang.annotation.Repeatable' + - reason: 'Kotlin does not support @Inherited annotation, see https://youtrack.jetbrains.com/issue/KT-22265' + value: 'java.lang.annotation.Inherited' + ForbiddenComment: + active: true + comments: + - reason: 'Forbidden FIXME todo marker in comment, please fix the problem.' + value: 'FIXME:' + - reason: 'Forbidden STOPSHIP todo marker in comment, please address the problem before shipping the code.' + value: 'STOPSHIP:' + - reason: 'Forbidden TODO todo marker in comment, please do the changes.' + value: 'TODO:' + allowedPatterns: '' + ForbiddenImport: + active: true + imports: [] + forbiddenPatterns: '' + ForbiddenMethodCall: + active: true + methods: + - reason: 'print does not allow you to configure the output stream. Use a logger instead.' + value: 'kotlin.io.print' + - reason: 'println does not allow you to configure the output stream. Use a logger instead.' + value: 'kotlin.io.println' + ForbiddenSuppress: + active: true + rules: [] + ForbiddenVoid: + active: true + ignoreOverridden: true + ignoreUsageInGenerics: true + FunctionOnlyReturningConstant: + active: true + ignoreOverridableFunction: true + ignoreActualFunction: true + excludedFunctions: [] + LoopWithTooManyJumpStatements: + active: true + maxJumpCount: 1 + MagicNumber: + active: true + excludes: ['**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/androidUnitTest/**', '**/androidInstrumentedTest/**', '**/jsTest/**', '**/iosTest/**', '**/*.kts'] + ignoreNumbers: + - '-1' + - '0' + - '1' + - '2' + ignoreHashCodeFunction: true + ignorePropertyDeclaration: true + ignoreLocalVariableDeclaration: true + ignoreConstantDeclaration: true + ignoreCompanionObjectPropertyDeclaration: true + ignoreAnnotation: true + ignoreNamedArgument: true + ignoreEnums: true + ignoreRanges: true + ignoreExtensionFunctions: true + MandatoryBracesLoops: + active: true + MaxChainedCallsOnSameLine: + active: true + maxChainedCalls: 5 + MaxLineLength: + active: true + maxLineLength: 120 + excludePackageStatements: true + excludeImportStatements: true + excludeCommentStatements: true + excludeRawStrings: true + MayBeConst: + active: true + ModifierOrder: + active: true + MultilineLambdaItParameter: + active: true + MultilineRawStringIndentation: + active: true + indentSize: 4 + trimmingMethods: + - 'trimIndent' + - 'trimMargin' + NestedClassesVisibility: + active: true + NewLineAtEndOfFile: + active: true + NoTabs: + active: true + NullableBooleanCheck: + active: true + ObjectLiteralToLambda: + active: true + OptionalAbstractKeyword: + active: true + OptionalUnit: + active: true + PreferToOverPairSyntax: + active: true + ProtectedMemberInFinalClass: + active: true + RedundantExplicitType: + active: true + RedundantHigherOrderMapUsage: + active: true + RedundantVisibilityModifierRule: + active: true + ReturnCount: + active: true + max: 2 + excludedFunctions: + - 'equals' + excludeLabeled: true + excludeReturnFromLambda: true + excludeGuardClauses: true + SafeCast: + active: true + SerialVersionUIDInSerializableClass: + active: true + SpacingBetweenPackageAndImports: + active: true + StringShouldBeRawString: + active: true + maxEscapedCharacterCount: 2 + ignoredCharacters: [] + ThrowsCount: + active: true + max: 2 + excludeGuardClauses: true + TrailingWhitespace: + active: true + TrimMultilineRawString: + active: true + trimmingMethods: + - 'trimIndent' + - 'trimMargin' + UnderscoresInNumericLiterals: + active: true + acceptableLength: 4 + allowNonStandardGrouping: true + UnnecessaryAbstractClass: + active: true + UnnecessaryAnnotationUseSiteTarget: + active: true + UnnecessaryApply: + active: true + UnnecessaryBackticks: + active: true + UnnecessaryBracesAroundTrailingLambda: + active: true + UnnecessaryFilter: + active: true + UnnecessaryInheritance: + active: true + UnnecessaryInnerClass: + active: true + UnnecessaryLet: + active: true + UnnecessaryParentheses: + active: true + allowForUnclearPrecedence: true + UntilInsteadOfRangeTo: + active: true + UnusedImports: + active: true + UnusedParameter: + active: true + allowedNames: 'ignored|expected' + UnusedPrivateClass: + active: true + UnusedPrivateMember: + active: true + allowedNames: '' + UnusedPrivateProperty: + active: true + allowedNames: '_|ignored|expected|serialVersionUID' + UseAnyOrNoneInsteadOfFind: + active: true + UseArrayLiteralsInAnnotations: + active: true + UseCheckNotNull: + active: true + UseCheckOrError: + active: true + UseDataClass: + active: true + allowVars: true + UseEmptyCounterpart: + active: true + UseIfEmptyOrIfBlank: + active: true + UseIfInsteadOfWhen: + active: true + ignoreWhenContainingVariableDeclaration: true + UseIsNullOrEmpty: + active: true + UseLet: + active: true + UseOrEmpty: + active: true + UseRequire: + active: true + UseRequireNotNull: + active: true + UseSumOfInsteadOfFlatMapSize: + active: true + UselessCallOnNotNull: + active: true + UtilityClassWithPublicConstructor: + active: true + VarCouldBeVal: + active: true + ignoreLateinitVar: true + WildcardImport: + active: true + excludeImports: + - 'java.util.*' diff --git a/app/src/main/java/com/rine/upnpdiscovery/UPnPDevice.kt b/app/src/main/java/com/rine/upnpdiscovery/UPnPDevice.kt index 7314351..9f694a9 100644 --- a/app/src/main/java/com/rine/upnpdiscovery/UPnPDevice.kt +++ b/app/src/main/java/com/rine/upnpdiscovery/UPnPDevice.kt @@ -40,8 +40,8 @@ class UPnPDevice internal constructor(val hostAddress: String, header: String) { } } - override fun toString(): String { - return "FriendlyName: " + friendlyName + LINE_END + + override fun toString(): String = + "FriendlyName: " + friendlyName + LINE_END + "ModelName: " + modelName + LINE_END + "HostAddress: " + hostAddress + LINE_END + "Location: " + location + LINE_END + @@ -54,7 +54,6 @@ class UPnPDevice internal constructor(val hostAddress: String, header: String) { "ManufacturerURL: " + manufacturerURL + LINE_END + "UDN: " + udn + LINE_END + "URLBase: " + urlBase - } private fun parseHeader( mSearchAnswer: String, diff --git a/app/src/main/java/io/github/domi04151309/home/activities/AboutActivity.kt b/app/src/main/java/io/github/domi04151309/home/activities/AboutActivity.kt index 9e23a3e..d0866b8 100644 --- a/app/src/main/java/io/github/domi04151309/home/activities/AboutActivity.kt +++ b/app/src/main/java/io/github/domi04151309/home/activities/AboutActivity.kt @@ -10,12 +10,6 @@ import io.github.domi04151309.home.BuildConfig import io.github.domi04151309.home.R class AboutActivity : BaseActivity() { - companion object { - private const val REPOSITORY: String = "Domi04151309/HomeApp" - private const val BRANCH: String = "master" - private const val REPOSITORY_URL: String = "https://github.com/$REPOSITORY" - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_settings) @@ -113,4 +107,10 @@ class AboutActivity : BaseActivity() { } } } + + companion object { + private const val REPOSITORY: String = "Domi04151309/HomeApp" + private const val BRANCH: String = "master" + private const val REPOSITORY_URL: String = "https://github.com/$REPOSITORY" + } } diff --git a/app/src/main/java/io/github/domi04151309/home/activities/DeviceInfoActivity.kt b/app/src/main/java/io/github/domi04151309/home/activities/DeviceInfoActivity.kt index afc7c81..833a763 100644 --- a/app/src/main/java/io/github/domi04151309/home/activities/DeviceInfoActivity.kt +++ b/app/src/main/java/io/github/domi04151309/home/activities/DeviceInfoActivity.kt @@ -22,10 +22,6 @@ import java.util.concurrent.TimeUnit @Suppress("TooManyFunctions") class DeviceInfoActivity : BaseActivity(), RecyclerViewHelperInterface { - companion object { - private const val TO_PERCENT = 100 - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_devices) @@ -63,20 +59,20 @@ class DeviceInfoActivity : BaseActivity(), RecyclerViewHelperInterface { // Do nothing. } - private fun boolToString(bool: Boolean): String { - return resources.getString(if (bool) R.string.str_on else R.string.str_off) - } + private fun boolToString(bool: Boolean): String = + resources.getString( + if (bool) R.string.str_on else R.string.str_off, + ) @Suppress("MagicNumber") - private fun rssiToPercent(rssi: Int): Int { - return if (rssi <= -100) { + private fun rssiToPercent(rssi: Int): Int = + if (rssi <= -100) { 0 } else if (rssi >= -50) { 100 } else { 2 * (rssi + 100) } - } private fun formatUptime(uptime: Long) = String.format( @@ -233,4 +229,8 @@ class DeviceInfoActivity : BaseActivity(), RecyclerViewHelperInterface { ), ) } + + companion object { + private const val TO_PERCENT = 100 + } } diff --git a/app/src/main/java/io/github/domi04151309/home/activities/DevicesActivity.kt b/app/src/main/java/io/github/domi04151309/home/activities/DevicesActivity.kt index 55545d4..52b3f05 100644 --- a/app/src/main/java/io/github/domi04151309/home/activities/DevicesActivity.kt +++ b/app/src/main/java/io/github/domi04151309/home/activities/DevicesActivity.kt @@ -26,17 +26,14 @@ class DevicesActivity : BaseActivity(), RecyclerViewHelperInterfaceAdvanced { override fun getMovementFlags( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, - ): Int { - return if (viewHolder.adapterPosition == ( - recyclerView.adapter?.itemCount - ?: -1 - ) - 1 + ): Int = + if ( + viewHolder.adapterPosition == (recyclerView.adapter?.itemCount ?: -1) - 1 ) { makeMovementFlags(0, 0) } else { makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) } - } override fun onMove( recyclerView: RecyclerView, @@ -56,9 +53,7 @@ class DevicesActivity : BaseActivity(), RecyclerViewHelperInterfaceAdvanced { } } - override fun isLongPressDragEnabled(): Boolean { - return true - } + override fun isLongPressDragEnabled(): Boolean = true override fun onSwiped( viewHolder: RecyclerView.ViewHolder, diff --git a/app/src/main/java/io/github/domi04151309/home/activities/EditDeviceActivity.kt b/app/src/main/java/io/github/domi04151309/home/activities/EditDeviceActivity.kt index 301dfa4..58f784d 100644 --- a/app/src/main/java/io/github/domi04151309/home/activities/EditDeviceActivity.kt +++ b/app/src/main/java/io/github/domi04151309/home/activities/EditDeviceActivity.kt @@ -31,31 +31,6 @@ import io.github.domi04151309.home.helpers.Devices import io.github.domi04151309.home.helpers.Global class EditDeviceActivity : BaseActivity() { - companion object { - private val SUPPORTS_DIRECT_VIEW = - arrayOf( - "ESP Easy", - "Hue API", - "Shelly Gen 1", - "Shelly Gen 2", - "SimpleHome API", - "Tasmota", - ) - private val HAS_CONFIG = - arrayOf( - "Hue API", - "ESP Easy", - "Node-RED", - "Shelly Gen 1", - "Shelly Gen 2", - ) - private val HAS_INFO = - arrayOf( - "Hue API", - "Shelly Gen 2", - ) - } - private lateinit var devices: Devices private lateinit var deviceId: String private lateinit var deviceSecrets: DeviceSecrets @@ -106,7 +81,7 @@ class EditDeviceActivity : BaseActivity() { configButton = findViewById(R.id.configBtn) infoButton = findViewById(R.id.infoBtn) - findViewById(R.id.idTxt).text = (resources.getString(R.string.pref_add_id, deviceId)) + findViewById(R.id.idTxt).text = resources.getString(R.string.pref_add_id, deviceId) iconSpinner.addTextChangedListener( object : TextWatcher { @@ -388,13 +363,16 @@ class EditDeviceActivity : BaseActivity() { addressBox.editText?.text.toString() } - val newItem = DeviceItem(deviceId) - newItem.name = name + val newItem = + DeviceItem( + deviceId, + name, + modeSpinner.text.toString(), + iconSpinner.text.toString(), + configHide.isChecked, + configDirectView.isChecked, + ) newItem.address = tempAddress - newItem.mode = modeSpinner.text.toString() - newItem.iconName = iconSpinner.text.toString() - newItem.hide = configHide.isChecked - newItem.directView = configDirectView.isChecked devices.addDevice(newItem) deviceSecrets.username = usernameBox.editText?.text.toString() deviceSecrets.password = passwordBox.editText?.text.toString() @@ -410,4 +388,29 @@ class EditDeviceActivity : BaseActivity() { } return result } + + companion object { + private val SUPPORTS_DIRECT_VIEW = + arrayOf( + "ESP Easy", + "Hue API", + "Shelly Gen 1", + "Shelly Gen 2", + "SimpleHome API", + "Tasmota", + ) + private val HAS_CONFIG = + arrayOf( + "Hue API", + "ESP Easy", + "Node-RED", + "Shelly Gen 1", + "Shelly Gen 2", + ) + private val HAS_INFO = + arrayOf( + "Hue API", + "Shelly Gen 2", + ) + } } diff --git a/app/src/main/java/io/github/domi04151309/home/activities/HueConnectActivity.kt b/app/src/main/java/io/github/domi04151309/home/activities/HueConnectActivity.kt index 914067d..cbe222c 100644 --- a/app/src/main/java/io/github/domi04151309/home/activities/HueConnectActivity.kt +++ b/app/src/main/java/io/github/domi04151309/home/activities/HueConnectActivity.kt @@ -28,7 +28,7 @@ class HueConnectActivity : BaseActivity() { queue = Volley.newRequestQueue(this) val deviceId = intent.getStringExtra("deviceId") ?: "" - val jsonRequestObject = JSONObject("{\"devicetype\":\"Home App#${android.os.Build.PRODUCT}\"}") + val jsonRequestObject = JSONObject("""{ "devicetype": "Home App#${android.os.Build.PRODUCT}" }""") requestToRegisterUser = CustomJsonArrayRequest( Request.Method.POST, Devices(this).getDeviceById(deviceId).address + "api", jsonRequestObject, diff --git a/app/src/main/java/io/github/domi04151309/home/activities/HueSceneActivity.kt b/app/src/main/java/io/github/domi04151309/home/activities/HueSceneActivity.kt index d50e327..7f66082 100644 --- a/app/src/main/java/io/github/domi04151309/home/activities/HueSceneActivity.kt +++ b/app/src/main/java/io/github/domi04151309/home/activities/HueSceneActivity.kt @@ -249,12 +249,12 @@ class HueSceneActivity : id: String, title: String, state: JSONObject, - ): SceneListItem { - val item = SceneListItem(title) - item.state = state.optBoolean("on") - item.hidden = id - item.brightness = HueUtils.briToPercent(state.optInt("bri", MAX_BRIGHTNESS)) - item.color = + ): SceneListItem = + SceneListItem( + title, + id, + state.optBoolean("on"), + HueUtils.briToPercent(state.optInt("bri", MAX_BRIGHTNESS)), if (state.has("xy")) { val xyArray = state.getJSONArray("xy") ColorUtils.xyToRGB( @@ -267,9 +267,8 @@ class HueSceneActivity : HueUtils.ctToRGB(state.getInt("ct")) } else { Color.parseColor("#FFFFFF") - } - return item - } + }, + ) private fun onFloatingActionButtnClicked() { val name = nameBox.editText?.text.toString() @@ -286,7 +285,7 @@ class HueSceneActivity : CustomJsonArrayRequest( Request.Method.PUT, "$addressPrefix/scenes/$sceneId", - JSONObject("{\"name\":\"$name\",\"lightstates\":$lightStates}"), + JSONObject("""{ "name": "$name", "lightstates": $lightStates }"""), this, this, ) @@ -295,12 +294,7 @@ class HueSceneActivity : Request.Method.POST, "$addressPrefix/scenes", JSONObject( - "{" + - "\"name\":\"$name\"," + - "\"recycle\":false," + - "\"group\":\"$groupId\"," + - "\"type\":\"GroupScene\"" + - "}", + """{ "name": "$name", "recycle": false, "group": "$groupId", "type": "GroupScene" }""", ), this, this, diff --git a/app/src/main/java/io/github/domi04151309/home/activities/MainActivity.kt b/app/src/main/java/io/github/domi04151309/home/activities/MainActivity.kt index da651ba..3d37bd9 100644 --- a/app/src/main/java/io/github/domi04151309/home/activities/MainActivity.kt +++ b/app/src/main/java/io/github/domi04151309/home/activities/MainActivity.kt @@ -43,18 +43,6 @@ import kotlin.math.min @Suppress("TooManyFunctions") class MainActivity : BaseActivity() { - companion object { - private val WEB_MODES = - arrayOf( - "Fritz! Auto-Login", - "Node-RED", - "Website", - ) - private const val TINY_DELAY = 100L - private const val COLUMN_COUNT_FRACTION = 240 - private const val MAX_RESPONSE_LENGTH = 64 - } - private var tasmotaPosition: Int = 0 private var shouldReset: Boolean = false private val updateHandler = UpdateHandler() @@ -69,12 +57,6 @@ class MainActivity : BaseActivity() { private var columns: Int? = null - private fun getColumns(): Int? = - ( - PreferenceManager.getDefaultSharedPreferences(this) - .getString(P.PREF_COLUMNS, P.PREF_COLUMNS_DEFAULT) ?: P.PREF_COLUMNS_DEFAULT - ).toIntOrNull() - /* * Unified callbacks */ @@ -280,6 +262,13 @@ class MainActivity : BaseActivity() { } } + private fun getColumns(): Int? = + ( + PreferenceManager.getDefaultSharedPreferences(this) + .getString(P.PREF_COLUMNS, P.PREF_COLUMNS_DEFAULT) + ?: P.PREF_COLUMNS_DEFAULT + ).toIntOrNull() + /* * Activity methods */ @@ -413,7 +402,9 @@ class MainActivity : BaseActivity() { .show() true } - else -> super.onContextItemSelected(item) + else -> { + super.onContextItemSelected(item) + } } } @@ -445,11 +436,11 @@ class MainActivity : BaseActivity() { val displayMetrics: DisplayMetrics = resources.displayMetrics val horizontal: Int = ( - (displayMetrics.widthPixels / displayMetrics.density) / COLUMN_COUNT_FRACTION + displayMetrics.widthPixels / displayMetrics.density / COLUMN_COUNT_FRACTION ).toInt() val vertical: Int = ( - (displayMetrics.heightPixels / displayMetrics.density) / COLUMN_COUNT_FRACTION + displayMetrics.heightPixels / displayMetrics.density / COLUMN_COUNT_FRACTION ).toInt() return max(1, min(horizontal, vertical)) } @@ -612,7 +603,20 @@ class MainActivity : BaseActivity() { .setMessage(result) .setPositiveButton(android.R.string.ok) { _, _ -> } .show() - }.show() + } + .show() } } + + companion object { + private val WEB_MODES = + arrayOf( + "Fritz! Auto-Login", + "Node-RED", + "Website", + ) + private const val TINY_DELAY = 100L + private const val COLUMN_COUNT_FRACTION = 240 + private const val MAX_RESPONSE_LENGTH = 64 + } } diff --git a/app/src/main/java/io/github/domi04151309/home/activities/SearchDevicesActivity.kt b/app/src/main/java/io/github/domi04151309/home/activities/SearchDevicesActivity.kt index 57aece3..778bca1 100644 --- a/app/src/main/java/io/github/domi04151309/home/activities/SearchDevicesActivity.kt +++ b/app/src/main/java/io/github/domi04151309/home/activities/SearchDevicesActivity.kt @@ -133,7 +133,7 @@ class SearchDevicesActivity : BaseActivity(), RecyclerViewHelperInterface { ) }.start() - nsdManager = (getSystemService(NSD_SERVICE) as NsdManager) + nsdManager = getSystemService(NSD_SERVICE) as NsdManager resolveListener = object : NsdManager.ResolveListener { override fun onServiceResolved(serviceInfo: NsdServiceInfo) { @@ -249,10 +249,9 @@ class SearchDevicesActivity : BaseActivity(), RecyclerViewHelperInterface { } @Suppress("MagicNumber") - private fun intToIp(address: Int): String { - return (address and 0xFF).toString() + "." + (address shr 8 and 0xFF) + "." + + private fun intToIp(address: Int): String = + (address and 0xFF).toString() + "." + (address shr 8 and 0xFF) + "." + (address shr 16 and 0xFF) + "." + (address shr 24 and 0xFF) - } override fun onItemClicked( view: View, @@ -265,11 +264,14 @@ class SearchDevicesActivity : BaseActivity(), RecyclerViewHelperInterface { .setTitle(R.string.pref_add_dialog) .setMessage(resources.getString(R.string.pref_add_dialog_message, name)) .setPositiveButton(R.string.str_add) { _, _ -> - val newItem = DeviceItem(devices.generateNewId()) - newItem.name = name + val newItem = + DeviceItem( + devices.generateNewId(), + name, + hidden.substring(0, hidden.indexOf('#')), + hidden.substring(hidden.lastIndexOf('#') + 1), + ) newItem.address = view.findViewById(R.id.summary).text.toString() - newItem.mode = hidden.substring(0, hidden.indexOf('#')) - newItem.iconName = hidden.substring(hidden.lastIndexOf('#') + 1) devices.addDevice(newItem) adapter.changeState(position, true) } diff --git a/app/src/main/java/io/github/domi04151309/home/activities/SettingsActivity.kt b/app/src/main/java/io/github/domi04151309/home/activities/SettingsActivity.kt index 16389e6..5ea353d 100644 --- a/app/src/main/java/io/github/domi04151309/home/activities/SettingsActivity.kt +++ b/app/src/main/java/io/github/domi04151309/home/activities/SettingsActivity.kt @@ -46,7 +46,8 @@ class SettingsActivity : BaseActivity() { .setMessage(R.string.pref_reset_question) .setPositiveButton(R.string.str_delete) { _, _ -> PreferenceManager.getDefaultSharedPreferences(requireContext()).edit() - .putString("devices_json", Global.DEFAULT_JSON).apply() + .putString("devices_json", Global.DEFAULT_JSON) + .apply() Toast.makeText(context, R.string.pref_reset_toast, Toast.LENGTH_LONG).show() Devices.reloadFromPreferences() } diff --git a/app/src/main/java/io/github/domi04151309/home/activities/ShortcutHueSceneActivity.kt b/app/src/main/java/io/github/domi04151309/home/activities/ShortcutHueSceneActivity.kt index 6c8b330..e715750 100644 --- a/app/src/main/java/io/github/domi04151309/home/activities/ShortcutHueSceneActivity.kt +++ b/app/src/main/java/io/github/domi04151309/home/activities/ShortcutHueSceneActivity.kt @@ -32,6 +32,12 @@ class ShortcutHueSceneActivity : BaseActivity(), RecyclerViewHelperInterface { private var group: String? = null private lateinit var recyclerView: RecyclerView + private val device: DeviceItem + get() = Devices(this).getDeviceById(deviceId ?: error("Device ID is null.")) + + private val api: HueAPI + get() = HueAPI(this, deviceId ?: error("Device ID is null.")) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_devices) @@ -172,10 +178,4 @@ class ShortcutHueSceneActivity : BaseActivity(), RecyclerViewHelperInterface { createShortcut(view) } } - - private val device: DeviceItem - get() = Devices(this).getDeviceById(deviceId ?: error("Device ID is null.")) - - private val api: HueAPI - get() = HueAPI(this, deviceId ?: error("Device ID is null.")) } diff --git a/app/src/main/java/io/github/domi04151309/home/activities/WebActivity.kt b/app/src/main/java/io/github/domi04151309/home/activities/WebActivity.kt index bd43496..b31ea17 100644 --- a/app/src/main/java/io/github/domi04151309/home/activities/WebActivity.kt +++ b/app/src/main/java/io/github/domi04151309/home/activities/WebActivity.kt @@ -211,12 +211,11 @@ class WebActivity : BaseActivity() { } override fun onOptionsItemSelected(item: MenuItem): Boolean { - return if (item.itemId == R.id.action_open) { + if (item.itemId == R.id.action_open) { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(webView.url))) - true - } else { - super.onOptionsItemSelected(item) + return true } + return super.onOptionsItemSelected(item) } override fun onKeyDown( diff --git a/app/src/main/java/io/github/domi04151309/home/adapters/DeviceDiscoveryListAdapter.kt b/app/src/main/java/io/github/domi04151309/home/adapters/DeviceDiscoveryListAdapter.kt index 6ab1204..2f12026 100644 --- a/app/src/main/java/io/github/domi04151309/home/adapters/DeviceDiscoveryListAdapter.kt +++ b/app/src/main/java/io/github/domi04151309/home/adapters/DeviceDiscoveryListAdapter.kt @@ -17,13 +17,12 @@ class DeviceDiscoveryListAdapter( override fun onCreateViewHolder( parent: ViewGroup, viewType: Int, - ): ViewHolder { - return ViewHolder( + ): ViewHolder = + ViewHolder( LayoutInflater .from(parent.context) .inflate(R.layout.list_item_device_discovery, parent, false), ) - } override fun onBindViewHolder( holder: ViewHolder, @@ -43,9 +42,7 @@ class DeviceDiscoveryListAdapter( holder.itemView.setOnClickListener { helperInterface.onItemClicked(holder.itemView, position) } } - override fun getItemCount(): Int { - return items.size - } + override fun getItemCount(): Int = items.size fun add(item: ListViewItem): Int { items.add(item) diff --git a/app/src/main/java/io/github/domi04151309/home/adapters/DeviceListAdapter.kt b/app/src/main/java/io/github/domi04151309/home/adapters/DeviceListAdapter.kt index 92da685..ee2c569 100644 --- a/app/src/main/java/io/github/domi04151309/home/adapters/DeviceListAdapter.kt +++ b/app/src/main/java/io/github/domi04151309/home/adapters/DeviceListAdapter.kt @@ -19,13 +19,12 @@ class DeviceListAdapter( override fun onCreateViewHolder( parent: ViewGroup, viewType: Int, - ): ViewHolder { - return ViewHolder( + ): ViewHolder = + ViewHolder( LayoutInflater .from(parent.context) .inflate(R.layout.list_item_devices, parent, false), ) - } @SuppressLint("ClickableViewAccessibility") override fun onBindViewHolder( @@ -44,14 +43,12 @@ class DeviceListAdapter( if (event.actionMasked == MotionEvent.ACTION_DOWN) { helperInterface.onItemHandleTouched(holder) } - return@setOnTouchListener view.performClick() + view.performClick() } } } - override fun getItemCount(): Int { - return items.size - } + override fun getItemCount(): Int = items.size class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val drawable: ImageView = view.findViewById(R.id.drawable) diff --git a/app/src/main/java/io/github/domi04151309/home/adapters/HueDetailsTabAdapter.kt b/app/src/main/java/io/github/domi04151309/home/adapters/HueDetailsTabAdapter.kt index e96e783..d1bbcf8 100644 --- a/app/src/main/java/io/github/domi04151309/home/adapters/HueDetailsTabAdapter.kt +++ b/app/src/main/java/io/github/domi04151309/home/adapters/HueDetailsTabAdapter.kt @@ -10,14 +10,13 @@ import io.github.domi04151309.home.fragments.HueScenesFragment class HueDetailsTabAdapter( activity: FragmentActivity, ) : FragmentStateAdapter(activity) { - override fun createFragment(position: Int): Fragment { - return when (position) { + override fun createFragment(position: Int): Fragment = + when (position) { 0 -> HueColorFragment() 1 -> HueScenesFragment() 2 -> HueLampsFragment() else -> Fragment() } - } override fun getItemCount(): Int = 3 } diff --git a/app/src/main/java/io/github/domi04151309/home/adapters/HueLampListAdapter.kt b/app/src/main/java/io/github/domi04151309/home/adapters/HueLampListAdapter.kt index c400074..7e9a2cd 100644 --- a/app/src/main/java/io/github/domi04151309/home/adapters/HueLampListAdapter.kt +++ b/app/src/main/java/io/github/domi04151309/home/adapters/HueLampListAdapter.kt @@ -25,13 +25,12 @@ class HueLampListAdapter( override fun onCreateViewHolder( parent: ViewGroup, viewType: Int, - ): ViewHolder { - return ViewHolder( + ): ViewHolder = + ViewHolder( LayoutInflater .from(parent.context) .inflate(R.layout.list_item, parent, false), ) - } override fun onBindViewHolder( holder: ViewHolder, @@ -55,9 +54,7 @@ class HueLampListAdapter( holder.itemView.setOnClickListener { helperInterface.onItemClicked(holder.itemView, position) } } - override fun getItemCount(): Int { - return items.size - } + override fun getItemCount(): Int = items.size @SuppressLint("NotifyDataSetChanged") fun updateData( diff --git a/app/src/main/java/io/github/domi04151309/home/adapters/HueSceneGridAdapter.kt b/app/src/main/java/io/github/domi04151309/home/adapters/HueSceneGridAdapter.kt index 28bb5f7..3aefd5e 100644 --- a/app/src/main/java/io/github/domi04151309/home/adapters/HueSceneGridAdapter.kt +++ b/app/src/main/java/io/github/domi04151309/home/adapters/HueSceneGridAdapter.kt @@ -23,13 +23,12 @@ class HueSceneGridAdapter( override fun onCreateViewHolder( parent: ViewGroup, viewType: Int, - ): ViewHolder { - return ViewHolder( + ): ViewHolder = + ViewHolder( LayoutInflater .from(parent.context) .inflate(R.layout.grid_item, parent, false), ) - } override fun onBindViewHolder( holder: ViewHolder, @@ -55,9 +54,7 @@ class HueSceneGridAdapter( holder.itemView.setOnCreateContextMenuListener(contextMenuListener) } - override fun getItemCount(): Int { - return items.size - } + override fun getItemCount(): Int = items.size @SuppressLint("NotifyDataSetChanged") fun updateData(newItems: MutableList) { diff --git a/app/src/main/java/io/github/domi04151309/home/adapters/HueSceneLampListAdapter.kt b/app/src/main/java/io/github/domi04151309/home/adapters/HueSceneLampListAdapter.kt index f1e2fa1..7e0e999 100644 --- a/app/src/main/java/io/github/domi04151309/home/adapters/HueSceneLampListAdapter.kt +++ b/app/src/main/java/io/github/domi04151309/home/adapters/HueSceneLampListAdapter.kt @@ -1,8 +1,8 @@ package io.github.domi04151309.home.adapters import android.annotation.SuppressLint -import android.content.Context import android.content.res.ColorStateList +import android.content.res.Resources import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -19,27 +19,24 @@ class HueSceneLampListAdapter( private var items: List, private var helperInterface: SceneRecyclerViewHelperInterface, ) : RecyclerView.Adapter() { - lateinit var c: Context - init { setHasStableIds(true) } - override fun getItemId(position: Int): Long { - return (position.toString() + '#' + items[position].hidden).hashCode().toLong() - } + override fun getItemId(position: Int): Long = + (position.toString() + '#' + items[position].hidden) + .hashCode() + .toLong() override fun onCreateViewHolder( parent: ViewGroup, viewType: Int, - ): ViewHolder { - c = parent.context - return ViewHolder( + ): ViewHolder = + ViewHolder( LayoutInflater .from(parent.context) .inflate(R.layout.list_item, parent, false), ) - } @SuppressLint("SetTextI18n") override fun onBindViewHolder( @@ -47,14 +44,14 @@ class HueSceneLampListAdapter( position: Int, ) { val id = getItemId(position) - holder.drawable.setImageResource(items[position].icon) + holder.drawable.setImageResource(R.drawable.ic_circle) holder.title.text = items[position].title - holder.summary.text = generateSummary(items[position]) + holder.summary.text = generateSummary(holder.itemView.resources, items[position]) holder.hidden.text = items[position].hidden holder.stateSwitch.isChecked = items[position].state holder.stateSwitch.setOnCheckedChangeListener { compoundButton, b -> items[getPosFromId(id)].state = b - holder.summary.text = generateSummary(items[getPosFromId(id)]) + holder.summary.text = generateSummary(holder.itemView.resources, items[getPosFromId(id)]) if (compoundButton.isPressed) { helperInterface.onStateChanged( holder.itemView, @@ -72,9 +69,7 @@ class HueSceneLampListAdapter( } } - override fun getItemCount(): Int { - return items.size - } + override fun getItemCount(): Int = items.size fun changeSceneBrightness(brightness: String) { for (i in items.indices) { @@ -101,15 +96,17 @@ class HueSceneLampListAdapter( notifyItemChanged(i) } - private fun generateSummary(item: SceneListItem): String { - return c.resources.getString(if (item.state) R.string.str_on else R.string.str_off) + - " · " + c.resources.getString(R.string.hue_brightness) + + private fun generateSummary( + resources: Resources, + item: SceneListItem, + ): String = + resources.getString( + if (item.state) R.string.str_on else R.string.str_off, + ) + + " · " + resources.getString(R.string.hue_brightness) + ": " + if (item.state) item.brightness else "0%" - } - private fun getPosFromId(id: Long): Int { - return items.indices.indexOfFirst { getItemId(it) == id } - } + private fun getPosFromId(id: Long): Int = items.indices.indexOfFirst { getItemId(it) == id } class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val drawable: ImageView = view.findViewById(R.id.drawable) diff --git a/app/src/main/java/io/github/domi04151309/home/adapters/IconSpinnerAdapter.kt b/app/src/main/java/io/github/domi04151309/home/adapters/IconSpinnerAdapter.kt index b2fc03f..3f2c826 100644 --- a/app/src/main/java/io/github/domi04151309/home/adapters/IconSpinnerAdapter.kt +++ b/app/src/main/java/io/github/domi04151309/home/adapters/IconSpinnerAdapter.kt @@ -14,21 +14,13 @@ import io.github.domi04151309.home.helpers.Global internal class IconSpinnerAdapter( private var itemArray: Array, ) : BaseAdapter(), Filterable { - override fun getCount(): Int { - return itemArray.size - } + override fun getCount(): Int = itemArray.size - override fun getItem(position: Int): String { - return itemArray[position] - } + override fun getItem(position: Int): String = itemArray[position] - override fun getItemId(position: Int): Long { - return position.toLong() - } + override fun getItemId(position: Int): Long = position.toLong() - override fun getFilter(): Filter { - return ItemFilter() - } + override fun getFilter(): Filter = ItemFilter() override fun getView( position: Int, diff --git a/app/src/main/java/io/github/domi04151309/home/adapters/MainListAdapter.kt b/app/src/main/java/io/github/domi04151309/home/adapters/MainListAdapter.kt index 2d8eae7..360f762 100644 --- a/app/src/main/java/io/github/domi04151309/home/adapters/MainListAdapter.kt +++ b/app/src/main/java/io/github/domi04151309/home/adapters/MainListAdapter.kt @@ -21,10 +21,6 @@ import io.github.domi04151309.home.interfaces.HomeRecyclerViewHelperInterface @Suppress("TooManyFunctions") class MainListAdapter(private var attachedTo: RecyclerView) : RecyclerView.Adapter() { - companion object { - private const val ANIMATION_DURATION = 300L - } - private var items: MutableList = mutableListOf() private var helperInterface: HomeRecyclerViewHelperInterface? = null private var animate: Boolean = true @@ -34,20 +30,20 @@ class MainListAdapter(private var attachedTo: RecyclerView) : RecyclerView.Adapt setHasStableIds(true) } - override fun getItemId(position: Int): Long { - return (position.toString() + '#' + items[position].hidden).hashCode().toLong() - } + override fun getItemId(position: Int): Long = + (position.toString() + '#' + items[position].hidden) + .hashCode() + .toLong() override fun onCreateViewHolder( parent: ViewGroup, viewType: Int, - ): ViewHolder { - return ViewHolder( + ): ViewHolder = + ViewHolder( LayoutInflater .from(parent.context) .inflate(R.layout.list_item, parent, false), ) - } override fun onBindViewHolder( holder: ViewHolder, @@ -86,9 +82,7 @@ class MainListAdapter(private var attachedTo: RecyclerView) : RecyclerView.Adapt super.onViewRecycled(holder) } - override fun getItemCount(): Int { - return items.size - } + override fun getItemCount(): Int = items.size override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { super.onAttachedToRecyclerView(recyclerView) @@ -151,9 +145,7 @@ class MainListAdapter(private var attachedTo: RecyclerView) : RecyclerView.Adapt } } - fun getOffset(pos: Int): Int { - return offsets.copyOfRange(0, pos).sum() - } + fun getOffset(pos: Int): Int = offsets.copyOfRange(0, pos).sum() fun updateDirectView( id: String, @@ -173,9 +165,7 @@ class MainListAdapter(private var attachedTo: RecyclerView) : RecyclerView.Adapt notifyItemRangeInserted(correctOffset, newItems.size) } - private fun getPosFromId(id: Long): Int { - return items.indices.indexOfFirst { getItemId(it) == id } - } + private fun getPosFromId(id: Long): Int = items.indices.indexOfFirst { getItemId(it) == id } fun getDirectViewPos(deviceId: String): Int { var currentPos = 0 @@ -217,4 +207,8 @@ class MainListAdapter(private var attachedTo: RecyclerView) : RecyclerView.Adapt val hidden: TextView = view.findViewById(R.id.hidden) val stateSwitch: MaterialSwitch = view.findViewById(R.id.state) } + + companion object { + private const val ANIMATION_DURATION = 300L + } } diff --git a/app/src/main/java/io/github/domi04151309/home/adapters/SimpleListAdapter.kt b/app/src/main/java/io/github/domi04151309/home/adapters/SimpleListAdapter.kt index 7caabda..efeea65 100644 --- a/app/src/main/java/io/github/domi04151309/home/adapters/SimpleListAdapter.kt +++ b/app/src/main/java/io/github/domi04151309/home/adapters/SimpleListAdapter.kt @@ -18,13 +18,12 @@ class SimpleListAdapter( override fun onCreateViewHolder( parent: ViewGroup, viewType: Int, - ): ViewHolder { - return ViewHolder( + ): ViewHolder = + ViewHolder( LayoutInflater .from(parent.context) .inflate(R.layout.list_item_simple, parent, false), ) - } @SuppressLint("ClickableViewAccessibility") override fun onBindViewHolder( @@ -38,9 +37,7 @@ class SimpleListAdapter( holder.itemView.setOnClickListener { helperInterface.onItemClicked(holder.itemView, position) } } - override fun getItemCount(): Int { - return items.size - } + override fun getItemCount(): Int = items.size class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val drawable: ImageView = view.findViewById(R.id.drawable) diff --git a/app/src/main/java/io/github/domi04151309/home/api/EspEasyAPI.kt b/app/src/main/java/io/github/domi04151309/home/api/EspEasyAPI.kt index 9ef356b..48b905c 100644 --- a/app/src/main/java/io/github/domi04151309/home/api/EspEasyAPI.kt +++ b/app/src/main/java/io/github/domi04151309/home/api/EspEasyAPI.kt @@ -75,7 +75,7 @@ class EspEasyAPI( id: String, state: Boolean, ) { - val switchUrl = url + "control?cmd=GPIO," + id + "," + (if (state) "1" else "0") + val switchUrl = url + "control?cmd=GPIO," + id + "," + if (state) "1" else "0" val jsonObjectRequest = JsonObjectRequest( switchUrl, diff --git a/app/src/main/java/io/github/domi04151309/home/api/HueAPI.kt b/app/src/main/java/io/github/domi04151309/home/api/HueAPI.kt index 35083f3..37e8ab3 100644 --- a/app/src/main/java/io/github/domi04151309/home/api/HueAPI.kt +++ b/app/src/main/java/io/github/domi04151309/home/api/HueAPI.kt @@ -25,10 +25,6 @@ class HueAPI( deviceId: String, recyclerViewInterface: HomeRecyclerViewHelperInterface? = null, ) : UnifiedAPI(c, deviceId, recyclerViewInterface) { - companion object { - private const val UPDATE_DELAY = 100L - } - private val parser = HueAPIParser(c.resources) var readyForRequest: Boolean = true @@ -41,9 +37,10 @@ class HueAPI( fun onLightsLoaded(response: JSONObject?) } - fun getUsername(): String { - return PreferenceManager.getDefaultSharedPreferences(c).getString(deviceId, "") ?: "" - } + fun getUsername(): String = + PreferenceManager.getDefaultSharedPreferences(c) + .getString(deviceId, "") + ?: "" // For unified API override fun loadList( @@ -188,7 +185,7 @@ class HueAPI( hue: Int, sat: Int, ) { - putObject("/lights/$lightID/state", "{\"hue\":$hue, \"sat\":$sat}") + putObject("/lights/$lightID/state", """{ "hue": $hue, "sat": $sat }""") } fun switchGroupByID( @@ -231,14 +228,14 @@ class HueAPI( hue: Int, sat: Int, ) { - putObject("/groups/$groupID/action", "{\"hue\":$hue, \"sat\":$sat}") + putObject("/groups/$groupID/action", """{ "hue": $hue, "sat": $sat }""") } fun activateSceneOfGroup( groupID: String, scene: String, ) { - putObject("/groups/$groupID/action", "{\"scene\":$scene}") + putObject("/groups/$groupID/action", """{ "scene": $scene }""") } private fun putObject( @@ -259,4 +256,8 @@ class HueAPI( Handler(Looper.getMainLooper()).postDelayed({ readyForRequest = true }, UPDATE_DELAY) } } + + companion object { + private const val UPDATE_DELAY = 100L + } } diff --git a/app/src/main/java/io/github/domi04151309/home/api/HueAPIParser.kt b/app/src/main/java/io/github/domi04151309/home/api/HueAPIParser.kt index eb5e5fa..90109cc 100644 --- a/app/src/main/java/io/github/domi04151309/home/api/HueAPIParser.kt +++ b/app/src/main/java/io/github/domi04151309/home/api/HueAPIParser.kt @@ -9,6 +9,66 @@ import java.util.TreeMap import kotlin.collections.ArrayList class HueAPIParser(resources: Resources) : UnifiedAPI.Parser(resources) { + override fun parseResponse(response: JSONObject): List { + val listItems: ArrayList = ArrayList(response.length()) + val rooms: TreeMap> = TreeMap() + val zones: TreeMap> = TreeMap() + var currentObject: JSONObject + for (i in response.keys()) { + currentObject = response.getJSONObject(i) + when (currentObject.getString("type")) { + "Room" -> rooms[currentObject.getString("name")] = Pair(i, currentObject) + "Zone" -> zones[currentObject.getString("name")] = Pair(i, currentObject) + } + } + for (i in rooms.keys) listItems.add( + parseGroupObj( + rooms[i] ?: error("Room $i does not exist."), + false, + ), + ) + for (i in zones.keys) listItems.add( + parseGroupObj( + zones[i] ?: error("Zone $i does not exist."), + true, + ), + ) + return listItems + } + + override fun parseStates(response: JSONObject): List { + val states: ArrayList = ArrayList(response.length()) + val rooms: TreeMap> = TreeMap() + val zones: TreeMap> = TreeMap() + var currentObject: JSONObject + for (i in response.keys()) { + currentObject = response.getJSONObject(i) + when (currentObject.getString("type")) { + "Room" -> + rooms[currentObject.getString("name")] = + Pair(i, currentObject.optJSONObject("state")?.optBoolean("any_on")) + "Zone" -> + zones[currentObject.getString("name")] = + Pair(i, currentObject.optJSONObject("state")?.optBoolean("any_on")) + } + } + for (i in rooms.keys) states.add(rooms[i]?.second) + for (i in zones.keys) states.add(zones[i]?.second) + return states + } + + private fun parseGroupObj( + pair: Pair, + isZone: Boolean, + ): ListViewItem = + ListViewItem( + title = pair.second.getString("name"), + summary = resources.getString(R.string.hue_tap), + hidden = pair.first, + icon = if (isZone) R.drawable.ic_zone else R.drawable.ic_room, + state = pair.second.optJSONObject("state")?.optBoolean("any_on"), + ) + companion object { fun parseHueConfig( resources: Resources, @@ -114,65 +174,4 @@ class HueAPIParser(resources: Resources) : UnifiedAPI.Parser(resources) { return items } } - - override fun parseResponse(response: JSONObject): List { - val listItems: ArrayList = ArrayList(response.length()) - val rooms: TreeMap> = TreeMap() - val zones: TreeMap> = TreeMap() - var currentObject: JSONObject - for (i in response.keys()) { - currentObject = response.getJSONObject(i) - when (currentObject.getString("type")) { - "Room" -> rooms[currentObject.getString("name")] = Pair(i, currentObject) - "Zone" -> zones[currentObject.getString("name")] = Pair(i, currentObject) - } - } - for (i in rooms.keys) listItems.add( - parseGroupObj( - rooms[i] ?: error("Room $i does not exist."), - false, - ), - ) - for (i in zones.keys) listItems.add( - parseGroupObj( - zones[i] ?: error("Zone $i does not exist."), - true, - ), - ) - return listItems - } - - override fun parseStates(response: JSONObject): List { - val states: ArrayList = ArrayList(response.length()) - val rooms: TreeMap> = TreeMap() - val zones: TreeMap> = TreeMap() - var currentObject: JSONObject - for (i in response.keys()) { - currentObject = response.getJSONObject(i) - when (currentObject.getString("type")) { - "Room" -> - rooms[currentObject.getString("name")] = - Pair(i, currentObject.optJSONObject("state")?.optBoolean("any_on")) - "Zone" -> - zones[currentObject.getString("name")] = - Pair(i, currentObject.optJSONObject("state")?.optBoolean("any_on")) - } - } - for (i in rooms.keys) states.add(rooms[i]?.second) - for (i in zones.keys) states.add(zones[i]?.second) - return states - } - - private fun parseGroupObj( - pair: Pair, - isZone: Boolean, - ): ListViewItem { - return ListViewItem( - title = pair.second.getString("name"), - summary = resources.getString(R.string.hue_tap), - hidden = pair.first, - icon = if (isZone) R.drawable.ic_zone else R.drawable.ic_room, - state = pair.second.optJSONObject("state")?.optBoolean("any_on"), - ) - } } diff --git a/app/src/main/java/io/github/domi04151309/home/api/ShellyAPI.kt b/app/src/main/java/io/github/domi04151309/home/api/ShellyAPI.kt index d7404e3..4726013 100644 --- a/app/src/main/java/io/github/domi04151309/home/api/ShellyAPI.kt +++ b/app/src/main/java/io/github/domi04151309/home/api/ShellyAPI.kt @@ -199,7 +199,7 @@ class ShellyAPI( id: String, state: Boolean, ) { - val requestUrl = url + "relay/$id?turn=" + (if (state) "on" else "off") + val requestUrl = url + "relay/$id?turn=" + if (state) "on" else "off" val jsonObjectRequest = when (version) { 1 -> @@ -226,20 +226,19 @@ class ShellyAPI( companion object { /** - * Detect the name of the shelly device during discovery + * Detect the name of the shelly device during discovery. */ fun loadName( url: String, version: Int, listener: Response.Listener, - ): JsonObjectRequest { - return JsonObjectRequest( - url + (if (version == 1) "settings" else "shelly"), + ): JsonObjectRequest = + JsonObjectRequest( + url + if (version == 1) "settings" else "shelly", { statusResponse -> listener.onResponse(if (statusResponse.isNull("name")) "" else statusResponse.optString("name")) }, {}, ) - } } } diff --git a/app/src/main/java/io/github/domi04151309/home/api/ShellyAPIParser.kt b/app/src/main/java/io/github/domi04151309/home/api/ShellyAPIParser.kt index ae02206..04291b5 100644 --- a/app/src/main/java/io/github/domi04151309/home/api/ShellyAPIParser.kt +++ b/app/src/main/java/io/github/domi04151309/home/api/ShellyAPIParser.kt @@ -12,16 +12,12 @@ class ShellyAPIParser(resources: Resources, private val version: Int) : fun parseResponse( config: JSONObject, status: JSONObject, - ): List { - return if (version == 1) { + ): List = + if (version == 1) { parseResponseV1(config, status) } else { - parseResponseV2( - config, - status, - ) + parseResponseV2(config, status) } - } private fun parseResponseV1( settings: JSONObject, @@ -151,7 +147,9 @@ class ShellyAPIParser(resources: Resources, private val version: Int) : icon = Global.getIcon( config.optJSONObject("sys")?.optJSONObject("ui_data") - ?.optJSONArray("consumption_types")?.getString(currentId) ?: "", + ?.optJSONArray("consumption_types") + ?.getString(currentId) + ?: "", R.drawable.ic_do, ), ) @@ -162,9 +160,7 @@ class ShellyAPIParser(resources: Resources, private val version: Int) : fun parseStates( config: JSONObject, status: JSONObject, - ): List { - return if (version == 1) parseStatesV1(config, status) else parseStatesV2(config, status) - } + ): List = if (version == 1) parseStatesV1(config, status) else parseStatesV2(config, status) private fun parseStatesV1( settings: JSONObject, @@ -219,11 +215,10 @@ class ShellyAPIParser(resources: Resources, private val version: Int) : private fun nameOrDefault( name: String, id: Int, - ): String { - return if (name.trim().isEmpty()) { + ): String = + if (name.trim().isEmpty()) { resources.getString(R.string.shelly_switch_title, id + 1) } else { name } - } } diff --git a/app/src/main/java/io/github/domi04151309/home/api/SimpleHomeAPI.kt b/app/src/main/java/io/github/domi04151309/home/api/SimpleHomeAPI.kt index 2afafc4..dcd3876 100644 --- a/app/src/main/java/io/github/domi04151309/home/api/SimpleHomeAPI.kt +++ b/app/src/main/java/io/github/domi04151309/home/api/SimpleHomeAPI.kt @@ -132,7 +132,7 @@ class SimpleHomeAPI( val jsonObjectRequest = JsonObjectRequest( Request.Method.GET, - url + id.substring(id.lastIndexOf('@') + 1) + "?input=" + (if (state) 1 else 0), + url + id.substring(id.lastIndexOf('@') + 1) + "?input=" + if (state) 1 else 0, null, { }, { e -> Log.e(Global.LOG_TAG, e.toString()) }, diff --git a/app/src/main/java/io/github/domi04151309/home/api/UnifiedAPI.kt b/app/src/main/java/io/github/domi04151309/home/api/UnifiedAPI.kt index c45744e..6c37231 100644 --- a/app/src/main/java/io/github/domi04151309/home/api/UnifiedAPI.kt +++ b/app/src/main/java/io/github/domi04151309/home/api/UnifiedAPI.kt @@ -15,11 +15,6 @@ open class UnifiedAPI( val deviceId: String, protected val recyclerViewInterface: HomeRecyclerViewHelperInterface?, ) { - companion object { - private const val LIST_REQUEST_TIMEOUT = 1000 - private val listCache: MutableMap>> = mutableMapOf() - } - interface CallbackInterface { fun onItemsLoaded( holder: UnifiedRequestCallback, @@ -83,4 +78,9 @@ open class UnifiedAPI( open fun parseStates(response: JSONObject): List = listOf() } + + companion object { + private const val LIST_REQUEST_TIMEOUT = 1000 + private val listCache: MutableMap>> = mutableMapOf() + } } diff --git a/app/src/main/java/io/github/domi04151309/home/custom/CustomJsonArrayRequest.kt b/app/src/main/java/io/github/domi04151309/home/custom/CustomJsonArrayRequest.kt index d56b663..3df8287 100644 --- a/app/src/main/java/io/github/domi04151309/home/custom/CustomJsonArrayRequest.kt +++ b/app/src/main/java/io/github/domi04151309/home/custom/CustomJsonArrayRequest.kt @@ -18,8 +18,8 @@ class CustomJsonArrayRequest( listener: Response.Listener, errorListener: Response.ErrorListener, ) : JsonRequest(method, url, jsonRequest?.toString(), listener, errorListener) { - override fun parseNetworkResponse(response: NetworkResponse): Response { - return try { + override fun parseNetworkResponse(response: NetworkResponse): Response = + try { val jsonString = String(response.data, Charset.forName(HttpHeaderParser.parseCharset(response.headers))) Response.success( JSONArray(jsonString), @@ -30,5 +30,4 @@ class CustomJsonArrayRequest( } catch (e: JSONException) { Response.error(ParseError(e)) } - } } diff --git a/app/src/main/java/io/github/domi04151309/home/data/DeviceItem.kt b/app/src/main/java/io/github/domi04151309/home/data/DeviceItem.kt index f6a0029..39b34d7 100644 --- a/app/src/main/java/io/github/domi04151309/home/data/DeviceItem.kt +++ b/app/src/main/java/io/github/domi04151309/home/data/DeviceItem.kt @@ -2,19 +2,20 @@ package io.github.domi04151309.home.data import io.github.domi04151309.home.helpers.Global -data class DeviceItem(val id: String) { - var name: String = "Device" +class DeviceItem( + val id: String, + val name: String = "Device", + val mode: String = "Default", + val iconName: String = "Lamp", + val hide: Boolean = false, + val directView: Boolean = false, +) { var address: String = "http://127.0.0.1/" set(value) { field = formatAddress(value) } - var mode: String = "Default" - var iconName: String = "Lamp" val iconId: Int get() = Global.getIcon(iconName) - var hide: Boolean = false - var directView: Boolean = false - companion object { fun formatAddress(address: String): String { var url = address diff --git a/app/src/main/java/io/github/domi04151309/home/data/LightStates.kt b/app/src/main/java/io/github/domi04151309/home/data/LightStates.kt index 3338bce..fb21bc6 100644 --- a/app/src/main/java/io/github/domi04151309/home/data/LightStates.kt +++ b/app/src/main/java/io/github/domi04151309/home/data/LightStates.kt @@ -81,7 +81,7 @@ class LightStates { return json.toString() } - data class Light( + class Light( var on: Boolean = false, var bri: Int = -1, var xy: JSONArray? = null, diff --git a/app/src/main/java/io/github/domi04151309/home/data/SceneListItem.kt b/app/src/main/java/io/github/domi04151309/home/data/SceneListItem.kt index 43f5c70..a9102e8 100644 --- a/app/src/main/java/io/github/domi04151309/home/data/SceneListItem.kt +++ b/app/src/main/java/io/github/domi04151309/home/data/SceneListItem.kt @@ -1,11 +1,8 @@ package io.github.domi04151309.home.data -import io.github.domi04151309.home.R - -data class SceneListItem( - var title: String = "", - var hidden: String = "", - var icon: Int = R.drawable.ic_circle, +class SceneListItem( + val title: String = "", + val hidden: String = "", var state: Boolean = false, var brightness: String = "", var color: Int = 0, diff --git a/app/src/main/java/io/github/domi04151309/home/fragments/HueColorFragment.kt b/app/src/main/java/io/github/domi04151309/home/fragments/HueColorFragment.kt index c8894bc..027e694 100644 --- a/app/src/main/java/io/github/domi04151309/home/fragments/HueColorFragment.kt +++ b/app/src/main/java/io/github/domi04151309/home/fragments/HueColorFragment.kt @@ -23,11 +23,6 @@ import io.github.domi04151309.home.helpers.SliderUtils import io.github.domi04151309.home.interfaces.HueRoomInterface class HueColorFragment : Fragment(R.layout.fragment_hue_color) { - companion object { - private const val LOADING_DELAY = 200L - private const val UPDATE_DELAY = 5000L - } - private lateinit var lampInterface: HueRoomInterface private lateinit var hueAPI: HueAPI private lateinit var colorPickerView: ColorPickerView @@ -236,4 +231,9 @@ class HueColorFragment : Fragment(R.layout.fragment_hue_color) { }, ) } + + companion object { + private const val LOADING_DELAY = 200L + private const val UPDATE_DELAY = 5000L + } } diff --git a/app/src/main/java/io/github/domi04151309/home/fragments/HueScenesFragment.kt b/app/src/main/java/io/github/domi04151309/home/fragments/HueScenesFragment.kt index ae28fd1..7647b54 100644 --- a/app/src/main/java/io/github/domi04151309/home/fragments/HueScenesFragment.kt +++ b/app/src/main/java/io/github/domi04151309/home/fragments/HueScenesFragment.kt @@ -39,13 +39,6 @@ class HueScenesFragment : Fragment(R.layout.fragment_hue_scenes), RecyclerViewHelperInterface, Response.Listener { - companion object { - private const val SCENE_FRACTION_ESTIMATE = 4 - private const val COLUMNS = 3 - - var scenesChanged: Boolean = false - } - private var scenesRequest: JsonObjectRequest? = null private var selectedScene: CharSequence = "" private var selectedSceneName: CharSequence = "" @@ -217,8 +210,8 @@ class HueScenesFragment : if (selectedScene != "add") MenuInflater(requireContext()).inflate(R.menu.activity_hue_lamp_context, menu) } - override fun onContextItemSelected(item: MenuItem): Boolean { - return when (item.title) { + override fun onContextItemSelected(item: MenuItem): Boolean = + when (item.title) { resources.getString(R.string.str_edit) -> { startActivity( Intent(requireContext(), HueSceneActivity::class.java) @@ -251,7 +244,6 @@ class HueScenesFragment : super.onContextItemSelected(item) } } - } override fun onStart() { super.onStart() @@ -260,4 +252,11 @@ class HueScenesFragment : queue.add(scenesRequest) } } + + companion object { + private const val SCENE_FRACTION_ESTIMATE = 4 + private const val COLUMNS = 3 + + var scenesChanged: Boolean = false + } } diff --git a/app/src/main/java/io/github/domi04151309/home/helpers/ColorUtils.kt b/app/src/main/java/io/github/domi04151309/home/helpers/ColorUtils.kt index c48e0bc..f0e5949 100644 --- a/app/src/main/java/io/github/domi04151309/home/helpers/ColorUtils.kt +++ b/app/src/main/java/io/github/domi04151309/home/helpers/ColorUtils.kt @@ -38,8 +38,8 @@ object ColorUtils { y: Double, ): Int { val cieY = 1.0 - val cieX = (cieY * x) / y - val cieZ = ((1 - x - y) * cieY) / y + val cieX = cieY * x / y + val cieZ = (1 - x - y) * cieY / y val r = +3.2404542 * cieX - 1.5371385 * cieY - 0.4985314 * cieZ val g = -0.9692660 * cieX + 1.8760108 * cieY + 0.0415560 * cieZ @@ -48,15 +48,15 @@ object ColorUtils { return Color.rgb(formatXyzValue(r), formatXyzValue(g), formatXyzValue(b)) } - private fun formatXyzValue(v: Double): Int { - return clamp((if (v <= 0.0031308) 12.92 * v else 1.055 * v.pow(1.0 / 2.4) - 0.055) * MAX) - } + private fun formatXyzValue(v: Double): Int = + clamp( + (if (v <= 0.0031308) 12.92 * v else 1.055 * v.pow(1.0 / 2.4) - 0.055) * MAX, + ) - private fun clamp(value: Double): Int { - return when { + private fun clamp(value: Double): Int = + when { value < MIN -> MIN.toInt() value > MAX -> MAX.toInt() else -> value.toInt() } - } } diff --git a/app/src/main/java/io/github/domi04151309/home/helpers/DeviceSecrets.kt b/app/src/main/java/io/github/domi04151309/home/helpers/DeviceSecrets.kt index 887cc4f..f5d8e4e 100644 --- a/app/src/main/java/io/github/domi04151309/home/helpers/DeviceSecrets.kt +++ b/app/src/main/java/io/github/domi04151309/home/helpers/DeviceSecrets.kt @@ -7,10 +7,6 @@ import androidx.security.crypto.MasterKey import org.json.JSONObject class DeviceSecrets(context: Context, private val id: String) { - companion object { - private const val DEFAULT_JSON = "{\"username\": \"\", \"password\": \"\"}" - } - private val masterKeyAlias = MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) @@ -31,27 +27,27 @@ class DeviceSecrets(context: Context, private val id: String) { ?: DEFAULT_JSON, ) - fun updateDeviceSecrets() { - preferences.edit().putString(id, secrets.toString()).apply() - } - - fun deleteDeviceSecrets() { - preferences.edit().remove(id).apply() - } - var username: String - get() { - return secrets.optString("username") - } + get() = secrets.optString("username") set(value) { secrets.put("username", value) } var password: String - get() { - return secrets.optString("password") - } + get() = secrets.optString("password") set(value) { secrets.put("password", value) } + + fun updateDeviceSecrets() { + preferences.edit().putString(id, secrets.toString()).apply() + } + + fun deleteDeviceSecrets() { + preferences.edit().remove(id).apply() + } + + companion object { + private const val DEFAULT_JSON = """{ "username": "", "password": "" }""" + } } diff --git a/app/src/main/java/io/github/domi04151309/home/helpers/Devices.kt b/app/src/main/java/io/github/domi04151309/home/helpers/Devices.kt index 08d9698..005c7b4 100644 --- a/app/src/main/java/io/github/domi04151309/home/helpers/Devices.kt +++ b/app/src/main/java/io/github/domi04151309/home/helpers/Devices.kt @@ -12,18 +12,10 @@ import java.util.Random @Suppress("TooManyFunctions") class Devices(private val context: Context) { - companion object { - private const val ALLOWED_CHARACTERS = "0123456789abcdefghijklmnobqrstuvw" - private const val ID_LENGTH = 8 - private var storedData: JSONObject? = null - - fun reloadFromPreferences() { - storedData = null - } - } - private val preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) + val length: Int get() = deviceOrder.length() + private val data: JSONObject get() { if (storedData == null) { storedData = @@ -40,9 +32,7 @@ class Devices(private val context: Context) { return storedData!! } - private val devicesObject: JSONObject get() { - return data.optJSONObject("devices") ?: JSONObject() - } + private val devicesObject: JSONObject get() = data.optJSONObject("devices") ?: JSONObject() private val deviceOrder: JSONArray get() { if (!data.has("order")) { @@ -53,40 +43,33 @@ class Devices(private val context: Context) { private fun generateRandomId(): String { val random = Random() - val sb = StringBuilder(ID_LENGTH) - for (i in 0 until ID_LENGTH) - sb.append(ALLOWED_CHARACTERS[random.nextInt(ALLOWED_CHARACTERS.length)]) - return sb.toString() + val builder = StringBuilder(ID_LENGTH) + for (index in 0 until ID_LENGTH) { + builder.append(ALLOWED_CHARACTERS[random.nextInt(ALLOWED_CHARACTERS.length)]) + } + return builder.toString() } private fun convertToDeviceItem(id: String): DeviceItem { val json = devicesObject.optJSONObject(id) ?: JSONObject() - val device = DeviceItem(id) - device.name = json.optString("name") + val device = + DeviceItem( + id, + json.optString("name"), + json.optString("mode"), + json.optString("icon"), + json.optBoolean("hide", false), + json.optBoolean("direct_view", false), + ) device.address = json.optString("address") - device.mode = json.optString("mode") - device.iconName = json.optString("icon") - device.hide = json.optBoolean("hide", false) - device.directView = json.optBoolean("direct_view", false) return device } - fun getDeviceById(id: String): DeviceItem { - return convertToDeviceItem(id) - } - - fun getDeviceByIndex(index: Int): DeviceItem { - val id = deviceOrder.getString(index) - return convertToDeviceItem(id) - } + fun getDeviceById(id: String): DeviceItem = convertToDeviceItem(id) - val length: Int get() { - return deviceOrder.length() - } + fun getDeviceByIndex(index: Int): DeviceItem = convertToDeviceItem(deviceOrder.getString(index)) - fun idExists(id: String): Boolean { - return devicesObject.has(id) - } + fun idExists(id: String): Boolean = devicesObject.has(id) fun addressExists(address: String): Boolean { val formattedAddress = DeviceItem.formatAddress(address) @@ -145,4 +128,14 @@ class Devices(private val context: Context) { fun saveChanges() { preferences.edit().putString("devices_json", data.toString()).apply() } + + companion object { + private const val ALLOWED_CHARACTERS = "0123456789abcdefghijklmnobqrstuvw" + private const val ID_LENGTH = 8 + private var storedData: JSONObject? = null + + fun reloadFromPreferences() { + storedData = null + } + } } diff --git a/app/src/main/java/io/github/domi04151309/home/helpers/Global.kt b/app/src/main/java/io/github/domi04151309/home/helpers/Global.kt index 60339b3..3bc0353 100644 --- a/app/src/main/java/io/github/domi04151309/home/helpers/Global.kt +++ b/app/src/main/java/io/github/domi04151309/home/helpers/Global.kt @@ -51,8 +51,8 @@ internal object Global { deviceId: String, recyclerViewInterface: HomeRecyclerViewHelperInterface? = null, tasmotaHelperInterface: HomeRecyclerViewHelperInterface? = null, - ): UnifiedAPI { - return when (identifier) { + ): UnifiedAPI = + when (identifier) { "ESP Easy" -> EspEasyAPI(context, deviceId, recyclerViewInterface) "Hue API" -> HueAPI(context, deviceId, recyclerViewInterface) "SimpleHome API" -> SimpleHomeAPI(context, deviceId, recyclerViewInterface) @@ -61,14 +61,13 @@ internal object Global { "Shelly Gen 2" -> ShellyAPI(context, deviceId, recyclerViewInterface, 2) else -> UnifiedAPI(context, deviceId, recyclerViewInterface) } - } @Suppress("CyclomaticComplexMethod") fun getIcon( icon: String, default: Int = R.drawable.ic_warning, - ): Int { - return when (icon.lowercase()) { + ): Int = + when (icon.lowercase()) { "christmas tree" -> R.drawable.ic_device_christmas_tree "clock" -> R.drawable.ic_device_clock "display" -> R.drawable.ic_device_display @@ -90,11 +89,10 @@ internal object Global { "webcam" -> R.drawable.ic_device_webcam else -> default } - } @RequiresApi(Build.VERSION_CODES.R) - fun getDeviceType(icon: String): Int { - return when (icon.lowercase()) { + fun getDeviceType(icon: String): Int = + when (icon.lowercase()) { "christmas tree", "electricity", "schwibbogen", "socket" -> DeviceTypes.TYPE_OUTLET "display", "display alt" -> DeviceTypes.TYPE_DISPLAY "gauge", "heating", "thermometer" -> DeviceTypes.TYPE_AC_HEATER @@ -103,7 +101,6 @@ internal object Global { "webcam" -> DeviceTypes.TYPE_CAMERA else -> DeviceTypes.TYPE_UNKNOWN } - } fun checkNetwork(context: Context): Boolean { if ( diff --git a/app/src/main/java/io/github/domi04151309/home/helpers/HueUtils.kt b/app/src/main/java/io/github/domi04151309/home/helpers/HueUtils.kt index 496f9fd..01a7cbd 100644 --- a/app/src/main/java/io/github/domi04151309/home/helpers/HueUtils.kt +++ b/app/src/main/java/io/github/domi04151309/home/helpers/HueUtils.kt @@ -27,17 +27,17 @@ object HueUtils { hue: Int, sat: Int, ): Int { - require(!(hue > 65535 || sat > 254)) { "Argument out of range" } + require(!(hue > 65_535 || sat > 254)) { "Argument out of range" } return Color.HSVToColor(floatArrayOf(hue * 0.005493248F, sat / 254F, 1F)) } fun hueToRGB(hue: Int): Int { - require(hue <= 65535) { "Argument out of range" } + require(hue <= 65_535) { "Argument out of range" } return Color.HSVToColor(floatArrayOf(hue * 0.005493248F, 1F, 1F)) } fun hueToDegree(hue: Int): String { - require(hue <= 65535) { "Argument out of range" } + require(hue <= 65_535) { "Argument out of range" } return "${(hue * 0.005493248F).toInt()}°" } @@ -46,13 +46,12 @@ object HueUtils { return "${(sat / 254F * 100).toInt()}%" } - fun briToPercent(bri: Int): String { - return when { + fun briToPercent(bri: Int): String = + when { bri < 1 -> "0%" bri > 254 -> "100%" else -> "${(bri / 254F * 100).toInt()}%" } - } fun rgbToHueSat(color: Int): IntArray { val hsv = FloatArray(3) diff --git a/app/src/main/java/io/github/domi04151309/home/helpers/SliderUtils.kt b/app/src/main/java/io/github/domi04151309/home/helpers/SliderUtils.kt index 281ab47..2884e9b 100644 --- a/app/src/main/java/io/github/domi04151309/home/helpers/SliderUtils.kt +++ b/app/src/main/java/io/github/domi04151309/home/helpers/SliderUtils.kt @@ -19,9 +19,7 @@ object SliderUtils { private fun dpToPx( resources: Resources, dp: Int, - ): Int { - return (dp * resources.displayMetrics.density).toInt() - } + ): Int = (dp * resources.displayMetrics.density).toInt() fun setSliderGradientNow( view: View, diff --git a/app/src/main/java/io/github/domi04151309/home/helpers/TasmotaHelper.kt b/app/src/main/java/io/github/domi04151309/home/helpers/TasmotaHelper.kt index f384cfd..23d52ba 100644 --- a/app/src/main/java/io/github/domi04151309/home/helpers/TasmotaHelper.kt +++ b/app/src/main/java/io/github/domi04151309/home/helpers/TasmotaHelper.kt @@ -12,10 +12,6 @@ import org.json.JSONArray import org.json.JSONObject class TasmotaHelper(private val c: Context, private val tasmota: UnifiedAPI) { - companion object { - const val EMPTY_ARRAY: String = "[]" - } - private val prefs = PreferenceManager.getDefaultSharedPreferences(c) private val nullParent: ViewGroup? = null @@ -139,4 +135,8 @@ class TasmotaHelper(private val c: Context, private val tasmota: UnifiedAPI) { .setNegativeButton(android.R.string.cancel) { _, _ -> } .show() } + + companion object { + const val EMPTY_ARRAY: String = "[]" + } } diff --git a/app/src/main/java/io/github/domi04151309/home/helpers/UpdateHandler.kt b/app/src/main/java/io/github/domi04151309/home/helpers/UpdateHandler.kt index 1a0f5e6..0c63aab 100644 --- a/app/src/main/java/io/github/domi04151309/home/helpers/UpdateHandler.kt +++ b/app/src/main/java/io/github/domi04151309/home/helpers/UpdateHandler.kt @@ -4,10 +4,6 @@ import android.os.Handler import android.os.Looper class UpdateHandler : Handler(Looper.getMainLooper()) { - companion object { - private const val UPDATE_DELAY = 1000L - } - var running: Boolean = false private set @@ -29,4 +25,8 @@ class UpdateHandler : Handler(Looper.getMainLooper()) { running = false removeCallbacksAndMessages(null) } + + companion object { + private const val UPDATE_DELAY = 1000L + } } diff --git a/app/src/main/java/io/github/domi04151309/home/services/ControlService.kt b/app/src/main/java/io/github/domi04151309/home/services/ControlService.kt index 05c3739..f080cb5 100644 --- a/app/src/main/java/io/github/domi04151309/home/services/ControlService.kt +++ b/app/src/main/java/io/github/domi04151309/home/services/ControlService.kt @@ -30,17 +30,14 @@ import java.util.function.Consumer @RequiresApi(Build.VERSION_CODES.R) class ControlService : ControlsProviderService() { - companion object { - private const val UPDATE_DELAY = 100L - } - private var updateSubscriber: Flow.Subscriber? = null - override fun createPublisherForAllAvailable(): Flow.Publisher { - return Flow.Publisher { subscriber -> + override fun createPublisherForAllAvailable(): Flow.Publisher = + Flow.Publisher { subscriber -> updateSubscriber = subscriber if (!Global.checkNetwork(this)) { subscriber.onComplete() + @Suppress("LabeledExpression") return@Publisher } val devices = Devices(this) @@ -100,14 +97,13 @@ class ControlService : ControlsProviderService() { ) } } - } internal fun getUnreachableControl( id: String, device: DeviceItem, pi: PendingIntent, - ): Control { - return Control.StatefulBuilder(id, pi) + ): Control = + Control.StatefulBuilder(id, pi) .setTitle(device.name) .setZone(device.name) .setStructure(resources.getString(R.string.app_name)) @@ -115,7 +111,6 @@ class ControlService : ControlsProviderService() { .setStatus(Control.STATUS_DISABLED) .setStatusText(resources.getString(R.string.str_unreachable)) .build() - } private fun loadStatefulControl( subscriber: Flow.Subscriber?, @@ -203,8 +198,8 @@ class ControlService : ControlsProviderService() { } } - override fun createPublisherFor(controlIds: MutableList): Flow.Publisher { - return Flow.Publisher { subscriber -> + override fun createPublisherFor(controlIds: MutableList): Flow.Publisher = + Flow.Publisher { subscriber -> updateSubscriber = subscriber subscriber.onSubscribe( object : Flow.Subscription { @@ -221,7 +216,6 @@ class ControlService : ControlsProviderService() { loadStatefulControl(subscriber, id) } } - } override fun performControlAction( controlId: String, @@ -263,4 +257,8 @@ class ControlService : ControlsProviderService() { consumer.accept(ControlAction.RESPONSE_FAIL) } } + + companion object { + private const val UPDATE_DELAY = 100L + } } diff --git a/app/src/test/java/io/github/domi04151309/home/Helpers.kt b/app/src/test/java/io/github/domi04151309/home/Helpers.kt index 544f09d..4b049a9 100644 --- a/app/src/test/java/io/github/domi04151309/home/Helpers.kt +++ b/app/src/test/java/io/github/domi04151309/home/Helpers.kt @@ -1,7 +1,8 @@ package io.github.domi04151309.home object Helpers { - fun getFileContents(path: String): String { - return javaClass.getResource(path)?.readText() ?: error("Cannot get file contents.") - } + fun getFileContents(path: String): String = + javaClass.getResource(path) + ?.readText() + ?: error("Cannot get file contents.") }