Skip to content

Commit 511b432

Browse files
committed
Initial commit
0 parents  commit 511b432

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2429
-0
lines changed

.gitignore

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
*.iml
2+
.gradle
3+
/local.properties
4+
/.idea
5+
/.idea/caches
6+
/.idea/libraries
7+
/.idea/modules.xml
8+
/.idea/workspace.xml
9+
/.idea/navEditor.xml
10+
/.idea/assetWizardSettings.xml
11+
.DS_Store
12+
/build
13+
/captures
14+
.externalNativeBuild
15+
.cxx
16+
local.properties

README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Introduction
2+
3+
This is very much a Work-In-Progress and that'll be evident at this early stage.
4+
5+
TapStrapApp (//TODO: Get a real name), Android Input Method Editor (IME) designed for use with the Tap Strap chording keyboard.
6+
This project aims to significantly enhance the typing experience for Tap Strap users by addressing the inherent limitations of the device's firmware.
7+
8+
## Present Features:
9+
10+
* Useable for More Than Text: Overcomes the Tap Strap's issue of typing the wrong letter followed by a backspace.
11+
* On-Screen IME Keyboard: Displays the current state, indicating the recently chorded fingers and held meta keys (ALT, SHIFT, CTRL, META). These indicators are also clickable for convenience.
12+
* Advanced Chording Feedback: Shows the output for single, double, and triple taps (and beyond), offering more depth than the standard Tap Strap behavior. These are also clickable if you like.
13+
* Customizable Chording: Unlike the Tap Strap's web-based configuration, our IME allows local programming of any chord using standard CSV files, providing flexibility and extensive customization. ANY CHORD. You sick of several chords being reserved and un-editable in the Tap Systems Tapmapper? NO LONGER!
14+
* More Taps: You want triple-taps? Quad-taps? There's no limit! I just don't know what you call 5 or more. You want SHIFT mode to do double-taps, triple taps? Have it! You want SWITCH mode to... okay you get it.
15+
* More shifts/switches: You can define as many different modes as you like and each can have infinite taps.
16+
* (Optional) Tap Cycling: If you keep double-triple-etc tapping, after you go past the end of what's configured, it wraps back around to single-tap again, so you can "go back" without getting the wrong output first.
17+
* Does All the Keys: You know the list of like a thousand Android KeyCodes? Well, we do! ( See /config/DEFAULT_1 )
18+
* Multiple Configuration Profiles: Supports switching between unlimited chording configurations to suit different use cases.
19+
* Super Untested And Unfinished Code: You won't see that from Tap Systems!
20+
21+
## Upcoming Features:
22+
23+
* Hopefully nothing -- it would be infintely prefereable to see Tap Systems make these features available on-device, most particularly the elimination of the backspace behavior and the ability to program chords offline. But until they do...
24+
* Mouse mode
25+
* Better UI/In-app Configuration
26+
* System service to make it useful even when Android doesn't want to use the IME (anytime you're not in a text entry, basically) -- work more like a hardware keyboard.
27+
* I'd still like to add a HID "passthrough" that allows you to connect your phone to another device and actually use the Tap Strap with these IME features on any bluetooth device. But so far, no luck.
28+
29+
## IMPORTANT NOTE:
30+
31+
It's full of bugs and will probably set your phone on fire. I can't take any responsibility for you choosing to use what's OBVIOUSLY unfinished code!
32+
33+
## Getting Started
34+
35+
### "Quick" Start:
36+
* Install APK
37+
* Run the "TapStrapIME" App and take note of the directory it tells you to put the configuration files into.
38+
* Copy the default configuration from /config in this repository or write you own and put them in the indicated directory on your phone. You can use any file manager, or adb, there's a script in the config.
39+
* Go to your Android settings, find the keyboard settings (it varies) and you should find a page there where you can chose which of your installed IME/Kayboards are allowed to run. Turn on TapStrapIME. You can and should leave your usual keyboards enabled as well.
40+
* (Optional but HIGHLY suggested:) If you have an option to always display the keyboard switcher (maybe "Keyboard button") make sure it's turned on.
41+
* (Optional but suggested:) Probably on another settings screen, find the option to display the software keyboard even if a physical keyboard is attached.
42+
* Start up your Tap Strap, make sure it's connected to your Android device successfully.
43+
* Use the settings menu or the "Keyboard button" you just turned on to switch your default keyboard to the TapStrapIME.
44+
* Find someplace to input text and try it out. The provided config files should match the defaults in the TapStrap hardware.
45+
46+
47+
## Developer's Note(s)
48+
49+
I absolutely love the Tap Strap, but as soon as I finished learning all the single-tap letters, I realized I'd never be able to use it as a general-purpose keyboard while it had this typing-the-wrong-character-then-backspacing behavior.
50+
51+
Although the best way to fix the problem would be in the Tap Strap firmware directly, Tap Systems haven't implemented an option to toggle the bizarre backspace behavior... yet. They did provide a really nice SDK we used for this project, but it can only take you so far.
52+
53+
If this project eventually becomes useful to you and you'd like to show support, the developer appreciates donations to the Kitten.Academy Patreon. https://www.patreon.com/kittenacademy
54+
55+
## Copyright Chris Jansen, etc, etc, you can use this for yourself, but we haven't arranged any licensing for you to modify the source or redistribute yet.
56+
57+
Aside from the Android boilerplate and the actual Tap Systems SDK, this is all original code so as of now the licensing is pretty much my problem.
58+
I'm supposed to say "all rights reserved" here, right?
59+
60+
I'm not easy to contact but you can try [email protected] if you like.

