Skip to content

Commit 4ab7588

Browse files
committed
Login Wip working?
1 parent e0b97aa commit 4ab7588

3 files changed

Lines changed: 481 additions & 7 deletions

File tree

Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
package community.dcts.app;
2+
3+
import android.app.Activity;
4+
import android.app.AlertDialog;
5+
import android.content.Context;
6+
import android.content.SharedPreferences;
7+
import android.graphics.Color;
8+
import android.graphics.Typeface;
9+
import android.graphics.drawable.GradientDrawable;
10+
import android.view.Gravity;
11+
import android.view.View;
12+
import android.view.ViewGroup;
13+
import android.widget.ImageView;
14+
import android.widget.LinearLayout;
15+
import android.widget.TextView;
16+
17+
import org.json.JSONArray;
18+
import org.json.JSONObject;
19+
20+
import java.util.ArrayList;
21+
import java.util.Iterator;
22+
import java.util.List;
23+
24+
public class Accounts {
25+
26+
private static final String PREFS = "dcts_accounts";
27+
private final SharedPreferences prefs;
28+
private final Context context;
29+
30+
public interface OnPick { void onAccount(JSONObject account); }
31+
public interface OnScan { void onScanRequested(); }
32+
33+
public Accounts(Context context) {
34+
this.context = context;
35+
this.prefs = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
36+
}
37+
38+
private String norm(String url) {
39+
if (url == null) return "";
40+
url = url.toLowerCase().trim();
41+
url = url.replaceFirst("^https?://", "");
42+
while (url.endsWith("/")) url = url.substring(0, url.length() - 1);
43+
return url;
44+
}
45+
46+
private JSONObject loadAll() {
47+
try {
48+
return new JSONObject(prefs.getString("data", "{}"));
49+
} catch (Exception e) {
50+
return new JSONObject();
51+
}
52+
}
53+
54+
private JSONArray load(String url) {
55+
JSONArray arr = loadAll().optJSONArray(url);
56+
return arr != null ? arr : new JSONArray();
57+
}
58+
59+
private void write(String url, JSONArray list) {
60+
try {
61+
JSONObject all = loadAll();
62+
63+
if (list.length() == 0) all.remove(url);
64+
else all.put(url, list);
65+
66+
prefs.edit().putString("data", all.toString()).apply();
67+
} catch (Exception e) {
68+
e.printStackTrace();
69+
}
70+
}
71+
72+
private int findIndex(JSONArray list, String id) {
73+
for (int i = 0; i < list.length(); i++) {
74+
try {
75+
if (id.equals(list.getJSONObject(i).optString("id"))) return i;
76+
} catch (Exception ignored) {}
77+
}
78+
return -1;
79+
}
80+
81+
public void save(String url, JSONObject account) {
82+
url = norm(url);
83+
84+
try {
85+
JSONArray list = load(url);
86+
String id = account.getString("id");
87+
int idx = findIndex(list, id);
88+
89+
if (idx >= 0) list.put(idx, account);
90+
else list.put(account);
91+
92+
write(url, list);
93+
} catch (Exception e) {
94+
e.printStackTrace();
95+
}
96+
}
97+
98+
public void delete(String url, String id) {
99+
url = norm(url);
100+
JSONArray list = load(url);
101+
JSONArray filtered = new JSONArray();
102+
103+
for (int i = 0; i < list.length(); i++) {
104+
try {
105+
if (!id.equals(list.getJSONObject(i).optString("id")))
106+
filtered.put(list.getJSONObject(i));
107+
} catch (Exception ignored) {}
108+
}
109+
110+
write(url, filtered);
111+
}
112+
113+
public JSONObject get(String url, String id) {
114+
url = norm(url);
115+
JSONArray list = load(url);
116+
int idx = findIndex(list, id);
117+
118+
if (idx >= 0) {
119+
try { return list.getJSONObject(idx); }
120+
catch (Exception ignored) {}
121+
}
122+
123+
return null;
124+
}
125+
126+
public List<JSONObject> getAll(String url) {
127+
url = norm(url);
128+
JSONArray list = load(url);
129+
List<JSONObject> result = new ArrayList<>();
130+
131+
for (int i = 0; i < list.length(); i++) {
132+
try { result.add(list.getJSONObject(i)); }
133+
catch (Exception ignored) {}
134+
}
135+
136+
return result;
137+
}
138+
139+
private List<JSONObject> collectAllAccounts(List<String> origins) {
140+
List<JSONObject> items = new ArrayList<>();
141+
List<String> seenIds = new ArrayList<>();
142+
143+
try {
144+
JSONObject all = loadAll();
145+
Iterator<String> keys = all.keys();
146+
147+
while (keys.hasNext()) {
148+
String url = keys.next();
149+
JSONArray list = all.getJSONArray(url);
150+
151+
for (int i = 0; i < list.length(); i++) {
152+
JSONObject acc = list.getJSONObject(i);
153+
String accId = acc.optString("id");
154+
155+
if (seenIds.contains(accId)) continue;
156+
157+
seenIds.add(accId);
158+
items.add(acc);
159+
origins.add(url);
160+
}
161+
}
162+
} catch (Exception ignored) {}
163+
164+
return items;
165+
}
166+
167+
private View buildAvatar(String icon, String name) {
168+
GradientDrawable circle = new GradientDrawable();
169+
circle.setShape(GradientDrawable.OVAL);
170+
circle.setColor(Color.parseColor("#000000"));
171+
circle.setSize(96, 96);
172+
173+
if (icon != null && !icon.isEmpty() && icon.startsWith("https")) {
174+
ImageView img = new ImageView(context);
175+
img.setScaleType(ImageView.ScaleType.CENTER_CROP);
176+
img.setLayoutParams(new LinearLayout.LayoutParams(96, 96));
177+
img.setBackground(circle);
178+
img.setClipToOutline(true);
179+
180+
new Thread(() -> {
181+
try {
182+
java.io.InputStream in = new java.net.URL(icon).openStream();
183+
android.graphics.Bitmap bmp = android.graphics.BitmapFactory.decodeStream(in);
184+
((Activity) context).runOnUiThread(() -> img.setImageBitmap(bmp));
185+
} catch (Exception ignored) {}
186+
}).start();
187+
188+
return img;
189+
}
190+
191+
TextView letter = new TextView(context);
192+
letter.setBackground(circle);
193+
letter.setText(name.substring(0, 1).toUpperCase());
194+
letter.setTextColor(Color.WHITE);
195+
letter.setTextSize(18);
196+
letter.setGravity(Gravity.CENTER);
197+
letter.setWidth(96);
198+
letter.setHeight(96);
199+
200+
return letter;
201+
}
202+
203+
private LinearLayout buildInfoColumn(String name, String status) {
204+
LinearLayout info = new LinearLayout(context);
205+
info.setOrientation(LinearLayout.VERTICAL);
206+
info.setPadding(24, 0, 0, 0);
207+
208+
TextView nameView = new TextView(context);
209+
nameView.setText(name);
210+
nameView.setTextSize(16);
211+
nameView.setTypeface(null, Typeface.BOLD);
212+
info.addView(nameView);
213+
214+
if (status != null && !status.isEmpty()) {
215+
TextView statusView = new TextView(context);
216+
statusView.setText(status);
217+
statusView.setTextSize(13);
218+
statusView.setAlpha(0.6f);
219+
info.addView(statusView);
220+
}
221+
222+
return info;
223+
}
224+
225+
private TextView buildDeleteButton(String origin, String id, String name,
226+
String currentUrl, AlertDialog[] dialogRef,
227+
OnPick onPick, OnScan onScan) {
228+
229+
TextView btn = new TextView(context);
230+
btn.setText("\u2715");
231+
btn.setTextSize(18);
232+
btn.setTextColor(Color.parseColor("#FF4444"));
233+
btn.setPadding(24, 0, 0, 0);
234+
235+
btn.setOnClickListener(v -> {
236+
new AlertDialog.Builder(context)
237+
.setMessage("Remove " + name + "?")
238+
.setPositiveButton("Remove", (d, w) -> {
239+
delete(origin, id);
240+
if (dialogRef[0] != null) dialogRef[0].dismiss();
241+
pick(currentUrl, onPick, onScan);
242+
})
243+
.setNegativeButton("Cancel", null)
244+
.show();
245+
});
246+
247+
return btn;
248+
}
249+
250+
private LinearLayout buildScanButton() {
251+
LinearLayout scanBtn = new LinearLayout(context);
252+
scanBtn.setOrientation(LinearLayout.HORIZONTAL);
253+
scanBtn.setGravity(Gravity.CENTER_VERTICAL);
254+
scanBtn.setPadding(48, 24, 48, 24);
255+
256+
TextView icon = new TextView(context);
257+
icon.setText("\uD83D\uDCF7");
258+
icon.setTextSize(20);
259+
scanBtn.addView(icon);
260+
261+
TextView label = new TextView(context);
262+
label.setText("Scan QR Code");
263+
label.setTextSize(16);
264+
label.setPadding(24, 0, 0, 0);
265+
scanBtn.addView(label);
266+
267+
return scanBtn;
268+
}
269+
270+
private LinearLayout buildAccountRow(JSONObject account, String origin, String currentUrl,
271+
AlertDialog[] dialogRef, OnPick onPick, OnScan onScan) {
272+
273+
String id = account.optString("id");
274+
String icon = account.optString("icon");
275+
String name = account.optString("name", account.optString("loginName", "?"));
276+
String status = account.optString("status", "");
277+
boolean foreign = !origin.equals(currentUrl);
278+
279+
LinearLayout row = new LinearLayout(context);
280+
row.setOrientation(LinearLayout.HORIZONTAL);
281+
row.setGravity(Gravity.CENTER_VERTICAL);
282+
row.setPadding(48, 28, 48, 28);
283+
284+
if (foreign) row.setAlpha(0.6f);
285+
286+
row.addView(buildAvatar(icon, name));
287+
288+
String displayName = name;
289+
LinearLayout info = buildInfoColumn(displayName, status);
290+
row.addView(info, new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1));
291+
292+
row.addView(buildDeleteButton(origin, id, name, currentUrl, dialogRef, onPick, onScan));
293+
294+
row.setOnClickListener(v -> {
295+
if (dialogRef[0] != null) dialogRef[0].dismiss();
296+
297+
try {
298+
JSONObject safe = new JSONObject(account.toString());
299+
if (foreign) safe.remove("token");
300+
onPick.onAccount(safe);
301+
} catch (Exception ignored) {}
302+
});
303+
304+
return row;
305+
}
306+
307+
public void pick(String currentUrl, OnPick onPick, OnScan onScan) {
308+
currentUrl = norm(currentUrl);
309+
String finalUrl = currentUrl;
310+
311+
List<String> origins = new ArrayList<>();
312+
List<JSONObject> items = collectAllAccounts(origins);
313+
314+
LinearLayout root = new LinearLayout(context);
315+
root.setOrientation(LinearLayout.VERTICAL);
316+
root.setPadding(0, 24, 0, 0);
317+
318+
LinearLayout scanBtn = buildScanButton();
319+
root.addView(scanBtn);
320+
321+
View divider = new View(context);
322+
divider.setBackgroundColor(Color.parseColor("#33888888"));
323+
root.addView(divider, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 2));
324+
325+
AlertDialog[] dialogRef = new AlertDialog[1];
326+
327+
for (int i = 0; i < items.size(); i++) {
328+
root.addView(buildAccountRow(items.get(i), origins.get(i), finalUrl, dialogRef, onPick, onScan));
329+
}
330+
331+
if (items.isEmpty()) {
332+
TextView empty = new TextView(context);
333+
empty.setText("No accounts yet");
334+
empty.setGravity(Gravity.CENTER);
335+
empty.setPadding(48, 64, 48, 64);
336+
empty.setAlpha(0.5f);
337+
root.addView(empty);
338+
}
339+
340+
scanBtn.setOnClickListener(v -> {
341+
if (dialogRef[0] != null) dialogRef[0].dismiss();
342+
onScan.onScanRequested();
343+
});
344+
345+
dialogRef[0] = new AlertDialog.Builder(context)
346+
.setTitle("Pick Account")
347+
.setView(root)
348+
.setNegativeButton("Cancel", null)
349+
.show();
350+
}
351+
}

0 commit comments

Comments
 (0)