Refactor the InputView

This commit is contained in:
2024-11-22 12:55:42 +01:00
parent a7de68929a
commit 6abc48c121
6 changed files with 212 additions and 103 deletions
@@ -32,6 +32,8 @@ import android.view.inputmethod.InputConnection;
import java.util.ArrayList;
import eur.cfpifpsi.titanboard.views.InputView;
// TODO: Revamp the keymap system — separate device keymap, keymap types (12-key, alphabetic, etc.)
// TODO: Add auto-capitalization
// TODO: Add auto-periods
@@ -218,7 +220,7 @@ public class TitanboardIME extends InputMethodService {
@Override
public void onFinishInput() {
mMetaKeyManager.resetMetaKeyState();
mInputView.mClipboardPopup.dismiss();
mInputView.mPopup.dismiss();
}
// Cyclers & runnables
@@ -0,0 +1,81 @@
/*
* 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.R;
import eur.cfpifpsi.titanboard.TitanboardIME;
public class ClipboardView extends ScrollView {
private final TitanboardIME mTitanboardIME;
private final LinearLayout.LayoutParams mClipLayout;
private LinearLayout mClipList;
public ClipboardView(Context context, AttributeSet attrs) {
super(context, attrs, 0);
mTitanboardIME = (TitanboardIME) context;
mClipLayout = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT, 0);
}
@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 -> mTitanboardIME.getCurrentInputConnection()
.commitText(item.getText(), 1));
LinearLayout finalClipView = clipView;
clipView.findViewById(R.id.button_delete).setOnClickListener(button -> {
mClipList.removeView(finalClipView);
mTitanboardIME.mClipManager.removeItem(item);
});
}
}
}
@@ -16,65 +16,55 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package eur.cfpifpsi.titanboard;
package eur.cfpifpsi.titanboard.views;
import android.annotation.TargetApi;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Build;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.HorizontalScrollView;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import java.util.ArrayList;
// TODO: programmatically scroll if current selected symbol is outside the visible space of the
// scrollview
import eur.cfpifpsi.titanboard.KeymapManager;
import eur.cfpifpsi.titanboard.MetaKeyManager;
import eur.cfpifpsi.titanboard.R;
import eur.cfpifpsi.titanboard.SettingsActivity;
import eur.cfpifpsi.titanboard.TitanboardIME;
public final class InputView extends RelativeLayout {
private final int[] buttonStateToggled = {R.attr.state_toggled};
private final TitanboardIME mTitanboardIME;
private final LinearLayout.LayoutParams mSymbolLayout;
private final LinearLayout.LayoutParams mClipLayout;
public PopupWindow mClipboardPopup;
private LinearLayout mSymbolList;
private LinearLayout mClipList;
public PopupWindow mPopup;
private Button mStateButton;
private ImageButton mClipboardButton;
private SymbolsView mSymbolsView;
private ClipboardView mClipboardView;
public InputView(Context context, AttributeSet attrs) {
super(context, attrs, 0);
mTitanboardIME = (TitanboardIME) context;
mSymbolLayout = new LinearLayout.LayoutParams(
getResources().getDimensionPixelSize(R.dimen.symbol_width),
ViewGroup.LayoutParams.MATCH_PARENT, 0);
mClipLayout = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT, 0);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mSymbolList = findViewById(R.id.symbol_list);
mStateButton = findViewById(R.id.state_button);
mClipboardButton = findViewById(R.id.clipboard_button);
ScrollView mClipboardView = (ScrollView) inflate(getContext(), R.layout.clipboard, null);
mClipList = mClipboardView.findViewById(R.id.clip_list);
mClipboardPopup = new PopupWindow(mClipboardView,
mSymbolsView = findViewById(R.id.symbols_view);
mClipboardView = (ClipboardView) inflate(getContext(), R.layout.clipboard, null);
mPopup = new PopupWindow(mClipboardView,
getResources().getDimensionPixelSize(R.dimen.clipboard_popup_width),
getResources().getDimensionPixelSize(R.dimen.clipboard_popup_height), true);
mClipboardPopup.setOnDismissListener(() -> mClipboardButton.setImageState(null, true));
mPopup.setOnDismissListener(() -> mClipboardButton.setImageState(null, true));
mStateButton.setOnClickListener(button -> {
MetaKeyManager metaManager = mTitanboardIME.mMetaKeyManager;
@@ -96,8 +86,8 @@ public final class InputView extends RelativeLayout {
});
mClipboardButton.setOnClickListener(button -> {
mClipboardPopup.showAsDropDown(mClipboardButton,
-(mClipboardPopup.getWidth() - mClipboardButton.getWidth()) / 2, 0);
mPopup.showAsDropDown(mClipboardButton,
-(mPopup.getWidth() - mClipboardButton.getWidth()) / 2, 0);
mClipboardButton.setImageState(buttonStateToggled, true);
});
@@ -149,83 +139,19 @@ public final class InputView extends RelativeLayout {
mStateButton.setText(keymapManager.getCurrentLabel());
}
@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 -> mTitanboardIME.getCurrentInputConnection()
.commitText(item.getText(), 1));
LinearLayout finalClipView = clipView;
clipView.findViewById(R.id.button_delete).setOnClickListener(button -> {
mClipList.removeView(finalClipView);
mTitanboardIME.mClipManager.removeItem(item);
});
}
mClipboardView.setClips(items);
}
public void setSymbolsVisible(boolean state) {
mSymbolList.setVisibility(state ? VISIBLE : GONE);
mSymbolsView.setSymbolsVisible(state);
}
/**
* @noinspection MethodWithMultipleLoops
*/
public void setSymbols(ArrayList<String> strings) {
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);
}
mSymbolsView.setSymbols(strings);
}
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) {
HorizontalScrollView hsw = findViewById(R.id.symbol_list_container);
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.
hsw.smoothScrollTo(tv.getRight()
+ (getResources().getDimensionPixelSize(R.dimen.default_space) << 1)
- hsw.getRight(), 0);
} else {
tv.setPressed(false);
tv.setTypeface(null, Typeface.NORMAL);
}
}
mSymbolsView.setSelectedSymbol(index);
}
}
@@ -0,0 +1,100 @@
/*
* 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.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, 0);
mSymbolLayout = new LinearLayout.LayoutParams(
getResources().getDimensionPixelSize(R.dimen.symbol_width),
LinearLayout.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) {
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);
}
}
}
}
+2 -2
View File
@@ -16,7 +16,7 @@
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
<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"
@@ -29,4 +29,4 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</ScrollView>
</eur.cfpifpsi.titanboard.views.ClipboardView>
+5 -5
View File
@@ -16,7 +16,7 @@
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<eur.cfpifpsi.titanboard.InputView xmlns:android="http://schemas.android.com/apk/res/android"
<eur.cfpifpsi.titanboard.views.InputView 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"
@@ -24,8 +24,8 @@
android:background="@color/background"
tools:ignore="Overdraw">
<HorizontalScrollView
android:id="@+id/symbol_list_container"
<eur.cfpifpsi.titanboard.views.SymbolsView
android:id="@+id/symbols_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignTop="@id/clipboard_button"
@@ -39,7 +39,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal" />
</HorizontalScrollView>
</eur.cfpifpsi.titanboard.views.SymbolsView>
<ImageButton
android:id="@+id/clipboard_button"
@@ -64,4 +64,4 @@
android:textColor="@color/background"
android:textSize="@dimen/default_text_size" />
</eur.cfpifpsi.titanboard.InputView>
</eur.cfpifpsi.titanboard.views.InputView>