Add Complete Server Browser UI System

MAJOR UPDATE - In-game server management without rebuilding APK!

SERVER BROWSER UI:
- Beautiful WebView-based interface
- Add/edit/delete unlimited servers
- Real-time online/offline status
- One-click server switching
- Favorites system
- Connection testing before save
- Professional UX with racing theme

HTML ASSETS:
+ assets/community_servers_list.html
  - Main server browser interface
  - Server cards with status indicators
  - Connect/Edit/Delete actions
  - Empty state and loading states

+ assets/community_server_edit.html
  - Add/edit server form
  - URL validation and testing
  - Favorite marking
  - Professional form design

INSTALLATION TOOL:
+ RR3-Server-Browser-Installer.ps1
  - Automated installation script
  - Decompiles APK with apktool
  - Injects HTML assets
  - Updates AndroidManifest.xml
  - Rebuilds and signs APK
  - Pre-configure default servers
  - Full error handling

DOCUMENTATION:
+ docs/SERVER_BROWSER_GUIDE.md
  - Complete user guide
  - Adding/editing/deleting servers
  - Connection flow
  - Troubleshooting
  - Developer integration

+ docs/SMALI_REFERENCE.md
  - Java bridge code reference
  - CommunityServerManager class
  - WebView activity hosts
  - Smali conversion guide
  - Testing & debugging tips

UPDATED README:
* Comprehensive overview
* Quick start examples
* Feature highlights
* Use cases (players/owners/devs)
* Architecture explanation
* Screenshots in ASCII art

ARCHITECTURE:
- HTML/CSS/JS UI layer (assets/)
- JavascriptInterface bridge (smali)
- SharedPreferences storage
- SynergyEnvironmentImpl patch
- WebView activities for hosting

USER FLOW:
1. Open Server Browser from game
2. Add server (name + URL)
3. Test connection
4. Save server
5. Tap Connect
6. Restart game -> Active!

BENEFITS:
✓ One APK for unlimited servers
✓ No rebuild needed to change servers
✓ Users can add servers themselves
✓ Server owners can share one APK
✓ Professional UI experience
✓ Local + LAN + public servers
✓ Favorites and status tracking

TECHNICAL DETAILS:
- Data stored in SharedPreferences
- JavaScript <-> Android bridge
- Async server pinging
- URL validation
- Toast notifications
- File:// asset loading

This enables true community server ecosystem!
Users can maintain their own server list
without technical knowledge or APK rebuilding.

Perfect companion to rr3-server project!

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-02-17 22:29:22 -08:00
parent d144aec853
commit ad15ecb2d7
6 changed files with 1663 additions and 6 deletions

View File

