Open avatars and description pictures full size on Android; fold minor client facts

A client's sheet now shows only their server and channel groups, with
the rest (contact, talk power, version, microphone, unique ID, ...)
behind "more…". Core marks those facts minor, for frontends short of
room; groups without a name or an icon are left out on both clients.

Tapping an avatar or a picture in a description opens it on black, to
pinch or double-tap zoom. Its menu saves it to Pictures/TeamSpeak, takes
it as our avatar (not on our own), copies its address, and opens the
link it sat in, which a tap used to do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 08:27:50 +00:00
parent e799218444
commit 7f30a27d73
11 changed files with 349 additions and 24 deletions

View File

@@ -23,10 +23,19 @@ public record Details(String title, List<Field> fields, String description, bool
*
* @param groups the groups {@code value} names, in order, for frontends that show their icons;
* empty when it names none
* @param minor for frontends short of room to show only on asking
*/
public record Field(String label, String value, List<Group> groups) {
public record Field(String label, String value, List<Group> groups, boolean minor) {
Field(String label, String value) {
this(label, value, List.of());
this(label, value, List.of(), false);
}
Field(String label, String value, List<Group> groups) {
this(label, value, groups, false);
}
static Field minor(String label, String value) {
return new Field(label, value, List.of(), true);
}
}
@@ -66,7 +75,7 @@ public record Details(String title, List<Field> fields, String description, bool
if (contact != null) {
String type = contact.type.label();
if (!contact.phoneticNickname.isEmpty()) type += " (" + contact.phoneticNickname + ")";
fields.add(new Field("Contact", type));
fields.add(Field.minor("Contact", type));
}
List<Group> serverGroups = model.serverGroupsOf(cl.serverGroupIds);
@@ -74,27 +83,33 @@ public record Details(String title, List<Field> fields, String description, bool
List<String> names = model.serverGroupNames(cl.serverGroupIds);
fields.add(new Field("Server groups", names.isEmpty() ? "—" : String.join(", ", names)));
} else {
// A nameless group is there for its icon only; without one, there is nothing to show of it.
List<Group> shown = new ArrayList<>();
List<String> names = new ArrayList<>();
for (Group g : serverGroups) names.add(g.name);
fields.add(new Field("Server groups", String.join(", ", names), serverGroups));
for (Group g : serverGroups) {
if (g.name.isBlank() && g.iconId == 0) continue;
shown.add(g);
if (!g.name.isBlank()) names.add(g.name.trim());
}
fields.add(new Field("Server groups", names.isEmpty() ? "—" : String.join(", ", names), shown));
}
Group channelGroup = model.channelGroup(cl.channelGroupId);
fields.add(channelGroup != null
? new Field("Channel group", channelGroup.name, List.of(channelGroup))
: new Field("Channel group", "#" + cl.channelGroupId));
if (cl.talkPower != 0) fields.add(new Field("Talk power", Integer.toString(cl.talkPower)));
if (cl.talkPower != 0) fields.add(Field.minor("Talk power", Integer.toString(cl.talkPower)));
if (!cl.version.isEmpty()) {
fields.add(new Field("Version", cl.version + (cl.platform.isEmpty() ? "" : " on " + cl.platform)));
fields.add(Field.minor("Version", cl.version + (cl.platform.isEmpty() ? "" : " on " + cl.platform)));
} else if (!cl.platform.isEmpty()) {
fields.add(new Field("Platform", cl.platform));
fields.add(Field.minor("Platform", cl.platform));
}
if (cl.away) fields.add(new Field("Status", "Away"));
if (!cl.inputHardware) fields.add(new Field("Microphone", "disabled"));
else if (cl.inputMuted) fields.add(new Field("Microphone", "muted"));
if (!cl.outputHardware) fields.add(new Field("Speakers", "disabled"));
else if (cl.outputMuted) fields.add(new Field("Speakers", "muted"));
if (!cl.uniqueId.isEmpty()) fields.add(new Field("Unique ID", cl.uniqueId));
if (cl.away) fields.add(Field.minor("Status", "Away"));
if (!cl.inputHardware) fields.add(Field.minor("Microphone", "disabled"));
else if (cl.inputMuted) fields.add(Field.minor("Microphone", "muted"));
if (!cl.outputHardware) fields.add(Field.minor("Speakers", "disabled"));
else if (cl.outputMuted) fields.add(Field.minor("Speakers", "muted"));
if (!cl.uniqueId.isEmpty()) fields.add(Field.minor("Unique ID", cl.uniqueId));
String name = contact == null ? cl.nickname : contact.displayName(cl.nickname);
return new Details(name, fields, cl.description == null ? "" : cl.description, false);