aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/invalid/lena/scrcpy/Main.java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/invalid/lena/scrcpy/Main.java')
-rw-r--r--app/src/main/java/invalid/lena/scrcpy/Main.java117
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);
});