@@ -0,0 +1,289 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Community Servers</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', Arial, sans-serif;
background: linear-gradient(135deg, #1d3557 0%, #457b9d 100%);
color: white;
padding: 20px;
min-height: 100vh;
}
.container {
max-width: 600px;
margin: 0 auto;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
background: rgba(255,255,255,0.1);
padding: 20px;
border-radius: 15px;
}
.header h1 {
font-size: 28px;
color: #f1faee;
}
.settings-btn {
background: rgba(230, 57, 70, 0.8);
border: none;
padding: 10px 20px;
border-radius: 8px;
color: white;
font-size: 16px;
cursor: pointer;
transition: all 0.3s;
}
.settings-btn:hover {
background: rgba(230, 57, 70, 1);
transform: scale(1.05);
}
.server-card {
background: rgba(255,255,255,0.1);
backdrop-filter: blur(10px);
border-radius: 15px;
padding: 20px;
margin-bottom: 15px;
box-shadow: 0 4px 16px rgba(0,0,0,0.2);
transition: all 0.3s;
}
.server-card:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0,0,0,0.3);
}
.server-card.active {
border: 2px solid #2ed573;
background: rgba(46, 213, 115, 0.2);
}
.server-name {
font-size: 20px;
font-weight: 600;
margin-bottom: 8px;
color: #f1faee;
}
.server-url {
font-size: 14px;
color: #a8dadc;
margin-bottom: 12px;
word-break: break-all;
}
.server-footer {
display: flex;
justify-content: space-between;
align-items: center;
}
.server-status {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
}
.status-indicator {
width: 10px;
height: 10px;
border-radius: 50%;
display: inline-block;
}
.status-online { background: #2ed573; }
.status-offline { background: #ff4757; }
.status-checking { background: #ffa502; animation: pulse 1s infinite; }
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.server-actions {
display: flex;
gap: 10px;
}
.btn {
padding: 8px 16px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
}
.btn-connect {
background: #2ed573;
color: white;
}
.btn-connect:hover {
background: #26de81;
}
.btn-edit {
background: #ffa502;
color: white;
}
.btn-edit:hover {
background: #ff8c00;
}
.btn-delete {
background: #ff4757;
color: white;
}
.btn-delete:hover {
background: #ee5a6f;
}
.add-server-btn {
width: 100%;
padding: 15px;
background: rgba(230, 57, 70, 0.8);
border: 2px dashed rgba(255,255,255,0.3);
border-radius: 10px;
color: white;
font-size: 18px;
cursor: pointer;
transition: all 0.3s;
margin-top: 20px;
}
.add-server-btn:hover {
background: rgba(230, 57, 70, 1);
border-color: rgba(255,255,255,0.6);
}
.empty-state {
text-align: center;
padding: 60px 20px;
background: rgba(255,255,255,0.1);
border-radius: 15px;
margin-bottom: 20px;
}
.empty-state-icon {
font-size: 64px;
margin-bottom: 20px;
}
.empty-state-text {
font-size: 18px;
color: #a8dadc;
}
.back-btn {
background: rgba(255,255,255,0.1);
border: none;
padding: 10px 20px;
border-radius: 8px;
color: white;
font-size: 16px;
cursor: pointer;
margin-bottom: 20px;
}
.back-btn:hover {
background: rgba(255,255,255,0.2);
}
</style>
</head>
<body>
<div class="container">
<button class="back-btn" onclick="AndroidInterface.closeScreen()">← Back to Game</button>
<div class="header">
<h1>🌐 Community Servers</h1>
</div>
<div id="serverList"></div>
<button class="add-server-btn" onclick="addNewServer()">
+ Add New Server
</button>
</div>
<script>
let servers = [];
function loadServers() {
const serversJson = AndroidInterface.getServers();
servers = JSON.parse(serversJson || '[]');
const activeServerId = AndroidInterface.getActiveServerId();
renderServerList(activeServerId);
// Start pinging all servers
servers.forEach(server => {
AndroidInterface.pingServer(server.id, server.url);
});
}
function renderServerList(activeServerId) {
const container = document.getElementById('serverList');
if (servers.length === 0) {
container.innerHTML = `
<div class="empty-state">
<div class="empty-state-icon">🌍</div>
<div class="empty-state-text">
No servers added yet.<br>
Tap "Add New Server" to get started!
</div>
</div>
`;
return;
}
container.innerHTML = servers.map(server => `
<div class="server-card ${server.id === activeServerId ? 'active' : ''}" data-server-id="${server.id}">
<div class="server-name">
${server.isFavorite ? '⭐ ' : ''}${server.name}
${server.id === activeServerId ? ' <span style="color:#2ed573">(Active)</span>' : ''}
</div>
<div class="server-url">${server.url}</div>
<div class="server-footer">
<div class="server-status">
<span class="status-indicator status-checking" id="status-${server.id}"></span>
<span id="status-text-${server.id}">Checking...</span>
</div>
<div class="server-actions">
${server.id !== activeServerId ? `<button class="btn btn-connect" onclick="connectToServer('${server.id}')">Connect</button>` : ''}
<button class="btn btn-edit" onclick="editServer('${server.id}')">✏️</button>
<button class="btn btn-delete" onclick="deleteServer('${server.id}')">🗑️</button>
</div>
</div>
</div>
`).join('');
}
function updateServerStatus(serverId, isOnline) {
const indicator = document.getElementById(`status-${serverId}`);
const text = document.getElementById(`status-text-${serverId}`);
if (indicator && text) {
indicator.className = `status-indicator ${isOnline ? 'status-online' : 'status-offline'}`;
text.textContent = isOnline ? 'Online' : 'Offline';
}
}
function connectToServer(serverId) {
const server = servers.find(s => s.id === serverId);
if (server) {
AndroidInterface.setActiveServer(serverId);
AndroidInterface.showToast(`Connected to ${server.name}. Restart game to apply.`);
loadServers(); // Refresh to show active state
}
}
function addNewServer() {
AndroidInterface.openServerEdit('');
}
function editServer(serverId) {
AndroidInterface.openServerEdit(serverId);
}
function deleteServer(serverId) {
const server = servers.find(s => s.id === serverId);
if (server && confirm(`Delete "${server.name}"?`)) {
AndroidInterface.deleteServer(serverId);
loadServers();
AndroidInterface.showToast('Server deleted');
}
}
// Load servers on page load
loadServers();
</script>
</body>
</html>