diff options
Diffstat (limited to 'app/src/main/java/invalid/lena/scrcpy/Main.java')
| -rw-r--r-- | app/src/main/java/invalid/lena/scrcpy/Main.java | 117 |
1 files changed, 65 insertions, 52 deletions
diff --git a/app/src/main/java/invalid/lena/scrcpy/Main.java b/app/src/main/java/invalid/lena/scrcpy/Main.java index c069850..24126e9 100644 --- a/app/src/main/java/invalid/lena/scrcpy/Main.java +++ b/app/src/main/java/invalid/lena/scrcpy/Main.java @@ -7,13 +7,13 @@ import android.os.Bundle; import android.text.TextUtils; import android.view.View; import android.view.WindowInsets; -import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; -import android.widget.ListView; +import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; +import java.io.IOException; import java.util.List; // Pairing form + saved-device list. @@ -23,14 +23,13 @@ import java.util.List; // the target's Wireless debugging screen; pairing happens on a different, // short-lived port from the pairing dialog, against the same host. After a // successful pair() against the daemon, the row is appended to devices.json -// with the *connect* port. Tapping a saved row launches the Mirror activity -// with the target host/port; Mirror owns its own Adb instance loaded from -// the same on-disk keypair. +// with the *connect* port. Tapping a saved row launches the Mirror activity. public final class Main extends Activity { private volatile Adb adb; - private ArrayAdapter<Devices.Device> adapter; - private Button pairButton; + private LinearLayout deviceList; + private TextView devicesEmpty; + private Button pairButton; @Override protected void onCreate(Bundle saved) { @@ -38,8 +37,9 @@ public final class Main extends Activity { setContentView(R.layout.main); // The IME inset is in the mask too: the window is edge-to-edge, so - // nothing resizes it when the keyboard opens. Padding the root - // shrinks the weighted device list and keeps the form reachable. + // nothing resizes it when the keyboard opens. The page is one + // ScrollView, so padding the root keeps the form reachable behind + // the keyboard. Ui.padForInsets(findViewById(R.id.root), WindowInsets.Type.systemBars() | WindowInsets.Type.ime()); @@ -48,28 +48,34 @@ public final class Main extends Activity { EditText pairCode = findViewById(R.id.pair_code); pairButton = findViewById(R.id.pair); View settingsBtn = findViewById(R.id.settings); - ListView devices = findViewById(R.id.devices); - TextView devicesEmpty = findViewById(R.id.devices_empty); + deviceList = findViewById(R.id.devices); + devicesEmpty = findViewById(R.id.devices_empty); settingsBtn.setOnClickListener(v -> startActivity( new Intent(this, SettingsActivity.class))); - adapter = new ArrayAdapter<>(this, R.layout.device_row, R.id.device_label, Devices.load(this)); - devices.setAdapter(adapter); - devices.setEmptyView(devicesEmpty); + try { + showDevices(Devices.load(this)); + } catch (IOException e) { + Log.e(e, "devices: load failed"); + showDevices(java.util.Collections.emptyList()); + Toast.makeText(this, R.string.device_list_unreadable, Toast.LENGTH_LONG).show(); + } // RSA keygen on first launch can take 1-3 s; never on the UI thread. pairButton.setEnabled(false); new Thread(() -> { try { - Adb a = Adb.getInstance(this); + Adb a = Adb.getInstance(getApplicationContext()); runOnUiThread(() -> { + if (isFinishing() || isDestroyed()) return; adb = a; pairButton.setEnabled(true); }); } catch (Exception e) { Log.e(e, "adb init failed"); runOnUiThread(() -> { + if (isFinishing() || isDestroyed()) return; Toast.makeText(this, "adb init failed: " + e.getMessage(), Toast.LENGTH_LONG).show(); finish(); @@ -77,38 +83,6 @@ public final class Main extends Activity { } }, "adb-init").start(); - devices.setOnItemClickListener((parent, view, pos, id) -> { - Devices.Device d = adapter.getItem(pos); - Log.i("connect tap: %s", d); - Intent i = new Intent(this, Mirror.class); - i.putExtra(Mirror.EXTRA_HOST, d.host); - i.putExtra(Mirror.EXTRA_PORT, d.port); - startActivity(i); - }); - - devices.setOnItemLongClickListener((parent, view, pos, id) -> { - Devices.Device d = adapter.getItem(pos); - new AlertDialog.Builder(this) - .setTitle(R.string.forget_device) - .setMessage(d.toString()) - .setPositiveButton(android.R.string.ok, (dlg, w) -> { - Log.i("forget device: %s", d); - try { - List<Devices.Device> updated = Devices.remove(this, d); - adapter.clear(); - adapter.addAll(updated); - adapter.notifyDataSetChanged(); - } catch (Exception e) { - Log.e(e, "forget device: save failed"); - Toast.makeText(this, "could not save device list", - Toast.LENGTH_LONG).show(); - } - }) - .setNegativeButton(android.R.string.cancel, null) - .show(); - return true; - }); - pairButton.setOnClickListener(v -> { if (adb == null) return; // still initialising // The saved endpoint is the address field verbatim; pairing @@ -124,7 +98,7 @@ public final class Main extends Activity { return; } String pc = pairCode.getText().toString().trim(); - if (TextUtils.isEmpty(pc)) { + if (TextUtils.isEmpty(pc) || !pc.matches("[0-9]{6}")) { Toast.makeText(this, R.string.bad_pair_code, Toast.LENGTH_LONG).show(); return; } @@ -134,22 +108,61 @@ public final class Main extends Activity { }); } + // Rebuild the saved-device rows. There is no adapter: the whole page + // is one ScrollView, a ListView cannot measure itself inside one, and + // a handful of rows does not need recycling. + private void showDevices(List<Devices.Device> devices) { + deviceList.removeAllViews(); + devicesEmpty.setVisibility(devices.isEmpty() ? View.VISIBLE : View.GONE); + for (Devices.Device d : devices) { + View row = getLayoutInflater().inflate(R.layout.device_row, deviceList, false); + ((TextView) row.findViewById(R.id.device_label)).setText(d.toString()); + row.setOnClickListener(v -> { + Log.i("connect tap: %s", d); + Intent i = new Intent(this, Mirror.class); + i.putExtra(Mirror.EXTRA_HOST, d.host); + i.putExtra(Mirror.EXTRA_PORT, d.port); + startActivity(i); + }); + row.setOnLongClickListener(v -> { confirmForget(d); return true; }); + deviceList.addView(row); + } + } + + private void confirmForget(Devices.Device d) { + new AlertDialog.Builder(this) + .setTitle(R.string.forget_device) + .setMessage(d.toString()) + .setPositiveButton(android.R.string.ok, (dlg, w) -> { + Log.i("forget device: %s", d); + try { + showDevices(Devices.remove(this, d)); + } catch (Exception e) { + Log.e(e, "forget device: save failed"); + Toast.makeText(this, "could not save device list", + Toast.LENGTH_LONG).show(); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .show(); + } + private void pairAndSave(Devices.Device target, int pairPort, String code, Button btn) { try { Log.i("pair: %s:%d", target.host, pairPort); adb.pairDevice(target.host, pairPort, code); Log.i("pair ok host=%s pair_port=%d", target.host, pairPort); - List<Devices.Device> updated = Devices.upsert(this, target); + List<Devices.Device> updated = Devices.upsert(getApplicationContext(), target); runOnUiThread(() -> { - adapter.clear(); - adapter.addAll(updated); - adapter.notifyDataSetChanged(); + if (isFinishing() || isDestroyed()) return; + showDevices(updated); Toast.makeText(this, "paired and saved", Toast.LENGTH_SHORT).show(); btn.setEnabled(true); }); } catch (Exception e) { Log.e(e, "pair failed"); runOnUiThread(() -> { + if (isFinishing() || isDestroyed()) return; Toast.makeText(this, "pair failed: " + e.getMessage(), Toast.LENGTH_LONG).show(); btn.setEnabled(true); }); |