diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c2a9bd1..8db4785 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,6 +5,8 @@ @@ -12,6 +14,8 @@ android:name=".InputMethod" android:directBootAware="true" android:exported="false" + android:roundIcon="@drawable/icon_round" + android:icon="@drawable/icon" android:label="@string/app_name" android:permission="android.permission.BIND_INPUT_METHOD"> @@ -26,6 +30,8 @@ diff --git a/app/src/main/java/cfpi_fpsi/eu/titanboard/ClipboardHandler.java b/app/src/main/java/cfpi_fpsi/eu/titanboard/ClipboardHandler.java index 3e5f036..104ccd1 100644 --- a/app/src/main/java/cfpi_fpsi/eu/titanboard/ClipboardHandler.java +++ b/app/src/main/java/cfpi_fpsi/eu/titanboard/ClipboardHandler.java @@ -25,21 +25,36 @@ import android.content.ClipboardManager; import android.content.Context; import android.os.Build; +import java.util.ArrayDeque; + @TargetApi(Build.VERSION_CODES.HONEYCOMB) public class ClipboardHandler implements ClipboardManager.OnPrimaryClipChangedListener { + private final InputMethod mInputMethod; private final ClipboardManager mClipboardManager; - private ClipData mCurrentClip; + private ArrayDeque mClips = new ArrayDeque<>(); - public ClipboardHandler(Context ctx) { - mClipboardManager = (ClipboardManager) ctx.getSystemService(Context.CLIPBOARD_SERVICE); + public ClipboardHandler(Context context) { + mInputMethod = (InputMethod) context; + mClipboardManager = (ClipboardManager) mInputMethod.getSystemService(Context.CLIPBOARD_SERVICE); + mClipboardManager.addPrimaryClipChangedListener(this); } @Override public void onPrimaryClipChanged() { - mCurrentClip = mClipboardManager.getPrimaryClip(); - } + // The callback can be launched several times, so we have to check if it isn't the same + // clipboard data. + if (mClips.peekFirst() != null) { + if (mClips.peekFirst().equals(mClipboardManager.getPrimaryClip())) { + return; + } + } - public ClipData getCurrentClip() { - return mCurrentClip; + mClips.addFirst(mClipboardManager.getPrimaryClip()); + + if (mClips.size() > mInputMethod.mPreferenceHolder.getMaxClipboardItems()) { + mClips.removeLast(); + } + + mInputMethod.mInputView.setClips(mClips.toArray()); } } diff --git a/app/src/main/java/cfpi_fpsi/eu/titanboard/InputMethod.java b/app/src/main/java/cfpi_fpsi/eu/titanboard/InputMethod.java index 81b1132..cc9c75b 100644 --- a/app/src/main/java/cfpi_fpsi/eu/titanboard/InputMethod.java +++ b/app/src/main/java/cfpi_fpsi/eu/titanboard/InputMethod.java @@ -46,7 +46,7 @@ public class InputMethod extends InputMethodService { public KeymapManager mKeymapManager; public PreferenceHolder mPreferenceHolder; private ClipboardHandler mClipboardHandler; - private InputView mInputView; + public InputView mInputView; private int mCurrentCursorPosition; private Handler mHandler; private Cycler mSymbolCycler; @@ -186,7 +186,7 @@ public class InputMethod extends InputMethodService { @Override public void onFirstCycle() { mInputView.setSymbolsVisible(true); - mInputView.prepareSymbols((ArrayList) getList()); + mInputView.setSymbols((ArrayList) getList()); getCurrentInputConnection().setSelection(mCurrentCursorPosition - getElementAt(0).toString().length(), mCurrentCursorPosition); } diff --git a/app/src/main/java/cfpi_fpsi/eu/titanboard/InputView.java b/app/src/main/java/cfpi_fpsi/eu/titanboard/InputView.java index 34874f0..01e106e 100644 --- a/app/src/main/java/cfpi_fpsi/eu/titanboard/InputView.java +++ b/app/src/main/java/cfpi_fpsi/eu/titanboard/InputView.java @@ -19,6 +19,8 @@ package cfpi_fpsi.eu.titanboard; +import android.annotation.TargetApi; +import android.content.ClipData; import android.content.Context; import android.content.Intent; import android.graphics.Paint; @@ -31,6 +33,7 @@ import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; +import java.util.ArrayDeque; import java.util.ArrayList; // TODO: programmatically scroll if current selected symbol is outside the visible space of the @@ -41,9 +44,8 @@ public class InputView extends RelativeLayout { private final LinearLayout.LayoutParams mSymbolLayout; private final LinearLayout.LayoutParams mCandidateLayout; private LinearLayout mSymbolList; - private LinearLayout mCandidateList; + private LinearLayout mClipsList; private Button mStateButton; - private Button mClipboardButton; public InputView(Context context, AttributeSet attrs) { super(context, attrs, 0); @@ -70,12 +72,8 @@ public class InputView extends RelativeLayout { protected void onFinishInflate() { super.onFinishInflate(); mSymbolList = findViewById(R.id.symbol_list); - mCandidateList = findViewById(R.id.candidate_list); + mClipsList = findViewById(R.id.clip_list); mStateButton = findViewById(R.id.state_button); - mClipboardButton = (Button) inflate(this.getContext(), R.layout.candidate, null); - mClipboardButton.setTag(0); - mClipboardButton.setLayoutParams(mCandidateLayout); - mCandidateList.addView(mClipboardButton); mStateButton.setOnClickListener(button -> { MetaKeyManager metaManager = mInputMethod.mMetaKeyManager; @@ -136,18 +134,30 @@ public class InputView extends RelativeLayout { mStateButton.setText(keymapManager.getCurrentKeymap().getCode()); } - public void setSymbolsVisible(boolean state) { - findViewById(R.id.symbol_list_container).setVisibility(state ? VISIBLE : GONE); - findViewById(R.id.candidate_list_container).setVisibility(state ? GONE : VISIBLE); + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public void setClips(Object[] clips) { + for (int i = 0; i < clips.length; ++i) { + Button b = (Button) mClipsList.getChildAt(i); + ClipData clip = (ClipData) clips[i]; + + if (b == null) { + b = (Button) inflate(this.getContext(), R.layout.candidate, null); + b.setTag(i); + b.setLayoutParams(mCandidateLayout); + mClipsList.addView(b); + } + + ClipData.Item item = clip.getItemAt(0); + b.setText(item.coerceToText(this.getContext()).toString()); + } } - /** - * Prepares the CyclerView for the display of given strings, creating additional TextViews if - * necessary, and hiding unnecessary ones. - * - * @param strings the strings to be displayed in the CyclerView - */ - public void prepareSymbols(ArrayList strings) { + public void setSymbolsVisible(boolean state) { + findViewById(R.id.symbol_list_container).setVisibility(state ? VISIBLE : GONE); + findViewById(R.id.clip_list_container).setVisibility(state ? GONE : VISIBLE); + } + + public void setSymbols(ArrayList strings) { for (int i = 0; i < strings.size(); ++i) { TextView tv = (TextView) mSymbolList.getChildAt(i); diff --git a/app/src/main/java/cfpi_fpsi/eu/titanboard/PreferenceHolder.java b/app/src/main/java/cfpi_fpsi/eu/titanboard/PreferenceHolder.java index 4098c74..e313c6a 100644 --- a/app/src/main/java/cfpi_fpsi/eu/titanboard/PreferenceHolder.java +++ b/app/src/main/java/cfpi_fpsi/eu/titanboard/PreferenceHolder.java @@ -67,6 +67,12 @@ public class PreferenceHolder implements SharedPreferences.OnSharedPreferenceCha mContext.getString(R.string.cycle_speed_default))); } + public int getMaxClipboardItems() { + return Integer.parseInt((String) + getOrDefault(mContext.getString(R.string.preferences_key_clipboard_max_items), + mContext.getString(R.string.clipboard_max_items_default))); + } + @SuppressWarnings("unchecked") public ArrayList getSelectedKeymaps() { Set selectedKeymaps = (Set) diff --git a/app/src/main/res/drawable/icon.xml b/app/src/main/res/drawable/icon.xml new file mode 100644 index 0000000..efa76cb --- /dev/null +++ b/app/src/main/res/drawable/icon.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/icon_round.xml b/app/src/main/res/drawable/icon_round.xml new file mode 100644 index 0000000..c75d4a6 --- /dev/null +++ b/app/src/main/res/drawable/icon_round.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/input.xml b/app/src/main/res/layout/input.xml index b8b1f38..ba75ffe 100644 --- a/app/src/main/res/layout/input.xml +++ b/app/src/main/res/layout/input.xml @@ -48,7 +48,7 @@ diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 3243aae..f517acb 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -19,7 +19,6 @@ - Test English German French @@ -32,7 +31,6 @@ - tt en de fr diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f448ee8..620e284 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,6 +8,7 @@ Floating interface Cycling speed + Maximum clipboard items Keymaps Copyleft 🄯 Emil Kosz, 2024. License: GPLv3 @@ -22,7 +23,9 @@ floating_interface selected_keymaps cycle_speed + clipboard_items en 500 + 10 \ No newline at end of file diff --git a/app/src/main/res/xml/keymaps_titan.xml b/app/src/main/res/xml/keymaps_titan.xml index 76e84ab..22e7337 100644 --- a/app/src/main/res/xml/keymaps_titan.xml +++ b/app/src/main/res/xml/keymaps_titan.xml @@ -18,27 +18,6 @@ --> - - - - - - - - - - - - - - - - - - +