clipboard: Forget recent copied text after 5 minutes

Slightly improves privacy. Entries older than that are not useful in the
intended use case.
This commit is contained in:
Jules Aguillon 2024-06-16 19:41:30 +02:00
parent 3d95af5806
commit d657d51c2b
2 changed files with 52 additions and 16 deletions

View File

@ -5,6 +5,7 @@ import android.content.ClipboardManager;
import android.content.Context; import android.content.Context;
import android.os.Build.VERSION; import android.os.Build.VERSION;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
public final class ClipboardHistoryService public final class ClipboardHistoryService
@ -30,21 +31,37 @@ public final class ClipboardHistoryService
gives a sense to the user that the history is not persisted and can be gives a sense to the user that the history is not persisted and can be
forgotten as soon as the app stops. */ forgotten as soon as the app stops. */
public static final int MAX_HISTORY_SIZE = 3; public static final int MAX_HISTORY_SIZE = 3;
/** Time in ms until history entries expire. */
public static final long HISTORY_TTL_MS = 5 * 60 * 1000;
static ClipboardHistoryService _service = null; static ClipboardHistoryService _service = null;
ClipboardManager _cm; ClipboardManager _cm;
List<String> _history; List<HistoryEntry> _history;
OnClipboardHistoryChange _listener = null; OnClipboardHistoryChange _listener = null;
ClipboardHistoryService(Context ctx) ClipboardHistoryService(Context ctx)
{ {
_history = new ArrayList<String>(); _history = new ArrayList<HistoryEntry>();
_cm = (ClipboardManager)ctx.getSystemService(Context.CLIPBOARD_SERVICE); _cm = (ClipboardManager)ctx.getSystemService(Context.CLIPBOARD_SERVICE);
_cm.addPrimaryClipChangedListener(this.new SystemListener()); _cm.addPrimaryClipChangedListener(this.new SystemListener());
} }
public List<String> get_history() { return _history; } public List<String> clear_expired_and_get_history()
{
long now_ms = System.currentTimeMillis();
List<String> dst = new ArrayList<String>();
Iterator<HistoryEntry> it = _history.iterator();
while (it.hasNext())
{
HistoryEntry ent = it.next();
if (ent.expiry_timestamp <= now_ms)
it.remove();
else
dst.add(ent.content);
}
return dst;
}
/** This will call [on_clipboard_history_change]. */ /** This will call [on_clipboard_history_change]. */
public void remove_history_entry(String clip) public void remove_history_entry(String clip)
@ -52,7 +69,7 @@ public final class ClipboardHistoryService
int last_pos = _history.size() - 1; int last_pos = _history.size() - 1;
for (int pos = last_pos; pos >= 0; pos--) for (int pos = last_pos; pos >= 0; pos--)
{ {
if (!_history.get(pos).equals(clip)) if (!_history.get(pos).content.equals(clip))
continue; continue;
// Removing the current clipboard, clear the system clipboard. // Removing the current clipboard, clear the system clipboard.
if (pos == last_pos) if (pos == last_pos)
@ -64,7 +81,7 @@ public final class ClipboardHistoryService
} }
_history.remove(pos); _history.remove(pos);
if (_listener != null) if (_listener != null)
_listener.on_clipboard_history_change(_history); _listener.on_clipboard_history_change();
} }
} }
@ -73,20 +90,20 @@ public final class ClipboardHistoryService
public void add_clip(String clip) public void add_clip(String clip)
{ {
int size = _history.size(); int size = _history.size();
if (clip.equals("") || (size > 0 && _history.get(size - 1).equals(clip))) if (clip.equals("") || (size > 0 && _history.get(size - 1).content.equals(clip)))
return; return;
if (size >= MAX_HISTORY_SIZE) if (size >= MAX_HISTORY_SIZE)
_history.remove(0); _history.remove(0);
_history.add(clip); _history.add(new HistoryEntry(clip));
if (_listener != null) if (_listener != null)
_listener.on_clipboard_history_change(_history); _listener.on_clipboard_history_change();
} }
public void set_on_clipboard_history_change(OnClipboardHistoryChange l) { _listener = l; } public void set_on_clipboard_history_change(OnClipboardHistoryChange l) { _listener = l; }
public static interface OnClipboardHistoryChange public static interface OnClipboardHistoryChange
{ {
public void on_clipboard_history_change(List<String> history); public void on_clipboard_history_change();
} }
final class SystemListener implements ClipboardManager.OnPrimaryClipChangedListener final class SystemListener implements ClipboardManager.OnPrimaryClipChangedListener
@ -104,4 +121,18 @@ public final class ClipboardHistoryService
add_clip(clip.getItemAt(i).getText().toString()); add_clip(clip.getItemAt(i).getText().toString());
} }
} }
static final class HistoryEntry
{
public final String content;
/** Time at which the entry expires. */
public final long expiry_timestamp;
public HistoryEntry(String c)
{
content = c;
expiry_timestamp = System.currentTimeMillis() + HISTORY_TTL_MS;
}
}
} }

View File

@ -27,7 +27,7 @@ public final class ClipboardHistoryView extends ListView
if (_service != null) if (_service != null)
{ {
_service.set_on_clipboard_history_change(this); _service.set_on_clipboard_history_change(this);
_history = _service.get_history(); _history = _service.clear_expired_and_get_history();
} }
setAdapter(_adapter); setAdapter(_adapter);
} }
@ -43,18 +43,23 @@ public final class ClipboardHistoryView extends ListView
} }
@Override @Override
public void on_clipboard_history_change(List<String> history) public void on_clipboard_history_change()
{ {
_history = history; update_data();
_adapter.notifyDataSetChanged();
invalidate();
} }
@Override @Override
protected void onAttachedToWindow() protected void onWindowVisibilityChanged(int visibility)
{ {
super.onAttachedToWindow(); if (visibility == View.VISIBLE)
update_data();
}
void update_data()
{
_history = _service.clear_expired_and_get_history();
_adapter.notifyDataSetChanged(); _adapter.notifyDataSetChanged();
invalidate();
} }
class ClipboardEntriesAdapter extends BaseAdapter class ClipboardEntriesAdapter extends BaseAdapter