# Smali Reference for Community Server Manager This document shows the Java code that needs to be converted to Smali for the server browser system. ## Core Classes Needed ### 1. CommunityServerManager.java This is the main bridge between JavaScript and Android. ```java package com.community; import android.content.Context; import android.content.SharedPreferences; import android.webkit.JavascriptInterface; import android.widget.Toast; import android.os.Handler; import android.os.Looper; import java.net.HttpURLConnection; import java.net.URL; public class CommunityServerManager { private Context context; private static final String PREFS_NAME = "com.ea.games.r3_row_preferences"; private static final String KEY_SERVERS = "community_servers"; private static final String KEY_ACTIVE_SERVER = "active_server_id"; public CommunityServerManager(Context context) { this.context = context; } @JavascriptInterface public String getServers() { SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); return prefs.getString(KEY_SERVERS, "[]"); } @JavascriptInterface public String getActiveServerId() { SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); return prefs.getString(KEY_ACTIVE_SERVER, ""); } @JavascriptInterface public String getServerById(String serverId) { String serversJson = getServers(); // Parse JSON and find server by ID // Return server JSON or "{}" return "{}"; // Simplified } @JavascriptInterface public void addServer(String serverJson) { SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); String existingServers = prefs.getString(KEY_SERVERS, "[]"); // Parse existing servers, add new one, save back // Simplified: just append to JSON array SharedPreferences.Editor editor = prefs.edit(); editor.putString(KEY_SERVERS, existingServers); // Updated array editor.apply(); } @JavascriptInterface public void updateServer(String serverJson) { // Similar to addServer but replaces existing entry } @JavascriptInterface public void deleteServer(String serverId) { SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); String serversJson = getServers(); // Parse JSON, remove server with matching ID, save back SharedPreferences.Editor editor = prefs.edit(); editor.putString(KEY_SERVERS, serversJson); // Updated array editor.apply(); } @JavascriptInterface public void setActiveServer(String serverId) { SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); editor.putString(KEY_ACTIVE_SERVER, serverId); editor.apply(); // Also update the server URL for game to use String serverUrl = getServerUrlById(serverId); if (!serverUrl.isEmpty()) { editor.putString("active_server_url", serverUrl); editor.apply(); } } @JavascriptInterface public String getActiveServerUrl() { SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); return prefs.getString("active_server_url", ""); } @JavascriptInterface public void showToast(final String message) { Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override public void run() { Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } }); } @JavascriptInterface public void pingServer(final String serverId, final String url) { // Run in background thread new Thread(new Runnable() { @Override public void run() { boolean isOnline = testServerConnection(url); // Call JavaScript callback: updateServerStatus(serverId, isOnline) } }).start(); } @JavascriptInterface public void testConnection(final String url) { new Thread(new Runnable() { @Override public void run() { boolean isOnline = testServerConnection(url); // Call JavaScript: showTestResult(message, success) } }).start(); } @JavascriptInterface public void openServerEdit(String serverId) { // Store editing server ID SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); editor.putString("editing_server_id", serverId); editor.apply(); // Launch ServerEditActivity // Intent intent = new Intent(context, ServerEditActivity.class); // context.startActivity(intent); } @JavascriptInterface public String getEditingServerId() { SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); return prefs.getString("editing_server_id", ""); } @JavascriptInterface public void goBackToServerList() { // Finish current activity and return to server list } @JavascriptInterface public void closeScreen() { // Close the WebView activity } private boolean testServerConnection(String urlString) { try { URL url = new URL(urlString + "/director"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); int responseCode = conn.getResponseCode(); conn.disconnect(); return responseCode >= 200 && responseCode < 400; } catch (Exception e) { return false; } } private String getServerUrlById(String serverId) { String serversJson = getServers(); // Parse JSON and find server URL by ID return ""; // Simplified } } ``` ### 2. CommunityServersActivity.java WebView host for the server list. ```java package com.community; import android.app.Activity; import android.os.Bundle; import android.webkit.WebView; import android.webkit.WebSettings; public class CommunityServersActivity extends Activity { private WebView webView; private CommunityServerManager serverManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create WebView webView = new WebView(this); setContentView(webView); // Configure WebView WebSettings settings = webView.getSettings(); settings.setJavaScriptEnabled(true); settings.setDomStorageEnabled(true); settings.setAllowFileAccess(true); // Add JavaScript interface serverManager = new CommunityServerManager(this); webView.addJavascriptInterface(serverManager, "AndroidInterface"); // Load HTML from assets webView.loadUrl("file:///android_asset/community_servers_list.html"); } @Override public void onBackPressed() { super.onBackPressed(); finish(); } } ``` ### 3. ServerEditActivity.java WebView host for the server edit form. ```java package com.community; import android.app.Activity; import android.os.Bundle; import android.webkit.WebView; import android.webkit.WebSettings; public class ServerEditActivity extends Activity { private WebView webView; private CommunityServerManager serverManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); webView = new WebView(this); setContentView(webView); WebSettings settings = webView.getSettings(); settings.setJavaScriptEnabled(true); settings.setDomStorageEnabled(true); serverManager = new CommunityServerManager(this); webView.addJavascriptInterface(serverManager, "AndroidInterface"); webView.loadUrl("file:///android_asset/community_server_edit.html"); } } ``` ## Patching SynergyEnvironmentImpl The game needs to check for active server URL when initializing network environment. ### Original Code (SynergyEnvironmentImpl.java) ```java public String getEnvironmentUrls() { // Original EA server URLs return "https://rr3-prod.ea.com"; } ``` ### Patched Code ```java public String getEnvironmentUrls() { // Check for community server override SharedPreferences prefs = context.getSharedPreferences( "com.ea.games.r3_row_preferences", Context.MODE_PRIVATE ); String communityServerUrl = prefs.getString("active_server_url", ""); if (!communityServerUrl.isEmpty()) { // Use community server return communityServerUrl; } // Fall back to EA servers return "https://rr3-prod.ea.com"; } ``` ## Converting to Smali To convert these Java classes to Smali: 1. **Write the Java code** in a new Android project 2. **Compile to bytecode** (.class files) 3. **Convert to Smali** using: ```bash javac -source 1.7 -target 1.7 CommunityServerManager.java baksmali d CommunityServerManager.class -o output/ ``` 4. **Copy .smali files** to APK's `smali/com/community/` directory ## Key Smali Patterns ### JavascriptInterface Annotation ```smali .annotation runtime Landroid/webkit/JavascriptInterface; .end annotation ``` ### SharedPreferences Access ```smali const-string v1, "com.ea.games.r3_row_preferences" const/4 v2, 0x0 invoke-virtual {v0, v1, v2}, Landroid/content/Context;->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences; move-result-object v0 ``` ### String Operations ```smali const-string v1, "community_servers" const-string v2, "[]" invoke-interface {v0, v1, v2}, Landroid/content/SharedPreferences;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; move-result-object v0 return-object v0 ``` ## Testing After adding smali files: 1. Rebuild APK with `apktool b` 2. Sign APK 3. Install on device 4. Test with: ```bash adb shell am start -n com.ea.games.r3_row/com.community.CommunityServersActivity ``` 5. Check logcat for errors: ```bash adb logcat | grep -E "(Community|WebView|JavaScript)" ``` ## Debugging Tips - Use `android.util.Log` liberally in Java code - Test each method individually via JavaScript console - Verify SharedPreferences with: ```bash adb shell run-as com.ea.games.r3_row cat shared_prefs/com.ea.games.r3_row_preferences.xml ``` - Use Chrome DevTools to debug WebView: 1. Enable WebView debugging in code 2. Open `chrome://inspect` on PC 3. Connect device and inspect WebView ## Notes - All JavaScript interfaces must run on UI thread or handle threading manually - JSON parsing can use `org.json.JSONObject` and `org.json.JSONArray` - Network operations MUST be on background thread - WebView file access requires proper permissions in manifest --- **This reference provides the Java structure needed to generate correct Smali code for the server browser system.**