55 Commits
Author SHA1 Message Date
emil f5043031e7 Update README 2025-01-16 13:53:07 +01:00
emil c710fd1e6a Add screenshot 2025-01-16 13:42:02 +01:00
emil bddfc5c725 Manual updates 2025-01-16 13:19:47 +01:00
emil cdb0e55e67 Bugfixes, refactoring, add Czech and Hebrew 2025-01-16 13:02:43 +01:00
emil 0816c1a1d9 Refactoring 2024-11-28 22:44:25 +01:00
emil 3ab7bf1eba More refractoring, add symbol meta key definition in XML 2024-11-28 20:37:30 +01:00
emil c03c3f01c5 Some slight refactoring 2024-11-28 14:47:20 +01:00
emil 7b5da54aea Keymap fixes 2024-11-27 00:09:11 +01:00
emil 3a40699cc9 TODO 2024-11-26 02:17:09 +01:00
emil 6076c69f3f More keymap refactoring 2024-11-26 01:44:15 +01:00
emil e47227ce6d Some major changes, keymap restructuring with device and language keymaps 2024-11-25 21:14:45 +01:00
emil ec931a53a6 Basic tweak functionality 2024-11-22 22:52:17 +01:00
emil 7cb31ff224 Add preference categories, update the manual 2024-11-22 18:50:28 +01:00
emil 7f60898dd8 Add a user manual screen 2024-11-22 17:44:08 +01:00
emil 6abc48c121 Refactor the InputView 2024-11-22 12:55:42 +01:00
emil a7de68929a Shift+Enter always sends newline, misc. fixes 2024-11-21 23:54:45 +01:00
emil 68f48dce09 Add some TODOs 2024-11-21 22:51:04 +01:00
emil 32ae47e349 Fix alt+backspace deleting lines of text, add Greek question mark under Alt+X in Greek layout, move Arabic diacritics to shifted symbols, fix button being displayed in all caps 2024-11-13 17:01:31 +01:00
emil 185d518faa Bring back automatic cycling 2024-11-09 00:03:41 +01:00
emil 0628a4bdb9 Linting 2024-11-08 23:55:46 +01:00
emil dbb3ddb7ff Some additional input checks 2024-11-08 23:48:13 +01:00
emil 35693a3981 Add additional method for cycling symbols using consecutive presses 2024-11-08 23:44:13 +01:00
emil 8850c99b93 Simplify key event handling 2024-11-08 21:53:55 +01:00
emil b4a0e12257 UI style revamp 2024-11-08 21:30:03 +01:00
emil 913d4aad40 Make the space key definable in keymaps 2024-11-08 19:37:06 +01:00
emil 09238d8056 Small misc. changes, renames, etc. 2024-11-08 18:00:59 +01:00
emil 001ce70392 Strict inspection warnings fixed, slight restructurings 2024-11-05 23:36:09 +01:00
emil 0445279671 Minor keymap tweaks 2024-11-05 20:58:28 +01:00
emil adc60420b2 Change package name, yet again 2024-11-05 18:28:56 +01:00
emil 68c8ed9a54 Update lint profile 2024-11-05 18:00:01 +01:00
emil 3462a5eb9f Very strict linter profile 2024-11-05 15:45:09 +01:00
emil 970f65f245 Inspection error fixes 2024-11-05 11:31:05 +01:00
emil fdbf73720b Misc. 2024-11-04 23:44:21 +01:00
emil 792216e22d Update compile and targetSdk 2024-11-04 20:18:56 +01:00
emil e430bbfeac Misc. changes, arabic fixes 2024-11-04 19:22:04 +01:00
emil b441d822d2 Change package name, fix drawable bugs 2024-11-04 16:45:21 +01:00
emil c29fcc4b5f Arabic revamp 2024-11-03 23:22:23 +01:00
emil 361de2a76c Miscellaneous changes 2024-11-03 20:17:41 +01:00
emil 5f1ee64952 Clipboard managment pretty much finished 2024-11-03 18:44:31 +01:00
emil ff9d115306 New icon, preliminary clipboard functionality 2024-11-01 17:15:56 +01:00
emil ec3e21d8cf Interface code improvements 2024-11-01 00:57:27 +01:00
emil 004dfdbe8f Miscellaneous changes, more languages 2024-10-31 19:06:36 +01:00
emil d5e7102de3 Miscellaneous changes, more languages 2024-10-31 19:04:39 +01:00
emil 11fd71508b Greek polytonic diacritics 2024-10-23 23:46:27 +02:00
emil 335cffe433 Bugfixes and possibility to have multiple-character symbols. 2024-10-23 23:24:18 +02:00
emil 640ad71198 A rather working version 2024-10-23 22:43:31 +02:00
emil 7108e2beb9 Auto-capitalization on newline 2024-10-23 20:31:36 +02:00
emil 989752669a Support for down to Android 1.5 (Cupcake) 2024-10-23 20:24:06 +02:00
emil 681e2024de Fix auto-capitalization so that it works rather flawlessly 2024-10-22 19:49:22 +02:00
emil 463beaf170 Don't remember 2024-10-22 13:42:40 +02:00
emil 6d63f53919 Code revamping, InputMethod is now clear of unnecessary methods 2024-10-21 16:53:43 +02:00
emil 552fa0d94f New method for cycling symbols. 2024-10-21 15:15:35 +02:00
emil 5d66997f77 Working preferences, keymap choosing. 2024-10-20 23:40:46 +02:00
emil bf695b19e0 Code restructuring, miscellaneous fixes. 2024-10-20 18:25:19 +02:00
emil e98639dc9d Different way of inputting alternate characters (long press), miscellaneous fixes. 2024-10-19 16:20:48 +02:00
63 changed files with 6402 additions and 1945 deletions
+967
View File
@@ -0,0 +1,967 @@
<profile version="1.0">
<option name="myName" value="Very strict" />
<inspection_tool class="AbsoluteAlignmentInUserInterface" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AbstractClassExtendsConcreteClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AbstractClassNeverImplemented" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AbstractClassWithOnlyOneDirectInheritor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AbstractClassWithoutAbstractMethods" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AbstractMethodCallInConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AbstractMethodOverridesAbstractMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AbstractMethodOverridesConcreteMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AbstractMethodWithMissingImplementations" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AccessToNonThreadSafeStaticFieldFromInstance" enabled="true" level="WARNING" enabled_by_default="true">
<option name="nonThreadSafeClasses">
<value />
</option>
<option name="nonThreadSafeTypes" value="" />
</inspection_tool>
<inspection_tool class="AccessToStaticFieldLockedOnInstance" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AddConversionCallMigration" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AmbiguousExpressionInWhenBranchMigration" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AmbiguousFieldAccess" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AmbiguousMethodCall" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintAppCompatMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintAppLinksAutoVerify" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="AndroidLintBackButton" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintConvertToWebp" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintDuplicateStrings" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintEasterEgg" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintExpensiveAssertion" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintIconExpectedSize" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintImplicitSamInstance" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintKotlinPropertyAccess" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintKotlincFE10" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintLambdaLast" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintLogConditional" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintMangledCRLF" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="AndroidLintNegativeMargin" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintNewerVersionAvailable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintNoHardKeywords" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintNoOp" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintPermissionNamingConvention" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintRegistered" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintSelectableText" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintStopShip" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="AndroidLintStringFormatTrivial" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintSyntheticAccessor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintTypographyQuotes" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintUnsafeImplicitIntentLaunch" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="AndroidLintUnsupportedChromeOsHardware" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="AndroidLintUnusedIds" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintValidActionsXml" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="AndroidLintWrongThreadInterprocedural" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="AnnotationClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AnonymousClassComplexity" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="3" />
</inspection_tool>
<inspection_tool class="AnonymousClassMethodCount" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="1" />
</inspection_tool>
<inspection_tool class="AnonymousClassVariableHidesContainingMethodVariable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AnonymousInnerClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AnonymousInnerClassMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ArrayEquality" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ArrayLengthInLoopCondition" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssertEqualsCalledOnArray" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssertEqualsMayBeAssertSame" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssertMessageNotString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssertStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssertWithoutMessage" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentOrReturnOfFieldWithMutableType" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentToForLoopParameter" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_checkForeachParameters" value="true" />
</inspection_tool>
<inspection_tool class="AssignmentToLambdaParameter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentToMethodParameter" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreTransformationOfOriginalParameter" value="false" />
</inspection_tool>
<inspection_tool class="AssignmentToNull" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentToStaticFieldFromInstanceMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentToSuperclassField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AwaitNotInLoop" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AwaitWithoutCorrespondingSignal" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BadExceptionCaught" enabled="true" level="WARNING" enabled_by_default="true">
<option name="exceptionsString" value="" />
<option name="exceptions">
<value />
</option>
</inspection_tool>
<inspection_tool class="BadExceptionDeclared" enabled="true" level="WARNING" enabled_by_default="true">
<option name="exceptionsString" value="" />
<option name="exceptions">
<value />
</option>
<option name="ignoreTestCases" value="false" />
<option name="ignoreLibraryOverrides" value="false" />
</inspection_tool>
<inspection_tool class="BadExceptionThrown" enabled="true" level="WARNING" enabled_by_default="true">
<option name="exceptionsString" value="" />
<option name="exceptions">
<value />
</option>
</inspection_tool>
<inspection_tool class="BadOddness" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BigDecimalEquals" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BlockMarkerComments" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BooleanMethodNameMustStartWithQuestion" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreBooleanMethods" value="false" />
<option name="ignoreInAnnotationInterface" value="true" />
<option name="onlyWarnOnBaseMethods" value="true" />
<option name="questionString" value="are,can,check,contains,could,endsWith,equals,has,is,matches,must,shall,should,startsWith,was,were,will,would" />
</inspection_tool>
<inspection_tool class="BooleanParameter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BoundedWildcard" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BreakStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BreakStatementWithLabel" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BulkFileAttributesRead" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CallToNativeMethodWhileLocked" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CallToSimpleGetterInClass" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreGetterCallsOnOtherObjects" value="false" />
<option name="onlyReportPrivateGetter" value="false" />
</inspection_tool>
<inspection_tool class="CallToSimpleSetterInClass" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreSetterCallsOnOtherObjects" value="false" />
<option name="onlyReportPrivateSetter" value="false" />
</inspection_tool>
<inspection_tool class="CallToStringConcatCanBeReplacedByOperator" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CallingMethodShouldBeRequiresBlockingContext" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="CancellationCheckInLoops" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CastConflictsWithInstanceof" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CastDueToProgressionResolutionChangeMigration" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CastThatLosesPrecision" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreIntegerCharCasts" value="false" />
<option name="ignoreOverflowingByteCasts" value="false" />
</inspection_tool>
<inspection_tool class="CastToIncompatibleInterface" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ChainedEquality" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ChannelResource" enabled="true" level="WARNING" enabled_by_default="true">
<option name="insideTryAllowed" value="false" />
</inspection_tool>
<inspection_tool class="CharUsedInArithmeticContext" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CharacterComparison" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CheckForOutOfMemoryOnLargeArrayAllocation" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="64" />
</inspection_tool>
<inspection_tool class="CheckedExceptionClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClangTidy" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClashingGetters" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClassComplexity" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="80" />
</inspection_tool>
<inspection_tool class="ClassCoupling" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_includeJavaClasses" value="false" />
<option name="m_includeLibraryClasses" value="false" />
<option name="m_limit" value="15" />
</inspection_tool>
<inspection_tool class="ClassInheritanceDepth" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="2" />
</inspection_tool>
<inspection_tool class="ClassInitializer" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClassLoaderInstantiation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClassNameDiffersFromFileName" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClassNamePrefixedWithPackageName" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClassNameSameAsAncestorName" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClassNestingDepth" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="1" />
</inspection_tool>
<inspection_tool class="ClassNewInstance" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClassOnlyUsedInOneModule" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClassOnlyUsedInOnePackage" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClassReferencesSubclass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClassWithMultipleLoggers" enabled="true" level="WARNING" enabled_by_default="true">
<option name="loggerNamesString" value="java.util.logging.Logger,org.slf4j.Logger,org.apache.commons.logging.Log,org.apache.log4j.Logger,org.apache.logging.log4j.Logger" />
</inspection_tool>
<inspection_tool class="ClassWithOnlyPrivateConstructors" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClassWithTooManyDependencies" enabled="true" level="WARNING" enabled_by_default="true">
<option name="limit" value="10" />
</inspection_tool>
<inspection_tool class="ClassWithTooManyDependents" enabled="true" level="WARNING" enabled_by_default="true">
<option name="limit" value="10" />
</inspection_tool>
<inspection_tool class="ClassWithTooManyTransitiveDependencies" enabled="true" level="WARNING" enabled_by_default="true">
<option name="limit" value="35" />
</inspection_tool>
<inspection_tool class="ClassWithTooManyTransitiveDependents" enabled="true" level="WARNING" enabled_by_default="true">
<option name="limit" value="35" />
</inspection_tool>
<inspection_tool class="CloneCallsConstructors" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CloneInNonCloneableClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CloneReturnsClassType" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CollectionsFieldAccessReplaceableByMethodCall" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CollectionsMustHaveInitialCapacity" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ComparableImplementedButEqualsNotOverridden" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ComparatorNotSerializable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CompareToUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ComparisonOfShortAndChar" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConditionSignal" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConfusingFloatingPointLiteral" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConfusingOctalEscape" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConnectionResource" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantAssertArgument" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantAssertCondition" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantDeclaredInAbstractClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantDeclaredInInterface" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantMathCall" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantValueVariableUse" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstructorCount" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreDeprecatedConstructors" value="false" />
<option name="m_limit" value="5" />
</inspection_tool>
<inspection_tool class="ContinueStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ContinueStatementWithLabel" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConvertJavadoc" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConvertOldAnnotations" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CovariantEquals" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CustomClassloader" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CustomRegExpInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CustomSecurityManager" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CyclicClassDependency" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CyclicPackageDependency" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CyclomaticComplexity" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="10" />
</inspection_tool>
<inspection_tool class="DateToString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DeclaringClassMigration" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DeconstructionCanBeUsed" enabled="true" level="INFORMATION" enabled_by_default="true" />
<inspection_tool class="DefaultNotLastCaseInSwitch" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="Destructure" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="DisjointPackage" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DollarSignInName" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DoubleLiteralMayBeFloatLiteral" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DriverManagerGetConnection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DuplicateStringLiteralInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="MIN_STRING_LENGTH" value="5" />
<option name="IGNORE_PROPERTY_KEYS" value="false" />
</inspection_tool>
<inspection_tool class="DynamicRegexReplaceableByCompiledPattern" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EmptyClass" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignorableAnnotations">
<value />
</option>
<option name="ignoreClassWithParameterization" value="false" />
<option name="ignoreThrowables" value="true" />
<option name="commentsAreContent" value="true" />
</inspection_tool>
<inspection_tool class="EmptyDirectory" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EnumClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EnumerationCanBeIteration" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EqualsAndHashcode" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EqualsCalledOnEnumConstant" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EqualsUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ErrorRethrown" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ExceptionFromCatchWhichDoesntWrap" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreGetMessage" value="false" />
<option name="ignoreCantWrap" value="false" />
</inspection_tool>
<inspection_tool class="ExceptionNameDoesntEndWithException" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ExceptionPackage" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ExpectedExceptionNeverThrown" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ExpectedExceptionNeverThrownTestNG" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ExtendsConcreteCollection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ExtendsThread" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ExtendsThrowable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ExtendsUtilityClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ExtensionClassShouldBeFinalAndNonPublic" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ExternalizableWithSerializationMethods" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="FallthruInSwitchStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="FeatureEnvy" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreTestCases" value="false" />
</inspection_tool>
<inspection_tool class="FieldAccessNotGuarded" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="FieldAccessedSynchronizedAndUnsynchronized" enabled="true" level="WARNING" enabled_by_default="true">
<option name="countGettersAndSetters" value="false" />
</inspection_tool>
<inspection_tool class="FieldCount" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_countConstantFields" value="false" />
<option name="m_considerStaticFinalFieldsConstant" value="false" />
<option name="myCountEnumConstants" value="false" />
<option name="m_limit" value="10" />
</inspection_tool>
<inspection_tool class="FieldHasSetterButNoGetter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="FieldHidesSuperclassField" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreInvisibleFields" value="true" />
</inspection_tool>
<inspection_tool class="FieldMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="FieldNamingConvention" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="FieldNotUsedInToString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="Finalize" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreTrivialFinalizers" value="true" />
</inspection_tool>
<inspection_tool class="FloatingPointEquality" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ForLoopWithMissingComponent" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreCollectionLoops" value="false" />
</inspection_tool>
<inspection_tool class="ForeachStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="FromClosedRangeMigration" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="GrStringStyleViolation" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="GradleKotlinxCoroutinesDeprecation" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="GroovyAssignmentToForLoopParameter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyAssignmentToMethodParameter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyBreak" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyBusyWait" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyConditional" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyConstantNamingConvention" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_regex" value="[A-Z\d]*" />
<option name="m_minLength" value="4" />
<option name="m_maxLength" value="32" />
</inspection_tool>
<inspection_tool class="GroovyContinue" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyContinueOrBreakFromFinallyBlock" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyDoubleCheckedLocking" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreOnVolatileVariables" value="false" />
</inspection_tool>
<inspection_tool class="GroovyEmptyCatchBlock" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyEmptyFinallyBlock" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyEmptySyncBlock" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyEmptyTryBlock" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyIfStatementWithTooManyBranches" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="3" />
</inspection_tool>
<inspection_tool class="GroovyInstanceMethodNamingConvention" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_regex" value="[a-z][A-Za-z\d]*" />
<option name="m_minLength" value="4" />
<option name="m_maxLength" value="32" />
</inspection_tool>
<inspection_tool class="GroovyInstanceVariableNamingConvention" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_regex" value="m_[a-z][A-Za-z\d]*" />
<option name="m_minLength" value="1" />
<option name="m_maxLength" value="32" />
</inspection_tool>
<inspection_tool class="GroovyListGetCanBeKeyedAccess" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyListSetCanBeKeyedAccess" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyLocalVariableNamingConvention" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_regex" value="[a-z][A-Za-z\d]*" />
<option name="m_minLength" value="4" />
<option name="m_maxLength" value="32" />
</inspection_tool>
<inspection_tool class="GroovyLoopStatementThatDoesntLoop" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyMapGetCanBeKeyedAccess" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyMapPutCanBeKeyedAccess" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyMethodParameterCount" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="5" />
</inspection_tool>
<inspection_tool class="GroovyMethodWithMoreThanThreeNegations" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyMultipleReturnPointsPerMethod" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="1" />
</inspection_tool>
<inspection_tool class="GroovyNegatedConditional" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyNegatedIf" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyNestedAssignment" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyNestedConditional" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyNestedSwitch" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyNestedSynchronizedStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyNonShortCircuitBoolean" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyNotifyWhileNotSynchronized" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyOctalInteger" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyOverlyComplexArithmeticExpression" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="3" />
</inspection_tool>
<inspection_tool class="GroovyOverlyComplexBooleanExpression" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="3" />
</inspection_tool>
<inspection_tool class="GroovyOverlyComplexMethod" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="10" />
</inspection_tool>
<inspection_tool class="GroovyOverlyLongMethod" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="30" />
</inspection_tool>
<inspection_tool class="GroovyOverlyNestedMethod" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="5" />
</inspection_tool>
<inspection_tool class="GroovyParameterNamingConvention" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_regex" value="[a-z][A-Za-z\d]*" />
<option name="m_minLength" value="4" />
<option name="m_maxLength" value="32" />
</inspection_tool>
<inspection_tool class="GroovyPointlessArithmetic" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyPublicFieldAccessedInSynchronizedContext" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyRangeTypeCheck" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyResultOfAssignmentUsed" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyResultOfIncrementOrDecrementUsed" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyReturnFromClosureCanBeImplicit" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyReturnFromFinallyBlock" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyStaticMethodNamingConvention" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_regex" value="[a-z][A-Za-z\d]*" />
<option name="m_minLength" value="4" />
<option name="m_maxLength" value="32" />
</inspection_tool>
<inspection_tool class="GroovyStaticVariableNamingConvention" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_regex" value="s_[a-z][A-Za-z\d]*" />
<option name="m_minLength" value="4" />
<option name="m_maxLength" value="32" />
</inspection_tool>
<inspection_tool class="GroovySwitchStatementWithNoDefault" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovySynchronizationOnThis" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovySynchronizedMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovySystemRunFinalizersOnExit" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyThreadStopSuspendResume" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyThrowFromFinallyBlock" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyUnconditionalWait" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyUntypedAccess" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyVariableCanBeFinal" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyWaitCallNotInLoop" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyWaitWhileNotSynchronized" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GroovyWhileLoopSpinsOnField" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreNonEmtpyLoops" value="false" />
</inspection_tool>
<inspection_tool class="Guava" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="HardCodedStringLiteral" enabled="true" level="WARNING" enabled_by_default="false">
<scope name="Project Source Files" level="WARNING" enabled="true">
<option name="ignoreForAssertStatements" value="true" />
<option name="ignoreForExceptionConstructors" value="true" />
<option name="ignoreForSpecifiedExceptionConstructors" value="" />
<option name="ignoreForJUnitAsserts" value="true" />
<option name="ignoreForClassReferences" value="true" />
<option name="ignoreForPropertyKeyReferences" value="true" />
<option name="ignoreForNonAlpha" value="true" />
<option name="ignoreAssignedToConstants" value="false" />
<option name="ignoreToString" value="false" />
<option name="nonNlsCommentPattern" value="NON-NLS" />
</scope>
<option name="ignoreForAssertStatements" value="true" />
<option name="ignoreForExceptionConstructors" value="true" />
<option name="ignoreForSpecifiedExceptionConstructors" value="" />
<option name="ignoreForJUnitAsserts" value="true" />
<option name="ignoreForClassReferences" value="true" />
<option name="ignoreForPropertyKeyReferences" value="true" />
<option name="ignoreForNonAlpha" value="true" />
<option name="ignoreAssignedToConstants" value="false" />
<option name="ignoreToString" value="false" />
<option name="nonNlsCommentPattern" value="NON-NLS" />
</inspection_tool>
<inspection_tool class="HardcodedFileSeparators" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_recognizeExampleMediaType" value="false" />
</inspection_tool>
<inspection_tool class="HardcodedLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="HashCodeUsesNonFinalVariable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="HibernateResource" enabled="true" level="WARNING" enabled_by_default="true">
<option name="insideTryAllowed" value="false" />
</inspection_tool>
<inspection_tool class="HtmlNonExistentInternetResource" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="HtmlPresentationalElement" enabled="true" level="INFORMATION" enabled_by_default="true" />
<inspection_tool class="HtmlRequiredSummaryAttribute" enabled="true" level="INFORMATION" enabled_by_default="true" />
<inspection_tool class="HtmlRequiredTitleAttribute" enabled="true" level="INFORMATION" enabled_by_default="true" />
<inspection_tool class="HtmlTagCanBeJavadocTag" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IOResource" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredTypesString" value="java.io.ByteArrayOutputStream,java.io.ByteArrayInputStream,java.io.StringBufferInputStream,java.io.CharArrayWriter,java.io.CharArrayReader,java.io.StringWriter,java.io.StringReader" />
<option name="insideTryAllowed" value="false" />
</inspection_tool>
<inspection_tool class="IfStatementWithTooManyBranches" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="3" />
</inspection_tool>
<inspection_tool class="IgnoredJUnitTest" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IllegalDependencyOnInternalPackage" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ImplicitDefaultCharsetUsage" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IncompleteDestructuring" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InconsistentLanguageLevel" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IncorrectProcessCanceledExceptionHandling" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="IncrementDecrementUsedAsExpression" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InlineClassDeprecatedMigration" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InnerClassOnInterface" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreInnerInterfaces" value="false" />
</inspection_tool>
<inspection_tool class="InnerClassReferencedViaSubclass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InnerClassVariableHidesOuterClassVariable" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreInvisibleFields" value="true" />
</inspection_tool>
<inspection_tool class="InstanceGuardedByStatic" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InstanceVariableUninitializedUse" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignorePrimitives" value="false" />
<option name="annotationNamesString" value="" />
</inspection_tool>
<inspection_tool class="InstanceofCatchParameter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InstanceofChain" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreInstanceofOnLibraryClasses" value="false" />
</inspection_tool>
<inspection_tool class="InstanceofIncompatibleInterface" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InstanceofThis" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IntLiteralMayBeLongLiteral" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InterfaceMayBeAnnotatedFunctional" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="InterfaceNeverImplemented" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreInterfacesThatOnlyDeclareConstants" value="false" />
</inspection_tool>
<inspection_tool class="InterfaceWithOnlyOneDirectInheritor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IteratorNextDoesNotThrowNoSuchElementException" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JDBCExecuteWithNonConstantString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JDBCPrepareStatementWithNonConstantString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JDBCResource" enabled="true" level="WARNING" enabled_by_default="true">
<option name="insideTryAllowed" value="false" />
</inspection_tool>
<inspection_tool class="JNDIResource" enabled="true" level="WARNING" enabled_by_default="true">
<option name="insideTryAllowed" value="false" />
</inspection_tool>
<inspection_tool class="JUnit5Converter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JUnitTestNG" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JavaLangImport" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JavadocHtmlLint" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="KDocMissingDocumentation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="KeySetIterationMayUseEntrySet" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="KotlinLoggerInitializedWithForeignClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="LabeledStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="LambdaParameterHidingMemberVariable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="LambdaParameterNamingConvention" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="LambdaUnfriendlyMethodOverload" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="LengthOneStringInIndexOf" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="LimitedScopeInnerClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ListenerMayUseAdapter" enabled="true" level="WARNING" enabled_by_default="true">
<option name="checkForEmptyMethods" value="true" />
</inspection_tool>
<inspection_tool class="LiteralAsArgToStringEquals" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="LoadLibraryWithNonConstantString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="LocalVariableHidingMemberVariable" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreInvisibleFields" value="true" />
<option name="m_ignoreStaticMethods" value="true" />
</inspection_tool>
<inspection_tool class="LocalVariableNamingConvention" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreForLoopParameters" value="false" />
<option name="m_ignoreCatchParameters" value="false" />
<option name="m_regex" value="[a-z][A-Za-z\d]*" />
<option name="m_minLength" value="1" />
<option name="m_maxLength" value="20" />
</inspection_tool>
<inspection_tool class="LogStatementGuardedByLogCondition" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="LoggerInitializedWithForeignClass" enabled="true" level="WARNING" enabled_by_default="true">
<option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" />
</inspection_tool>
<inspection_tool class="LoggingConditionDisagreesWithLogLevelStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="LongLine" enabled="true" level="WEAK WARNING" enabled_by_default="true" editorAttributes="INFO_ATTRIBUTES" />
<inspection_tool class="LoopWithImplicitTerminationCondition" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MagicCharacter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MagicNumber" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MapReplaceableByEnumMap" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MarkerInterface" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MethodCallInLoopCondition" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MethodCount" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="20" />
<option name="ignoreGettersAndSetters" value="false" />
<option name="ignoreOverridingMethods" value="false" />
</inspection_tool>
<inspection_tool class="MethodCoupling" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_includeJavaClasses" value="false" />
<option name="m_includeLibraryClasses" value="false" />
<option name="m_limit" value="10" />
</inspection_tool>
<inspection_tool class="MethodMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_onlyPrivateOrFinal" value="false" />
<option name="m_ignoreEmptyMethods" value="true" />
</inspection_tool>
<inspection_tool class="MethodMayBeSynchronized" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MethodNameSameAsParentName" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MethodOnlyUsedFromInnerClass" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreMethodsAccessedFromAnonymousClass" value="false" />
<option name="ignoreStaticMethodsFromNonStaticInnerClass" value="false" />
<option name="onlyReportStaticMethods" value="false" />
</inspection_tool>
<inspection_tool class="MethodOverloadsParentMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MethodOverridesInaccessibleMethodOfSuper" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MethodOverridesStaticMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MethodReturnAlwaysConstant" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MethodWithMultipleLoops" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MigrateAssertToMatcherAssert" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MisorderedAssertEqualsArguments" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="Misra" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MissingDeprecatedAnnotation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MisspelledEquals" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MisspelledMethodName" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ModuleWithTooFewClasses" enabled="true" level="WARNING" enabled_by_default="true">
<option name="limit" value="10" />
</inspection_tool>
<inspection_tool class="ModuleWithTooManyClasses" enabled="true" level="WARNING" enabled_by_default="true">
<option name="limit" value="100" />
</inspection_tool>
<inspection_tool class="MultipleExceptionsDeclaredOnTestMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MultipleReturnPointsPerMethod" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreGuardClauses" value="false" />
<option name="ignoreEqualsMethod" value="false" />
<option name="m_limit" value="1" />
</inspection_tool>
<inspection_tool class="MultipleTopLevelClassesInFile" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MultiplyOrDivideByPowerOfTwo" enabled="true" level="WARNING" enabled_by_default="true">
<option name="checkDivision" value="false" />
</inspection_tool>
<inspection_tool class="NakedNotify" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NativeMethods" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NegatedConditional" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreNegatedNullComparison" value="true" />
</inspection_tool>
<inspection_tool class="NegatedConditionalExpression" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NegatedEqualityExpression" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NegatedIfElse" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreNegatedNullComparison" value="true" />
<option name="m_ignoreNegatedZeroComparison" value="false" />
</inspection_tool>
<inspection_tool class="NegativelyNamedBooleanVariable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NestedAssignment" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NestedConditionalExpression" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NestedSwitchStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NestedSynchronizedStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NestedTryStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NestingDepth" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="5" />
</inspection_tool>
<inspection_tool class="NewExceptionWithoutArguments" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NewGroovyClassNamingConvention" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NewMethodNamingConvention" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NoConstructorMigration" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="NonBooleanMethodNameMayNotStartWithQuestion" enabled="true" level="WARNING" enabled_by_default="true">
<option name="questionString" value="are,can,check,contains,could,endsWith,equals,has,is,matches,must,shall,should,startsWith,was,were,will,would" />
<option name="ignoreBooleanMethods" value="false" />
<option name="onlyWarnOnBaseMethods" value="true" />
</inspection_tool>
<inspection_tool class="NonCommentSourceStatements" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="30" />
</inspection_tool>
<inspection_tool class="NonExceptionNameEndsWithException" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonExhaustiveWhenStatementMigration" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonFinalClone" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonFinalFieldInImmutable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonFinalFieldOfException" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonFinalGuard" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonFinalStaticVariableUsedInClassInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonFinalUtilityClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonNullableBooleanPropertyInExternalInterface" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonProtectedConstructorInAbstractClass" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreNonPublicClasses" value="false" />
</inspection_tool>
<inspection_tool class="NonPublicClone" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonReproducibleMathCall" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonSerializableObjectBoundToHttpSession" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonSerializableObjectPassedToObjectStream" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonSerializableWithSerializationMethods" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonShortCircuitBoolean" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonStaticFinalLogger" enabled="true" level="WARNING" enabled_by_default="true">
<option name="loggerClassName" value="java.util.logging.Logger,org.slf4j.Logger,org.apache.commons.logging.Log,org.apache.log4j.Logger,org.apache.logging.log4j.Logger" />
</inspection_tool>
<inspection_tool class="NonSynchronizedMethodOverridesSynchronizedMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NonThreadSafeLazyInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NoopMethodInAbstractClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NotifyCalledOnCondition" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NotifyWithoutCorrespondingWait" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NullThrown" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NumericToString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OCDFA" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="OCInconsistentNaming" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="OCUnusedIncludeDirective" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ObjectAllocationInLoop" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ObjectInstantiationInEqualsHashCode" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ObjectNotify" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ObjectToString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ObsoleteCollection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreRequiredObsoleteCollectionTypes" value="true" />
</inspection_tool>
<inspection_tool class="ObsoleteExperimentalCoroutines" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ObsoleteKotlinJsPackages" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="OctalAndDecimalIntegersMixed" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OnDemandImport" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OptionalContainsCollection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OverloadedMethodsWithSameNumberOfParameters" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreInconvertibleTypes" value="true" />
</inspection_tool>
<inspection_tool class="OverloadedVarargsMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OverlyComplexArithmeticExpression" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="6" />
</inspection_tool>
<inspection_tool class="OverlyComplexBooleanExpression" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="3" />
<option name="m_ignorePureConjunctionsDisjunctions" value="true" />
</inspection_tool>
<inspection_tool class="OverlyLargePrimitiveArrayInitializer" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="64" />
</inspection_tool>
<inspection_tool class="OverlyLongLambda" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OverlyStrongTypeCast" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreInMatchingInstanceof" value="false" />
</inspection_tool>
<inspection_tool class="OverridableMethodCallDuringObjectConstruction" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OverriddenMethodCallDuringObjectConstruction" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OverrideDeprecatedMigration" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="PackageDotHtmlMayBePackageInfo" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PackageInMultipleModules" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PackageNamingConvention" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_regex" value="[a-z]*" />
<option name="m_minLength" value="3" />
<option name="m_maxLength" value="16" />
</inspection_tool>
<inspection_tool class="PackageWithTooFewClasses" enabled="true" level="WARNING" enabled_by_default="true">
<option name="limit" value="3" />
</inspection_tool>
<inspection_tool class="PackageWithTooManyClasses" enabled="true" level="WARNING" enabled_by_default="true">
<option name="limit" value="50" />
</inspection_tool>
<inspection_tool class="ParameterHidingMemberVariable" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreInvisibleFields" value="true" />
<option name="m_ignoreStaticMethodParametersHidingInstanceFields" value="true" />
<option name="m_ignoreForConstructors" value="false" />
<option name="m_ignoreForPropertySetters" value="false" />
<option name="m_ignoreForAbstractMethods" value="false" />
</inspection_tool>
<inspection_tool class="ParameterNameDiffersFromOverriddenParameter" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreSingleCharacterNames" value="false" />
<option name="m_ignoreOverridesOfLibraryMethods" value="false" />
</inspection_tool>
<inspection_tool class="ParameterNamingConvention" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_regex" value="[a-z][A-Za-z\d]*" />
<option name="m_minLength" value="1" />
<option name="m_maxLength" value="20" />
</inspection_tool>
<inspection_tool class="ParameterTypePreventsOverriding" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ParameterizedParametersStaticCollection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ParametersPerConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ParametersPerMethod" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="5" />
</inspection_tool>
<inspection_tool class="PluginXmlI18n" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PointlessIndexOfComparison" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PrivateMemberAccessBetweenOuterAndInnerClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ProblematicVarargsMethodOverride" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ProblematicWhitespace" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ProhibitJvmOverloadsOnConstructorsOfAnnotationClassesMigration" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ProhibitRepeatedUseSiteTargetAnnotationsMigration" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ProhibitTypeParametersForLocalVariablesMigration" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ProhibitUseSiteTargetAnnotationsOnSuperTypesMigration" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="PropertyValueSetToItself" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ProtectedField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ProtectedInnerClass" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreEnums" value="false" />
<option name="ignoreInterfaces" value="false" />
</inspection_tool>
<inspection_tool class="PublicApiImplicitType" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PublicConstructorInNonPublicClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PublicFieldAccessedInSynchronizedContext" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PublicInnerClass" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreEnums" value="false" />
<option name="ignoreInterfaces" value="false" />
</inspection_tool>
<inspection_tool class="PublicStaticArrayField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PublicStaticCollectionField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="QuestionableName" enabled="true" level="WARNING" enabled_by_default="true">
<option name="nameString" value="aa,abc,bad,bar,bar2,baz,baz1,baz2,baz3,bb,blah,bogus,bool,cc,dd,defau1t,dummy,dummy2,ee,fa1se,ff,foo,foo1,foo2,foo3,foobar,four,fred,fred1,fred2,gg,hh,hello,hello1,hello2,hello3,ii,nu11,one,silly,silly2,string,two,that,then,three,whi1e,var" />
</inspection_tool>
<inspection_tool class="RandomDoubleForRandomInteger" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ReadObjectAndWriteObjectPrivate" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ReadObjectInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ReadResolveAndWriteReplaceProtected" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RecordStoreResource" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantFieldInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantImplements" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreSerializable" value="false" />
<option name="ignoreCloneable" value="false" />
</inspection_tool>
<inspection_tool class="RedundantInnerClassModifier" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantLabelMigration" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantNotNullExtensionReceiverOfInline" enabled="true" level="INFORMATION" enabled_by_default="true" />
<inspection_tool class="RedundantValueArgument" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="RegExpAnonymousGroup" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ReplaceCollectionCountWithSize" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="ReplaceNotNullAssertionWithElvisReturn" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="RestrictReturnStatementTargetMigration" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ResultOfObjectAllocationIgnored" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ResultSetIndexZero" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ReturnNull" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_reportObjectMethods" value="true" />
<option name="m_reportArrayMethods" value="true" />
<option name="m_reportCollectionMethods" value="true" />
<option name="m_ignorePrivateMethods" value="false" />
</inspection_tool>
<inspection_tool class="ReturnOfInnerClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ReturnThis" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RuntimeExec" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RuntimeExecWithNonConstantString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SafeLock" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SamePackageImport" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SerialPersistentFieldsWithWrongSignature" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SerializableCtor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SerializableInnerClassHasSerialVersionUIDField" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreAnonymousInnerClasses" value="false" />
<option name="superClassString" value="java.awt.Component" />
</inspection_tool>
<inspection_tool class="SerializableInnerClassWithNonSerializableOuterClass" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreAnonymousInnerClasses" value="false" />
<option name="superClassString" value="java.awt.Component" />
</inspection_tool>
<inspection_tool class="SerializableStoresNonSerializable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SerializableWithUnconstructableAncestor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SetReplaceableByEnumSet" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SharedThreadLocalRandom" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SignalWithoutCorrespondingAwait" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SimpleDateFormatWithoutLocale" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SimplifiableAnnotation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SimplifiableEqualsExpression" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SingleCharacterStartsWith" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="Singleton" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SleepWhileHoldingLock" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SocketResource" enabled="true" level="WARNING" enabled_by_default="true">
<option name="insideTryAllowed" value="false" />
</inspection_tool>
<inspection_tool class="StandardVariableNames" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticCallOnSubclass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticCollection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreWeakCollections" value="false" />
</inspection_tool>
<inspection_tool class="StaticFieldReferenceOnSubclass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticGuardedByInstance" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticImport" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticInheritance" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticMethodOnlyUsedInOneClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticNonFinalField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticPseudoFunctionalStyleMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticVariableInitialization" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignorePrimitives" value="false" />
</inspection_tool>
<inspection_tool class="StaticVariableUninitializedUse" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignorePrimitives" value="false" />
</inspection_tool>
<inspection_tool class="StringBufferField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StringBufferMustHaveInitialCapacity" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StringBufferToStringInConcatenation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StringConcatenation" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreAsserts" value="false" />
<option name="ignoreSystemOuts" value="false" />
<option name="ignoreSystemErrs" value="false" />
<option name="ignoreThrowableArguments" value="false" />
<option name="ignoreConstantInitializers" value="false" />
<option name="ignoreInTestCode" value="false" />
<option name="ignoreInToString" value="false" />
</inspection_tool>
<inspection_tool class="StringConcatenationArgumentToLogCall" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StringConcatenationInFormatCall" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StringConcatenationInMessageFormatCall" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StringConcatenationMissingWhitespace" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StringReplaceableByStringBuffer" enabled="true" level="WARNING" enabled_by_default="true">
<option name="onlyWarnOnLoop" value="true" />
</inspection_tool>
<inspection_tool class="StringToUpperWithoutLocale" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StringTokenizer" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SubtractionInCompareTo" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SuperTearDownInFinally" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SuspiciousArrayCast" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SuspiciousLiteralUnderscore" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SuspiciousLocalesLanguages" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SwitchStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SwitchStatementDensity" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="20" />
</inspection_tool>
<inspection_tool class="SwitchStatementWithTooManyBranches" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="10" />
</inspection_tool>
<inspection_tool class="SynchronizationOnStaticField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SynchronizeOnLock" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SynchronizeOnThis" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SynchronizedMethod" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_includeNativeMethods" value="true" />
<option name="ignoreSynchronizedSuperMethods" value="true" />
</inspection_tool>
<inspection_tool class="SynchronizedOnLiteralObject" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SystemExit" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SystemGC" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SystemGetenv" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SystemOutErr" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SystemProperties" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SystemSetSecurityManager" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TestCaseWithConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TestCaseWithNoTestMethods" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TestInProductSource" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TestMethodWithoutAssertion" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TestOnlyProblems" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ThreadDeathRethrown" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ThreadDumpStack" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ThreadLocalNotStaticFinal" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ThreadPriority" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ThreadStartInConstruction" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ThreadStopSuspendResume" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ThreadYield" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ThreadingConcurrency" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ThreeNegationsPerMethod" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreInEquals" value="true" />
<option name="ignoreInAssert" value="false" />
</inspection_tool>
<inspection_tool class="ThrowCaughtLocally" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreRethrownExceptions" value="false" />
</inspection_tool>
<inspection_tool class="ThrownExceptionsPerMethod" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="3" />
</inspection_tool>
<inspection_tool class="ThrowsRuntimeException" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TimeToString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TodoComment" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TooBroadCatch" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TooBroadThrows" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TransientFieldInNonSerializableClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TransientFieldNotInitialized" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TypeMayBeWeakened" enabled="true" level="WARNING" enabled_by_default="true">
<option name="useRighthandTypeAsWeakestTypeInAssignments" value="true" />
<option name="useParameterizedTypeForCollectionMethods" value="true" />
<option name="doNotWeakenToJavaLangObject" value="true" />
<option name="onlyWeakentoInterface" value="true" />
</inspection_tool>
<inspection_tool class="TypeParameterExtendsFinalClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UncheckedExceptionClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnconditionalWait" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UndeclaredTests" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnknownGuard" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessarilyQualifiedStaticUsage" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreStaticFieldAccesses" value="false" />
<option name="m_ignoreStaticMethodCalls" value="false" />
<option name="m_ignoreStaticAccessFromStaticContext" value="false" />
</inspection_tool>
<inspection_tool class="UnnecessarilyQualifiedStaticallyImportedElement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryConstantArrayCreationExpression" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryExplicitNumericCast" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryFinalOnLocalVariableOrParameter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryInheritDoc" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryJavaDocLink" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreInlineLinkToSuper" value="false" />
</inspection_tool>
<inspection_tool class="UnnecessaryQualifierForThis" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessarySuperConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessarySuperQualifier" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryThis" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnqualifiedInnerClassAccess" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreReferencesToLocalInnerClasses" value="true" />
</inspection_tool>
<inspection_tool class="UnsecureRandomNumberGeneration" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnstableTypeUsedInSignature" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedDefine" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedLibrary" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedMainParameter" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="UpperCaseFieldNameNotConstant" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UseEllipsisInPropertyInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UseOfAWTPeerClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UseOfAnotherObjectsPrivateField" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreSameClass" value="false" />
<option name="ignoreEquals" value="false" />
</inspection_tool>
<inspection_tool class="UseOfClone" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UseOfJDBCDriverClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UseOfObsoleteAssert" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UseOfObsoleteDateTimeApi" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UseOfProcessBuilder" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UseOfPropertiesAsHashtable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UseOfSunClasses" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UsePlatformProcessAwaitExit" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="UtilityClass" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignorableAnnotations">
<value />
</option>
</inspection_tool>
<inspection_tool class="UtilityClassCanBeEnum" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UtilityClassWithPublicConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UtilityClassWithoutPrivateConstructor" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignorableAnnotations">
<value />
</option>
<option name="ignoreClassesWithOnlyMain" value="false" />
</inspection_tool>
<inspection_tool class="VariableNotUsedInsideIf" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="VolatileArrayField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WaitCalledOnCondition" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WaitNotInLoop" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WaitNotifyNotInSynchronizedContext" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WaitOrAwaitWithoutTimeout" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WaitWithoutCorrespondingNotify" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WarningOnMainUnusedParameterMigration" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="WhenWithOnlyElse" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="ZeroLengthArrayInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
+46
View File
@@ -0,0 +1,46 @@
# Titanboard
![Screenshot of the program at work](screenshot.png "Titanboard")
Titanboard is an input method for physical keyboard phones, supporting as low as Android 1.5
Cupcake.
## Features
- Support for very low Android versions, down to 1.5
- Nimble and small; I set out to have the APK be at most 100 kB
- Each physical key can have several additional symbols, which the user can cycle by long pressing
a given key. This also applies for alternate (alt) symbols
- 11 supported languages, which are trivial to add yourself (consult res/xml/language_keymaps.xml)
- No hard to memorize key combinations; reasonably uses what the physical keyboard has to offer
- Clipboard management
- Fixes for PKB quirks
- Reasonably customizable
## Planned features
- For now, Titanboard is an abandoned, but stable project.
## Supported languages
- English
- German
- French
- Italian
- Spanish
- Polish
- Russian (Phonetic)
- Greek (polytonic diacritics under Q)
- Arabic (Phonetic)
- Hebrew
- Czech
## Supported devices
Thus far, the supported devices are:
- Unihertz Titan
- Unihertz Titan Slim
- Unihertz Titan Pocket
It's very easy to add new devices as well, just look into res/xml/device_keymaps.xml.
-98
View File
@@ -1,98 +0,0 @@
# Titanboard user experience
Keyboards are an integral part of the functioning of any computer, including the smartphone. Care
should then be taken to design keyboards to be as intuitive, easy to use, and unobstructed as
possible.
The Unihertz Titan already has a physical keyboard. It's poorly designed in many aspects. Mainly, it
isn't very reminiscent of a regular QWERTY keyboard which people are used to:
1. The worst part is arguably the shift and alt keys being at the top of the keyboard, instead of on
the bottom.
2. The spacebar key is on the same row as the bottom QWERTY alphabet keys.
3. The numerical keys, which are some of the most commonly used keys in phones (passwords, PIN
codes)
and in regular language are right below the alt key, which makes typing them very awkward. One
could
also argue that the numpad is in a "reversed" layout, but this is consistent with the usual
layout
found on phones.
4. It lacks some commonly used symbols: ';', '&', '=', '<', '>', '$'. Other than that, it lacks some
other
symbols from regular QWERTY keyboards.
5. There is no extra alt-gr key to type in localized letters.
6. In my opinion, it also lacks a control key, but that is unnecessary on smartphones, unless you
use the terminal or modify spreadsheets. However, providing appropriate UI elements for the
actions
possible on regular computers should be the responsibility of the application. Smartphones are
simply different devices, with smaller screens and less space for complexity.
7. Emojis.
However, when designing a keyboard app for it one should already use all pre-existing quirks and
decisions which materialized in the physical keyboard. For example, the alt key should be used for
the alternate characters printed on the keyboard, the shift key should give us uppercase characters
and so on.
# Solutions
Here are my solutions for the above listed problems:
1. This is solvable with shift/alt latching. This makes it so when you press these keys once, the
next key pressed will be either uppercased or the alternate key visible on the physical button.
Additionally, the shift and alt key can be toggled by double-clicking in a fast succession,
similar
to regular visual keyboards on Android.
2. This is unfortunately unsolvable with software.
3. See 1., latching the keys solves this issue.
4. This requires a visual keyboard, the UI of which is discussed later. Why does this require a
visual keyboard, and not some alternate "symbol" mode, perhaps accessible with some keyboard
shortcuts? That would make the learning curve of the keyboard higher, and it would be less
intuitive. One should make sure not to complicate the physical keyboard any more than it already
is,
and use all of its idiosyncrasies but add no more.
5. Similar to regular visual keyboards on Android, a diacritic/alternate key is assigned to each key
according to the language. Long pressing a key will print it. Ideally, there should also be a
popup
menu allowing for several characters under one key. Cycling through the keys would use the
shift/alt
keys (so that no matter what key you use, the other hand can cycle through) and selecting it
would
happen on release of the key.
6. I have no plans on adding a control key, as it is largely unnecessary on phones.
7. I do not really use emojis, but I am aware that they are used by the majority of people. Most
IM apps provide their own emoji list, so this isn't a priority. Designing a good UX for it is
also
hard on the Titan.
Taking into account typing ergonomics and pre-existing UX choices of other keyboard designers, this
input method should make typing on the Unihertz Titan a pleasant experience. Other than fixing the
fundamental issues, the keyboard should also:
1. Provide a way of managing the clipboard, or at the very least pasting things with an UI element.
This is to make the usage intuitive and not introduce any additional keys such as a ctrl key.
2. Autocorrect, language switching, auto caps and periods.
3. Some minimal customization, but just enough not to overwhelm the user.
For the Unihertz Titan Pocket/Slim, the sym key will be used as the alt+gr key, providing diacritics
and such. This is because in the official documentation and implementation the alt key is designed
for the alternate keys printed on the buttons.
# User interface
Google recommends that visual keyboards should not occupy more than half of the screen estate.
However, most phones have long, vertical screens, which means essentially that the keyboard will
be a square, and so will the remaining view. On the Titan, the screen is already square, so the
remaining space will be proportionally much smaller. Therefore, the keyboard should occupy very
minimal screen estate, as to allow the user to see the application they are interacting with.
For this reason, I make sure the keyboard occupies 20–25 % screen estate. This is enough for a
symbol bar and a suggestions bar, as well as some additional buttons such as language change or
settings.
Both the symbol bar and suggestions bar are scrollable. The suggestions being scrollable is a
feature on some visual keyboards already, however, I have come to the decision to make this for the
symbol bar as well. An expandable menu would make the keyboard occupy half, or more of the screen.
A scrollable bar is more intuitive and can hold an indefinite amount of characters, possibly also
user-defined.
A simple divider between the suggestion and symbol bar is added to avoid accidental clicks.
+18 -8
View File
@@ -3,24 +3,33 @@ plugins {
} }
android { android {
namespace 'cfpi_fpsi.eu.titanboard' namespace 'eur.cfpifpsi.titanboard'
compileSdk 34 compileSdk 35
defaultConfig { defaultConfig {
applicationId "cfpi_fpsi.eu.titanboard" applicationId 'eur.cfpifpsi.titanboard'
minSdk 19 minSdk 3
targetSdk 34 targetSdk 35
versionCode 21 versionCode 40
versionName "0.2.1" versionName '0.4'
} }
buildTypes { buildTypes {
release { release {
minifyEnabled true minifyEnabled true
shrinkResources true shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
debug {
minifyEnabled false
shrinkResources false
applicationIdSuffix '.debug'
versionNameSuffix '-DEBUG'
} }
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
@@ -29,3 +38,4 @@ android {
+37 -9
View File
@@ -1,38 +1,66 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_CLIPBOARD" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application <application
android:icon="@drawable/icon" android:icon="@drawable/ic_app"
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@drawable/ic_app_round"
android:supportsRtl="true" android:supportsRtl="true"
tools:targetApi="31"> tools:targetApi="31">
<service <service
android:name=".IME" android:name=".TitanboardIME"
android:directBootAware="true" android:directBootAware="true"
android:exported="false" android:exported="false"
android:icon="@drawable/icon" android:label="@string/app_name"
android:label="@string/ime_label"
android:permission="android.permission.BIND_INPUT_METHOD"> android:permission="android.permission.BIND_INPUT_METHOD">
<intent-filter> <intent-filter>
<action android:name="android.view.InputMethod" /> <action android:name="android.view.InputMethod" />
</intent-filter> </intent-filter>
<meta-data <meta-data
android:name="android.view.im" android:name="android.view.im"
android:resource="@xml/method" /> android:resource="@xml/method" />
</service> </service>
<activity <activity
android:name=".PreferencesActivity" android:name=".SettingsActivity"
android:exported="true" android:exported="false"
android:label="@string/preferences_label"> android:label="@string/preferences_name">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".ManualActivity"
android:exported="true"
android:label="@string/manual_name">
<intent-filter>
<action android:name="eur.cfpifpsi.titanboard.intent.SHOW_MANUAL" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application> </application>
</manifest> </manifest>
@@ -1,208 +0,0 @@
/*
* An input method for the Unihertz Titan/physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
package cfpi_fpsi.eu.titanboard;
import android.annotation.SuppressLint;
import android.inputmethodservice.InputMethodService;
import android.os.Vibrator;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
// TODO: Implement long-click for extra keys.
// TODO: Implement auto-correct.
// TODO: Implement auto-caps.
// TODO: Implement device recognition and a keymap picking based on it.
// TODO: Make preferences accessible and saveable.
public class IME extends InputMethodService {
private MetaKeyManager mMetaKeyManager;
private KeymapManager mKeymapManager;
private Deque<String> mLanguageQueue = new ArrayDeque<>();
private String mCurrentLanguage;
private LinearLayout mInputView;
private Vibrator mVibrator;
@Override
public void onCreate() {
super.onCreate();
// Ideally, detect the device, but for now, load all available keymaps.
mKeymapManager = new KeymapManager(getResources().getXml(R.xml.keymaps_titan));
Collections.addAll(mLanguageQueue, mKeymapManager.getAvailableLanguages());
mCurrentLanguage = mLanguageQueue.pop();
mLanguageQueue.addLast(mCurrentLanguage);
mVibrator = (Vibrator) getBaseContext().getSystemService(VIBRATOR_SERVICE);
mMetaKeyManager = new MetaKeyManager(mVibrator);
}
@SuppressLint("InflateParams")
@Override
public void onInitializeInterface() {
mInputView = (LinearLayout) getLayoutInflater().inflate(R.layout.input, null);
}
@Override
public boolean onEvaluateFullscreenMode() {
// This is to override the default behaviour, which seems to do more than detect landscape
// mode.
return false;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
mMetaKeyManager.updateMetaStateDown(keyCode, event);
updateMetaStateTextView();
InputConnection conn = getCurrentInputConnection();
if (conn == null) {
return false;
}
switch (keyCode) {
// If the keyboard is visible, hide it, otherwise, let the system handle the key.
case KeyEvent.KEYCODE_BACK:
if (isInputViewShown()) {
requestHideSelf(0);
return true;
}
return false;
case KeyEvent.KEYCODE_DEL:
case KeyEvent.KEYCODE_ENTER:
mMetaKeyManager.adjustMetaStateAfterKeypress();
return false;
case KeyEvent.KEYCODE_SPACE:
conn.commitText(" ", 1);
mMetaKeyManager.adjustMetaStateAfterKeypress();
return true;
default:
String key = getKey(keyCode);
if (key == null) {
return false;
}
Log.d("STATE", "Sending key: " + key + " whose keyCode is: " + keyCode);
conn.commitText(key, 1);
mMetaKeyManager.adjustMetaStateAfterKeypress();
return true;
}
}
public boolean onKeyUp(int keyCode, KeyEvent event) {
mMetaKeyManager.updateMetaStateUp(keyCode, event);
updateMetaStateTextView();
return false;
}
@Override
public View onCreateInputView() {
return mInputView;
}
@Override
public void onStartInput(EditorInfo info, boolean restarting) {
super.onStartInput(info, restarting);
updateMetaStateTextView();
}
@Override
public void onFinishInput() {
mMetaKeyManager.resetMetaKeyState();
updateMetaStateTextView();
}
private String getKey(int keyCode) {
if (mMetaKeyManager.getAlt().isActive()) {
return mKeymapManager.getKeyAlt(mCurrentLanguage, keyCode);
}
if (mMetaKeyManager.getSym().isActive() && mMetaKeyManager.getShift().isActive()) {
return mKeymapManager.getKeyShiftSym(mCurrentLanguage, keyCode);
}
if (mMetaKeyManager.getSym().isActive()) {
return mKeymapManager.getKeySym(mCurrentLanguage, keyCode);
}
if (mMetaKeyManager.getShift().isActive()) {
return mKeymapManager.getKeyShift(mCurrentLanguage, keyCode);
}
// Nothing active.
return mKeymapManager.getKeyNone(mCurrentLanguage, keyCode);
}
@SuppressLint("SetTextI18n")
private void updateMetaStateTextView() {
TextView shiftStateView = mInputView.findViewById(R.id.state_shift);
TextView altStateView = mInputView.findViewById(R.id.state_alt);
TextView symStateView = mInputView.findViewById(R.id.state_sym);
Button languageButton = mInputView.findViewById(R.id.language_button);
shiftStateView.setText(getStringForMetaKey(
mMetaKeyManager.getShift(), "aa", "Aa", "AA"));
altStateView.setText(getStringForMetaKey(
mMetaKeyManager.getAlt(), "..", "#?", "#!"));
symStateView.setText(getStringForMetaKey(
mMetaKeyManager.getSym(), "..", "þπ", "ÞΩ"));
languageButton.setText(mKeymapManager.getShortLabel(mCurrentLanguage));
}
private String getStringForMetaKey(MetaKeyManager.MetaKey key, String normal, String latched, String pressed) {
if (key.isLatched()) {
return latched;
}
if (key.isLocked() || key.isPressed()) {
return pressed;
}
return normal;
}
public void onInputViewLanguageClick(View v) {
mCurrentLanguage = mLanguageQueue.pop();
mLanguageQueue.addLast(mCurrentLanguage);
updateMetaStateTextView();
}
/**
* @noinspection unused
*/
public void onInputViewKeyClick(View v) {
Button key = (Button) v;
InputConnection conn = getCurrentInputConnection();
if (conn != null) {
conn.commitText(key.getText(), 1);
}
}
}
@@ -1,203 +0,0 @@
/*
* An input method for the Unihertz Titan/physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
package cfpi_fpsi.eu.titanboard;
import android.util.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.HashMap;
public class KeymapManager {
// Yeah... I know this is a little wacky.
private final HashMap<String, Keymap> mKeymaps = new HashMap<>();
public KeymapManager(XmlPullParser parser) {
Keymap keymap = null;
String lang = null, shortLabel = null, label = null;
int code = 0;
String none = null, shift = null, alt = null, sym = null, shiftSym = null;
try {
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
int type = parser.getEventType();
if (type == XmlPullParser.START_TAG && parser.getName().equals("keymap")) {
keymap = new Keymap();
lang = null;
shortLabel = null;
label = null;
for (int i = 0; i < parser.getAttributeCount(); ++i) {
switch (parser.getAttributeName(i)) {
case "lang":
lang = parser.getAttributeValue(i);
break;
case "short-label":
shortLabel = parser.getAttributeValue(i);
break;
case "label":
label = parser.getAttributeValue(i);
break;
}
}
Log.d("STATE", "BEGINNING OF KEYMAP: " + label);
} else if (type == XmlPullParser.END_TAG && parser.getName().equals("keymap")) {
Log.d("STATE", "END OF KEYMAP: " + label);
keymap.mShortLabel = shortLabel;
keymap.mLabel = label;
mKeymaps.put(lang, keymap);
}
if (type == XmlPullParser.START_TAG && parser.getName().equals("key")) {
none = null;
shift = null;
alt = null;
sym = null;
shiftSym = null;
for (int i = 0; i < parser.getAttributeCount(); ++i) {
String attr = parser.getAttributeName(i);
switch (attr) {
case "code":
code = Integer.parseInt(parser.getAttributeValue(i));
break;
case "none":
none = parser.getAttributeValue(i);
break;
case "shift":
shift = parser.getAttributeValue(i);
break;
case "alt":
alt = parser.getAttributeValue(i);
break;
case "sym":
sym = parser.getAttributeValue(i);
break;
case "shiftSym":
shiftSym = parser.getAttributeValue(i);
break;
}
}
Log.d("STATE", "Key code: " + code + ". Value sym: " + sym);
} else if (type == XmlPullParser.END_TAG && parser.getName().equals("key")) {
keymap.put(code, new Key(none, shift, alt, sym, shiftSym));
}
parser.next();
}
} catch (XmlPullParserException | IOException e) {
throw new RuntimeException(e);
}
}
public String[] getAvailableLanguages() {
return mKeymaps.keySet().toArray(new String[0]);
}
public String getShortLabel(String lang) {
Keymap keymap = mKeymaps.get(lang);
if (keymap != null) {
return keymap.mShortLabel;
} else {
return null;
}
}
public String getKeyNone(String lang, int keyCode) {
Key key = getKey(lang, keyCode);
if (key != null) {
return key.mNone;
} else {
return null;
}
}
public String getKeyShift(String lang, int keyCode) {
Key key = getKey(lang, keyCode);
if (key != null) {
return key.mShift;
} else {
return null;
}
}
public String getKeyAlt(String lang, int keyCode) {
Key key = getKey(lang, keyCode);
if (key != null) {
return key.mAlt;
} else {
return null;
}
}
public String getKeySym(String lang, int keyCode) {
Key key = getKey(lang, keyCode);
if (key != null) {
return key.mSym;
} else {
return null;
}
}
public String getKeyShiftSym(String lang, int keyCode) {
Key key = getKey(lang, keyCode);
if (key != null) {
return key.mShiftSym;
} else {
return null;
}
}
public Key getKey(String lang, int keyCode) {
HashMap<Integer, Key> keymap = mKeymaps.get(lang);
if (keymap != null) {
Key key = keymap.get(keyCode);
if (key != null) {
return key;
} else {
return null;
}
} else {
return null;
}
}
private static class Keymap extends HashMap<Integer, Key> {
public String mShortLabel;
public String mLabel;
}
public static class Key {
public String mNone;
public String mShift;
public String mAlt;
public String mSym;
public String mShiftSym;
public Key(String none, String shift, String alt, String sym, String shiftSym) {
mNone = none;
mShift = shift;
mAlt = alt;
mSym = sym;
mShiftSym = shiftSym;
}
}
}
@@ -1,198 +0,0 @@
/*
* An input method for the Unihertz Titan/physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
package cfpi_fpsi.eu.titanboard;
import android.os.Vibrator;
import android.view.KeyEvent;
public class MetaKeyManager {
public MetaKey mShift = new MetaKey();
public MetaKey mAlt = new MetaKey();
public MetaKey mSym = new MetaKey();
private Vibrator mVibrator;
public MetaKeyManager(Vibrator vibrator) {
mVibrator = vibrator;
}
public void updateMetaStateDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_ALT_LEFT:
case KeyEvent.KEYCODE_ALT_RIGHT:
updateMetaKeyDown(mAlt, event);
break;
case KeyEvent.KEYCODE_SHIFT_LEFT:
case KeyEvent.KEYCODE_SHIFT_RIGHT:
updateMetaKeyDown(mShift, event);
break;
case KeyEvent.KEYCODE_SYM:
updateMetaKeyDown(mSym, event);
break;
default:
break;
}
}
void updateMetaKeyDown(MetaKey key, KeyEvent event) {
// Perhaps this function should be renamed to something...
key.setUsed(false);
key.setPressed(true);
key.setLatched(key.isLocked() ? key.isLocked() : key.isLatched());
key.setLocked(false);
if (mShift.isPressed() && mAlt.isPressed()) {
mSym.setLatched(!mSym.isLatched());
mShift.setUsed(true);
mAlt.setUsed(true);
mVibrator.vibrate(20);
}
}
public void updateMetaStateUp(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_ALT_LEFT:
case KeyEvent.KEYCODE_ALT_RIGHT:
updateMetaKeyUp(mAlt, event);
break;
case KeyEvent.KEYCODE_SHIFT_LEFT:
case KeyEvent.KEYCODE_SHIFT_RIGHT:
updateMetaKeyUp(mShift, event);
break;
case KeyEvent.KEYCODE_SYM:
updateMetaKeyUp(mSym, event);
break;
default:
break;
}
}
public void updateMetaKeyUp(MetaKey key, KeyEvent event) {
if (event.getEventTime() - key.getKeyUpTime() < 400) {
key.setLocked(true);
mVibrator.vibrate(30);
}
key.setKeyUpTime(event.getEventTime());
key.setPressed(false);
key.setLatched(!key.isLatched() && !key.isLocked() && !key.isUsed());
}
public void adjustMetaStateAfterKeypress() {
adjustMetaStateHelper(mShift);
adjustMetaStateHelper(mAlt);
adjustMetaStateHelper(mSym);
}
public void adjustMetaStateHelper(MetaKey key) {
if (key.isLatched()) {
key.setLatched(false);
key.setUsed(false);
}
if (key.isPressed()) {
key.setUsed(true);
}
}
public void resetMetaKeyState() {
mShift.resetState();
mAlt.resetState();
mSym.resetState();
}
public MetaKey getShift() {
return mShift;
}
public MetaKey getAlt() {
return mAlt;
}
public MetaKey getSym() {
return mSym;
}
public static class MetaKey {
private boolean mPressed = false;
private boolean mLatched = false;
private boolean mLocked = false;
private boolean mUsed = false;
private long mKeyUpTime;
public void resetState() {
setPressed(false);
setLatched(false);
setLocked(false);
setUsed(false);
}
public boolean isPressed() {
return mPressed;
}
public void setPressed(boolean state) {
mPressed = state;
}
public boolean isLatched() {
return mLatched;
}
public void setLatched(boolean state) {
mLatched = state;
}
public boolean isLocked() {
return mLocked;
}
public void setLocked(boolean state) {
mLocked = state;
}
public boolean isUsed() {
return mUsed;
}
public void setUsed(boolean state) {
mUsed = state;
}
public boolean isActive() {
return isPressed() || isLocked() || isLatched();
}
public long getKeyUpTime() {
return mKeyUpTime;
}
public void setKeyUpTime(long time) {
mKeyUpTime = time;
}
public enum MetaKeyType {
ALT,
SHIFT,
SYM
}
}
}
@@ -0,0 +1,85 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard;
import android.annotation.TargetApi;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Build;
import java.util.ArrayList;
import eur.cfpifpsi.titanboard.views.InputView;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class ClipManager implements ClipboardManager.OnPrimaryClipChangedListener {
private final PreferenceHolder mPreferenceHolder;
private final InputView mInputView;
private final ClipboardManager mClipboardManager;
private final ArrayList<ClipData.Item> mClips;
private long mLastCallbackTimestamp;
public ClipManager(Context context, PreferenceHolder preferenceHolder, InputView inputView) {
mPreferenceHolder = preferenceHolder;
mInputView = inputView;
mClips = new ArrayList<>(mPreferenceHolder.getClipboardSize());
mClipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
mClipboardManager.addPrimaryClipChangedListener(this);
}
@Override
public void onPrimaryClipChanged() {
// The callback can be launched several times at once, so we have to check if it isn't the
// same clipboard data.
if (System.currentTimeMillis() - mLastCallbackTimestamp >= 100) {
mLastCallbackTimestamp = System.currentTimeMillis();
ClipData clip = mClipboardManager.getPrimaryClip();
if (clip != null) {
int itemCount = clip.getItemCount();
for (int i = 0; i < itemCount; ++i) {
ClipData.Item item = clip.getItemAt(i);
// We do not support non-text clips for now, so all we add are text clips.
if (item.getText() != null) {
mClips.add(0, item);
}
if (mClips.size() > mPreferenceHolder.getClipboardSize()) {
mClips.remove(mClips.size() - 1);
}
}
mInputView.setClips(mClips);
}
}
}
public boolean isEmpty() {
return mClips.isEmpty();
}
public void removeItem(ClipData.Item item) {
mClips.remove(item);
}
public ClipData.Item getItemAt(int index) {
return mClips.get(index);
}
}
@@ -0,0 +1,35 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard;
import android.app.Activity;
import android.os.Bundle;
import android.text.Html;
import android.widget.TextView;
public class ManualActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manual);
// Convert the HTML manual into a TextView format
TextView tv = findViewById(R.id.manual_text);
tv.setText(Html.fromHtml(getString(R.string.user_manual_html)));
}
}
@@ -0,0 +1,83 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard;
import android.view.KeyEvent;
public class MetaKey {
private static final long TOGGLE_DURATION = 400L;
private boolean mPressed;
private boolean mLatched;
private boolean mLocked;
private boolean mUsed;
private long mLastKeyUpTime;
public void onDown() {
mUsed = false;
mPressed = true;
// It's not true that mLocked is always true...
//noinspection ConstantValue
mLatched = mLocked ? mLocked : mLatched;
mLocked = false;
}
public void onUp(long eventTime) {
if (eventTime - mLastKeyUpTime < TOGGLE_DURATION) {
mLocked = true;
}
mLastKeyUpTime = eventTime;
mPressed = false;
mLatched = !mLatched && !mLocked && !mUsed;
}
public void afterKeypress() {
if (mLatched) {
mLatched = false;
mUsed = false;
}
if (mPressed) {
mUsed = true;
}
}
public void resetState() {
mPressed = false;
mLatched = false;
mLocked = false;
mUsed = false;
}
public boolean isPressed() {
return mPressed;
}
public boolean isLatched() {
return mLatched;
}
public boolean isLocked() {
return mLocked;
}
public boolean isActive() {
return mPressed || mLocked || mLatched;
}
}
@@ -0,0 +1,86 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard;
import android.os.Vibrator;
import android.view.KeyEvent;
import java.util.ArrayList;
public class MetaKeyManager {
private static final int TOGGLE_VIBRATION_TIME = 30;
public final MetaKey mShift = new MetaKey();
public final MetaKey mSymbol = new MetaKey();
private final Vibrator mVibrator;
public MetaKeyManager(Vibrator vibrator) {
mVibrator = vibrator;
}
public boolean updateMetaStateDown(int keyCode, ArrayList<Integer> symbolMeta) {
if (symbolMeta.contains(keyCode)) {
mSymbol.onDown();
return true;
} else if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
mShift.onDown();
return true;
}
return false;
}
/**
* @noinspection FeatureEnvy
*/
public boolean updateMetaStateUp(int keyCode, long eventTime, ArrayList<Integer> symbolMeta) {
if (symbolMeta.contains(keyCode)) {
mSymbol.onUp(eventTime);
if (mSymbol.isLocked()) {
mVibrator.vibrate(TOGGLE_VIBRATION_TIME);
}
return true;
} else if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
mShift.onUp(eventTime);
if (mShift.isLocked()) {
mVibrator.vibrate(TOGGLE_VIBRATION_TIME);
}
return true;
}
return false;
}
public void adjustMetaStateAfterKeypress() {
mShift.afterKeypress();
mSymbol.afterKeypress();
}
public void resetMetaKeyState() {
mShift.resetState();
mSymbol.resetState();
}
public MetaKey getShift() {
return mShift;
}
public MetaKey getSymbol() {
return mSymbol;
}
}
@@ -0,0 +1,113 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
/**
* @noinspection deprecation
*/
public class PreferenceHolder implements SharedPreferences.OnSharedPreferenceChangeListener {
private final Map<String, Object> mPreferenceValues;
private final Context mContext;
private final SharedPreferences.Editor mEditor;
@SuppressWarnings("unchecked") //NON-NLS
public PreferenceHolder(Context context) {
mContext = context;
SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
PreferenceManager.setDefaultValues(mContext, R.xml.preferences, false);
mPreferences.registerOnSharedPreferenceChangeListener(this);
mPreferenceValues = (Map<String, Object>) mPreferences.getAll();
mEditor = mPreferences.edit();
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Object value = sharedPreferences.getAll().get(key);
if (value != null) {
mPreferenceValues.put(key, value);
}
}
public Object getOrDefault(String key, Object defaultValue) {
Object value = mPreferenceValues.get(key);
return value == null ? defaultValue : value;
}
public String getDevice() {
return (String) getOrDefault(mContext.getString(R.string.preferences_key_device),
mContext.getString(R.string.device_default));
}
public boolean isCycleManualOn() {
return (boolean) getOrDefault(mContext.getString(R.string.preferences_key_cycle_manual),
mContext.getResources().getBoolean(R.bool.cycle_manual_default));
}
public int getCycleSpeed() {
return Integer.parseInt((String)
getOrDefault(mContext.getString(R.string.preferences_key_cycle_speed),
mContext.getString(R.string.cycle_speed_default)));
}
public int getClipboardSize() {
return Integer.parseInt((String)
getOrDefault(mContext.getString(R.string.preferences_key_clipboard_size),
mContext.getString(R.string.clipboard_size_default)));
}
@SuppressWarnings("unchecked") //NON-NLS
public ArrayList<String> getSelectedKeymaps() {
Set<String> selectedKeymaps = (Set<String>)
getOrDefault(mContext.getString(R.string.preferences_key_languages),
mContext.getResources()
.getStringArray(R.array.preferences_language_defaults));
return new ArrayList<>(selectedKeymaps);
}
public boolean isAlternateComposingOn(String appName) {
return (boolean) getOrDefault(
mContext.getString(R.string.tweaks_key_alternate_composing) + appName, false);
}
public boolean isEnterAlwaysSendsOn(String appName) {
return (boolean) getOrDefault(
mContext.getString(R.string.tweaks_key_enter_always_sends) + appName, false);
}
public void setAlternateComposing(String appName, boolean value) {
mEditor.putBoolean(
mContext.getString(R.string.tweaks_key_alternate_composing) + appName, value);
mEditor.commit();
}
public void setEnterAlwaysSends(String appName, boolean value) {
mEditor.putBoolean(
mContext.getString(R.string.tweaks_key_enter_always_sends) + appName, value);
mEditor.commit();
}
}
@@ -1,6 +1,6 @@
/* /*
* An input method for the Unihertz Titan/physical keyboard phones * An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz * Copyright (C) 2024 Emil Kosz
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -14,18 +14,20 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/ */
package cfpi_fpsi.eu.titanboard; package eur.cfpifpsi.titanboard;
import android.app.Activity;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceActivity;
public class PreferencesActivity extends Activity { /**
* @noinspection deprecation
*/
public class SettingsActivity extends PreferenceActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.preferences); addPreferencesFromResource(R.xml.preferences);
} }
} }
@@ -0,0 +1,216 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard;
import android.annotation.SuppressLint;
import android.inputmethodservice.InputMethodService;
import android.os.Vibrator;
import android.text.InputType;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import java.util.ArrayList;
import eur.cfpifpsi.titanboard.input.SymbolInputHandler;
import eur.cfpifpsi.titanboard.keymapping.KeymapManager;
import eur.cfpifpsi.titanboard.views.InputView;
// TODO: Add auto-capitalization
// TODO: Add auto-periods
// TODO: Add auto-correct
// TODO: Emojis with popup window
// TODO: Composing with diacritics, polytonic Greek as well
// TODO: Fix up the tweaks popup so it matches in style other UI elements
// TODO: Call answering/rejecting like in Pocketboard
// TODO: Floating input view
// TODO: Make the IME work after reboot, e.g. to type in passwords
public class TitanboardIME extends InputMethodService {
private MetaKeyManager mMetaKeyManager;
private KeymapManager mKeymapManager;
private PreferenceHolder mPreferenceHolder;
private ClipManager mClipManager;
private InputView mInputView;
private Vibrator mVibrator;
private SymbolInputHandler mSymbolInputHandler;
@Override
public void onCreate() {
super.onCreate();
mPreferenceHolder = new PreferenceHolder(this);
mVibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
mMetaKeyManager = new MetaKeyManager(mVibrator);
mKeymapManager = new KeymapManager(this);
mKeymapManager.nextKeymap(mPreferenceHolder.getSelectedKeymaps());
}
@SuppressLint("InflateParams")
@Override
public void onInitializeInterface() {
mInputView = (InputView) getLayoutInflater().inflate(R.layout.input, null);
// These things shouldn't depend on one another like that, this is bound to break
// something badly.
mClipManager = new ClipManager(this, mPreferenceHolder, mInputView);
mInputView.setDependencies(mKeymapManager, mMetaKeyManager, mPreferenceHolder, mClipManager);
mSymbolInputHandler = new SymbolInputHandler(mInputView, mVibrator);
mInputView.updateStateText();
mInputView.setSymbolsVisible(false);
}
@Override
public View onCreateInputView() {
return mInputView;
}
@Override
public boolean onEvaluateFullscreenMode() {
// This is to override the default behaviour, which seems to do more than detect landscape
// mode.
return false;
}
/**
* @noinspection FeatureEnvy
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO: make sure to return false if it's a key we don't really know, so that media keys
// etc. on some keyboards are handled correctly
if (mMetaKeyManager.updateMetaStateDown(keyCode,
mKeymapManager.getDeviceMetaSymbolKeyCodes(mPreferenceHolder.getDevice()))) {
mInputView.updateStateText();
return true;
}
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (isInputViewShown()) {
requestHideSelf(0);
return true;
}
return false;
}
ArrayList<String> symbols = mKeymapManager.getCurrentKeySymbolsMeta(keyCode,
mPreferenceHolder.getDevice(), mMetaKeyManager);
InputConnection conn = getCurrentInputConnection();
EditorInfo info = getCurrentInputEditorInfo();
if (conn == null || info == null || info.inputType == InputType.TYPE_NULL) {
return false;
}
if (keyCode == KeyEvent.KEYCODE_DEL) {
return handleDelete();
}
if (keyCode == KeyEvent.KEYCODE_ENTER) {
return handleEnter(conn, mPreferenceHolder.isEnterAlwaysSendsOn(info.packageName));
}
// TODO: It's kind of a mess, isn't it? I have to rethink this.
if (event.getRepeatCount() == 0) {
mMetaKeyManager.adjustMetaStateAfterKeypress();
mInputView.updateStateText();
mSymbolInputHandler.handleSymbolInput(conn, symbols, keyCode,
mPreferenceHolder.isAlternateComposingOn(info.packageName),
mPreferenceHolder.isCycleManualOn(), mPreferenceHolder.getCycleSpeed());
}
return true;
}
public boolean handleDelete() {
if (mMetaKeyManager.getSymbol().isPressed()) {
return true;
}
mMetaKeyManager.adjustMetaStateAfterKeypress();
mInputView.updateStateText();
return false;
}
public boolean handleEnter(InputConnection conn, boolean enterAlwaysSends) {
if (mMetaKeyManager.getShift().isPressed()) {
conn.commitText("\n", 1);
mMetaKeyManager.adjustMetaStateAfterKeypress();
mInputView.updateStateText();
return true;
}
if (enterAlwaysSends) {
conn.performEditorAction(EditorInfo.IME_ACTION_SEND);
return true;
}
return false;
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
InputConnection conn = getCurrentInputConnection();
// If manual cycling is on, all this is done using a Runnable after some timeout.
if (!mPreferenceHolder.isCycleManualOn() && conn != null) {
mSymbolInputHandler.finishCycling(conn);
}
if (mMetaKeyManager.updateMetaStateUp(keyCode, event.getEventTime(),
mKeymapManager.getDeviceMetaSymbolKeyCodes(mPreferenceHolder.getDevice()))) {
mInputView.updateStateText();
return true;
}
return false;
}
@Override
public void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd,
int candidatesStart, int candidatesEnd) {
mSymbolInputHandler.setCursorPos(newSelStart);
}
@Override
public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
// getCharacterBounds() --- API 21 (Lollipop)
// getInsertionMarkerTop() -- API 21 (Lollipop)
}
@Override
public void onStartInput(EditorInfo attribute, boolean restarting) {
super.onStartInput(attribute, restarting);
mKeymapManager.adjustCurrentKeymap(mPreferenceHolder.getSelectedKeymaps());
mInputView.updateStateText();
mInputView.updateTweaks();
// Soon™
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// getCurrentInputConnection().requestCursorUpdates(InputConnection.CURSOR_UPDATE_MONITOR);
// }
}
@Override
public void onFinishInput() {
mMetaKeyManager.resetMetaKeyState();
mInputView.dismissPopup();
}
}
@@ -0,0 +1,65 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard.input;
import java.util.ArrayList;
public abstract class Cycler implements Runnable {
private static final int LIST_INITIAL_SIZE = 10;
private ArrayList<?> mList = new ArrayList<>(LIST_INITIAL_SIZE);
private boolean mHasCycled;
private int mIndex;
public int getIndex() {
return mIndex;
}
public ArrayList<?> getList() {
return mList;
}
public Object getElementAt(int index) {
return mList.get(index);
}
public Object getCurrentElement() {
return getElementAt(mIndex);
}
public void prepare(ArrayList<?> list) {
mList = list;
mIndex = 0;
mHasCycled = false;
}
@Override
public void run() {
if (!mHasCycled) {
onFirstCycle();
mHasCycled = true;
}
mIndex = (mIndex + 1) % mList.size();
onCycle();
}
public abstract void onFirstCycle();
public abstract void onCycle();
}
@@ -0,0 +1,85 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard.input;
import android.os.Handler;
import android.os.Vibrator;
import android.view.inputmethod.InputConnection;
import java.util.ArrayList;
import eur.cfpifpsi.titanboard.views.InputView;
public class SymbolCycler extends Cycler {
private static final int CYCLE_VIBRATION_TIME = 10;
private final InputView mInputView;
private final Vibrator mVibrator;
private final Handler mHandler;
private InputConnection mInputConnection;
private boolean mAlternateComposing;
private boolean mManual;
private int mCycleSpeed;
private int mLastCursorPosition;
public SymbolCycler(InputView inputView, Vibrator vibrator, Handler handler) {
mInputView = inputView;
mVibrator = vibrator;
mHandler = handler;
}
public void setInputParameters(InputConnection conn, boolean alternateComposing,
boolean automatic, int cycleSpeed) {
mInputConnection = conn;
mAlternateComposing = alternateComposing;
mManual = automatic;
mCycleSpeed = cycleSpeed;
}
public void setCursorPos(int cursorPos) {
mLastCursorPosition = cursorPos;
}
@Override
public void onFirstCycle() {
mInputView.setSymbolsVisible(true);
mInputView.setSymbols((ArrayList<String>) getList());
if (!mAlternateComposing) {
mInputConnection.setSelection(mLastCursorPosition - getElementAt(0).toString().length(),
mLastCursorPosition);
}
}
@Override
public void onCycle() {
mVibrator.vibrate(CYCLE_VIBRATION_TIME);
mInputView.setSelectedSymbol(getIndex());
if (mAlternateComposing) {
mInputConnection.deleteSurroundingText(getElementAt(0).toString().length(), 0);
mInputConnection.commitText(getCurrentElement().toString(), 1);
} else {
mInputConnection.setComposingText(getCurrentElement().toString(), 1);
}
if (!mManual) {
mHandler.postDelayed(this, mCycleSpeed);
}
}
}
@@ -0,0 +1,98 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard.input;
import android.os.Handler;
import android.os.Looper;
import android.os.Vibrator;
import android.view.inputmethod.InputConnection;
import java.util.ArrayList;
import eur.cfpifpsi.titanboard.views.InputView;
// TODO: This looks kind of weird. Think about refactoring it.
// TODO: Cycling doesn't stop when pressing a button that isn't defined or something. Strange...
// Only happens in manual mode.
public class SymbolInputHandler {
private final InputView mInputView;
private final SymbolCycler mSymbolCycler;
private final Handler mHandler;
private int mLastSymbolKeyCode;
public SymbolInputHandler(InputView inputView, Vibrator vibrator) {
mHandler = new Handler(Looper.getMainLooper());
mInputView = inputView;
mSymbolCycler = new SymbolCycler(inputView, vibrator, mHandler);
}
public void setCursorPos(int value) {
mSymbolCycler.setCursorPos(value);
}
public void handleSymbolInput(InputConnection inputConnection, ArrayList<String> symbols,
int keyCode, boolean isAlternateComposingOn, boolean isManual,
int cycleSpeed) {
mSymbolCycler.setInputParameters(inputConnection, isAlternateComposingOn, isManual,
cycleSpeed);
if (isManual) {
cycleManual(inputConnection, symbols, keyCode, cycleSpeed);
} else {
cycleAutomatic(inputConnection, symbols, cycleSpeed);
}
}
public void cycleManual(InputConnection inputConnection, ArrayList<String> symbols,
int keyCode, int cycleSpeed) {
boolean isCycling = mHandler.hasMessages(0);
if (isCycling && keyCode == mLastSymbolKeyCode
&& mSymbolCycler.getList().size() > 1) {
mSymbolCycler.run();
} else if (symbols != null && !symbols.isEmpty()) {
finishCycling(inputConnection);
inputConnection.commitText(symbols.get(0), 1);
mSymbolCycler.prepare(symbols);
}
mHandler.removeCallbacksAndMessages(null);
mHandler.postDelayed(() -> finishCycling(inputConnection), cycleSpeed);
mLastSymbolKeyCode = keyCode;
}
public void cycleAutomatic(InputConnection inputConnection, ArrayList<String> symbols,
int cycleSpeed) {
if (symbols != null && !symbols.isEmpty()) {
inputConnection.commitText(symbols.get(0), 1);
if (symbols.size() > 1) {
mSymbolCycler.prepare(symbols);
mHandler.removeCallbacksAndMessages(null);
mHandler.postDelayed(mSymbolCycler, cycleSpeed);
}
}
}
public void finishCycling(InputConnection inputConnection) {
inputConnection.finishComposingText();
mHandler.removeCallbacksAndMessages(null);
mInputView.setSymbolsVisible(false);
}
}
@@ -0,0 +1,33 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard.keymapping;
import java.util.ArrayList;
public class DeviceKeymap extends Keymap {
private final ArrayList<Integer> mMetaSymbolKeyCodes = new ArrayList<>(2);
public ArrayList<Integer> getMetaSymbolKeyCodes() {
return mMetaSymbolKeyCodes;
}
public void addMetaSymbolKeyCode(Integer keyCode) {
mMetaSymbolKeyCodes.add(keyCode);
}
}
@@ -0,0 +1,52 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard.keymapping;
import java.util.ArrayList;
public final class KeySymbols {
private final static int INITIAL_SIZE = 6;
private final ArrayList<String> mBase = new ArrayList<>(INITIAL_SIZE);
private final ArrayList<String> mShift = new ArrayList<>(INITIAL_SIZE);
private final ArrayList<String> mAlt = new ArrayList<>(INITIAL_SIZE);
public ArrayList<String> getBase() {
return mBase;
}
public ArrayList<String> getShift() {
return mShift;
}
public ArrayList<String> getAlt() {
return mAlt;
}
public void addBase(String symbol) {
mBase.add(symbol);
}
public void addShift(String symbol) {
mShift.add(symbol);
}
public void addAlt(String symbol) {
mAlt.add(symbol);
}
}
@@ -0,0 +1,58 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard.keymapping;
import java.util.ArrayList;
import java.util.HashMap;
import eur.cfpifpsi.titanboard.MetaKeyManager;
public class Keymap {
private final static int INITIAL_KEYMAP_SIZE = 64; // Enough for just about any QWERTY.
private final HashMap<Integer, KeySymbols> mMap = new HashMap<>(INITIAL_KEYMAP_SIZE);
public ArrayList<String> getSymbolsMeta(int keyCode, MetaKeyManager metaKeyManager) {
if (metaKeyManager.getSymbol().isActive()) {
return getSymbolsAlt(keyCode);
} else if (metaKeyManager.getShift().isActive()) {
return getSymbolsShift(keyCode);
} else {
return getSymbolsBase(keyCode);
}
}
public ArrayList<String> getSymbolsBase(int keyCode) {
KeySymbols key = mMap.get(keyCode);
return key == null ? null : key.getBase();
}
public ArrayList<String> getSymbolsAlt(int keyCode) {
KeySymbols key = mMap.get(keyCode);
return key == null ? null : key.getAlt();
}
public ArrayList<String> getSymbolsShift(int keyCode) {
KeySymbols key = mMap.get(keyCode);
return key == null ? null : key.getShift();
}
public void putKeySymbols(Integer key, KeySymbols value) {
mMap.put(key, value);
}
}
@@ -0,0 +1,219 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard.keymapping;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
public enum KeymapLoader {
;
private static final String TAG_SYMBOLS = "symbols"; //NON-NLS
private static final String TAG_ATTACHED_SYMBOLS = "attached-symbols"; //NON-NLS
private static final String TAG_LANGUAGE_KEYMAP = "language-keymap"; //NON-NLS
private static final String TAG_DEVICE_KEYMAP = "device-keymap"; //NON-NLS
private static final String TAG_SYMBOL = "symbol"; //NON-NLS
private static final String TAG_BASE = "base"; //NON-NLS
private static final String TAG_ALT = "alt"; //NON-NLS
private static final String TAG_SHIFT = "shift"; //NON-NLS
private static final String TAG_SPACE = "space"; //NON-NLS
private static final String TAG_FOR_CODE = "for-code"; //NON-NLS
private static final String TAG_FOR_SYMBOL = "for-symbol"; //NON-NLS
private static final String TAG_CODE = "code"; //NON-NLS
private static final String TAG_LATCHED_CODE = "latched-code"; //NON-NLS
private static final String TAG_LOCKED_CODE = "locked-code"; //NON-NLS
private static final String TAG_ID = "id"; //NON-NLS
private static final String TAG_SYMBOLS_META_KEY = "symbol-meta-key-code"; //NON-NLS
public static void loadLanguageKeymaps(HashMap<String, LanguageKeymap> keymaps,
XmlPullParser parser) {
LanguageKeymap keymap = null;
int code = 0;
KeySymbols keySymbols = null;
String forSymbol = null;
ArrayList<String> attachedSymbols = null;
int eventType;
while (true) {
try {
eventType = parser.getEventType();
if (eventType == XmlPullParser.END_DOCUMENT) break;
if (eventType == XmlPullParser.START_TAG) {
switch (parser.getName()) {
case TAG_LANGUAGE_KEYMAP:
keymap = new LanguageKeymap();
break;
case TAG_CODE:
assert keymap != null : "<code> not preceded by <language-keymap>";
keymap.setCode(nextTextLowerApi(parser));
break;
case TAG_LATCHED_CODE:
assert keymap != null : "<latched-code> not preceded by <language-keymap>";
keymap.setLatchedCode(nextTextLowerApi(parser));
break;
case TAG_LOCKED_CODE:
assert keymap != null : "<locked-code> not preceded by <language-keymap>";
keymap.setLockedCode(nextTextLowerApi(parser));
break;
case TAG_SYMBOLS:
keySymbols = new KeySymbols();
break;
case TAG_FOR_CODE:
code = Integer.parseInt(nextTextLowerApi(parser));
break;
case TAG_BASE:
assert keySymbols != null : "<base> tag isn't preceded by <symbols>";
keySymbols.addBase(nextTextLowerApi(parser));
break;
case TAG_SHIFT:
assert keySymbols != null : "<shift> tag isn't preceded by <symbols>";
keySymbols.addShift(nextTextLowerApi(parser));
break;
case TAG_ATTACHED_SYMBOLS:
attachedSymbols = new ArrayList<>();
break;
case TAG_FOR_SYMBOL:
forSymbol = nextTextLowerApi(parser);
break;
case TAG_SYMBOL:
assert attachedSymbols != null : "<symbol> tag isn't preceded by <attached-symbols>";
attachedSymbols.add(nextTextLowerApi(parser));
break;
}
}
if (eventType == XmlPullParser.END_TAG) {
switch (parser.getName()) {
case TAG_LANGUAGE_KEYMAP:
assert keymap != null : "<language-keymap> tag not defined";
keymaps.put(keymap.getCode(), keymap);
break;
case TAG_SYMBOLS:
assert keymap != null : "<language-keymap> tag not defined";
keymap.putKeySymbols(code, keySymbols);
keySymbols = new KeySymbols();
break;
case TAG_ATTACHED_SYMBOLS:
assert keymap != null : "<language-keymap> tag not defined";
keymap.putAttachedSymbols(forSymbol, attachedSymbols);
break;
}
}
parser.next();
} catch (XmlPullParserException | IOException e) {
// TODO: Real error handling?
break;
}
}
}
public static void loadDeviceKeymaps(HashMap<String, DeviceKeymap> keymaps,
XmlPullParser parser) {
DeviceKeymap keymap = null;
int code = 0;
String id = null;
KeySymbols keySymbols = null;
int eventType;
while (true) {
try {
eventType = parser.getEventType();
if (eventType == XmlPullParser.END_DOCUMENT) break;
if (eventType == XmlPullParser.START_TAG) {
switch (parser.getName()) {
case TAG_DEVICE_KEYMAP:
keymap = new DeviceKeymap();
break;
case TAG_ID:
id = nextTextLowerApi(parser);
break;
case TAG_SYMBOLS_META_KEY:
assert keymap != null : "<device-keymap> tag not defined";
keymap.addMetaSymbolKeyCode(Integer.parseInt(nextTextLowerApi(parser)));
break;
case TAG_SYMBOLS:
keySymbols = new KeySymbols();
break;
case TAG_FOR_CODE:
code = Integer.parseInt(nextTextLowerApi(parser));
break;
case TAG_BASE:
assert keySymbols != null : "<base> tag isn't preceded by <symbols>";
keySymbols.addBase(nextTextLowerApi(parser));
break;
case TAG_SHIFT:
assert keySymbols != null : "<shift> tag isn't preceded by <symbols>";
keySymbols.addShift(nextTextLowerApi(parser));
break;
case TAG_ALT:
assert keySymbols != null : "<alt> tag isn't preceded by <symbols>";
keySymbols.addAlt(nextTextLowerApi(parser));
break;
case TAG_SPACE:
assert keySymbols != null : "</space> tag isn't preceded by <symbols>";
keySymbols.addBase(" ");
keySymbols.addShift(" ");
keySymbols.addAlt(" ");
break;
}
}
if (eventType == XmlPullParser.END_TAG) {
switch (parser.getName()) {
case TAG_DEVICE_KEYMAP:
keymaps.put(id, keymap);
break;
case TAG_SYMBOLS:
assert keymap != null : "<device-keymap> tag not defined";
keymap.putKeySymbols(code, keySymbols);
keySymbols = new KeySymbols();
break;
}
}
parser.next();
} catch (XmlPullParserException | IOException e) {
// TODO: Real error handling?
break;
}
}
}
// Taken from the javadoc of XmlPullParser.nextText
public static String nextTextLowerApi(XmlPullParser parser)
throws XmlPullParserException, IOException {
String text = parser.nextText();
if (parser.getEventType() != XmlPullParser.END_TAG) {
parser.next();
}
return text;
}
}
@@ -0,0 +1,114 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard.keymapping;
import android.content.Context;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import eur.cfpifpsi.titanboard.R;
import eur.cfpifpsi.titanboard.MetaKeyManager;
public class KeymapManager {
private final Context mContext;
private final HashMap<String, DeviceKeymap> mDeviceKeymaps;
private final HashMap<String, LanguageKeymap> mLanguageKeymaps;
private LanguageKeymap mCurrentLanguageKeymap;
public KeymapManager(Context context) {
mContext = context;
mDeviceKeymaps = new HashMap<>(2);
mLanguageKeymaps = new HashMap<>(context.getResources()
.getStringArray(R.array.preferences_languages).length);
KeymapLoader.loadDeviceKeymaps(mDeviceKeymaps,
context.getResources().getXml(R.xml.device_keymaps));
KeymapLoader.loadLanguageKeymaps(mLanguageKeymaps,
context.getResources().getXml(R.xml.language_keymaps));
}
public ArrayList<String> getCurrentKeySymbolsMeta(int keyCode,
String device,
MetaKeyManager metaKeyManager) {
Keymap deviceKeymap = mDeviceKeymaps.get(device);
ArrayList<String> deviceSymbols = deviceKeymap != null
? deviceKeymap.getSymbolsMeta(keyCode, metaKeyManager) : new ArrayList<>();
ArrayList<String> languageSymbols = mCurrentLanguageKeymap.getSymbolsMeta(keyCode, metaKeyManager);
ArrayList<String> combinedSymbols = new ArrayList<>();
if (deviceSymbols != null) {
combinedSymbols.addAll(deviceSymbols);
}
if (languageSymbols != null) {
combinedSymbols.addAll(languageSymbols);
}
// We NEED to do this as not to iterate and modify combinedSymbols at the same time.
ArrayList<String> finalSymbols = new ArrayList<>(combinedSymbols);
for (String symbol : combinedSymbols) {
ArrayList<String> attachedSymbols = mCurrentLanguageKeymap.getAttachedSymbols(symbol);
if (attachedSymbols != null) {
finalSymbols.addAll(attachedSymbols);
}
}
return finalSymbols;
}
public void adjustCurrentKeymap(List<String> selectedKeymaps) {
if (!selectedKeymaps.contains(mCurrentLanguageKeymap.getCode())) {
nextKeymap(selectedKeymaps);
}
}
public void nextKeymap(List<String> selectedKeymaps) {
if (!selectedKeymaps.isEmpty() && mCurrentLanguageKeymap != null) {
int index = selectedKeymaps.indexOf(mCurrentLanguageKeymap.getCode());
index++;
if (index == selectedKeymaps.size()) {
index = 0;
}
mCurrentLanguageKeymap = mLanguageKeymaps.get(selectedKeymaps.get(index));
} else {
mCurrentLanguageKeymap =
mLanguageKeymaps.get(mContext.getResources().getString(R.string.language_default));
}
}
public ArrayList<Integer> getDeviceMetaSymbolKeyCodes(String device) {
return mDeviceKeymaps.get(device).getMetaSymbolKeyCodes();
}
public String getCurrentCode() {
return mCurrentLanguageKeymap.getCode();
}
public String getCurrentLatchedCode() {
return mCurrentLanguageKeymap.getLatchedCode();
}
public String getCurrentLockedCode() {
return mCurrentLanguageKeymap.getLockedCode();
}
}
@@ -0,0 +1,61 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard.keymapping;
import java.util.ArrayList;
import java.util.HashMap;
public final class LanguageKeymap extends Keymap {
private final HashMap<String, ArrayList<String>> mAttachedSymbols = new HashMap<>();
private String mCode;
private String mLatchedCode;
private String mLockedCode;
public String getCode() {
return mCode;
}
public void setCode(String value) {
mCode = value;
}
public String getLatchedCode() {
return mLatchedCode;
}
public void setLatchedCode(String value) {
mLatchedCode = value;
}
public String getLockedCode() {
return mLockedCode;
}
public void setLockedCode(String value) {
mLockedCode = value;
}
public ArrayList<String> getAttachedSymbols(String key) {
return mAttachedSymbols.get(key);
}
public void putAttachedSymbols(String key, ArrayList<String> value) {
mAttachedSymbols.put(key, value);
}
}
@@ -0,0 +1,94 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard.views;
import android.annotation.TargetApi;
import android.content.ClipData;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import java.util.ArrayList;
import eur.cfpifpsi.titanboard.ClipManager;
import eur.cfpifpsi.titanboard.R;
import eur.cfpifpsi.titanboard.TitanboardIME;
public class ClipboardView extends ScrollView {
private final TitanboardIME mTitanboardIME;
private final LinearLayout.LayoutParams mClipLayout;
private ClipManager mClipManager;
private LinearLayout mClipList;
public ClipboardView(Context context, AttributeSet attrs) {
super(context, attrs);
mTitanboardIME = (TitanboardIME) context;
mClipLayout = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT, 0);
}
public void setDependencies(ClipManager clipManager) {
mClipManager = clipManager;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mClipList = findViewById(R.id.clip_list);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void setClips(ArrayList<? extends ClipData.Item> items) {
int size = items.size();
for (int i = 0; i < size; ++i) {
ClipData.Item item = items.get(i);
LinearLayout clipView = (LinearLayout) mClipList.getChildAt(i);
if (clipView == null) {
clipView = (LinearLayout) inflate(getContext(), R.layout.clip, null);
clipView.setTag(i);
clipView.setLayoutParams(mClipLayout);
mClipList.addView(clipView);
}
Button pasteButton = clipView.findViewById(R.id.paste_button);
pasteButton.setText(item.getText());
pasteButton.setOnClickListener(button -> pasteClip(item));
LinearLayout finalClipView = clipView;
clipView.findViewById(R.id.button_delete)
.setOnClickListener(button -> deleteClip(finalClipView, item));
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void pasteClip(ClipData.Item item) {
mTitanboardIME.getCurrentInputConnection().commitText(item.getText(), 1);
}
private void deleteClip(LinearLayout clipView, ClipData.Item item) {
mClipList.removeView(clipView);
mClipManager.removeItem(item);
}
}
@@ -0,0 +1,166 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard.views;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageButton;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import java.util.ArrayList;
import eur.cfpifpsi.titanboard.ClipManager;
import eur.cfpifpsi.titanboard.PreferenceHolder;
import eur.cfpifpsi.titanboard.R;
import eur.cfpifpsi.titanboard.SettingsActivity;
import eur.cfpifpsi.titanboard.TitanboardIME;
import eur.cfpifpsi.titanboard.MetaKeyManager;
import eur.cfpifpsi.titanboard.keymapping.KeymapManager;
public final class InputView extends RelativeLayout {
private final int[] buttonStateToggled = {R.attr.state_toggled};
private final TitanboardIME mTitanboardIME;
private KeymapManager mKeymapManager;
private PreferenceHolder mPreferenceHolder;
private MetaKeyManager mMetaKeyManager;
private ClipManager mClipManager;
private PopupWindow mPopup;
private StateButton mStateButton;
private ImageButton mClipboardButton;
private ImageButton mTweaksButton;
private SymbolsView mSymbolsView;
private ClipboardView mClipboardView;
private TweaksView mTweaksView;
public InputView(Context context, AttributeSet attrs) {
super(context, attrs);
mTitanboardIME = (TitanboardIME) context;
}
public void setDependencies(KeymapManager keymapManager, MetaKeyManager metaKeyManager,
PreferenceHolder preferenceHolder, ClipManager clipManager) {
mKeymapManager = keymapManager;
mMetaKeyManager = metaKeyManager;
mPreferenceHolder = preferenceHolder;
mClipManager = clipManager;
mClipboardView.setDependencies(clipManager);
mTweaksView.setDependencies(preferenceHolder);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mStateButton = findViewById(R.id.state_button);
mClipboardButton = findViewById(R.id.clipboard_button);
mTweaksButton = findViewById(R.id.tweaks_button);
mSymbolsView = findViewById(R.id.symbols_view);
mClipboardView = (ClipboardView) inflate(getContext(), R.layout.clipboard, null);
mTweaksView = (TweaksView) inflate(getContext(), R.layout.tweaks, null);
mPopup = new PopupWindow(mClipboardView,
getResources().getDimensionPixelSize(R.dimen.popup_width),
getResources().getDimensionPixelSize(R.dimen.clipboard_popup_height), true);
mPopup.setOnDismissListener(() -> {
mClipboardButton.setImageState(null, true);
mTweaksButton.setImageState(null, true);
});
mStateButton.setOnClickListener(button -> {
if (mMetaKeyManager.getSymbol().isActive()) {
mMetaKeyManager.getSymbol().resetState();
} else {
mKeymapManager.nextKeymap(mPreferenceHolder.getSelectedKeymaps());
}
updateStateText();
});
mStateButton.setOnLongClickListener(button -> {
Intent intent = new Intent(getContext(), SettingsActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getContext().startActivity(intent);
// For some reason, this vibrates by default, so we don't need to call the vibrator.
return true;
});
mClipboardButton.setOnClickListener(button -> showPopup(mClipboardButton, mClipboardView,
R.dimen.clipboard_popup_height));
mClipboardButton.setOnLongClickListener(button -> {
// On a long click, we just send the latest clip (if any).
if (!mClipManager.isEmpty()
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mTitanboardIME.getCurrentInputConnection()
.commitText(mClipManager.getItemAt(0).getText(), 1);
}
return true;
});
mTweaksButton.setOnClickListener(button -> showPopup(mTweaksButton, mTweaksView,
R.dimen.tweaks_popup_height));
}
public void dismissPopup() {
mPopup.dismiss();
}
private void showPopup(ImageButton button, View contentView, int popupHeight) {
mPopup.setContentView(contentView);
mPopup.setHeight(getResources().getDimensionPixelSize(popupHeight));
mPopup.showAsDropDown(button,
-(mPopup.getWidth() - button.getWidth()) / 2, 0);
button.setImageState(buttonStateToggled, true);
}
public void updateStateText() {
mStateButton.updateStateText(mMetaKeyManager.getShift(), mMetaKeyManager.getSymbol(),
mKeymapManager.getCurrentCode(), mKeymapManager.getCurrentLatchedCode(),
mKeymapManager.getCurrentLockedCode());
}
public void updateTweaks() {
mTweaksView.updateTweaks();
}
public void setClips(ArrayList<? extends ClipData.Item> items) {
mClipboardView.setClips(items);
}
public void setSymbolsVisible(boolean state) {
mSymbolsView.setSymbolsVisible(state);
}
public void setSymbols(ArrayList<String> strings) {
mSymbolsView.setSymbols(strings);
}
public void setSelectedSymbol(int index) {
mSymbolsView.setSelectedSymbol(index);
}
}
@@ -0,0 +1,54 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard.views;
import android.content.Context;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.widget.Button;
import eur.cfpifpsi.titanboard.R;
import eur.cfpifpsi.titanboard.MetaKey;
public class StateButton extends Button {
public StateButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void updateStateText(MetaKey shift, MetaKey alt, String code, String latchedCode,
String lockedCode) {
setPaintFlags(getPaintFlags() & ~(Paint.UNDERLINE_TEXT_FLAG));
if (alt.isLocked() || shift.isLocked()) {
setPaintFlags(getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
}
if (alt.isPressed() || alt.isLocked()) {
setText(R.string.state_button_alt_locked);
} else if (alt.isLatched()) {
setText(R.string.state_button_alt_latched);
} else if (shift.isPressed() || shift.isLocked()) {
setText(lockedCode);
} else if (shift.isLatched()) {
setText(latchedCode);
} else {
setText(code);
}
}
}
@@ -0,0 +1,106 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard.views;
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
import eur.cfpifpsi.titanboard.R;
public class SymbolsView extends HorizontalScrollView {
private final LinearLayout.LayoutParams mSymbolLayout;
private LinearLayout mSymbolList;
public SymbolsView(Context context, AttributeSet attrs) {
super(context, attrs);
mSymbolLayout = new LinearLayout.LayoutParams(
getResources().getDimensionPixelSize(R.dimen.symbol_width),
ViewGroup.LayoutParams.MATCH_PARENT, 0);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mSymbolList = findViewById(R.id.symbol_list);
}
public void setSymbolsVisible(boolean state) {
mSymbolList.setVisibility(state ? VISIBLE : GONE);
}
/**
* @noinspection MethodWithMultipleLoops
*/
public void setSymbols(ArrayList<String> strings) {
// Once we set the symbols, we also scroll back to the very front. This is in case we have
// previously used a symbol that's far away, so that the user doesn't get a visual shock
// since setSelectedSymbol would smoothScroll to the beginning.
scrollTo(0, 0);
int size = strings.size();
for (int i = 0; i < size; ++i) {
TextView tv = (TextView) mSymbolList.getChildAt(i);
if (tv == null) {
tv = (TextView) inflate(getContext(), R.layout.symbol, null);
tv.setTag(i);
tv.setLayoutParams(mSymbolLayout);
mSymbolList.addView(tv);
}
tv.setText(strings.get(i));
tv.setVisibility(VISIBLE);
}
int childCount = mSymbolList.getChildCount();
for (int i = size; i < childCount; ++i) {
mSymbolList.getChildAt(i).setVisibility(GONE);
}
}
public void setSelectedSymbol(int index) {
int childCount = mSymbolList.getChildCount();
for (int i = 0; i < childCount; ++i) {
TextView tv = (TextView) mSymbolList.getChildAt(i);
if (i == index) {
tv.setPressed(true);
tv.setTypeface(null, Typeface.BOLD);
// This, if the symbol list is larger than the scroll view, scrolls it just to show
// the selected symbol at the very end. It isn't really necessary, as it's a rarity
// that a key has more than five symbols, but perhaps someone will want to add a
// keymap that has a lot of symbols under one key, and also this is a safeguard for
// smaller screen sizes.
smoothScrollTo(tv.getRight()
+ (getResources().getDimensionPixelSize(R.dimen.default_space) << 1)
- getRight(), 0);
} else {
tv.setPressed(false);
tv.setTypeface(null, Typeface.NORMAL);
}
}
}
}
@@ -0,0 +1,77 @@
/*
* An input method for physical keyboard phones
* Copyright (C) 2024 Emil Kosz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard.views;
import android.content.Context;
import android.util.AttributeSet;
import android.view.inputmethod.EditorInfo;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import eur.cfpifpsi.titanboard.PreferenceHolder;
import eur.cfpifpsi.titanboard.R;
import eur.cfpifpsi.titanboard.TitanboardIME;
public class TweaksView extends LinearLayout {
private final TitanboardIME mTitanboardIME;
private PreferenceHolder mPreferenceHolder;
private CheckBox mComposingCheckbox;
private CheckBox mEnterCheckbox;
public TweaksView(Context context, AttributeSet attrs) {
super(context, attrs);
mTitanboardIME = (TitanboardIME) context;
}
public void setDependencies(PreferenceHolder preferenceHolder) {
mPreferenceHolder = preferenceHolder;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mComposingCheckbox = findViewById(R.id.composing_checkbox);
mEnterCheckbox = findViewById(R.id.enter_checkbox);
mComposingCheckbox.setOnCheckedChangeListener((button, state) -> {
EditorInfo editorInfo = mTitanboardIME.getCurrentInputEditorInfo();
if (editorInfo != null) {
mPreferenceHolder.setAlternateComposing(editorInfo.packageName, state);
}
});
mEnterCheckbox.setOnCheckedChangeListener((button, state) -> {
EditorInfo editorInfo = mTitanboardIME.getCurrentInputEditorInfo();
if (editorInfo != null) {
mPreferenceHolder.setEnterAlwaysSends(editorInfo.packageName, state);
}
});
}
public void updateTweaks() {
EditorInfo editorInfo = mTitanboardIME.getCurrentInputEditorInfo();
if (editorInfo != null) {
mComposingCheckbox.setChecked(mPreferenceHolder.isAlternateComposingOn(editorInfo.packageName));
mEnterCheckbox.setChecked(mPreferenceHolder.isEnterAlwaysSendsOn(editorInfo.packageName));
}
}
}
+35
View File
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
<item app:state_toggled="true">
<shape android:shape="rectangle">
<solid android:color="@color/button_toggled" />
</shape>
</item>
<item android:state_pressed="false">
<shape android:shape="rectangle">
<solid android:color="@android:color/transparent" />
</shape>
</item>
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="@color/button_active" />
</shape>
</item>
</selector>
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_state" android:state_pressed="false" />
<item android:state_pressed="true">
<layer-list>
<item>
<shape android:shape="rectangle">
<solid android:color="@color/button_active" />
</shape>
</item>
<item android:drawable="@drawable/ic_state" />
</layer-list>
</item>
</selector>
+82
View File
@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Background -->
<item>
<shape android:shape="rectangle">
<size
android:width="128px"
android:height="128px" />
<solid android:color="#333" />
</shape>
</item>
<!-- Helper -->
<!-- <item android:top="24px"-->
<!-- android:bottom="24px"-->
<!-- android:left="24px"-->
<!-- android:right="24px">-->
<!-- <shape android:shape="rectangle">-->
<!-- <solid android:color="@android:color/darker_gray" />-->
<!-- </shape>-->
<!-- </item>-->
<!-- Top -->
<item
android:bottom="90px"
android:left="24px"
android:right="24px"
android:top="24px">
<shape android:shape="rectangle">
<solid android:color="@color/text_color" />
<corners android:radius="36px" />
</shape>
</item>
<!-- Stem of the T -->
<item
android:bottom="24px"
android:left="40px"
android:right="72px"
android:top="24px">
<shape android:shape="rectangle">
<solid android:color="@color/text_color" />
<corners android:radius="36px" />
</shape>
</item>
<!-- Stem of the I -->
<item
android:bottom="24px"
android:left="72px"
android:right="40px"
android:top="24px">
<shape android:shape="rectangle">
<solid android:color="@color/text_color" />
<corners android:radius="36px" />
</shape>
</item>
<!-- Bottom of the I -->
<item
android:bottom="24px"
android:left="60px"
android:right="29px"
android:top="90px">
<shape android:shape="rectangle">
<solid android:color="@color/text_color" />
<corners android:radius="36px" />
</shape>
</item>
</layer-list>
@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Background -->
<item>
<shape android:shape="oval">
<size
android:width="128px"
android:height="128px" />
<solid android:color="#333" />
</shape>
</item>
<!-- Helper -->
<!-- <item android:top="24px"-->
<!-- android:bottom="24px"-->
<!-- android:left="24px"-->
<!-- android:right="24px">-->
<!-- <shape android:shape="rectangle">-->
<!-- <solid android:color="@android:color/darker_gray" />-->
<!-- </shape>-->
<!-- </item>-->
<!-- Top -->
<item
android:bottom="90px"
android:left="24px"
android:right="24px"
android:top="24px">
<shape android:shape="rectangle">
<solid android:color="@color/text_color" />
<corners android:radius="36px" />
</shape>
</item>
<!-- Stem of the T -->
<item
android:bottom="24px"
android:left="40px"
android:right="72px"
android:top="24px">
<shape android:shape="rectangle">
<solid android:color="@color/text_color" />
<corners android:radius="36px" />
</shape>
</item>
<!-- Stem of the I -->
<item
android:bottom="24px"
android:left="72px"
android:right="40px"
android:top="24px">
<shape android:shape="rectangle">
<solid android:color="@color/text_color" />
<corners android:radius="36px" />
</shape>
</item>
<!-- Bottom of the I -->
<item
android:bottom="24px"
android:left="60px"
android:right="29px"
android:top="90px">
<shape android:shape="rectangle">
<solid android:color="@color/text_color" />
<corners android:radius="36px" />
</shape>
</item>
</layer-list>
@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- <item android:top="11dp"-->
<!-- android:bottom="11dp"-->
<!-- android:right="14dp"-->
<!-- android:left="14dp">-->
<!-- <shape android:shape="rectangle">-->
<!-- <solid android:color="@android:color/darker_gray" />-->
<!-- </shape>-->
<!-- </item>-->
<!-- Main board -->
<item
android:bottom="11dp"
android:left="14dp"
android:right="14dp"
android:top="12.5dp">
<shape android:shape="rectangle">
<stroke
android:width="2dp"
android:color="@color/text_color" />
<corners android:radius="2dp" />
</shape>
</item>
<!-- The clip -->
<item
android:bottom="32dp"
android:left="20dp"
android:right="20dp"
android:top="11dp">
<shape android:shape="rectangle">
<solid android:color="@color/text_color" />
<corners android:radius="2dp" />
</shape>
</item>
<!-- Squiggles -->
<item
android:bottom="7dp"
android:left="18dp"
android:right="18dp">
<shape android:shape="line">
<stroke
android:width="2dp"
android:color="@color/text_color" />
</shape>
</item>
<item
android:left="18dp"
android:right="18dp"
android:top="2dp">
<shape android:shape="line">
<stroke
android:width="2dp"
android:color="@color/text_color" />
</shape>
</item>
<item
android:left="18dp"
android:right="22dp"
android:top="11dp">
<shape android:shape="line">
<stroke
android:width="2dp"
android:color="@color/text_color" />
</shape>
</item>
</layer-list>
+88
View File
@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- <item android:top="11dp"-->
<!-- android:bottom="11dp"-->
<!-- android:right="13dp"-->
<!-- android:left="13dp">-->
<!-- <shape android:shape="rectangle">-->
<!-- <solid android:color="@android:color/darker_gray" />-->
<!-- </shape>-->
<!-- </item>-->
<item
android:bottom="11dp"
android:left="15dp"
android:right="31dp"
android:top="11dp">
<shape android:shape="rectangle">
<solid android:color="@color/text_color" />
<corners android:radius="2dp" />
</shape>
</item>
<item
android:bottom="21dp"
android:left="13dp"
android:right="29dp"
android:top="21dp">
<shape android:shape="oval">
<solid android:color="@color/text_color" />
</shape>
</item>
<item
android:bottom="11dp"
android:left="31dp"
android:right="15dp"
android:top="11dp">
<shape android:shape="rectangle">
<solid android:color="@color/text_color" />
<corners android:radius="2dp" />
</shape>
</item>
<item
android:bottom="15dp"
android:left="29dp"
android:right="13dp"
android:top="27dp">
<shape android:shape="oval">
<solid android:color="@color/text_color" />
</shape>
</item>
<item
android:bottom="11dp"
android:left="23dp"
android:right="23dp"
android:top="11dp">
<shape android:shape="rectangle">
<solid android:color="@color/text_color" />
<corners android:radius="2dp" />
</shape>
</item>
<item
android:bottom="27dp"
android:left="21dp"
android:right="21dp"
android:top="15dp">
<shape android:shape="oval">
<solid android:color="@color/text_color" />
</shape>
</item>
</layer-list>
+30
View File
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:bottom="11dp"
android:left="9dp"
android:right="9dp"
android:top="11dp">
<shape android:shape="rectangle">
<solid android:color="@color/text_color" />
<corners android:radius="2dp" />
</shape>
</item>
</layer-list>
+83
View File
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Icons are 32x32 dp, leaving 8dp of padding on each side -->
<!-- It is tempting to use insets here, but they lead to unexpected behaviour. -->
<!-- Helper: so that the icon is relatively proportional -->
<!-- <item android:top="11dp"-->
<!-- android:bottom="11dp"-->
<!-- android:right="14dp"-->
<!-- android:left="14dp">-->
<!-- <shape android:shape="rectangle">-->
<!-- <solid android:color="@android:color/darker_gray" />-->
<!-- </shape>-->
<!-- </item>-->
<!-- Handle -->
<item
android:bottom="31dp"
android:left="19dp"
android:right="19dp"
android:top="11dp">
<shape android:shape="rectangle">
<stroke
android:width="2dp"
android:color="@color/danger_text_color" />
<corners android:radius="8dp" />
</shape>
</item>
<!-- Lid -->
<item
android:bottom="16dp"
android:left="13dp"
android:right="13dp">
<shape android:shape="line">
<stroke
android:width="2dp"
android:color="@color/danger_text_color" />
</shape>
</item>
<!-- Bin -->
<item
android:bottom="11dp"
android:left="15dp"
android:right="15dp"
android:top="15dp">
<shape android:shape="rectangle">
<stroke
android:width="2dp"
android:color="@color/danger_text_color" />
<corners
android:bottomLeftRadius="4dp"
android:bottomRightRadius="4dp" />
</shape>
</item>
<!-- Stripes -->
<item
android:bottom="11dp"
android:left="20dp"
android:right="20dp"
android:top="15dp">
<shape android:shape="rectangle">
<stroke
android:width="2dp"
android:color="@color/danger_text_color" />
</shape>
</item>
</layer-list>
-19
View File
@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000"
android:viewportWidth="98"
android:viewportHeight="200.67188">
<group
android:scaleX="0.44929066"
android:scaleY="0.92"
android:translateX="26.984758"
android:translateY="8.0268755">
<group android:translateY="158.69531">
<path
android:fillColor="#000000"
android:pathData="M95.765625,-88.8125Q90.5625,-88.8125,79.734375,-89.65625Q69.1875,-90.421875,63.703125,-90.359375L58.71875,-90.359375Q58.921875,-80.65625,60.609375,-58.859375Q62.15625,-38.53125,62.15625,-27.28125Q62.15625,-24.828125,62.640625,-19.828125Q63.140625,-14.84375,63.140625,-12.3125Q63.140625,-7.53125,61.59375,-4.078125Q59.625,0.5625,55.484375,0.5625Q52.671875,0.5625,50.421875,-1.40625Q48.171875,-3.375,48.171875,-6.46875Q48.171875,-7.53125,48.515625,-9.640625Q48.875,-11.75,48.875,-12.796875Q48.875,-16.25,48.40625,-23.140625Q47.953125,-30.03125,47.953125,-33.546875Q47.953125,-43.59375,46.34375,-61.8125Q44.65625,-81.5,44.4375,-90.078125L38.046875,-90Q22.640625,-90,13.859375,-92.25Q8.15625,-93.734375,8.15625,-99.21875Q8.15625,-102.09375,9.984375,-104.28125Q12.09375,-106.734375,15.40625,-106.53125Q17.859375,-106.390625,26.65625,-105.265625Q33.6875,-104.421875,38.046875,-104.421875Q42.265625,-104.421875,50.84375,-104.59375Q59.421875,-104.765625,63.640625,-104.765625Q69.046875,-104.765625,79.703125,-103.984375Q90.359375,-103.21875,95.765625,-103.21875Q98.796875,-103.21875,100.828125,-101.140625Q102.875,-99.078125,102.875,-95.984375Q102.875,-92.953125,100.828125,-90.875Q98.796875,-88.8125,95.765625,-88.8125Z" />
</group>
</group>
</vector>
-13
View File
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false">
<shape>
<solid android:color="#333" />
</shape>
</item>
<item android:state_pressed="true">
<shape>
<solid android:color="#555" />
</shape>
</item>
</selector>
-13
View File
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false">
<shape>
<solid android:color="#222" />
</shape>
</item>
<item android:state_pressed="true">
<shape>
<solid android:color="#333" />
</shape>
</item>
</selector>
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/manual_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/default_space"
android:text="@string/app_name"
android:textColor="@android:color/primary_text_light"
android:textSize="@dimen/default_text_size" />
</ScrollView>
+49
View File
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/paste_button"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/button"
android:ellipsize="end"
android:gravity="center_vertical"
android:paddingLeft="14dp"
android:paddingTop="@dimen/default_space"
android:paddingRight="14dp"
android:paddingBottom="@dimen/default_space"
android:singleLine="true"
android:text="@string/clip_default_text"
android:textColor="@color/text_color"
android:textSize="@dimen/default_text_size" />
<ImageButton
android:id="@+id/button_delete"
android:layout_width="@dimen/button_size"
android:layout_height="@dimen/button_size"
android:background="@drawable/button"
android:contentDescription="@string/delete_button_description"
android:src="@drawable/ic_trashcan"
tools:ignore="UnusedAttribute" />
</LinearLayout>
+32
View File
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<eur.cfpifpsi.titanboard.views.ClipboardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/button_toggled"
android:scrollbarThumbVertical="@color/text_color"
tools:ignore="Overdraw">
<LinearLayout
android:id="@+id/clip_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</eur.cfpifpsi.titanboard.views.ClipboardView>
+64 -141
View File
@@ -1,155 +1,78 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!--
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" ~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<eur.cfpifpsi.titanboard.views.InputView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
style="@style/KeyboardBackground"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:orientation="vertical"> android:background="@color/background"
tools:ignore="Overdraw">
<LinearLayout <eur.cfpifpsi.titanboard.views.SymbolsView
android:id="@+id/candidates" android:id="@+id/symbols_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="match_parent"
android:orientation="horizontal" android:layout_alignTop="@id/clipboard_button"
android:visibility="visible"> android:layout_alignBottom="@id/clipboard_button"
android:layout_toStartOf="@id/tweaks_button"
<TextView android:layout_toLeftOf="@id/tweaks_button"
android:id="@+id/state_shift" android:contentDescription="@string/symbols_view_description"
style="@style/KeyboardState" android:scrollbarThumbHorizontal="@color/text_color">
android:layout_height="wrap_content"
android:text="aa"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/state_alt"
style="@style/KeyboardState"
android:layout_height="wrap_content"
android:text=".."
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/state_sym"
style="@style/KeyboardState"
android:layout_height="wrap_content"
android:layout_weight="0"
android:text=".."
tools:ignore="HardcodedText" />
<Button
android:id="@+id/language_button"
style="@style/KeyboardExtraButton"
android:layout_height="wrap_content"
android:onClick="onInputViewLanguageClick" />
</LinearLayout>
<View
style="@style/KeyboardDivider"
android:layout_width="match_parent" />
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout <LinearLayout
android:id="@+id/symbol_list"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="horizontal"> android:orientation="horizontal" />
</eur.cfpifpsi.titanboard.views.SymbolsView>
<Button <ImageButton
style="@style/KeyboardKey" android:id="@+id/clipboard_button"
android:layout_height="wrap_content" android:layout_width="@dimen/button_size"
android:text="^" android:layout_height="@dimen/button_size"
tools:ignore="HardcodedText" /> android:layout_toStartOf="@id/state_button"
android:layout_toLeftOf="@id/state_button"
android:background="@drawable/button"
android:contentDescription="@string/clipboard_button_description"
android:src="@drawable/ic_clipboard"
tools:ignore="UnusedAttribute" />
<Button <ImageButton
style="@style/KeyboardKey" android:id="@+id/tweaks_button"
android:layout_height="wrap_content" android:layout_width="@dimen/button_size"
android:text="~" android:layout_height="@dimen/button_size"
tools:ignore="HardcodedText" /> android:layout_toStartOf="@id/clipboard_button"
android:layout_toLeftOf="@id/clipboard_button"
android:background="@drawable/button"
android:contentDescription="@string/tweaks_button_description"
android:src="@drawable/ic_faders" />
<Button <eur.cfpifpsi.titanboard.views.StateButton
style="@style/KeyboardKey" android:id="@+id/state_button"
android:layout_height="wrap_content" android:layout_width="@dimen/button_size"
android:text="\\" android:layout_height="@dimen/button_size"
tools:ignore="HardcodedText" /> android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:background="@drawable/button_state"
android:text="@string/state_button_default"
android:textAllCaps="false"
android:textColor="@color/background"
android:textSize="@dimen/default_text_size" />
<Button </eur.cfpifpsi.titanboard.views.InputView>
style="@style/KeyboardKey"
android:layout_height="wrap_content"
android:text="="
tools:ignore="HardcodedText" />
<Button
style="@style/KeyboardKey"
android:layout_height="wrap_content"
android:text="&amp;"
tools:ignore="HardcodedText" />
<Button
style="@style/KeyboardKey"
android:layout_height="wrap_content"
android:text=";"
tools:ignore="HardcodedText" />
<Button
style="@style/KeyboardKey"
android:layout_height="wrap_content"
android:text="&lt;"
tools:ignore="HardcodedText" />
<Button
style="@style/KeyboardKey"
android:layout_height="wrap_content"
android:text="&gt;"
tools:ignore="HardcodedText" />
<Button
style="@style/KeyboardKey"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="¿"
tools:ignore="HardcodedText" />
<Button
style="@style/KeyboardKey"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="¡"
tools:ignore="HardcodedText" />
<Button
style="@style/KeyboardKey"
android:layout_height="wrap_content"
android:text="|"
tools:ignore="HardcodedText" />
<Button
style="@style/KeyboardKey"
android:layout_height="wrap_content"
android:text="["
tools:ignore="HardcodedText" />
<Button
style="@style/KeyboardKey"
android:layout_height="wrap_content"
android:text="]"
tools:ignore="HardcodedText" />
<Button
style="@style/KeyboardKey"
android:layout_height="wrap_content"
android:text="{"
tools:ignore="HardcodedText" />
<Button
style="@style/KeyboardKey"
android:layout_height="wrap_content"
android:text="}"
tools:ignore="HardcodedText" />
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
-28
View File
@@ -1,28 +0,0 @@
<?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">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Settings:" />
<Switch
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Auto-caps" />
<Switch
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Auto-period" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Languages:" />
</LinearLayout>
+27
View File
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/button"
android:gravity="center"
android:textColor="@color/text_color"
android:textSize="@dimen/symbol_text_size">
</TextView>
+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<eur.cfpifpsi.titanboard.views.TweaksView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/button_toggled"
android:orientation="vertical">
<CheckBox
android:id="@+id/composing_checkbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="@dimen/button_size"
android:padding="@dimen/default_space"
android:text="@string/tweaks_label_alternate_composing"
android:textColor="@color/text_color"
android:textSize="@dimen/default_text_size" />
<CheckBox
android:id="@+id/enter_checkbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="@dimen/button_size"
android:padding="@dimen/default_space"
android:text="@string/tweaks_label_enter_always_sends"
android:textColor="@color/text_color"
android:textSize="@dimen/default_text_size" />
</eur.cfpifpsi.titanboard.views.TweaksView>
+96
View File
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<resources>
<string-array name="preferences_language_labels">
<item>English</item>
<item>German</item>
<item>French</item>
<item>Italian</item>
<item>Spanish</item>
<item>Polish</item>
<item>Russian (Phonetic)</item>
<item>Greek (diacritics under Q)</item>
<item>Arabic (Phonetic)</item>
<item>Hebrew</item>
<item>Czech</item>
</string-array>
<string-array name="preferences_languages">
<item>en</item>
<item>de</item>
<item>fr</item>
<item>it</item>
<item>es</item>
<item>pl</item>
<item>ru</item>
<item>el</item>
<item>ar</item>
<item>he</item>
<item>cz</item>
</string-array>
<string-array name="preferences_language_defaults">
<item>en</item>
<item>ru</item>
</string-array>
<string-array name="preferences_device_labels">
<item>Unihertz Titan</item>
<item>Unihertz Titan Pocket/Slim</item>
</string-array>
<string-array name="preferences_devices">
<item>titan</item>
<item>titan_pocket_slim</item>
</string-array>
<string-array name="preferences_cycle_speed_labels">
<item>200 ms</item>
<item>300 ms</item>
<item>400 ms</item>
<item>500 ms</item>
<item>750 ms</item>
<item>1000 ms</item>
</string-array>
<string-array name="preferences_cycle_speeds">
<item>200</item>
<item>300</item>
<item>400</item>
<item>500</item>
<item>750</item>
<item>1000</item>
</string-array>
<string-array name="preferences_clipboard_size_labels">
<item>3 items</item>
<item>5 items</item>
<item>10 items</item>
<item>20 items</item>
<item>30 items</item>
</string-array>
<string-array name="preferences_clipboard_sizes">
<item>3</item>
<item>5</item>
<item>10</item>
<item>20</item>
<item>30</item>
</string-array>
</resources>
+21
View File
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<resources>
<attr name="state_toggled" format="boolean" />
</resources>
+21
View File
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<resources>
<bool name="cycle_manual_default">false</bool>
</resources>
+25
View File
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<resources>
<color name="background">#222</color>
<color name="button_toggled">#555</color>
<color name="button_active">#666</color>
<color name="text_color">#EEE</color>
<color name="danger_text_color">#FF0000</color>
</resources>
+31
View File
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<resources>
<dimen name="default_space">8dp</dimen>
<dimen name="button_size">48dp</dimen>
<dimen name="symbol_width">40dp</dimen>
<dimen name="popup_width">256dp</dimen>
<dimen name="clipboard_popup_height">256dp</dimen>
<dimen name="tweaks_popup_height">96dp</dimen>
<dimen name="default_text_size">16sp</dimen>
<dimen name="symbol_text_size">18sp</dimen>
</resources>
+191 -2
View File
@@ -1,5 +1,194 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<resources> <resources>
<string name="app_name">Titanboard</string> <string name="app_name">Titanboard</string>
<string name="ime_label">Titanboard</string> <string name="preferences_name">Titanboard preferences</string>
<string name="preferences_label">Titanboard preferences</string> <string name="manual_name">Titanboard manual</string>
<string name="device_default">titan</string>
<string name="language_default">en</string>
<string name="cycle_speed_default">500</string>
<string name="clipboard_size_default">5</string>
<string name="state_button_default">\??</string>
<string name="state_button_alt_locked">#!</string>
<string name="state_button_alt_latched">#?</string>
<string name="delete_button_description">Delete clipboard item</string>
<string name="clipboard_button_description">
Open clipboard or paste first item on long press
</string>
<string name="tweaks_button_description">
Open per-app tweaks window
</string>
<string name="symbols_view_description">
List visible when cycling through symbols
</string>
<string name="clip_default_text">Clip</string>
<string name="preferences_category_keyboard">Keyboard</string>
<string name="preferences_category_functionality">Functionality</string>
<string name="preferences_category_other">Other</string>
<string name="preferences_label_cycle_manual">
Cycle symbols by consecutively pressing a key
</string>
<string name="preferences_label_cycle_speed">Symbol cycling speed/timeout</string>
<string name="preferences_label_clipboard_size">Clipboard size</string>
<string name="preferences_label_device">Device</string>
<string name="preferences_label_languages">Languages</string>
<string name="preferences_label_user_manual">User manual</string>
<string name="preferences_summary_cycle_manual">
Overrides the default behaviour of long pressing to cycle
</string>
<string name="preferences_summary_copyright_info">
Copyleft 🄯 Emil Kosz, 2024. License: GPLv3
</string>
<!-- Even though some may not make sense, leave them be as not to break users' configurations
when updating -->
<string name="preferences_key_cycle_manual">cycle_manual</string>
<string name="preferences_key_languages">selected_keymaps</string>
<string name="preferences_key_device">device</string>
<string name="preferences_key_cycle_speed">cycle_speed</string>
<string name="preferences_key_clipboard_size">clipboard_size</string>
<string name="tweaks_label_alternate_composing">Alternate composing method</string>
<string name="tweaks_label_enter_always_sends">Enter always sends</string>
<string name="tweaks_key_alternate_composing">alternate_composing_for_</string>
<string name="tweaks_key_enter_always_sends">enter_always_sends_for_</string>
<string name="user_manual_html">
<![CDATA[
<h1>Introduction</h1>
<p>
Thank you for trying out Titanboard! This keyboard program is designed to make your
experience with physical keyboards as pleasant and powerful as possible. It was designed
to be intuitive, and work exactly as you would expect it to. This means that pressing
any key, with or without modifiers like Alt or Shift, will show the corresponding
character on the screen. For example, on the Unihertz Titan, pressing R will input
&quot;r&quot;, pressing Alt+R will input &quot;-&quot;, Shift+R will input
&quot;R&quot;. Whatever you see on your physical keyboard will show up on the screen.
</p>
<p>
However, on most physical keyboard devices, this makes it very hard to input characters
which aren&apos;t on it. For example, on the Unihertz Titan, it isn&apos;t possible to
input the &quot;&amp;&quot; symbol. The stock keyboard application solves this by using
an additional visual keyboard interface, but I think that misses the point. Instead,
Titanboard allows every symbol on the physical keyboard, even those with modifiers, to
have extra symbols. These are accessed by <i>cycling</i>.
</p>
<p>
<i>Cycling</i> is basically a method for accessing the extra symbols under a given key. By
default, this method is long pressing a key. For example, on the Unihertz Titan, the
) key (Alt+Y) has four symbols: &quot;)&quot;, &quot;&gt;&quot;, &quot;]&quot;,
&quot;}&quot;. The first is inputted by simply pressing Alt, then Y (or Alt and Y). The
rest can be accessed by pressing Alt (or holding), then pressing and holding Y. This
will start switching through the aforementioned characters at the rate specified in the
preferences (by default 500 ms).
</p>
<p>
There is an alternate cycling method which can be enabled under the &quot;Cycle symbols
by consecutively pressing a key&quot; setting in the preferences. This is
self-explanatory. Instead of long pressing like in the aforementioned example, you
instead quickly press the key several times. So, if I want to input &quot;]&quot;, I
press Alt, then I press Y three times. This is very good for fast typing.
</p>
<p>
If you tried the above, you might have noticed that the Alt or Shift keys can be
&quot;locked&quot; (or latched). This is the same behaviour like in regular virtual
keyboards Android. Simply put, pressing a modifier key once puts it into a latched
state, which means the next key pressed will be uppercased or be the alternative symbol.
You can also lock a modifier key by pressing it twice in a quick fashion. Otherwise,
modifier keys work as expected.
</p>
<h1>User Interface</h1>
<p>
The user interface contains four elements: the symbol list, the tweaks button (three
lines with circles mimicking fader controls), the clipboard button (a clipboard icon),
and the state/language button (the white square with letters). The first, if not
cycling, contains nothing and looks like a blank background. If you start typing in
extra symbols, then the available symbols and the currently selected one are shown.
</p>
<p>
The tweaks button, when pressed, will show a window with app-specific tweaks. These fix
often encountered problems that happen in interactions between apps and physical
keyboards. These settings can be set and are saved for every app. The &quot;Alternate
composing method&quot; tweak is useful for applications like a terminal, which do not
support composing characters. You&apos;ll know an application doesn&apos;t support it
when cycling symbols feels off, and extra characters are inputted other than the one you
desire. The &quot;Enter always sends&quot; tweak makes it so pressing the Enter key
always tries to send a message instead of inserting a new line.
</p>
<p>
The clipboard button, when pressed, shows the clipboard with all the recent copied
stuff. Each clip has a red trashcan next to it which deletes it from the list. Clicking
on the clip itself sends it to the application. Long pressing the clipboard button sends
the most recent clip to the application.
</p>
<p>
The language button shows the current language being used, in a two letter code
(English is en/En/EN, Arabic is ar/Ar/AR, etc.). It also shows the state of the modifier
keys: if there are no modifier keys latched, locked, or pressed, the language code is in
all lowercase — &quot;en&quot;. If Shift is latched, the first letter is uppercased —
&quot;En&quot;. If it is pressed, then the code is all uppercase — &quot;EN&quot;. If it
is locked, then it is all uppercase and underlined. The case is similar for the Alt key;
if Alt is latched, the button displays &quot;#?&quot;, if it is pressed, then
&quot;#!&quot;, and if locked, then the same as pressed but underlined.
</p>
<p>
Pressing the language button switches to the next language. Long pressing the language
button opens the keyboard settings.
</p>
<h1>Keymap quirks</h1>
<p>
Due to the limited nature of QWERTY phones, which rarely boast a full QWERTY (and for
that kind of phone this program is designed for), regular language keymaps cannot be
applied. However, Titanboard tries to be as close to them as possible anyway, unless
it is a naive feat. Here is a list of quirks in language keymaps, so that you can get
used to them:
</p>
<ul>
<li>All keymaps: language-specific currencies and punctuation marks are added onto
preexisting characters on the keyboard; for example, German quotes are under the
&quot; key.</li>
<li>English: no quirks at all</li>
<li>German: QWERTY, umlaut characters are under their corresponding characters
(ä under a, ß under s, etc.)</li>
<li>French: same as above</li>
<li>Italian: same as above</li>
<li>Spanish: same as above, quotation and exclamation mark keys have additional Spanish
opening types</li>
<li>Polish: same as above, except for the X key having Ź for easier typing</li>
<li>Russian: phonetic variant based on the Russian Phonetic Keyboard 2.0</li>
<li>Greek: same as a regular QWERTY layout, except the Q key has additional diacritics,
and all common diacriticized letters appear under their corresponding letters</li>
<li>Arabic: handmade layout, sort-of phonetic, keys with Shift are diacritics</li>
<li>Hebrew: almost like the QWERTY, except Alt-Gr symbols are moved to Shift</li>
<li>Czech: like all the others above</li>
<ul>
<h1>Miscellaneous</h1>
<p>
Regardless of the Enter key tweak, Shift+Enter will always input a new line.
</p>
]]>
</string>
</resources> </resources>
-59
View File
@@ -1,59 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="KeyboardBackground">
<item name="android:background">#222</item>
<item name="android:padding">2dp</item>
</style>
<style name="KeyboardDivider">
<item name="android:layout_height">3dp</item>
<item name="android:layout_marginTop">4dp</item>
<item name="android:layout_marginBottom">2dp</item>
<item name="android:background">#444</item>
</style>
<style name="KeyboardState">
<item name="android:textColor">#AAA</item>
<item name="android:textSize">18sp</item>
<item name="android:textAlignment">center</item>
<item name="android:typeface">monospace</item>
<item name="android:layout_width">40dp</item>
<item name="android:layout_gravity">center</item>
</style>
<style name="KeyboardExtraButton">
<item name="android:background">@drawable/key</item>
<item name="android:textColor">#FFF</item>
<item name="android:textSize">18sp</item>
<item name="android:layout_marginLeft">1dp</item>
<item name="android:layout_marginRight">1dp</item>
<item name="android:layout_marginTop">2dp</item>
<item name="android:layout_marginBottom">2dp</item>
<item name="android:layout_width">48dp</item>
</style>
<style name="KeyboardStateLocked" parent="KeyboardState">
<item name="android:textStyle">bold</item>
</style>
<style name="KeyboardCandidate">
<item name="android:background">@drawable/suggestion</item>
<item name="android:textColor">#FFF</item>
<item name="android:textSize">18sp</item>
<item name="android:layout_marginTop">2dp</item>
<item name="android:layout_width">wrap_content</item>
</style>
<style name="KeyboardKey">
<item name="android:background">@drawable/key</item>
<item name="android:textColor">#FFF</item>
<item name="android:textSize">18sp</item>
<item name="android:layout_marginLeft">1dp</item>
<item name="android:layout_marginRight">1dp</item>
<item name="android:layout_marginTop">2dp</item>
<item name="android:layout_marginBottom">2dp</item>
<item name="android:onClick">onInputViewKeyClick</item>
<item name="android:layout_width">72dp</item>
</style>
</resources>
+300
View File
@@ -0,0 +1,300 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<device-keymap-list>
<!-- This is the exemplary device keymap. -->
<device-keymap>
<!-- This is used to differentiate keymaps. It can be any string, really, but keep it
short and descriptive. -->
<id>titan</id>
<!-- These are the keycodes for the key that's responsible for accessing alternate symbols.
On BlackBerries, this is the sym key, here on the Titan, it's the alt key. -->
<symbol-meta-key-code>57</symbol-meta-key-code> <!-- Left alt -->
<symbol-meta-key-code>58</symbol-meta-key-code> <!-- Right alt -->
<!-- Symbols, apart from the keycode and alt values, can also contain <base> and <shift>
values, which can be used for phones that have extra keys, such as a dot key. It's
useful that, like in this example, you add more than the default single alternate key,
so the user can type in more symbols that don't exist on the physical keyboard. Make
sure to group them in a sensible way, for example, the : key having the ; symbol also, etc.
-->
<symbols>
<for-code>45</for-code> <!-- Q -->
<alt>:</alt>
<alt>;</alt>
</symbols>
<symbols>
<for-code>51</for-code> <!-- W -->
<alt>/</alt>
<alt>\</alt>
<alt>|</alt>
</symbols>
<symbols>
<for-code>33</for-code> <!-- E -->
<alt>_</alt>
<alt>~</alt>
</symbols>
<symbols>
<for-code>46</for-code> <!-- R -->
<alt>-</alt>
<alt>–</alt>
<alt>—</alt>
</symbols>
<symbols>
<for-code>48</for-code> <!-- T -->
<alt>(</alt>
<alt>&lt;</alt>
<alt>[</alt>
<alt>{</alt>
</symbols>
<symbols>
<for-code>53</for-code> <!-- Y -->
<alt>)</alt>
<alt>&gt;</alt>
<alt>]</alt>
<alt>}</alt>
</symbols>
<symbols>
<for-code>49</for-code> <!-- U -->
<alt>1</alt>
</symbols>
<symbols>
<for-code>37</for-code> <!-- I -->
<alt>2</alt>
</symbols>
<symbols>
<for-code>43</for-code> <!-- O -->
<alt>3</alt>
</symbols>
<symbols>
<for-code>44</for-code> <!-- P -->
<alt>0</alt>
</symbols>
<symbols>
<for-code>29</for-code> <!-- A -->
<alt>@</alt>
</symbols>
<symbols>
<for-code>47</for-code> <!-- S -->
<alt>'</alt>
</symbols>
<symbols>
<for-code>32</for-code> <!-- D -->
<alt>&quot;</alt>
</symbols>
<symbols>
<for-code>34</for-code> <!-- F -->
<alt>+</alt>
<alt>=</alt>
<alt>%</alt>
</symbols>
<symbols>
<for-code>35</for-code> <!-- G -->
<alt>*</alt>
<alt>^</alt>
<alt>`</alt>
</symbols>
<symbols>
<for-code>36</for-code> <!-- H -->
<alt>#</alt>
<alt>$</alt>
<alt>€</alt>
</symbols>
<symbols>
<for-code>38</for-code> <!-- J -->
<alt>4</alt>
</symbols>
<symbols>
<for-code>39</for-code> <!-- K -->
<alt>5</alt>
</symbols>
<symbols>
<for-code>40</for-code> <!-- L -->
<alt>6</alt>
</symbols>
<symbols>
<for-code>54</for-code> <!-- Z -->
<alt>!</alt>
</symbols>
<symbols>
<for-code>52</for-code> <!-- X -->
<alt>?</alt>
</symbols>
<symbols>
<for-code>31</for-code> <!-- C -->
<alt>,</alt>
<alt>&amp;</alt>
</symbols>
<symbols>
<for-code>50</for-code> <!-- V -->
<alt>.</alt>
<alt>…</alt>
</symbols>
<symbols>
<for-code>30</for-code> <!-- B -->
<alt>7</alt>
</symbols>
<symbols>
<for-code>42</for-code> <!-- N -->
<alt>8</alt>
</symbols>
<symbols>
<for-code>41</for-code> <!-- M -->
<alt>9</alt>
</symbols>
<symbols>
<for-code>62</for-code> <!-- Space -->
<!-- Yeah, a special tag, whatever. It still makes the code simpler... -->
<space />
</symbols>
</device-keymap>
<device-keymap>
<id>titan_pocket_slim</id>
<symbol-meta-key-code>57</symbol-meta-key-code> <!-- Left alt -->
<symbol-meta-key-code>58</symbol-meta-key-code> <!-- Right alt -->
<symbols>
<for-code>45</for-code> <!-- Q -->
<alt>0</alt>
</symbols>
<symbols>
<for-code>51</for-code> <!-- W -->
<alt>1</alt>
</symbols>
<symbols>
<for-code>33</for-code> <!-- E -->
<alt>2</alt>
</symbols>
<symbols>
<for-code>46</for-code> <!-- R -->
<alt>3</alt>
</symbols>
<symbols>
<for-code>48</for-code> <!-- T -->
<alt>(</alt>
<alt>&lt;</alt>
<alt>[</alt>
<alt>{</alt>
</symbols>
<symbols>
<for-code>53</for-code> <!-- Y -->
<alt>)</alt>
<alt>&gt;</alt>
<alt>]</alt>
<alt>}</alt>
</symbols>
<symbols>
<for-code>49</for-code> <!-- U -->
<alt>-</alt>
<alt>–</alt>
<alt>—</alt>
</symbols>
<symbols>
<for-code>37</for-code> <!-- I -->
<alt>_</alt>
<alt>~</alt>
</symbols>
<symbols>
<for-code>43</for-code> <!-- O -->
<alt>/</alt>
<alt>\</alt>
<alt>|</alt>
</symbols>
<symbols>
<for-code>44</for-code> <!-- P -->
<alt>:</alt>
<alt>;</alt>
</symbols>
<symbols>
<for-code>29</for-code> <!-- A -->
<alt>@</alt>
</symbols>
<symbols>
<for-code>47</for-code> <!-- S -->
<alt>4</alt>
</symbols>
<symbols>
<for-code>32</for-code> <!-- D -->
<alt>5</alt>
</symbols>
<symbols>
<for-code>34</for-code> <!-- F -->
<alt>6</alt>
</symbols>
<symbols>
<for-code>35</for-code> <!-- G -->
<alt>*</alt>
<alt>^</alt>
<alt>`</alt>
</symbols>
<symbols>
<for-code>36</for-code> <!-- H -->
<alt>#</alt>
<alt>$</alt>
<alt>€</alt>
</symbols>
<symbols>
<for-code>38</for-code> <!-- J -->
<alt>+</alt>
<alt>=</alt>
<alt>%</alt>
</symbols>
<symbols>
<for-code>39</for-code> <!-- K -->
<alt>"</alt>
</symbols>
<symbols>
<for-code>40</for-code> <!-- L -->
<alt>'</alt>
</symbols>
<symbols>
<for-code>54</for-code> <!-- Z -->
<alt>!</alt>
</symbols>
<symbols>
<for-code>52</for-code> <!-- X -->
<alt>7</alt>
</symbols>
<symbols>
<for-code>31</for-code> <!-- C -->
<alt>8</alt>
</symbols>
<symbols>
<for-code>50</for-code> <!-- V -->
<alt>9</alt>
</symbols>
<symbols>
<for-code>30</for-code> <!-- B -->
<alt>.</alt>
<alt>…</alt>
</symbols>
<symbols>
<for-code>42</for-code> <!-- N -->
<alt>,</alt>
<alt>&amp;</alt>
</symbols>
<symbols>
<for-code>41</for-code> <!-- M -->
<alt>?</alt>
</symbols>
<symbols>
<for-code>62</for-code> <!-- Space -->
<space />
</symbols>
</device-keymap>
</device-keymap-list>
-927
View File
@@ -1,927 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for the Unihertz Titan/physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
~
-->
<keymap-list>
<!-- Spanish-->
<keymap
label="Spanish"
lang="es_ES"
short-label="ES">
<key
alt="@"
code="29"
none="a"
shift="A"
shiftSym="Á"
sym="á" />
<key
alt="7"
code="30"
none="b"
shift="B" />
<key
alt=","
code="31"
none="c"
shift="C"
shiftSym="Ç"
sym="ç" />
<key
alt="&quot;"
code="32"
none="d"
shift="D" />
<key
alt="_"
code="33"
none="e"
shift="E"
shiftSym="É"
sym="é" />
<key
alt="+"
code="34"
none="f"
shift="F" />
<key
alt="*"
code="35"
none="g"
shift="G" />
<key
alt="#"
code="36"
none="h"
shift="H" />
<key
alt="2"
code="37"
none="i"
shift="I"
shiftSym="Í"
sym="í" />
<key
alt="4"
code="38"
none="j"
shift="J" />
<key
alt="5"
code="39"
none="k"
shift="K" />
<key
alt="6"
code="40"
none="l"
shift="L" />
<key
alt="9"
code="41"
none="m"
shift="M" />
<key
alt="8"
code="42"
none="n"
shift="N"
shiftSym="Ñ"
sym="ñ" />
<key
alt="3"
code="43"
none="o"
shift="O"
shiftSym="Ó"
sym="ó" />
<key
alt="0"
code="44"
none="p"
shift="P" />
<key
alt=":"
code="45"
none="q"
shift="Q" />
<key
alt="-"
code="46"
none="r"
shift="R" />
<key
alt="&apos;"
code="47"
none="s"
shift="S" />
<key
alt="("
code="48"
none="t"
shift="T" />
<key
alt="1"
code="49"
none="u"
shift="U"
shiftSym="Ú"
sym="ú" />
<key
alt="."
code="50"
none="v"
shift="V" />
<key
alt="/"
code="51"
none="w"
shift="W" />
<key
alt="?"
code="52"
none="x"
shift="X" />
<key
alt=")"
code="53"
none="y"
shift="Y" />
<key
alt="!"
code="54"
none="z"
shift="Z" />
</keymap>
<!-- French-->
<keymap
label="French"
lang="fr_FR"
short-label="FR">
<!-- A-->
<key
alt="@"
code="29"
none="q"
shift="Q" />
<key
alt="7"
code="30"
none="b"
shift="B" />
<key
alt=","
code="31"
none="c"
shift="C"
shiftSym="Ç"
sym="ç" />
<key
alt="&quot;"
code="32"
none="d"
shift="D" />
<key
alt="_"
code="33"
none="e"
shift="E"
shiftSym="É"
sym="é" />
<key
alt="+"
code="34"
none="f"
shift="F" />
<key
alt="*"
code="35"
none="g"
shift="G" />
<key
alt="#"
code="36"
none="h"
shift="H" />
<key
alt="2"
code="37"
none="i"
shift="I"
shiftSym="Î"
sym="î" />
<key
alt="4"
code="38"
none="j"
shift="J" />
<key
alt="5"
code="39"
none="k"
shift="K" />
<key
alt="6"
code="40"
none="l"
shift="L" />
<key
alt="9"
code="41"
none="m"
shift="M" />
<key
alt="8"
code="42"
none="n"
shift="N" />
<key
alt="3"
code="43"
none="o"
shift="O"
shiftSym="Ô"
sym="ô" />
<key
alt="0"
code="44"
none="p"
shift="P" />
<!-- Q-->
<key
alt=":"
code="45"
none="a"
shift="A"
shiftSym="À"
sym="à" />
<key
alt="-"
code="46"
none="r"
shift="R" />
<key
alt="&apos;"
code="47"
none="s"
shift="S" />
<key
alt="("
code="48"
none="t"
shift="T" />
<key
alt="1"
code="49"
none="u"
shift="U"
shiftSym="Ù"
sym="ù" />
<key
alt="."
code="50"
none="v"
shift="V" />
<!-- W-->
<key
alt="/"
code="51"
none="z"
shift="Z" />
<key
alt="?"
code="52"
none="x"
shift="X" />
<key
alt=")"
code="53"
none="y"
shift="Y"
shiftSym="Ÿ"
sym="ÿ" />
<!-- Z-->
<key
alt="!"
code="54"
none="w"
shift="W" />
</keymap>
<!-- Polish-->
<keymap
label="Polish"
lang="pl_PL"
short-label="PL">
<key
alt="@"
code="29"
none="a"
shift="A"
shiftSym="Ą"
sym="ą" />
<key
alt="7"
code="30"
none="b"
shift="B" />
<key
alt=","
code="31"
none="c"
shift="C"
shiftSym="Ć"
sym="ć" />
<key
alt="&quot;"
code="32"
none="d"
shift="D" />
<key
alt="_"
code="33"
none="e"
shift="E"
shiftSym="Ę"
sym="ę" />
<key
alt="+"
code="34"
none="f"
shift="F" />
<key
alt="*"
code="35"
none="g"
shift="G" />
<key
alt="#"
code="36"
none="h"
shift="H" />
<key
alt="2"
code="37"
none="i"
shift="I" />
<key
alt="4"
code="38"
none="j"
shift="J" />
<key
alt="5"
code="39"
none="k"
shift="K" />
<key
alt="6"
code="40"
none="l"
shift="L"
shiftSym="Ł"
sym="ł" />
<key
alt="9"
code="41"
none="m"
shift="M" />
<key
alt="8"
code="42"
none="n"
shift="N"
shiftSym="Ń"
sym="ń" />
<key
alt="3"
code="43"
none="o"
shift="O"
shiftSym="Ó"
sym="ó" />
<key
alt="0"
code="44"
none="p"
shift="P" />
<key
alt=":"
code="45"
none="q"
shift="Q" />
<key
alt="-"
code="46"
none="r"
shift="R" />
<key
alt="&apos;"
code="47"
none="s"
shift="S"
shiftSym="Ś"
sym="ś" />
<key
alt="("
code="48"
none="t"
shift="T" />
<key
alt="1"
code="49"
none="u"
shift="U" />
<key
alt="."
code="50"
none="v"
shift="V" />
<key
alt="/"
code="51"
none="w"
shift="W" />
<key
alt="?"
code="52"
none="x"
shift="X"
shiftSym="Ź"
sym="ź" />
<key
alt=")"
code="53"
none="y"
shift="Y" />
<key
alt="!"
code="54"
none="z"
shift="Z"
shiftSym="Ż"
sym="ż" />
</keymap>
<!-- Greek-->
<keymap
label="Greek"
lang="gr_GR"
short-label="GR">
<key
alt="@"
code="29"
none="α"
shift="Α" />
<key
alt="7"
code="30"
none="β"
shift="Β" />
<!-- C-->
<key
alt=","
code="31"
none="ψ"
shift="Ψ" />
<key
alt="&quot;"
code="32"
none="δ"
shift="Δ" />
<key
alt="_"
code="33"
none="ε"
shift="E" />
<key
alt="+"
code="34"
none="φ"
shift="Φ" />
<key
alt="*"
code="35"
none="γ"
shift="Γ" />
<key
alt="#"
code="36"
none="η"
shift="H" />
<key
alt="2"
code="37"
none="ι"
shift="I" />
<key
alt="4"
code="38"
none="ξ"
shift="Ξ" />
<key
alt="5"
code="39"
none="κ"
shift="K" />
<key
alt="6"
code="40"
none="λ"
shift="Λ" />
<key
alt="9"
code="41"
none="μ"
shift="M" />
<key
alt="8"
code="42"
none="ν"
shift="N" />
<key
alt="3"
code="43"
none="o"
shift="O" />
<key
alt="0"
code="44"
none="π"
shift="Π" />
<key
alt=":"
code="45"
none=";"
shift=":" />
<key
alt="-"
code="46"
none="ρ"
shift="Ρ" />
<key
alt="&apos;"
code="47"
none="σ"
shift="Σ" />
<key
alt="("
code="48"
none="τ"
shift="T" />
<key
alt="1"
code="49"
none="θ"
shift="Θ" />
<key
alt="."
code="50"
none="ω"
shift="Ω" />
<key
alt="/"
code="51"
none="ς" />
<key
alt="?"
code="52"
none="χ"
shift="X" />
<key
alt=")"
code="53"
none="υ"
shift="Y" />
<key
alt="!"
code="54"
none="ζ"
shift="Z" />
</keymap>
<!-- Russian-->
<keymap
label="Russian"
lang="ru_RU"
short-label="RU">
<!-- A-->
<key
alt="@"
code="29"
none="ф"
shift="Ф" />
<!-- B-->
<key
alt="7"
code="30"
none="и"
shift="И"
shiftSym="Ё"
sym="ё" />
<!-- C-->
<key
alt=","
code="31"
none="c"
shift="C" />
<!-- D-->
<key
alt="&quot;"
code="32"
none="в"
shift="В"
shiftSym="Б"
sym="б" />
<!-- E-->
<key
alt="_"
code="33"
none="у"
shift="У" />
<!-- F-->
<key
alt="+"
code="34"
none="a"
shift="A" />
<!-- G-->
<key
alt="*"
code="35"
none="п"
shift="П" />
<!-- H-->
<key
alt="#"
code="36"
none="р"
shift="Р" />
<!-- I-->
<key
alt="2"
code="37"
none="ш"
shift="Ш" />
<!-- J-->
<key
alt="4"
code="38"
none="o"
shift="O" />
<!-- K-->
<key
alt="5"
code="39"
none="л"
shift="Л" />
<!-- L-->
<key
alt="6"
code="40"
none="д"
shift="Д" />
<!-- M-->
<key
alt="9"
code="41"
none="ь"
shift="Ь"
shiftSym="Ъ"
sym="ъ" />
<!-- N-->
<key
alt="8"
code="42"
none="т"
shift="T" />
<!-- O-->
<key
alt="3"
code="43"
none="щ"
shift="Щ" />
<!-- P, — z & ż -->
<key
alt="0"
code="44"
none="з"
shift="З"
shiftSym="Ж"
sym="ж" />
<!-- Q-->
<key
alt=":"
code="45"
none="й"
shift="Й"
shiftSym="Ю"
sym="ю" />
<!-- R-->
<key
alt="-"
code="46"
none="к"
shift="K"
shiftSym="Х"
sym="х" />
<!-- S-->
<key
alt="&apos;"
code="47"
none="ы"
shift="Ы" />
<!-- T-->
<key
alt="("
code="48"
none="e"
shift="E"
shiftSym="Э"
sym="э" />
<!-- U-->
<key
alt="1"
code="49"
none="г"
shift="Г" />
<!-- V-->
<key
alt="."
code="50"
none="м"
shift="M" />
<!-- W-->
<key
alt="/"
code="51"
none="ц"
shift="Ц" />
<!-- X-->
<key
alt="?"
code="52"
none="ч"
shift="Ч" />
<!-- Y-->
<key
alt=")"
code="53"
none="н"
shift="Н" />
<!-- Z-->
<key
alt="!"
code="54"
none="я"
shift="Я" />
</keymap>
<!-- English (US)-->
<keymap
label="English"
lang="en_US"
short-label="US">
<key
alt="@"
code="29"
none="a"
shift="A" />
<key
alt="7"
code="30"
none="b"
shift="B" />
<key
alt=","
code="31"
none="c"
shift="C" />
<key
alt="&quot;"
code="32"
none="d"
shift="D" />
<key
alt="_"
code="33"
none="e"
shift="E" />
<key
alt="+"
code="34"
none="f"
shift="F" />
<key
alt="*"
code="35"
none="g"
shift="G" />
<key
alt="#"
code="36"
none="h"
shift="H" />
<key
alt="2"
code="37"
none="i"
shift="I" />
<key
alt="4"
code="38"
none="j"
shift="J" />
<key
alt="5"
code="39"
none="k"
shift="K" />
<key
alt="6"
code="40"
none="l"
shift="L" />
<key
alt="9"
code="41"
none="m"
shift="M" />
<key
alt="8"
code="42"
none="n"
shift="N" />
<key
alt="3"
code="43"
none="o"
shift="O" />
<key
alt="0"
code="44"
none="p"
shift="P" />
<key
alt=":"
code="45"
none="q"
shift="Q" />
<key
alt="-"
code="46"
none="r"
shift="R" />
<key
alt="&apos;"
code="47"
none="s"
shift="S" />
<key
alt="("
code="48"
none="t"
shift="T" />
<key
alt="1"
code="49"
none="u"
shift="U" />
<key
alt="."
code="50"
none="v"
shift="V" />
<key
alt="/"
code="51"
none="w"
shift="W" />
<key
alt="?"
code="52"
none="x"
shift="X" />
<key
alt=")"
code="53"
none="y"
shift="Y" />
<key
alt="!"
code="54"
none="z"
shift="Z" />
</keymap>
</keymap-list>
File diff suppressed because it is too large Load Diff
+27 -4
View File
@@ -1,9 +1,32 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!--
~ An input method for the Unihertz Titan/physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
~
-->
<input-method xmlns:android="http://schemas.android.com/apk/res/android" <input-method xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="cfpi_fpsi.eu.titanboard.PreferencesActivity" xmlns:tools="http://schemas.android.com/tools"
android:supportsSwitchingToNextInputMethod="false "> android:settingsActivity="eur.cfpifpsi.titanboard.SettingsActivity">
<!-- Needed for higher Android version so it can be the only used input method.
None of these subtypes are actually used. -->
<subtype
android:imeSubtypeMode="keyboard"
tools:targetApi="honeycomb" />
<subtype <subtype
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:isAsciiCapable="true" android:isAsciiCapable="true"
android:label="Default" /> tools:targetApi="kitkat" />
</input-method> </input-method>
+68
View File
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ An input method for the Unihertz Titan/physical keyboard phones
~ Copyright (C) 2024 Emil Kosz
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
~
-->
<!--suppress DeprecatedClassUsageInspection -->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/preferences_category_keyboard">
<ListPreference
android:defaultValue="@string/device_default"
android:entries="@array/preferences_device_labels"
android:entryValues="@array/preferences_devices"
android:key="@string/preferences_key_device"
android:title="@string/preferences_label_device" />
<MultiSelectListPreference
android:defaultValue="@array/preferences_language_defaults"
android:entries="@array/preferences_language_labels"
android:entryValues="@array/preferences_languages"
android:key="@string/preferences_key_languages"
android:title="@string/preferences_label_languages" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/preferences_category_functionality">
<SwitchPreference
android:defaultValue="@bool/cycle_manual_default"
android:key="@string/preferences_key_cycle_manual"
android:summary="@string/preferences_summary_cycle_manual"
android:title="@string/preferences_label_cycle_manual" />
<ListPreference
android:defaultValue="@string/cycle_speed_default"
android:disableDependentsState="true"
android:entries="@array/preferences_cycle_speed_labels"
android:entryValues="@array/preferences_cycle_speeds"
android:key="@string/preferences_key_cycle_speed"
android:title="@string/preferences_label_cycle_speed" />
<ListPreference
android:defaultValue="@string/clipboard_size_default"
android:entries="@array/preferences_clipboard_size_labels"
android:entryValues="@array/preferences_clipboard_sizes"
android:key="@string/preferences_key_clipboard_size"
android:title="@string/preferences_label_clipboard_size" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/preferences_category_other">
<Preference android:title="@string/preferences_label_user_manual">
<intent
android:action="eur.cfpifpsi.titanboard.intent.SHOW_MANUAL"
android:targetClass="eur.cfpifpsi.titanboard.ManualActivity" />
</Preference>
<Preference
android:selectable="false"
android:summary="@string/preferences_summary_copyright_info" />
</PreferenceCategory>
</PreferenceScreen>
+1 -1
View File
@@ -1,4 +1,4 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
alias(libs.plugins.android.application) apply false alias(libs.plugins.android.application) apply false
} }
+1 -3
View File
@@ -1,7 +1,5 @@
[versions] [versions]
agp = "8.4.1" agp = "8.4.2"
[libraries]
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

+4 -4
View File
@@ -2,9 +2,9 @@ pluginManagement {
repositories { repositories {
google { google {
content { content {
includeGroupByRegex("com\\.android.*") includeGroupByRegex('com\\.android.*')
includeGroupByRegex("com\\.google.*") includeGroupByRegex('com\\.google.*')
includeGroupByRegex("androidx.*") includeGroupByRegex('androidx.*')
} }
} }
mavenCentral() mavenCentral()
@@ -19,5 +19,5 @@ dependencyResolutionManagement {
} }
} }
rootProject.name = "Titanboard" rootProject.name = 'Titanboard'
include ':app' include ':app'