1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
/*
* mygram-droid: Android TV client for Foldergram.
* Copyright (C) 2026 Danilo M. <danix@danix.xyz>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* 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.
*/
package mygram.droid;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.InputType;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.util.LinkedHashMap;
import java.util.Map;
/** Instance picker: LAN scan, manual URL, then the password when the instance asks for one. */
public final class SetupActivity extends Activity {
private LinearLayout box;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
box = Ui.column(this);
setContentView(box);
scan();
}
private void scan() {
reset("Searching the network for Foldergram…");
Api.async(() -> {
Map<String, String> found = new LinkedHashMap<>();
String self = Discovery.localIpv4(this);
if (self != null)
for (String base : Discovery.scan(self))
found.put(base, Discovery.label(base));
return found;
}, this::found, e -> {
reset("Scan failed: " + e.getMessage());
actions();
});
}
private void found(Map<String, String> instances) {
reset(instances.isEmpty() ? "No Foldergram found on port " + Discovery.PORT : "Choose an instance");
for (Map.Entry<String, String> e : instances.entrySet()) {
TextView t = Ui.text(this, e.getValue(), 20);
int p = Ui.dp(this, 12);
t.setPadding(p * 2, p, p * 2, p);
Ui.focusable(t);
t.setOnClickListener(v -> choose(e.getKey()));
box.addView(t);
}
actions();
box.getChildAt(1).requestFocus();
}
private void actions() {
LinearLayout row = Ui.row(this);
row.addView(Ui.iconButton(this, R.drawable.ic_refresh, "Rescan", v -> scan()));
row.addView(Ui.iconButton(this, R.drawable.ic_edit, "Enter URL", v -> manual()));
box.addView(row);
}
private void manual() {
reset("Instance URL");
EditText url = Ui.field(this, "http://host:" + Discovery.PORT,
InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI);
box.addView(url);
box.addView(Ui.iconButton(this, R.drawable.ic_check, "Connect",
v -> choose(Api.normalize(url.getText().toString()))));
url.requestFocus();
}
private void choose(String base) {
reset("Connecting to " + base + "…");
Api.async(() -> Api.authStatus(base), status -> {
if ("public".equals(status.optString("accessMode")))
save(base, "");
else
password(base);
}, e -> {
reset("Cannot reach " + base + ": " + e.getMessage());
actions();
});
}
private void password(String base) {
reset("Viewer password for " + base);
EditText pw = Ui.field(this, "Password", Ui.password());
box.addView(pw);
box.addView(Ui.iconButton(this, R.drawable.ic_check, "Log in", v -> {
String s = pw.getText().toString();
Api.async(() -> {
Api.login(base, s);
return true;
}, ok -> save(base, s), e -> Toast.makeText(this,
e instanceof Api.HttpError && ((Api.HttpError) e).code == 401 ? "Wrong password" : e.getMessage(),
Toast.LENGTH_LONG).show());
}));
pw.requestFocus();
}
private void save(String base, String password) {
Prefs.setServer(base, password);
startActivity(new Intent(this, MainActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK));
}
private void reset(String title) {
box.removeAllViews();
box.addView(Ui.text(this, title, 24));
}
}
|