app/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

app/build.gradle.kts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import java.io.ByteArrayOutputStream
2+
3+
plugins {
4+
id("com.android.application")
5+
id("org.jetbrains.kotlin.android")
6+
}
7+
8+
android {
9+
namespace = "com.scribblej.tapstrapapp"
10+
compileSdk = 34
11+
12+
val gitCommitHash: String by lazy {
13+
ByteArrayOutputStream().also { outputStream ->
14+
exec {
15+
commandLine = "git rev-parse --short HEAD".split(" ")
16+
standardOutput = outputStream
17+
}
18+
}.toString().trim()
19+
}
20+
21+
defaultConfig {
22+
applicationId = "com.scribblej.tapstrapapp"
23+
minSdk = 26
24+
targetSdk = 34
25+
versionCode = 1
26+
versionName = "1.0.$gitCommitHash"
27+
28+
vectorDrawables {
29+
useSupportLibrary = true
30+
}
31+
32+
}
33+
34+
buildTypes {
35+
release {
36+
isMinifyEnabled = false
37+
proguardFiles(
38+
getDefaultProguardFile("proguard-android-optimize.txt"),
39+
"proguard-rules.pro"
40+
)
41+
}
42+
}
43+
compileOptions {
44+
sourceCompatibility = JavaVersion.VERSION_1_8
45+
targetCompatibility = JavaVersion.VERSION_1_8
46+
}
47+
kotlinOptions {
48+
jvmTarget = "1.8"
49+
}
50+
buildFeatures {
51+
compose = true
52+
}
53+
composeOptions {
54+
kotlinCompilerExtensionVersion = "1.4.3"
55+
}
56+
packaging {
57+
resources {
58+
excludes += "/META-INF/{AL2.0,LGPL2.1}"
59+
}
60+
}
61+
}
62+
63+
dependencies {
64+
65+
implementation("androidx.appcompat:appcompat:1.6.1")
66+
67+
implementation("androidx.core:core-ktx:1.12.0")
68+
implementation("androidx.percentlayout:percentlayout:1.0.0")
69+
implementation("androidx.legacy:legacy-support-v4:1.0.0")
70+
71+
implementation("androidx.recyclerview:recyclerview:1.3.2")
72+
implementation(platform("androidx.compose:compose-bom:2023.10.01"))
73+
implementation("androidx.compose.ui:ui")
74+
implementation("androidx.compose.ui:ui-tooling-preview")
75+
implementation("androidx.wear.compose:compose-material:1.2.1")
76+
implementation("androidx.wear.compose:compose-foundation:1.2.1")
77+
implementation("androidx.activity:activity-compose:1.8.2")
78+
implementation("androidx.compose.material:material-icons-core")
79+
80+
androidTestImplementation(platform("androidx.compose:compose-bom:2023.10.01"))
81+
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
82+
debugImplementation("androidx.compose.ui:ui-tooling")
83+
debugImplementation("androidx.compose.ui:ui-test-manifest")
84+
85+
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
86+
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2")
87+
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.2")
88+
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.2")
89+
implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.2")
90+
91+
// Annotation processor
92+
annotationProcessor("androidx.lifecycle:lifecycle-compiler:2.6.2")
93+
94+
implementation("androidx.preference:preference-ktx:1.2.1")
95+
96+
// optional - helpers for implementing LifecycleOwner in a Service
97+
implementation("androidx.lifecycle:lifecycle-service:2.6.2")
98+
99+
// optional - ProcessLifecycleOwner provides a lifecycle for the whole application process
100+
implementation("androidx.lifecycle:lifecycle-process:2.6.2")
101+
102+
// Tap Strap
103+
implementation("io.github.tapwithus:tap-android-sdk:0.3.6")
104+
105+
// Apache CSV parser (used in TapMap)
106+
implementation("org.apache.commons:commons-csv:1.10.0")
107+
}

app/proguard-rules.pro

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile

