Show pictures in descriptions, or links to them when turned off

[img] in a channel, client or server description is now a picture on both
clients, as TeamSpeak shows it; chat keeps it a link. Only web addresses
become pictures. While one loads it is TeamSpeak's loading icon; one that
cannot be had is its address behind the broken-picture icon.

"Show pictures in descriptions" (desktop: Chat settings; Android: Appearance)
turns them into links behind a picture icon instead, fetching nothing. A
picture that was itself a link keeps that link beside it.

Core's PictureDownloads fetches them for both clients: kept on disk for a
month after last shown, at most 8 MB, redirects followed across http and
https, and on Android plain http asked for over https. The desktop draws
them narrowed to the info panel, Android at a pixel to a dp, animated GIFs
playing on both; long addresses are shortened to host and file name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-30 23:06:13 +00:00
parent fd86d9e638
commit af0564702e
19 changed files with 805 additions and 68 deletions

View File

@@ -3,6 +3,7 @@ package com.ts3client.ui;
import com.formdev.flatlaf.util.UIScale;
import com.ts3client.audio.AudioBackend;
import com.ts3client.audio.desktop.DesktopAudioBackend;
import com.ts3client.config.AppDirs;
import com.ts3client.config.AwayMessages;
import com.ts3client.config.BanReasonPresets;
import com.ts3client.config.Bookmark;
@@ -13,6 +14,7 @@ import com.ts3client.myts.MyTeamSpeakLogin;
import com.ts3client.teamspeak.TeamSpeakImporter;
import com.ts3client.contacts.ContactStore;
import com.ts3client.net.ChannelNode;
import com.ts3client.net.PictureDownloads;
import com.ts3client.session.SelfState;
import com.ts3client.session.ServerSession;
import com.ts3client.session.Sessions;
@@ -33,6 +35,7 @@ import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import java.io.File;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
@@ -62,6 +65,7 @@ public final class MainFrame extends JFrame implements ServerTabPane.Listener {
/** Friends and blocked clients, shared by every connection. */
private final ContactStore contacts = ContactStore.load();
private final AudioBackend audio = new DesktopAudioBackend();
private final PictureDownloads pictures = new PictureDownloads(new File(AppDirs.profile(), "pictures"), true);
/** Sound pack playback, shared by every connection. */
private final SoundNotifier sounds;
private final SoundPlayer soundPlayer;
@@ -360,7 +364,7 @@ public final class MainFrame extends JFrame implements ServerTabPane.Listener {
// ---- tab management ----
private ServerTab newTab() {
ServerTab tab = new ServerTab(this, settings, identities, audio, sounds, contacts);
ServerTab tab = new ServerTab(this, settings, identities, audio, sounds, contacts, pictures);
tabs.add(tab);
sessions.add(tab.session());
tabPane.addTab(tab);