Skip to content

Commit 1a0ee1f

Browse files
authored
Merge pull request #5 from Sparkleseditor/terminal
2 parents 103314d + 5110e17 commit 1a0ee1f

20 files changed

Lines changed: 2510 additions & 30 deletions

app/build.gradle.kts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,13 @@ dependencies {
6363
implementation(libs.transition)
6464
implementation(libs.fragment)
6565
implementation(libs.activity)
66-
implementation(project(":filetree"))
66+
implementation(libs.terminal.view)
67+
implementation(libs.terminal.emulator)
68+
implementation(libs.utilcodex)
6769

6870

71+
// with Paths
72+
implementation(project(":filetree"))
6973

7074
//tests
7175
coreLibraryDesugaring(libs.desugar.jdk.libs)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.sparkleseditor.components.virtualkeys;
2+
3+
import androidx.annotation.NonNull;
4+
import java.util.HashMap;
5+
6+
/** The {@link Class} that implements special buttons for {@link VirtualKeysView}. */
7+
public class SpecialButton {
8+
9+
private static final HashMap<String, SpecialButton> map = new HashMap<>();
10+
public static final SpecialButton CTRL = new SpecialButton("CTRL");
11+
public static final SpecialButton ALT = new SpecialButton("ALT");
12+
public static final SpecialButton SHIFT = new SpecialButton("SHIFT");
13+
public static final SpecialButton FN = new SpecialButton("FN");
14+
15+
/** The special button key. */
16+
private final String key;
17+
18+
/**
19+
* Initialize a {@link SpecialButton}.
20+
*
21+
* @param key The unique key name for the special button. The key is registered in {@link #map}
22+
* with which the {@link SpecialButton} can be retrieved via a call to {@link
23+
* #valueOf(String)}.
24+
*/
25+
public SpecialButton(@NonNull final String key) {
26+
this.key = key;
27+
map.put(key, this);
28+
}
29+
30+
/**
31+
* Get the {@link SpecialButton} for {@code key}.
32+
*
33+
* @param key The unique key name for the special button.
34+
*/
35+
public static SpecialButton valueOf(String key) {
36+
return map.get(key);
37+
}
38+
39+
/** Get {@link #key} for this {@link SpecialButton}. */
40+
public String getKey() {
41+
return key;
42+
}
43+
44+
@NonNull
45+
@Override
46+
public String toString() {
47+
return key;
48+
}
49+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.sparkleseditor.components.virtualkeys;
2+
3+
import android.widget.Button;
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
7+
/** The {@link Class} that maintains a state of a {@link SpecialButton} */
8+
public class SpecialButtonState {
9+
10+
/** If special button has been created for the {@link VirtualKeysView}. */
11+
boolean isCreated = false;
12+
13+
/** If special button is active. */
14+
boolean isActive = false;
15+
16+
/**
17+
* If special button is locked due to long hold on it and should not be deactivated if its state
18+
* is read.
19+
*/
20+
boolean isLocked = false;
21+
22+
List<Button> buttons = new ArrayList<>();
23+
24+
VirtualKeysView mVirtualKeysView;
25+
26+
/**
27+
* Initialize a {@link SpecialButtonState} to maintain state of a {@link SpecialButton}.
28+
*
29+
* @param extraKeysView The {@link VirtualKeysView} instance in which the {@link SpecialButton} is
30+
* to be registered.
31+
*/
32+
public SpecialButtonState(VirtualKeysView extraKeysView) {
33+
mVirtualKeysView = extraKeysView;
34+
}
35+
36+
/** Set {@link #isCreated}. */
37+
public void setIsCreated(boolean value) {
38+
isCreated = value;
39+
}
40+
41+
/** Set {@link #isActive}. */
42+
public void setIsActive(boolean value) {
43+
isActive = value;
44+
for (Button button : buttons) {
45+
button.setTextColor(
46+
value
47+
? mVirtualKeysView.getButtonActiveTextColor()
48+
: mVirtualKeysView.getButtonTextColor());
49+
}
50+
}
51+
52+
/** Set {@link #isLocked}. */
53+
public void setIsLocked(boolean value) {
54+
isLocked = value;
55+
}
56+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package com.sparkleseditor.components.virtualkeys;
2+
3+
import android.text.TextUtils;
4+
import androidx.annotation.NonNull;
5+
import androidx.annotation.Nullable;
6+
import java.util.Arrays;
7+
import java.util.stream.Collectors;
8+
import org.json.JSONException;
9+
import org.json.JSONObject;
10+
11+
public class VirtualKeyButton {
12+
13+
/**
14+
* The key name for the name of the extra key if using a dict to define the extra key. {key: name,
15+
* ...}
16+
*/
17+
public static final String KEY_KEY_NAME = "key";
18+
19+
/**
20+
* The key name for the macro value of the extra key if using a dict to define the extra key.
21+
* {macro: value, ...}
22+
*/
23+
public static final String KEY_MACRO = "macro";
24+
25+
/**
26+
* The key name for the alternate display name of the extra key if using a dict to define the
27+
* extra key. {display: name, ...}
28+
*/
29+
public static final String KEY_DISPLAY_NAME = "display";
30+
31+
/**
32+
* The key name for the nested dict to define popup extra key info if using a dict to define the
33+
* extra key. {popup: {key: name, ...}, ...}
34+
*/
35+
public static final String KEY_POPUP = "popup";
36+
37+
/**
38+
* The key that will be sent to the terminal, either a control character, like defined in {@link
39+
* VirtualKeysConstants#PRIMARY_KEY_CODES_FOR_STRINGS} (LEFT, RIGHT, PGUP...) or some text.
40+
*/
41+
private final String key;
42+
43+
/** If the key is a macro, i.e. a sequence of keys separated by space. */
44+
private final boolean macro;
45+
46+
/** The text that will be displayed on the button. */
47+
private final String display;
48+
49+
/**
50+
* The {@link VirtualKeyButton} containing the information of the popup button (triggered by swipe
51+
* up).
52+
*/
53+
@Nullable private final VirtualKeyButton popup;
54+
55+
/**
56+
* Initialize a {@link VirtualKeyButton}.
57+
*
58+
* @param config The {@link JSONObject} containing the info to create the {@link
59+
* VirtualKeyButton}.
60+
* @param extraKeyDisplayMap The {@link VirtualKeysConstants.VirtualKeyDisplayMap} that defines
61+
* the display text mapping for the keys if a custom value is not defined by {@link
62+
* #KEY_DISPLAY_NAME}.
63+
* @param extraKeyAliasMap The {@link VirtualKeysConstants.VirtualKeyDisplayMap} that defines the
64+
* aliases for the actual key names.
65+
*/
66+
public VirtualKeyButton(
67+
@NonNull JSONObject config,
68+
@NonNull VirtualKeysConstants.VirtualKeyDisplayMap extraKeyDisplayMap,
69+
@NonNull VirtualKeysConstants.VirtualKeyDisplayMap extraKeyAliasMap)
70+
throws JSONException {
71+
this(config, null, extraKeyDisplayMap, extraKeyAliasMap);
72+
}
73+
74+
/**
75+
* Initialize a {@link VirtualKeyButton}.
76+
*
77+
* @param config The {@link JSONObject} containing the info to create the {@link
78+
* VirtualKeyButton}.
79+
* @param popup The {@link VirtualKeyButton} optional {@link #popup} button.
80+
* @param extraKeyDisplayMap The {@link VirtualKeysConstants.VirtualKeyDisplayMap} that defines
81+
* the display text mapping for the keys if a custom value is not defined by {@link
82+
* #KEY_DISPLAY_NAME}.
83+
* @param extraKeyAliasMap The {@link VirtualKeysConstants.VirtualKeyDisplayMap} that defines the
84+
* aliases for the actual key names.
85+
*/
86+
public VirtualKeyButton(
87+
@NonNull JSONObject config,
88+
@Nullable VirtualKeyButton popup,
89+
@NonNull VirtualKeysConstants.VirtualKeyDisplayMap extraKeyDisplayMap,
90+
@NonNull VirtualKeysConstants.VirtualKeyDisplayMap extraKeyAliasMap)
91+
throws JSONException {
92+
String keyFromConfig = getStringFromJson(config, KEY_KEY_NAME);
93+
String macroFromConfig = getStringFromJson(config, KEY_MACRO);
94+
String[] keys;
95+
if (keyFromConfig != null && macroFromConfig != null) {
96+
throw new JSONException(
97+
"Both key and macro can't be set for the same key. key: \""
98+
+ keyFromConfig
99+
+ "\", macro: \""
100+
+ macroFromConfig
101+
+ "\"");
102+
} else if (keyFromConfig != null) {
103+
keys = new String[] {keyFromConfig};
104+
this.macro = false;
105+
} else if (macroFromConfig != null) {
106+
keys = macroFromConfig.split(" ");
107+
this.macro = true;
108+
} else {
109+
throw new JSONException("All keys have to specify either key or macro");
110+
}
111+
112+
for (int i = 0; i < keys.length; i++) {
113+
keys[i] = replaceAlias(extraKeyAliasMap, keys[i]);
114+
}
115+
116+
this.key = TextUtils.join(" ", keys);
117+
118+
String displayFromConfig = getStringFromJson(config, KEY_DISPLAY_NAME);
119+
if (displayFromConfig != null) {
120+
this.display = displayFromConfig;
121+
} else {
122+
this.display =
123+
Arrays.stream(keys)
124+
.map(key -> extraKeyDisplayMap.get(key, key))
125+
.collect(Collectors.joining(" "));
126+
}
127+
128+
this.popup = popup;
129+
}
130+
131+
public String getStringFromJson(@NonNull JSONObject config, @NonNull String key) {
132+
try {
133+
return config.getString(key);
134+
} catch (JSONException e) {
135+
return null;
136+
}
137+
}
138+
139+
/** Replace the alias with its actual key name if found in extraKeyAliasMap. */
140+
public static String replaceAlias(
141+
@NonNull VirtualKeysConstants.VirtualKeyDisplayMap extraKeyAliasMap, String key) {
142+
return extraKeyAliasMap.get(key, key);
143+
}
144+
145+
/** Get {@link #key}. */
146+
public String getKey() {
147+
return key;
148+
}
149+
150+
/** Check whether a {@link #macro} is defined or not. */
151+
public boolean isMacro() {
152+
return macro;
153+
}
154+
155+
/** Get {@link #display}. */
156+
public String getDisplay() {
157+
return display;
158+
}
159+
160+
/** Get {@link #popup}. */
161+
@Nullable
162+
public VirtualKeyButton getPopup() {
163+
return popup;
164+
}
165+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.sparkleseditor.components.virtualkeys
2+
3+
import android.view.View
4+
import android.widget.Button
5+
import com.termux.terminal.TerminalSession
6+
7+
class VirtualKeyClient(val session: TerminalSession) : VirtualKeysView.IVirtualKeysView {
8+
override fun onVirtualKeyButtonClick(
9+
view: View?,
10+
buttonInfo: VirtualKeyButton?,
11+
button: Button?,
12+
) {
13+
val key = buttonInfo?.key
14+
if (key.isNullOrEmpty()) {
15+
return
16+
}
17+
when (key) {
18+
"ESC" -> session.write("\u001B") // ESC
19+
"TAB" -> session.write("\u0009") // TAB
20+
"HOME" -> session.write("\u001B[H") // HOME
21+
"UP" -> session.write("\u001B[A") // UP Arrow (ANSI escape code)
22+
"DOWN" -> session.write("\u001B[B") // DOWN Arrow (ANSI escape code)
23+
"LEFT" -> session.write("\u001B[D") // LEFT Arrow (ANSI escape code)
24+
"RIGHT" -> session.write("\u001B[C") // RIGHT Arrow (ANSI escape code)
25+
"PGUP" -> session.write("\u001B[5~") // Page Up (ANSI escape code)
26+
"PGDN" -> session.write("\u001B[6~") // Page Down (ANSI escape code)
27+
"END" -> session.write("\u001B[4~") // End (ANSI escape code, may vary)
28+
else -> session.write(buttonInfo.key)
29+
}
30+
}
31+
32+
override fun performVirtualKeyButtonHapticFeedback(
33+
view: View?,
34+
buttonInfo: VirtualKeyButton?,
35+
button: Button?,
36+
): Boolean {
37+
return false
38+
}
39+
}

0 commit comments

Comments
 (0)