Show status information
authorChristian Weiske <cweiske@cweiske.de>
Thu, 5 May 2022 19:46:50 +0000 (21:46 +0200)
committerChristian Weiske <cweiske@cweiske.de>
Thu, 5 May 2022 19:46:50 +0000 (21:46 +0200)
src/main/AndroidManifest.xml
src/main/java/de/cweiske/ouya/louyapi/MainActivity.java
src/main/res/layout/activity_main.xml
src/main/res/values/strings.xml
src/main/res/values/styles.xml [new file with mode: 0644]

index 3180b4190404796a873b81b913c44605c3a29ce7..b61c74c086978c6150980d1766c6a66b7907c30d 100644 (file)
@@ -6,6 +6,10 @@
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- start when the OUYA booted -->
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
 
     <application
         android:allowBackup="true"
index c5ca1c21972fce7e5b368538b8115265f56c6b30..656af0beaba94d845b704cef9516194ac16f76a1 100644 (file)
@@ -1,13 +1,42 @@
 package de.cweiske.ouya.louyapi;
 
 import android.app.Activity;
+import android.content.Context;
 import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiManager;
 import android.os.Bundle;
+import android.os.Environment;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Properties;
 
 public class MainActivity extends Activity {
 
+    private static final String TAG = "louyapi-ui";
+
     protected Intent serviceIntent;
 
+    protected String configFilePath;
+    protected String configFileBackupPath;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -15,6 +44,201 @@ public class MainActivity extends Activity {
 
         serviceIntent = new Intent(this, HttpService.class);
         startService(serviceIntent);
+
+        configFilePath = Environment.getExternalStorageDirectory().getPath() + "/ouya_config.properties";
+        configFileBackupPath = Environment.getExternalStorageDirectory().getPath() + "/ouya_config.properties.backup";
+
+        loadStatus();
+    }
+
+    protected void loadStatus() {
+        TextView statusPortNumber = (TextView) findViewById(R.id.statusPortNumber);
+        statusPortNumber.setText("8080");
+
+        TextView statusCurrentServer = (TextView) findViewById(R.id.statusCurrentServer);
+        String currentServer = loadCurrentServer();
+        if (currentServer == null) {
+            statusCurrentServer.setText("https://devs.ouya.tv/ (default)");
+        } else {
+            statusCurrentServer.setText(currentServer);
+        }
+
+        TextView statusIpAddress = (TextView) findViewById(R.id.statusIpAddress);
+        statusIpAddress.setText(getCurrentIpAddressStatus());
+
+        Button useDefault = (Button) findViewById(R.id.buttonUseDefault);
+        Button backupCreate = (Button) findViewById(R.id.buttonBackupCreate);
+        File conf = new File(configFilePath);
+        useDefault.setEnabled(conf.exists());
+        backupCreate.setEnabled(conf.exists());
+
+        Button backupRestore = (Button) findViewById(R.id.buttonBackupRestore);
+        TextView backupTime = (TextView) findViewById(R.id.backupDate);
+        File backup = new File(configFileBackupPath);
+        backupRestore.setEnabled(backup.exists());
+        if (backup.exists()) {
+            Date modified = new Date(backup.lastModified());
+            SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd HH:mm:ss z");
+            backupTime.setText(sdf.format(modified));
+        } else {
+            backupTime.setText("");
+        }
+    }
+
+    /**
+     * This got much more complicated than I anticipated :(
+     *
+     * @return The current IPv4 address plus the connection type
+     */
+    private String getCurrentIpAddressStatus() {
+        ConnectivityManager cm = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
+        if (cm == null) {
+            return "Unknown";
+        }
+
+        NetworkInfo info = cm.getActiveNetworkInfo();
+        if (info == null) {
+            return "No network connection";
+        }
+        if (info.getType() == ConnectivityManager.TYPE_WIFI) {
+            WifiManager wifi = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
+            int ip = wifi != null ? wifi.getConnectionInfo().getIpAddress() : 0;
+            if (ip != 0) {
+                String ipStr = formatIp(ip);
+                return ipStr + " (WiFi)";
+            }
+            return "WiFi";
+
+        } else if (info.getType() == ConnectivityManager.TYPE_ETHERNET) {
+            try {
+                NetworkInterface eth0 = null;
+                Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+                while (interfaces.hasMoreElements()) {
+                    NetworkInterface networkInterface = interfaces.nextElement();
+                    if (networkInterface.getName().equals("eth0")) {
+                        eth0 = networkInterface;
+                    }
+                }
+                if (eth0 == null) {
+                    return "unknown (Ethernet)";
+                }
+
+                Enumeration<InetAddress> addresses = eth0.getInetAddresses();
+                while (addresses.hasMoreElements()) {
+                    InetAddress address = addresses.nextElement();
+                    //I found that this gave me the correct IPv4 address
+                    if (address.isSiteLocalAddress()) {
+                        return address.getHostAddress() + " (Ethernet)";
+                    }
+                }
+
+                return "unknown (Ethernet)";
+            } catch (SocketException e) {
+                return "unknown (Ethernet)";
+            }
+
+        } else {
+            return "unknown (" + info.getTypeName() + ")";
+        }
+    }
+
+    protected String loadCurrentServer()
+    {
+        Properties props = new Properties();
+        try {
+            FileInputStream in = new FileInputStream(configFilePath);
+            props.load(in);
+        } catch (Exception e) {
+            return null;
+        }
+
+        return props.getProperty("OUYA_SERVER_URL", null);
+    }
+
+    public void onClickExit(View view) {
+        this.finish();
+    }
+
+    public void onClickBackupCreate(View view) {
+        if (copyFile(configFilePath, configFileBackupPath)) {
+            showInfo("Backup created.");
+        }
+        loadStatus();
+    }
+
+    public void onClickBackupRestore(View view) {
+        if (copyFile(configFileBackupPath, configFilePath)) {
+            showInfo("Backup restored.");
+        }
+        loadStatus();
+    }
+
+    private boolean copyFile(String source, String target) {
+        try {
+            InputStream in = new FileInputStream(source);
+            OutputStream out = new FileOutputStream(target);
+            byte[] buf = new byte[4096];
+            int len;
+            while ((len = in.read(buf)) > 0) {
+                out.write(buf, 0, len);
+            }
+            in.close();
+            out.close();
+        } catch (IOException e) {
+            showError(e.getMessage());
+            return false;
+        }
+        return true;
+    }
+
+    public void onClickUseDefault(View view) {
+        File f = new File(configFilePath);
+        if (f.delete()) {
+            showInfo("Configuration file deleted");
+        } else {
+            showError("Could not delete configuration file");
+        }
+        loadStatus();
+    }
+
+    public void onClickUseLouyapi(View view) {
+        writeConfig("http://127.0.0.1:8080/", "http://127.0.0.1:8080/api/v1/status");
+        loadStatus();
+    }
+
+    public void onClickUseCweiske(View view) {
+        writeConfig("http://ouya.cweiske.de/", "http://ouya.cweiske.de/api/v1/status");
+        loadStatus();
+    }
+
+    private void writeConfig(String serverUrl, String statusUrl) {
+        String content = "OUYA_SERVER_URL=" + serverUrl + "\n"
+            + "OUYA_STATUS_SERVER_URL=" + statusUrl + "\n";
+        try {
+            FileOutputStream fos = new FileOutputStream(configFilePath);
+            fos.write(content.getBytes());
+            showInfo("Configuration written");
+        } catch (IOException e) {
+            showError(e.getMessage());
+        }
+    }
+
+    private void showInfo(String message) {
+        showError(message);
+    }
+
+    private void showError(String message) {
+        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
+    }
+
+    protected String formatIp(int ip) {
+        return String.format(
+            Locale.ENGLISH,
+            "%d.%d.%d.%d",
+            (ip & 0xff),
+            (ip >> 8 & 0xff),
+            (ip >> 16 & 0xff),
+            (ip >> 24 & 0xff));
     }
 
     /**
index 63f705186f384e5bf856fcba452455a85491cfcb..f8bcc9cf0ecab1940834f0ef6275f54647a602f0 100644 (file)
 <?xml version="1.0" encoding="utf-8"?>
-<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:isScrollContainer="false"
+    android:orientation="vertical">
 
     <TextView
-        android:text="Foo"
-        android:textColor="#FFEB3B"
-        android:textSize="60sp">
-    </TextView>
-</GridLayout>
\ No newline at end of file
+        android:id="@+id/textView"
+        style="@style/headline"
+        android:text="@string/status" />
+
+    <TableLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <TableRow
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+            <TextView
+                android:id="@+id/textView2"
+                style="@style/status_label"
+                android:text="@string/current_api_server" />
+
+            <TextView
+                android:id="@+id/statusCurrentServer"
+                style="@style/status_value"
+                android:text="@string/loading" />
+        </TableRow>
+
+        <TableRow
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+            <TextView
+                android:id="@+id/textView4"
+                style="@style/status_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/ip_address" />
+
+            <TextView
+                android:id="@+id/statusIpAddress"
+                style="@style/status_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/loading" />
+        </TableRow>
+
+        <TableRow
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+            <TextView
+                android:id="@+id/textView6"
+                style="@style/status_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/louyapi_port_number" />
+
+            <TextView
+                android:id="@+id/statusPortNumber"
+                style="@style/status_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/loading" />
+        </TableRow>
+
+    </TableLayout>
+
+    <Space
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1" />
+
+    <TextView
+        android:id="@+id/textView8"
+        style="@style/headline"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/configuration_management_sdcard_ouya_config_properties" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/buttonUseLouyapi"
+            android:layout_width="0px"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:onClick="onClickUseLouyapi"
+            android:text="@string/use_local_api"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/buttonUseCweiske"
+            android:layout_width="0px"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:onClick="onClickUseCweiske"
+            android:text="@string/use_ouya_cweiske_de_api"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/buttonUseDefault"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:onClick="onClickUseDefault"
+            android:text="@string/use_default"
+            android:textAllCaps="false" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Button
+            android:id="@+id/buttonBackupCreate"
+            android:layout_width="0px"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:onClick="onClickBackupCreate"
+            android:text="@string/create_configuration_backup"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/buttonBackupRestore"
+            android:layout_width="0px"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:onClick="onClickBackupRestore"
+            android:text="@string/restore_backup"
+            android:textAllCaps="false" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <Space
+            android:layout_width="0px"
+            android:layout_height="wrap_content"
+            android:layout_weight="1" />
+
+        <TextView
+            android:id="@+id/backupDate"
+            android:layout_width="0px"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:gravity="center" />
+
+    </LinearLayout>
+
+    <Space
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1" />
+
+    <Button
+        android:id="@+id/buttonExit"
+        android:layout_width="100dp"
+        android:layout_height="wrap_content"
+        android:onClick="onClickExit"
+        android:text="@string/exit"
+        android:textAllCaps="false" />
+
+</LinearLayout>
index 7ac6a6a67d26b57e430638928f6f89b850f04e13..68134b2ac6d0fda63d21bc85d57e90546b66ea21 100644 (file)
@@ -1,3 +1,15 @@
 <resources>
     <string name="app_name">louyapi</string>
+    <string name="exit">Exit</string>
+    <string name="configuration_management_sdcard_ouya_config_properties">Configuration management: /sdcard/ouya_config.properties</string>
+    <string name="loading">loading…</string>
+    <string name="louyapi_port_number">louyapi port number:</string>
+    <string name="ip_address">IP address:</string>
+    <string name="current_api_server">Current API server:</string>
+    <string name="status">Status</string>
+    <string name="use_local_api">Use local API</string>
+    <string name="use_ouya_cweiske_de_api">Use ouya.cweiske.de API</string>
+    <string name="use_default">Use default</string>
+    <string name="create_configuration_backup">Create configuration backup</string>
+    <string name="restore_backup">Restore backup</string>
 </resources>
\ No newline at end of file
diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml
new file mode 100644 (file)
index 0000000..11bd98c
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <style name="headline">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:textSize">22sp</item>
+    </style>
+
+    <style name="status_label">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginRight">20dp</item>
+    </style>
+
+    <style name="status_value">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+    </style>
+</resources>
\ No newline at end of file