app/src/main/AndroidManifest.xml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="com.scribblej.tapstrapapp">
4+
5+
6+
<uses-permission android:name="android.permission.WAKE_LOCK" />
7+
8+
<uses-feature android:name="android.hardware.type.watch" android:required="false" />
9+
10+
<!-- Bluetooth Permissions -->
11+
<uses-permission android:name="android.permission.BLUETOOTH"/>
12+
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
13+
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
14+
15+
<!-- Location Permission (required for Bluetooth scanning on Android 6.0 and above) -->
16+
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
17+
18+
<application
19+
android:allowBackup="true"
20+
android:icon="@mipmap/ic_launcher"
21+
android:label="@string/app_name"
22+
android:supportsRtl="true"
23+
android:theme="@android:style/Theme.DeviceDefault">
24+
<uses-library
25+
android:name="com.google.android.wearable"
26+
android:required="false" />
27+
28+
<meta-data
29+
android:name="com.google.android.wearable.standalone"
30+
android:value="true" />
31+
32+
<activity android:name=".presentation.TapInputSettings"
33+
android:label="@string/tap_ime_name"
34+
android:exported="true" >
35+
<intent-filter>
36+
<action android:name="android.settings.INPUT_METHOD_SETTINGS" />
37+
<action android:name="android.intent.action.APPLICATION_PREFERENCES" />
38+
<category android:name="android.intent.category.DEFAULT" />
39+
<action android:name="android.intent.action.MAIN" />
40+
<category android:name="android.intent.category.LAUNCHER" />
41+
</intent-filter>
42+
</activity>
43+
44+
<service
45+
android:name=".TapInputMethodService"
46+
android:label="@string/tap_ime_name"
47+
android:permission="android.permission.BIND_INPUT_METHOD"
48+
android:theme="@android:style/Theme.DeviceDefault"
49+
android:exported="true">
50+
<intent-filter>
51+
<action android:name="android.view.InputMethod" />
52+
</intent-filter>
53+
<meta-data
54+
android:name="android.view.im"
55+
android:resource="@xml/method" />
56+
</service>
57+
</application>
58+
</manifest>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.scribblej.tapstrapapp
2+
3+
import android.content.Intent
4+
import android.inputmethodservice.InputMethodService
5+
import androidx.annotation.CallSuper
6+
import androidx.lifecycle.Lifecycle
7+
import androidx.lifecycle.LifecycleOwner
8+
import androidx.lifecycle.ServiceLifecycleDispatcher
9+
10+
abstract class LifecycleInputMethodService : InputMethodService(), LifecycleOwner {
11+
12+
protected val dispatcher = ServiceLifecycleDispatcher(this)
13+
14+
@CallSuper
15+
override fun onCreate() {
16+
dispatcher.onServicePreSuperOnCreate()
17+
super.onCreate()
18+
}
19+
20+
override fun onBindInput() {
21+
super.onBindInput()
22+
dispatcher.onServicePreSuperOnBind()
23+
}
24+
25+
// this method is added only to annotate it with @CallSuper.
26+
// In usual service super.onStartCommand is no-op, but in LifecycleService
27+
// it results in mDispatcher.onServicePreSuperOnStart() call, because
28+
// super.onStartCommand calls onStart().
29+
@CallSuper
30+
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
31+
return super.onStartCommand(intent, flags, startId)
32+
}
33+
34+
@CallSuper
35+
override fun onDestroy() {
36+
dispatcher.onServicePreSuperOnDestroy()
37+
super.onDestroy()
38+
}
39+
40+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.scribblej.tapstrapapp
2+
3+
object MetaKeysManager {
4+
private var _metaKeys: Int = 0
5+
val metaKeys: Int
6+
get() = _metaKeys
7+
private var _useOnce: Boolean = false
8+
val useOnce: Boolean
9+
get() = _useOnce
10+
11+
private val metaKeysObservers = mutableListOf<(Int, Boolean) -> Unit>()
12+
13+
fun activateMetaKeys(newMetaKeys: Int, newUseOnce: Boolean = false) {
14+
_metaKeys = _metaKeys or newMetaKeys
15+
_useOnce = newUseOnce
16+
notifyObservers()
17+
}
18+
19+
fun setMetaKeys(newMetaKeys: Int, newUseOnce: Boolean = false) {
20+
_metaKeys = newMetaKeys
21+
_useOnce = newUseOnce
22+
notifyObservers()
23+
}
24+
25+
fun resetMetaKeys() {
26+
setMetaKeys(0,false)
27+
}
28+
29+
fun toggleMetaKey(mK: Int) {
30+
_metaKeys = _metaKeys xor mK
31+
notifyObservers()
32+
}
33+
34+
private fun notifyObservers() {
35+
metaKeysObservers.forEach { it(_metaKeys, _useOnce) }
36+
}
37+
38+
fun addObserver(observer: (Int, Boolean) -> Unit) {
39+
metaKeysObservers.add(observer)
40+
}
41+
42+
fun removeObserver(observer: (Int, Boolean) -> Unit) {
43+
metaKeysObservers.remove(observer)
44+
}
45+
}

0 commit comments

Comments
 (0)